Skip to content

Commit

Permalink
Update test262 test suite and fix issues (#1589)
Browse files Browse the repository at this point in the history
* Array.group and Array.groupToMap are now Object.groupBy and Map.groupBy
* add new feature flags to ignore until implementation
  • Loading branch information
lahma committed Jul 27, 2023
1 parent 43b6356 commit 4057029
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 115 deletions.
4 changes: 3 additions & 1 deletion Jint.Tests.Test262/Test262Harness.settings.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
{
"SuiteGitSha": "9704d7f22f6342d6c4753ab9a8d62d6725de8c4e",
"SuiteGitSha": "9437cab774ab2f22c5cb971b11b8512eca705721",
//"SuiteDirectory": "//mnt/c/work/test262",
"TargetPath": "./Generated",
"Namespace": "Jint.Tests.Test262",
"Parallel": true,
"ExcludedFeatures": [
"Array.fromAsync",
"arraybuffer-transfer",
"async-iteration",
"Atomics",
"decorators",
"generators",
"import-assertions",
"iterator-helpers",
"regexp-duplicate-named-groups",
"regexp-lookbehind",
"regexp-unicode-property-escapes",
Expand Down
11 changes: 11 additions & 0 deletions Jint/JsValueExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -582,5 +582,16 @@ internal static BigInteger ToBigInteger(this JsValue value, Engine engine)
return default;
}
}

internal static ICallable GetCallable(this JsValue source, Realm realm)
{
if (source is ICallable callable)
{
return callable;
}

ExceptionHelper.ThrowTypeError(realm, "Argument must be callable");
return null;
}
}
}
79 changes: 1 addition & 78 deletions Jint/Native/Array/ArrayPrototype.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.Linq;
using Jint.Collections;
using Jint.Native.Iterator;
using Jint.Native.Map;
using Jint.Native.Number;
using Jint.Native.Object;
using Jint.Native.Symbol;
Expand Down Expand Up @@ -37,7 +36,7 @@ public sealed class ArrayPrototype : ArrayInstance
protected override void Initialize()
{
const PropertyFlag PropertyFlags = PropertyFlag.Writable | PropertyFlag.Configurable;
var properties = new PropertyDictionary(40, checkExistingKeys: false)
var properties = new PropertyDictionary(38, checkExistingKeys: false)
{
["constructor"] = new PropertyDescriptor(_constructor, PropertyFlag.NonEnumerable),

Expand All @@ -55,8 +54,6 @@ protected override void Initialize()
["flat"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "flat", Flat, 0, PropertyFlag.Configurable), PropertyFlags),
["flatMap"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "flatMap", FlatMap, 1, PropertyFlag.Configurable), PropertyFlags),
["forEach"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "forEach", ForEach, 1, PropertyFlag.Configurable), PropertyFlags),
["group"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "group", Group, 1, PropertyFlag.Configurable), PropertyFlags),
["groupToMap"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "groupToMap", GroupToMap, 1, PropertyFlag.Configurable), PropertyFlags),
["includes"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "includes", Includes, 1, PropertyFlag.Configurable), PropertyFlags),
["indexOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "indexOf", IndexOf, 1, PropertyFlag.Configurable), PropertyFlags),
["join"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "join", Join, 1, PropertyFlag.Configurable), PropertyFlags),
Expand Down Expand Up @@ -105,10 +102,6 @@ protected override void Initialize()
unscopables.FastSetDataProperty("findLastIndex", JsBoolean.True);
unscopables.FastSetDataProperty("flat", JsBoolean.True);
unscopables.FastSetDataProperty("flatMap", JsBoolean.True);
unscopables.FastSetDataProperty("group", JsBoolean.True);
unscopables.FastSetDataProperty("groupBy", JsBoolean.True);
unscopables.FastSetDataProperty("groupByToMap", JsBoolean.True);
unscopables.FastSetDataProperty("groupToMap", JsBoolean.True);
unscopables.FastSetDataProperty("includes", JsBoolean.True);
unscopables.FastSetDataProperty("keys", JsBoolean.True);
unscopables.FastSetDataProperty("toReversed", JsBoolean.True);
Expand Down Expand Up @@ -1655,76 +1648,6 @@ public JsValue Pop(JsValue thisObject, JsValue[] arguments)
return element;
}

