Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix some prototype chain issues uncovered by compat-table #1334

Merged
merged 1 commit into from
Oct 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
257 changes: 134 additions & 123 deletions Jint.Tests/Runtime/ArrayTests.cs
Original file line number Diff line number Diff line change
@@ -1,82 +1,76 @@
using Jint.Native.Array;

namespace Jint.Tests.Runtime
{
public class ArrayTests
{
private readonly Engine _engine;

public ArrayTests()
{
_engine = new Engine()
.SetValue("log", new Action<object>(Console.WriteLine))
.SetValue("assert", new Action<bool>(Assert.True))
.SetValue("equal", new Action<object, object>(Assert.Equal));
}
namespace Jint.Tests.Runtime;

private void RunTest(string source)
{
_engine.Execute(source);
}
public class ArrayTests
{
private readonly Engine _engine;

public ArrayTests()
{
_engine = new Engine()
.SetValue("log", new Action<object>(Console.WriteLine))
.SetValue("assert", new Action<bool>(Assert.True))
.SetValue("equal", new Action<object, object>(Assert.Equal));
}

[Fact]
public void ArrayPrototypeToStringWithArray()
{
var result = _engine.Evaluate("Array.prototype.toString.call([1,2,3]);").AsString();
[Fact]
public void ArrayPrototypeToStringWithArray()
{
var result = _engine.Evaluate("Array.prototype.toString.call([1,2,3]);").AsString();

Assert.Equal("1,2,3", result);
}
Assert.Equal("1,2,3", result);
}

[Fact]
public void ArrayPrototypeToStringWithNumber()
{
var result = _engine.Evaluate("Array.prototype.toString.call(1);").AsString();
[Fact]
public void ArrayPrototypeToStringWithNumber()
{
var result = _engine.Evaluate("Array.prototype.toString.call(1);").AsString();

Assert.Equal("[object Number]", result);
}
Assert.Equal("[object Number]", result);
}

[Fact]
public void ArrayPrototypeToStringWithObject()
{
var result = _engine.Evaluate("Array.prototype.toString.call({});").AsString();
[Fact]
public void ArrayPrototypeToStringWithObject()
{
var result = _engine.Evaluate("Array.prototype.toString.call({});").AsString();

Assert.Equal("[object Object]", result);
}
Assert.Equal("[object Object]", result);
}

[Fact]
public void EmptyStringKey()
{
var result = _engine.Evaluate("var x=[];x[\"\"]=8;x[\"\"];").AsNumber();
[Fact]
public void EmptyStringKey()
{
var result = _engine.Evaluate("var x=[];x[\"\"]=8;x[\"\"];").AsNumber();

Assert.Equal(8, result);
}
Assert.Equal(8, result);
}

[Fact]
public void LargeArraySize()
{
const string code = @"
[Fact]
public void LargeArraySize()
{
const string code = @"
let arr = [];
for (let i = 0; i < 10000; i++) arr.push(i);
for (let i=0;i<10000;i++) arr.splice(0, 1);
";
var engine = new Engine();
engine.Execute(code);
}
var engine = new Engine();
engine.Execute(code);
}

[Fact]
public void ArrayLengthFromInitialState()
{
var engine = new Engine();
var array = new ArrayInstance(engine, 0);
var length = (int) array.Length;
Assert.Equal(0, length);
}
[Fact]
public void ArrayLengthFromInitialState()
{
var engine = new Engine();
var array = new ArrayInstance(engine, 0);
var length = (int) array.Length;
Assert.Equal(0, length);
}

[Fact]
public void ArraySortIsStable()
{
const string code = @"
[Fact]
public void ArraySortIsStable()
{
const string code = @"
var items = [
{ name: 'Edward', value: 0 },
{ name: 'Sharpe', value: 0 },
Expand Down Expand Up @@ -110,8 +104,8 @@ public void ArraySortIsStable()
assert(a[5].name == 'The');
";

_engine.Execute(code);
}
_engine.Execute(code);
}

#if !NETCOREAPP
// this test case only triggers on older full framework where the is no checks for infinite comparisons
Expand All @@ -129,87 +123,104 @@ public void ArraySortShouldObeyExecutionConstraints()
}
#endif

[Fact]
public void ExtendingArrayAndInstanceOf()
{
const string script = @"
[Fact]
public void ExtendingArrayAndInstanceOf()
{
const string script = @"
class MyArr extends Array {
constructor(...args) {
super(...args);
}
}";

_engine.Execute(script);
_engine.Evaluate("const a = new MyArr(1,2);");
Assert.True(_engine.Evaluate("a instanceof MyArr").AsBoolean());
}

[Fact]
public void IteratorShouldBeConvertibleToArray()
{
Assert.Equal("hello;again", _engine.Evaluate("Array.from(['hello', 'again'].values()).join(';')"));
Assert.Equal("hello;another", _engine.Evaluate("Array.from(new Map([['hello', 'world'], ['another', 'value']]).keys()).join(';')"));
}
_engine.Execute(script);
_engine.Evaluate("const a = new MyArr(1,2);");
Assert.True(_engine.Evaluate("a instanceof MyArr").AsBoolean());
}

[Fact]
public void ArrayFromShouldNotFlattenInputArray()
{
Assert.Equal("a;b", _engine.Evaluate("[...['a', 'b']].join(';')"));
Assert.Equal("0,a;1,b", _engine.Evaluate("[...['a', 'b'].entries()].join(';')"));
Assert.Equal("0,c;1,d", _engine.Evaluate("Array.from(['c', 'd'].entries()).join(';')"));
Assert.Equal("0,e;1,f", _engine.Evaluate("Array.from([[0, 'e'],[1, 'f']]).join(';')"));
}
[Fact]
public void IteratorShouldBeConvertibleToArray()
{
Assert.Equal("hello;again", _engine.Evaluate("Array.from(['hello', 'again'].values()).join(';')"));
Assert.Equal("hello;another", _engine.Evaluate("Array.from(new Map([['hello', 'world'], ['another', 'value']]).keys()).join(';')"));
}

[Fact]
public void ArrayEntriesShouldReturnKeyValuePairs()
{
Assert.Equal("0,hello,1,world", _engine.Evaluate("Array.from(['hello', 'world'].entries()).join()"));
Assert.Equal("0,hello;1,world", _engine.Evaluate("Array.from(['hello', 'world'].entries()).join(';')"));
Assert.Equal("0,;1,1;2,5", _engine.Evaluate("Array.from([,1,5,].entries()).join(';')"));
}
[Fact]
public void ArrayFromShouldNotFlattenInputArray()
{
Assert.Equal("a;b", _engine.Evaluate("[...['a', 'b']].join(';')"));
Assert.Equal("0,a;1,b", _engine.Evaluate("[...['a', 'b'].entries()].join(';')"));
Assert.Equal("0,c;1,d", _engine.Evaluate("Array.from(['c', 'd'].entries()).join(';')"));
Assert.Equal("0,e;1,f", _engine.Evaluate("Array.from([[0, 'e'],[1, 'f']]).join(';')"));
}

[Fact]
public void IteratorsShouldHaveIteratorSymbol()
{
_engine.Execute("assert(!!['hello'].values()[Symbol.iterator])");
_engine.Execute("assert(!!new Map([['hello', 'world']]).keys()[Symbol.iterator])");
}
[Fact]
public void ArrayEntriesShouldReturnKeyValuePairs()
{
Assert.Equal("0,hello,1,world", _engine.Evaluate("Array.from(['hello', 'world'].entries()).join()"));
Assert.Equal("0,hello;1,world", _engine.Evaluate("Array.from(['hello', 'world'].entries()).join(';')"));
Assert.Equal("0,;1,1;2,5", _engine.Evaluate("Array.from([,1,5,].entries()).join(';')"));
}

[Fact]
public void IteratorsShouldHaveIteratorSymbol()
{
_engine.Execute("assert(!!['hello'].values()[Symbol.iterator])");
_engine.Execute("assert(!!new Map([['hello', 'world']]).keys()[Symbol.iterator])");
}


[Fact]
public void ArraySortDoesNotCrashInDebugMode()
[Fact]
public void ArraySortDoesNotCrashInDebugMode()
{
var engine = new Engine(o =>
{
var engine = new Engine(o =>
{
o.DebugMode(true);
});
engine.SetValue("equal", new Action<object, object>(Assert.Equal));
o.DebugMode(true);
});
engine.SetValue("equal", new Action<object, object>(Assert.Equal));

const string code = @"
const string code = @"
var items = [5,2,4,1];
items.sort((a,b) => a - b);
equal('1,2,4,5', items.join());
";

engine.Execute(code);
}
engine.Execute(code);
}

[Fact]
public void ArrayConstructorFromHoles()
{
_engine.Evaluate("var a = Array(...[,,]);");
Assert.True(_engine.Evaluate("\"0\" in a").AsBoolean());
Assert.True(_engine.Evaluate("\"1\" in a").AsBoolean());
Assert.Equal("undefinedundefined", _engine.Evaluate("'' + a[0] + a[1]"));
}
[Fact]
public void ArrayConstructorFromHoles()
{
_engine.Evaluate("var a = Array(...[,,]);");
Assert.True(_engine.Evaluate("\"0\" in a").AsBoolean());
Assert.True(_engine.Evaluate("\"1\" in a").AsBoolean());
Assert.Equal("undefinedundefined", _engine.Evaluate("'' + a[0] + a[1]"));
}

[Fact]
public void ArrayIsSubclassable()
{
_engine.Evaluate("class C extends Array {}");
_engine.Evaluate("var c = new C();");
Assert.True(_engine.Evaluate("c.map(Boolean) instanceof C").AsBoolean());
}
[Fact]
public void ArrayIsSubclassable()
{
_engine.Evaluate("class C extends Array {}");
_engine.Evaluate("var c = new C();");
Assert.True(_engine.Evaluate("c.map(Boolean) instanceof C").AsBoolean());
}

[Fact]
public void HasProperIteratorPrototypeChain()
{
const string Script = @"
// Iterator instance
var iterator = [][Symbol.iterator]();
// %ArrayIteratorPrototype%
var proto1 = Object.getPrototypeOf(iterator);
// %IteratorPrototype%
var proto2 = Object.getPrototypeOf(proto1);";

var engine = new Engine();
engine.Execute(Script);
Assert.True(engine.Evaluate("proto2.hasOwnProperty(Symbol.iterator)").AsBoolean());
Assert.True(engine.Evaluate("!proto1.hasOwnProperty(Symbol.iterator)").AsBoolean());
Assert.True(engine.Evaluate("!iterator.hasOwnProperty(Symbol.iterator)").AsBoolean());
Assert.True(engine.Evaluate("iterator[Symbol.iterator]() === iterator").AsBoolean());
}
}
49 changes: 41 additions & 8 deletions Jint.Tests/Runtime/MapTests.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,47 @@
using Jint.Runtime;

namespace Jint.Tests.Runtime
namespace Jint.Tests.Runtime;

public class MapTests
{
public class MapTests
[Fact]
public void ShouldThrowWhenCalledWithoutNew()
{
[Fact]
public void ShouldThrowWhenCalledWithoutNew()
{
var e = Assert.Throws<JavaScriptException>(() => new Engine().Execute("const m = new Map(); Map.call(m,[]);"));
Assert.Equal("Constructor Map requires 'new'", e.Message);
}
var e = Assert.Throws<JavaScriptException>(() => new Engine().Execute("const m = new Map(); Map.call(m,[]);"));
Assert.Equal("Constructor Map requires 'new'", e.Message);
}

[Fact]
public void NegativeZeroKeyConvertsToPositiveZero()
{
const string Script = @"
var map = new Map();
map.set(-0, ""foo"");
var k;
map.forEach(function (value, key) {
k = 1 / key;
});
return k === Infinity && map.get(+0) === ""foo"";";

Assert.True(new Engine().Evaluate(Script).AsBoolean());
}

[Fact]
public void HasProperIteratorPrototypeChain()
{
const string Script = @"
// Iterator instance
var iterator = new Map()[Symbol.iterator]();
// %MapIteratorPrototype%
var proto1 = Object.getPrototypeOf(iterator);
// %IteratorPrototype%
var proto2 = Object.getPrototypeOf(proto1);";

var engine = new Engine();
engine.Execute(Script);
Assert.True(engine.Evaluate("proto2.hasOwnProperty(Symbol.iterator)").AsBoolean());
Assert.True(engine.Evaluate("!proto1.hasOwnProperty(Symbol.iterator)").AsBoolean());
Assert.True(engine.Evaluate("!iterator.hasOwnProperty(Symbol.iterator)").AsBoolean());
Assert.True(engine.Evaluate("iterator[Symbol.iterator]() === iterator").AsBoolean());
}
}