Browse Source

Implement Array.group and Array.groupToMap (#1306)

Marko Lahma 2 years ago
parent
commit
c97bd04889

+ 0 - 1
Jint.Tests.Test262/Test262Harness.settings.json

@@ -5,7 +5,6 @@
   "Namespace": "Jint.Tests.Test262",
   "Parallel": true,
   "ExcludedFeatures": [
-    "array-grouping",
     "async-functions",
     "async-iteration",
     "Atomics",

+ 79 - 1
Jint/Native/Array/ArrayPrototype.cs

@@ -1,5 +1,6 @@
 using Jint.Collections;
 using Jint.Native.Iterator;
+using Jint.Native.Map;
 using Jint.Native.Number;
 using Jint.Native.Object;
 using Jint.Native.Symbol;
@@ -35,7 +36,7 @@ namespace Jint.Native.Array
         protected override void Initialize()
         {
             const PropertyFlag propertyFlags = PropertyFlag.Writable | PropertyFlag.Configurable;
-            var properties = new PropertyDictionary(34, checkExistingKeys: false)
+            var properties = new PropertyDictionary(36, checkExistingKeys: false)
             {
                 ["constructor"] = new PropertyDescriptor(_constructor, PropertyFlag.NonEnumerable),
                 ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToString, 0, PropertyFlag.Configurable), propertyFlags),
@@ -72,6 +73,8 @@ namespace Jint.Native.Array
                 ["flat"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "flat", Flat, 0, PropertyFlag.Configurable), propertyFlags),
                 ["flatMap"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "flatMap", FlatMap, 1, PropertyFlag.Configurable), propertyFlags),
                 ["at"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "at", At, 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),
             };
             SetProperties(properties);
 
@@ -101,6 +104,8 @@ namespace Jint.Native.Array
                     unscopables.SetDataProperty("includes", JsBoolean.True);
                     unscopables.SetDataProperty("keys", JsBoolean.True);
                     unscopables.SetDataProperty("values", JsBoolean.True);
+                    unscopables.SetDataProperty("group", JsBoolean.True);
+                    unscopables.SetDataProperty("groupToMap", JsBoolean.True);
 
                     return unscopables;
                 }, PropertyFlag.Configurable)
@@ -1462,6 +1467,79 @@ namespace Jint.Native.Array
             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(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, ArrayInstance> 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, ArrayInstance>();
+            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 ArrayInstance(_engine)
+                    {
+                        _length = new PropertyDescriptor(JsNumber.PositiveZero, PropertyFlag.OnlyWritable)
+                    };
+                }
+
+                list.SetIndexValue(list.GetLength(), kValue, updateLength: true);
+            }
+
+            _engine._jsValueArrayPool.ReturnArray(args);
+            return result;
+        }
+
         internal sealed class ArrayComparer : IComparer<JsValue>
         {
             /// <summary>

+ 0 - 12
Jint/Native/Function/FunctionInstance.cs

@@ -273,18 +273,6 @@ namespace Jint.Native.Function
             _homeObject = homeObject;
         }
 
-        /// <summary>
-        /// https://tc39.es/ecma262/#sec-ordinaryobjectcreate
-        /// </summary>
-        internal ObjectInstance OrdinaryObjectCreate(ObjectInstance proto)
-        {
-            var prototype = new ObjectInstance(_engine)
-            {
-                _prototype = proto
-            };
-            return prototype;
-        }
-
         /// <summary>
         /// https://tc39.es/ecma262/#sec-ordinarycallbindthis
         /// </summary>

+ 2 - 2
Jint/Native/Map/MapConstructor.cs

@@ -11,7 +11,7 @@ namespace Jint.Native.Map
 {
     public sealed class MapConstructor : FunctionInstance, IConstructor
     {
-        private static readonly JsString _functionName = new JsString("Map");
+        private static readonly JsString _functionName = new("Map");
 
         internal MapConstructor(
             Engine engine,
@@ -26,7 +26,7 @@ namespace Jint.Native.Map
             _prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
         }
 
-        public MapPrototype PrototypeObject { get; private set; }
+        public MapPrototype PrototypeObject { get; }
 
         protected override void Initialize()
         {

+ 12 - 0
Jint/Native/Object/ObjectInstance.cs

@@ -1240,6 +1240,18 @@ namespace Jint.Native.Object
             DefinePropertyOrThrow(p, newDesc);
         }
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-ordinaryobjectcreate
+        /// </summary>
+        internal ObjectInstance OrdinaryObjectCreate(ObjectInstance? proto)
+        {
+            var prototype = new ObjectInstance(_engine)
+            {
+                _prototype = proto
+            };
+            return prototype;
+        }
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal ICallable? GetMethod(JsValue property)
         {

+ 2 - 2
Jint/Native/Set/SetConstructor.cs

@@ -10,7 +10,7 @@ namespace Jint.Native.Set
 {
     public sealed class SetConstructor : FunctionInstance, IConstructor
     {
-        private static readonly JsString _functionName = new JsString("Set");
+        private static readonly JsString _functionName = new("Set");
 
         internal SetConstructor(
             Engine engine,
@@ -25,7 +25,7 @@ namespace Jint.Native.Set
             _prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
         }
 
-        public SetPrototype PrototypeObject { get; private set; }
+        public SetPrototype PrototypeObject { get; }
 
         protected override void Initialize()
         {

+ 1 - 0
README.md

@@ -108,6 +108,7 @@ The entire execution engine was rebuild with performance in mind, in many cases
 #### ECMAScript Stage 3 (no version yet)
 
 - ✔ Array find from last
+- ✔ Array.group and Array.groupToMap
 - ✔ ShadowRealm
 
 #### Other