/// <summary>
/// https://tc39.es/proposal-array-grouping/#sec-array.prototype.group
/// </summary>
private JsValue Group(JsValue thisObject, JsValue[] arguments)
{
var grouping = BuildArrayGrouping(thisObject, arguments, mapMode: false);

var obj = OrdinaryObjectCreate(_engine, null);
foreach (var pair in grouping)
{
obj.FastSetProperty(pair.Key, new PropertyDescriptor(pair.Value, PropertyFlag.ConfigurableEnumerableWritable));
}

return obj;
}

/// <summary>
/// https://tc39.es/proposal-array-grouping/#sec-array.prototype.grouptomap
/// </summary>
private JsValue GroupToMap(JsValue thisObject, JsValue[] arguments)
{
var grouping = BuildArrayGrouping(thisObject, arguments, mapMode: true);
var map = (MapInstance) Construct(_realm.Intrinsics.Map);
foreach (var pair in grouping)
{
map.MapSet(pair.Key, pair.Value);
}

return map;
}

private Dictionary<JsValue, JsArray> BuildArrayGrouping(JsValue thisObject, JsValue[] arguments, bool mapMode)
{
var o = ArrayOperations.For(_realm, thisObject);
var len = o.GetLongLength();
var callbackfn = arguments.At(0);
var callable = GetCallable(callbackfn);
var thisArg = arguments.At(1);

var result = new Dictionary<JsValue, JsArray>();
var args = _engine._jsValueArrayPool.RentArray(3);
args[2] = o.Target;
for (uint k = 0; k < len; k++)
{
var kValue = o.Get(k);
args[0] = kValue;
args[1] = k;

var value = callable.Call(thisArg, args);
JsValue key;
if (mapMode)
{
key = (value as JsNumber)?.IsNegativeZero() == true ? JsNumber.PositiveZero : value;
}
else
{
key = TypeConverter.ToPropertyKey(value);
}
if (!result.TryGetValue(key, out var list))
{
result[key] = list = new JsArray(_engine);
}

list.SetIndexValue(list.GetLength(), kValue, updateLength: true);
}

_engine._jsValueArrayPool.ReturnArray(args);
return result;
}

private object[] CreateBackingArray(ulong length)
{
ValidateArrayLength(length);
Expand Down
75 changes: 75 additions & 0 deletions Jint/Native/GroupByHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using Jint.Native.Array;
using Jint.Native.Iterator;
using Jint.Runtime;

namespace Jint.Native;

internal static class GroupByHelper
{
internal static Dictionary<JsValue, JsArray> GroupBy(
Engine engine,
Realm realm,
JsValue items,
JsValue callbackfn,
bool mapMode)
{
var callable = callbackfn.GetCallable(realm);
var groups = new Dictionary<JsValue, JsArray>();
var iteratorRecord = items.GetIterator(realm);
new GroupByProtocol(engine, groups, iteratorRecord, callable, mapMode).Execute();
return groups;
}

private sealed class GroupByProtocol : IteratorProtocol
{
private readonly Engine _engine;
private readonly Dictionary<JsValue, JsArray> _result;
private readonly ICallable _callable;
private readonly bool _mapMode;
private ulong _k;
private readonly JsValue[] _callArgs = new JsValue[2];

public GroupByProtocol(
Engine engine,
Dictionary<JsValue, JsArray> result,
IteratorInstance iterator,
ICallable callable,
bool mapMode) : base(engine, iterator, 0)
{
_engine = engine;
_result = result;
_callable = callable;
_mapMode = mapMode;
}

protected override void ProcessItem(JsValue[] args, JsValue currentValue)
{
if (_k >= ArrayOperations.MaxArrayLength)
{
ExceptionHelper.ThrowTypeError(_engine.Realm);
}

_callArgs[0] = currentValue;
_callArgs[1] = _k;

var value = _callable.Call(JsValue.Undefined, _callArgs);
JsValue key;
if (_mapMode)
{
key = (value as JsNumber)?.IsNegativeZero() == true ? JsNumber.PositiveZero : value;
}
else
{
key = TypeConverter.ToPropertyKey(value);
}

if (!_result.TryGetValue(key, out var list))
{
_result[key] = list = new JsArray(_engine);
}

list.Push(currentValue);
_k++;
}
}
}
25 changes: 25 additions & 0 deletions Jint/Native/Map/MapConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ internal sealed class MapConstructor : Constructor

protected override void Initialize()
{
const PropertyFlag PropertyFlags = PropertyFlag.Writable | PropertyFlag.Configurable;
var properties = new PropertyDictionary(1, checkExistingKeys: false)
{
["groupBy"] = new(new ClrFunctionInstance(Engine, "groupBy", GroupBy, 2, PropertyFlag.Configurable), PropertyFlags),
};
SetProperties(properties);

var symbols = new SymbolDictionary(1)
{
[GlobalSymbolRegistry.Species] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(_engine, "get [Symbol.species]", Species, 0, PropertyFlag.Configurable), set: Undefined, PropertyFlag.Configurable)
Expand Down Expand Up @@ -67,4 +74,22 @@ public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)

return map;
}


