diff --git a/Jint.Tests.Test262/Test262Harness.settings.json b/Jint.Tests.Test262/Test262Harness.settings.json
index 767943d29b..63d84c6d58 100644
--- a/Jint.Tests.Test262/Test262Harness.settings.json
+++ b/Jint.Tests.Test262/Test262Harness.settings.json
@@ -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",
diff --git a/Jint/JsValueExtensions.cs b/Jint/JsValueExtensions.cs
index 76d7abdb51..d84007e989 100644
--- a/Jint/JsValueExtensions.cs
+++ b/Jint/JsValueExtensions.cs
@@ -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;
+ }
}
}
diff --git a/Jint/Native/Array/ArrayPrototype.cs b/Jint/Native/Array/ArrayPrototype.cs
index 7c0ce6ed90..299223088a 100644
--- a/Jint/Native/Array/ArrayPrototype.cs
+++ b/Jint/Native/Array/ArrayPrototype.cs
@@ -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;
@@ -37,7 +36,7 @@ internal ArrayPrototype(
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),
@@ -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),
@@ -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);
@@ -1655,76 +1648,6 @@ public JsValue Pop(JsValue thisObject, JsValue[] arguments)
return element;
}
- ///
- /// https://tc39.es/proposal-array-grouping/#sec-array.prototype.group
- ///
- 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;
- }
-
- ///
- /// https://tc39.es/proposal-array-grouping/#sec-array.prototype.grouptomap
- ///
- 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 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();
- 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);
diff --git a/Jint/Native/GroupByHelper.cs b/Jint/Native/GroupByHelper.cs
new file mode 100644
index 0000000000..252e82132b
--- /dev/null
+++ b/Jint/Native/GroupByHelper.cs
@@ -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 GroupBy(
+ Engine engine,
+ Realm realm,
+ JsValue items,
+ JsValue callbackfn,
+ bool mapMode)
+ {
+ var callable = callbackfn.GetCallable(realm);
+ var groups = new Dictionary();
+ 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 _result;
+ private readonly ICallable _callable;
+ private readonly bool _mapMode;
+ private ulong _k;
+ private readonly JsValue[] _callArgs = new JsValue[2];
+
+ public GroupByProtocol(
+ Engine engine,
+ Dictionary 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++;
+ }
+ }
+}
diff --git a/Jint/Native/Map/MapConstructor.cs b/Jint/Native/Map/MapConstructor.cs
index 648379be6b..bc26e46aef 100644
--- a/Jint/Native/Map/MapConstructor.cs
+++ b/Jint/Native/Map/MapConstructor.cs
@@ -30,6 +30,13 @@ internal MapConstructor(
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)
@@ -67,4 +74,22 @@ public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
return map;
}
+
+
+ ///
+ /// https://tc39.es/proposal-array-grouping/#sec-map.groupby
+ ///
+ 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;
+ }
}
diff --git a/Jint/Native/Object/ObjectConstructor.cs b/Jint/Native/Object/ObjectConstructor.cs
index 485bad39cc..0b9a17513c 100644
--- a/Jint/Native/Object/ObjectConstructor.cs
+++ b/Jint/Native/Object/ObjectConstructor.cs
@@ -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);
}
@@ -525,6 +526,24 @@ private JsValue Values(JsValue thisObject, JsValue[] arguments)
return o.EnumerableOwnPropertyNames(EnumerableOwnPropertyNamesKind.Value);
}
+ ///
+ /// https://tc39.es/proposal-array-grouping/#sec-object.groupby
+ ///
+ 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();
diff --git a/Jint/Native/Object/ObjectInstance.cs b/Jint/Native/Object/ObjectInstance.cs
index 8db1094998..84d25dec63 100644
--- a/Jint/Native/Object/ObjectInstance.cs
+++ b/Jint/Native/Object/ObjectInstance.cs
@@ -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
{
diff --git a/NuGet.config b/NuGet.config
index 32b42fc44c..c9af0eb814 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -3,7 +3,9 @@
+
diff --git a/README.md b/README.md
index 55e8215b80..edaaf5a8df 100644
--- a/README.md
+++ b/README.md
@@ -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