/// <summary>
/// https://tc39.es/proposal-array-grouping/#sec-map.groupby
/// </summary>
private JsValue GroupBy(JsValue thisObject, JsValue[] arguments)
{
var items = arguments.At(0);
var callbackfn = arguments.At(1);
var grouping = GroupByHelper.GroupBy(_engine, _realm, items, callbackfn, mapMode: true);
var map = (MapInstance) Construct(_realm.Intrinsics.Map);
foreach (var pair in grouping)
{
map.MapSet(pair.Key, pair.Value);
}

return map;
}
}
69 changes: 44 additions & 25 deletions Jint/Native/Object/ObjectConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,32 +26,33 @@ protected override void Initialize()
{
_prototype = _realm.Intrinsics.Function.PrototypeObject;

const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
const PropertyFlag lengthFlags = PropertyFlag.Configurable;
var properties = new PropertyDictionary(15, checkExistingKeys: false)
const PropertyFlag PropertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
const PropertyFlag LengthFlags = PropertyFlag.Configurable;
var properties = new PropertyDictionary(16, checkExistingKeys: false)
{
["assign"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "assign", Assign, 2, lengthFlags), propertyFlags),
["entries"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "entries", Entries, 1, lengthFlags), propertyFlags),
["fromEntries"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "fromEntries", FromEntries, 1, lengthFlags), propertyFlags),
["getPrototypeOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getPrototypeOf", GetPrototypeOf, 1), propertyFlags),
["getOwnPropertyDescriptor"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertyDescriptor", GetOwnPropertyDescriptor, 2, lengthFlags), propertyFlags),
["getOwnPropertyDescriptors"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertyDescriptors", GetOwnPropertyDescriptors, 1, lengthFlags), propertyFlags),
["getOwnPropertyNames"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertyNames", GetOwnPropertyNames, 1), propertyFlags),
["getOwnPropertySymbols"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertySymbols", GetOwnPropertySymbols, 1, lengthFlags), propertyFlags),
["create"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "create", Create, 2), propertyFlags),
["defineProperty"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "defineProperty", DefineProperty, 3), propertyFlags),
["defineProperties"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "defineProperties", DefineProperties, 2), propertyFlags),
["is"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "is", Is, 2, lengthFlags), propertyFlags),
["seal"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "seal", Seal, 1, lengthFlags), propertyFlags),
["freeze"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "freeze", Freeze, 1), propertyFlags),
["preventExtensions"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "preventExtensions", PreventExtensions, 1), propertyFlags),
["isSealed"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "isSealed", IsSealed, 1), propertyFlags),
["isFrozen"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "isFrozen", IsFrozen, 1), propertyFlags),
["isExtensible"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "isExtensible", IsExtensible, 1), propertyFlags),
["keys"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "keys", Keys, 1, lengthFlags), propertyFlags),
["values"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "values", Values, 1, lengthFlags), propertyFlags),
["setPrototypeOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setPrototypeOf", SetPrototypeOf, 2, lengthFlags), propertyFlags),
["hasOwn"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "hasOwn", HasOwn, 2, lengthFlags), propertyFlags),
["assign"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "assign", Assign, 2, LengthFlags), PropertyFlags),
["entries"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "entries", Entries, 1, LengthFlags), PropertyFlags),
["fromEntries"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "fromEntries", FromEntries, 1, LengthFlags), PropertyFlags),
["getPrototypeOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getPrototypeOf", GetPrototypeOf, 1), PropertyFlags),
["getOwnPropertyDescriptor"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertyDescriptor", GetOwnPropertyDescriptor, 2, LengthFlags), PropertyFlags),
["getOwnPropertyDescriptors"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertyDescriptors", GetOwnPropertyDescriptors, 1, LengthFlags), PropertyFlags),
["getOwnPropertyNames"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertyNames", GetOwnPropertyNames, 1), PropertyFlags),
["getOwnPropertySymbols"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertySymbols", GetOwnPropertySymbols, 1, LengthFlags), PropertyFlags),
["groupBy"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "groupBy", GroupBy, 2, PropertyFlag.Configurable), PropertyFlags),
["create"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "create", Create, 2), PropertyFlags),
["defineProperty"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "defineProperty", DefineProperty, 3), PropertyFlags),
["defineProperties"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "defineProperties", DefineProperties, 2), PropertyFlags),
["is"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "is", Is, 2, LengthFlags), PropertyFlags),
["seal"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "seal", Seal, 1, LengthFlags), PropertyFlags),
["freeze"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "freeze", Freeze, 1), PropertyFlags),
["preventExtensions"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "preventExtensions", PreventExtensions, 1), PropertyFlags),
["isSealed"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "isSealed", IsSealed, 1), PropertyFlags),
["isFrozen"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "isFrozen", IsFrozen, 1), PropertyFlags),
["isExtensible"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "isExtensible", IsExtensible, 1), PropertyFlags),
["keys"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "keys", Keys, 1, LengthFlags), PropertyFlags),
["values"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "values", Values, 1, LengthFlags), PropertyFlags),
["setPrototypeOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setPrototypeOf", SetPrototypeOf, 2, LengthFlags), PropertyFlags),
["hasOwn"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "hasOwn", HasOwn, 2, LengthFlags), PropertyFlags),
};
SetProperties(properties);
}
Expand Down Expand Up @@ -525,6 +526,24 @@ private JsValue Values(JsValue thisObject, JsValue[] arguments)
return o.EnumerableOwnPropertyNames(EnumerableOwnPropertyNamesKind.Value);
}

/// <summary>
/// https://tc39.es/proposal-array-grouping/#sec-object.groupby
/// </summary>
private JsValue GroupBy(JsValue thisObject, JsValue[] arguments)
{
var items = arguments.At(0);
var callbackfn = arguments.At(1);
var grouping = GroupByHelper.GroupBy(_engine, _realm, items, callbackfn, mapMode: false);

var obj = OrdinaryObjectCreate(_engine, null);
foreach (var pair in grouping)
{
obj.FastSetProperty(pair.Key, new PropertyDescriptor(pair.Value, PropertyFlag.ConfigurableEnumerableWritable));
}

return obj;
}

private sealed class CreateDataPropertyOnObject : ICallable
{
internal static readonly CreateDataPropertyOnObject Instance = new();
Expand Down
11 changes: 1 addition & 10 deletions Jint/Native/Object/ObjectInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1173,16 +1173,7 @@ bool TryGetValue(ulong idx, out JsValue jsValue)
return false;
}

internal ICallable GetCallable(JsValue source)
{
if (source is ICallable callable)
{
return callable;
}

ExceptionHelper.ThrowTypeError(_engine.Realm, "Argument must be callable");
return null;
}
internal ICallable GetCallable(JsValue source) => source.GetCallable(_engine.Realm);

internal bool IsConcatSpreadable
{
Expand Down
2 changes: 2 additions & 0 deletions NuGet.config
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
<packageSources>
<clear />
<add key="NuGet" value="https://api.nuget.org/v3/index.json" />
<!--
<add key="Esprima" value="https://www.myget.org/F/esprimadotnet/api/v3/index.json" />
-->
</packageSources>
<disabledPackageSources />
</configuration>
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ Following features are supported in version 3.x.

#### ECMAScript Stage 3 (no version yet)

- ✔ Array.group and Array.groupToMap
- ✔ Array Grouping - `Object.groupBy` and `Map.groupBy`
- ✔ ShadowRealm

#### Other
Expand Down

0 comments on commit 4057029

Please sign in to comment.