Explorar o código

Implement change Array by copy (#1366)

Marko Lahma %!s(int64=2) %!d(string=hai) anos
pai
achega
486373eb0b

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

@@ -7,7 +7,6 @@
   "ExcludedFeatures": [
     "async-iteration",
     "Atomics",
-    "change-array-by-copy",
     "class-fields-private",
     "class-fields-public",
     "class-methods-private",

+ 18 - 18
Jint/Native/Array/ArrayInstance.cs

@@ -27,7 +27,7 @@ namespace Jint.Native.Array
             _dense = System.Array.Empty<object?>();
         }
 
-        private protected ArrayInstance(Engine engine, uint capacity = 0, uint length = 0) : base(engine)
+        private protected ArrayInstance(Engine engine, uint capacity = 0, uint length = 0) : base(engine, type: InternalTypes.Object | InternalTypes.Array)
         {
             _prototype = engine.Realm.Intrinsics.Array.PrototypeObject;
 
@@ -48,30 +48,30 @@ namespace Jint.Native.Array
             _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
         }
 
-        private protected ArrayInstance(Engine engine, JsValue[] items) : base(engine)
+        private protected ArrayInstance(Engine engine, JsValue[] items) : base(engine, type: InternalTypes.Object | InternalTypes.Array)
         {
-            _prototype = engine.Realm.Intrinsics.Array.PrototypeObject;
-            _isObjectArray = false;
+            Initialize(engine, items);
+        }
 
-            int length;
-            if (items == null || items.Length == 0)
-            {
-                _dense = System.Array.Empty<object>();
-                length = 0;
-            }
-            else
-            {
-                _dense = items;
-                length = items.Length;
-            }
+        private protected ArrayInstance(Engine engine, PropertyDescriptor[] items) : base(engine, type: InternalTypes.Object | InternalTypes.Array)
+        {
+            Initialize(engine, items);
+        }
 
-            _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
+        private protected ArrayInstance(Engine engine, object[] items) : base(engine, type: InternalTypes.Object | InternalTypes.Array)
+        {
+            Initialize(engine, items);
         }
 
-        private protected ArrayInstance(Engine engine, PropertyDescriptor[] items) : base(engine)
+        private void Initialize<T>(Engine engine, T[] items) where T : class
         {
+            if (items.Length > engine.Options.Constraints.MaxArraySize)
+            {
+                ThrowMaximumArraySizeReachedException(engine, (uint) items.Length);
+            }
+
             _prototype = engine.Realm.Intrinsics.Array.PrototypeObject;
-            _isObjectArray = false;
+            _isObjectArray = typeof(T) == typeof(object);
 
             int length;
             if (items == null || items.Length == 0)

+ 14 - 16
Jint/Native/Array/ArrayOperations.cs

@@ -47,19 +47,22 @@ namespace Jint.Native.Array
 
         public abstract JsValue Get(ulong index);
 
-        public virtual JsValue[] GetAll(Types elementTypes)
+        public virtual JsValue[] GetAll(
+            Types elementTypes = Types.Undefined | Types.Null | Types.Boolean | Types.String | Types.Symbol | Types.Number | Types.Object,
+            bool skipHoles = false)
         {
+            uint writeIndex = 0;
             var n = (int) GetLength();
             var jsValues = new JsValue[n];
             for (uint i = 0; i < (uint) jsValues.Length; i++)
             {
-                var jsValue = Get(i);
+                var jsValue = skipHoles && !HasProperty(i) ? JsValue.Undefined : Get(i);
                 if ((jsValue.Type & elementTypes) == 0)
                 {
                     ExceptionHelper.ThrowTypeErrorNoEngine("invalid type");
                 }
 
-                jsValues[i] = jsValue;
+                jsValues[writeIndex++] = jsValue;
             }
 
             return jsValues;
@@ -75,7 +78,7 @@ namespace Jint.Native.Array
 
         public abstract void DeletePropertyOrThrow(ulong index);
 
-        internal ArrayLikeIterator GetEnumerator() => new ArrayLikeIterator(this);
+        public ArrayLikeIterator GetEnumerator() => new ArrayLikeIterator(this);
 
         IEnumerator<JsValue> IEnumerable<JsValue>.GetEnumerator() => new ArrayLikeIterator(this);
 
@@ -100,14 +103,9 @@ namespace Jint.Native.Array
             {
                 get
                 {
-                    if (!_initialized)
-                    {
-                        return JsValue.Undefined;
-                    }
-                    else
-                    {
-                        return _obj.TryGetValue(_current, out var temp) ? temp : JsValue.Undefined;
-                    }
+                    return !_initialized
+                        ? JsValue.Undefined
+                        : _obj.TryGetValue(_current, out var temp) ? temp : JsValue.Undefined;
                 }
             }
 
@@ -232,7 +230,7 @@ namespace Jint.Native.Array
 
             public override JsValue Get(ulong index) => _target.Get((uint) index);
 
-            public override JsValue[] GetAll(Types elementTypes)
+            public override JsValue[] GetAll(Types elementTypes, bool skipHoles = false)
             {
                 var n = _target.GetLength();
 
@@ -242,6 +240,7 @@ namespace Jint.Native.Array
                 }
 
                 // optimized
+                uint writeIndex = 0;
                 var jsValues = new JsValue[n];
                 for (uint i = 0; i < (uint) jsValues.Length; i++)
                 {
@@ -255,13 +254,12 @@ namespace Jint.Native.Array
                         prop = _target.UnwrapJsValue((PropertyDescriptor) prop);
                     }
 
-                    var jsValue = (JsValue) prop;
-                    if ((jsValue.Type & elementTypes) == 0)
+                    if (prop is JsValue jsValue && (jsValue.Type & elementTypes) == 0)
                     {
                         ExceptionHelper.ThrowTypeErrorNoEngine("invalid type");
                     }
 
-                    jsValues[i] = jsValue;
+                    jsValues[writeIndex++] = (JsValue?) prop ?? JsValue.Undefined;
                 }
 
                 return jsValues;

+ 271 - 76
Jint/Native/Array/ArrayPrototype.cs

@@ -35,53 +35,58 @@ namespace Jint.Native.Array
 
         protected override void Initialize()
         {
-            const PropertyFlag propertyFlags = PropertyFlag.Writable | PropertyFlag.Configurable;
-            var properties = new PropertyDictionary(36, checkExistingKeys: false)
+            const PropertyFlag PropertyFlags = PropertyFlag.Writable | PropertyFlag.Configurable;
+            var properties = new PropertyDictionary(40, checkExistingKeys: false)
             {
                 ["constructor"] = new PropertyDescriptor(_constructor, PropertyFlag.NonEnumerable),
-                ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToString, 0, PropertyFlag.Configurable), propertyFlags),
-                ["toLocaleString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toLocaleString", ToLocaleString, 0, PropertyFlag.Configurable), propertyFlags),
-                ["concat"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "concat", Concat, 1, PropertyFlag.Configurable), propertyFlags),
-                ["copyWithin"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "copyWithin", CopyWithin, 2, PropertyFlag.Configurable), propertyFlags),
-                ["entries"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "entries", Entries, 0, PropertyFlag.Configurable), propertyFlags),
-                ["fill"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "fill", Fill, 1, PropertyFlag.Configurable), propertyFlags),
-                ["join"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "join", Join, 1, PropertyFlag.Configurable), propertyFlags),
-                ["pop"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "pop", Pop, 0, PropertyFlag.Configurable), propertyFlags),
-                ["push"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "push", Push, 1, PropertyFlag.Configurable), propertyFlags),
-                ["reverse"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "reverse", Reverse, 0, PropertyFlag.Configurable), propertyFlags),
-                ["shift"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "shift", Shift, 0, PropertyFlag.Configurable), propertyFlags),
-                ["slice"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "slice", Slice, 2, PropertyFlag.Configurable), propertyFlags),
-                ["sort"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "sort", Sort, 1, PropertyFlag.Configurable), propertyFlags),
-                ["splice"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "splice", Splice, 2, PropertyFlag.Configurable), propertyFlags),
-                ["unshift"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "unshift", Unshift, 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),
-                ["lastIndexOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "lastIndexOf", LastIndexOf, 1, PropertyFlag.Configurable), propertyFlags),
-                ["every"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "every", Every, 1, PropertyFlag.Configurable), propertyFlags),
-                ["some"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "some", Some, 1, PropertyFlag.Configurable), propertyFlags),
-                ["forEach"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "forEach", ForEach, 1, PropertyFlag.Configurable), propertyFlags),
-                ["map"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "map", Map, 1, PropertyFlag.Configurable), propertyFlags),
-                ["filter"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "filter", Filter, 1, PropertyFlag.Configurable), propertyFlags),
-                ["reduce"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "reduce", Reduce, 1, PropertyFlag.Configurable), propertyFlags),
-                ["reduceRight"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "reduceRight", ReduceRight, 1, PropertyFlag.Configurable), propertyFlags),
-                ["find"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "find", Find, 1, PropertyFlag.Configurable), propertyFlags),
-                ["findIndex"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "findIndex", FindIndex, 1, PropertyFlag.Configurable), propertyFlags),
-                ["findLast"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "findLast", FindLast, 1, PropertyFlag.Configurable), propertyFlags),
-                ["findLastIndex"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "findLastIndex", FindLastIndex, 1, PropertyFlag.Configurable), propertyFlags),
-                ["keys"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "keys", Keys, 0, PropertyFlag.Configurable), propertyFlags),
-                ["values"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "values", Values, 0, PropertyFlag.Configurable), propertyFlags),
-                ["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),
+
+                ["at"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "at", At, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["concat"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "concat", Concat, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["copyWithin"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "copyWithin", CopyWithin, 2, PropertyFlag.Configurable), PropertyFlags),
+                ["entries"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "entries", Entries, 0, PropertyFlag.Configurable), PropertyFlags),
+                ["every"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "every", Every, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["fill"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "fill", Fill, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["filter"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "filter", Filter, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["find"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "find", Find, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["findIndex"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "findIndex", FindIndex, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["findLast"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "findLast", FindLast, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["findLastIndex"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "findLastIndex", FindLastIndex, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["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),
+                ["keys"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "keys", Keys, 0, PropertyFlag.Configurable), PropertyFlags),
+                ["lastIndexOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "lastIndexOf", LastIndexOf, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["map"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "map", Map, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["pop"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "pop", Pop, 0, PropertyFlag.Configurable), PropertyFlags),
+                ["push"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "push", Push, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["reduce"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "reduce", Reduce, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["reduceRight"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "reduceRight", ReduceRight, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["reverse"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "reverse", Reverse, 0, PropertyFlag.Configurable), PropertyFlags),
+                ["shift"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "shift", Shift, 0, PropertyFlag.Configurable), PropertyFlags),
+                ["slice"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "slice", Slice, 2, PropertyFlag.Configurable), PropertyFlags),
+                ["some"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "some", Some, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["sort"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "sort", Sort, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["splice"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "splice", Splice, 2, PropertyFlag.Configurable), PropertyFlags),
+                ["toLocaleString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toLocaleString", ToLocaleString, 0, PropertyFlag.Configurable), PropertyFlags),
+                ["toReversed"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toReversed", ToReversed, 0, PropertyFlag.Configurable), PropertyFlags),
+                ["toSorted"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toSorted", ToSorted, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["toSpliced"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toSpliced", ToSpliced, 2, PropertyFlag.Configurable), PropertyFlags),
+                ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToString, 0, PropertyFlag.Configurable), PropertyFlags),
+                ["unshift"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "unshift", Unshift, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["values"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "values", Values, 0, PropertyFlag.Configurable), PropertyFlags),
+                ["with"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "with", With, 2, PropertyFlag.Configurable), PropertyFlags),
             };
             SetProperties(properties);
 
             _originalIteratorFunction = new ClrFunctionInstance(Engine, "iterator", Values, 1);
             var symbols = new SymbolDictionary(2)
             {
-                [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(_originalIteratorFunction, propertyFlags),
+                [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(_originalIteratorFunction, PropertyFlags),
                 [GlobalSymbolRegistry.Unscopables] = new LazyPropertyDescriptor(_engine, static state =>
                 {
                     var unscopables = new JsObject((Engine) state!)
@@ -89,23 +94,26 @@ namespace Jint.Native.Array
                         _prototype = null
                     };
 
-                    unscopables.SetDataProperty("at", JsBoolean.True);
-                    unscopables.SetDataProperty("copyWithin", JsBoolean.True);
-                    unscopables.SetDataProperty("entries", JsBoolean.True);
-                    unscopables.SetDataProperty("fill", JsBoolean.True);
-                    unscopables.SetDataProperty("find", JsBoolean.True);
-                    unscopables.SetDataProperty("findIndex", JsBoolean.True);
-                    unscopables.SetDataProperty("findLast", JsBoolean.True);
-                    unscopables.SetDataProperty("findLastIndex", JsBoolean.True);
-                    unscopables.SetDataProperty("flat", JsBoolean.True);
-                    unscopables.SetDataProperty("flatMap", JsBoolean.True);
-                    unscopables.SetDataProperty("groupBy", JsBoolean.True);
-                    unscopables.SetDataProperty("groupByToMap", JsBoolean.True);
-                    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);
+                    unscopables.FastSetDataProperty("at", JsBoolean.True);
+                    unscopables.FastSetDataProperty("copyWithin", JsBoolean.True);
+                    unscopables.FastSetDataProperty("entries", JsBoolean.True);
+                    unscopables.FastSetDataProperty("fill", JsBoolean.True);
+                    unscopables.FastSetDataProperty("find", JsBoolean.True);
+                    unscopables.FastSetDataProperty("findIndex", JsBoolean.True);
+                    unscopables.FastSetDataProperty("findLast", JsBoolean.True);
+                    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);
+                    unscopables.FastSetDataProperty("toSorted", JsBoolean.True);
+                    unscopables.FastSetDataProperty("toSpliced", JsBoolean.True);
+                    unscopables.FastSetDataProperty("values", JsBoolean.True);
 
                     return unscopables;
                 }, PropertyFlag.Configurable)
@@ -135,6 +143,38 @@ namespace Jint.Native.Array
             return null;
         }
 
+        private ObjectInstance With(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = ArrayOperations.For(TypeConverter.ToObject(_realm, thisObj));
+            var len = o.GetLongLength();
+            var relativeIndex = TypeConverter.ToIntegerOrInfinity(arguments.At(0));
+            var value = arguments.At(1);
+
+            long actualIndex;
+            if (relativeIndex >= 0)
+            {
+                actualIndex = (long) relativeIndex;
+            }
+            else
+            {
+                actualIndex = (long) (len + relativeIndex);
+            }
+
+            if (actualIndex >= (long) len || actualIndex < 0)
+            {
+                ExceptionHelper.ThrowRangeError(_realm, "Invalid start index");
+            }
+
+            var a = CreateBackingArray(len);
+            ulong k = 0;
+            while (k < len)
+            {
+                a[k] = k == (ulong) actualIndex ? value : o.Get(k);
+                k++;
+            }
+            return new JsArray(_engine, a);
+        }
+
         private ObjectInstance Entries(JsValue thisObj, JsValue[] arguments)
         {
             if (thisObj is ObjectInstance oi && oi.IsArrayLike)
@@ -1017,19 +1057,7 @@ namespace Jint.Native.Array
         {
             var objectInstance = TypeConverter.ToObject(_realm, thisObj);
             var obj = ArrayOperations.For(objectInstance);
-
-            var compareArg = arguments.At(0);
-            ICallable? compareFn = null;
-            if (!compareArg.IsUndefined())
-            {
-                if (compareArg is not ICallable callable)
-                {
-                    ExceptionHelper.ThrowTypeError(_realm, "The comparison function must be either a function or undefined");
-                    return null;
-                }
-
-                compareFn = callable;
-            }
+            var compareFn = GetCompareFunction(arguments.At(0));
 
             var len = obj.GetLength();
             if (len <= 1)
@@ -1377,6 +1405,164 @@ namespace Jint.Native.Array
             return func(array, Arguments.Empty);
         }
 
+        private JsValue ToReversed(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = ArrayOperations.For(TypeConverter.ToObject(_realm, thisObj));
+
+            var len = o.GetLongLength();
+
+            if (len == 0)
+            {
+                return new JsArray(_engine);
+            }
+
+            var a = CreateBackingArray(len);
+            ulong k = 0;
+            while (k < len)
+            {
+                var from = len - k - 1;
+                a[k++] = o.Get(from);
+            }
+            return new JsArray(_engine, a);
+        }
+
+        private JsValue ToSorted(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = ArrayOperations.For(TypeConverter.ToObject(_realm, thisObj));
+            var compareFn = GetCompareFunction(arguments.At(0));
+
+            var len = o.GetLongLength();
+            ValidateArrayLength(len);
+
+            if (len == 0)
+            {
+                return new JsArray(_engine);
+            }
+
+            var array = o.GetAll(skipHoles: true);
+
+            array = SortArray(array, compareFn);
+
+            return new JsArray(_engine, array);
+        }
+
+        private JsValue ToSpliced(JsValue thisObj, JsValue[] arguments)
+        {
+            var start = arguments.At(0);
+            var deleteCount = arguments.At(1);
+
+            var o = ArrayOperations.For(_realm, TypeConverter.ToObject(_realm, thisObj));
+            var len = o.GetLongLength();
+            var relativeStart = TypeConverter.ToIntegerOrInfinity(start);
+
+            ulong actualStart;
+            if (double.IsNegativeInfinity(relativeStart))
+            {
+                actualStart = 0;
+            }
+            else if (relativeStart < 0)
+            {
+                actualStart = (ulong) System.Math.Max(len + relativeStart, 0);
+            }
+            else
+            {
+                actualStart = (ulong) System.Math.Min(relativeStart, len);
+            }
+
+            var items = System.Array.Empty<JsValue>();
+            ulong insertCount;
+            ulong actualDeleteCount;
+            if (arguments.Length == 0)
+            {
+                insertCount = 0;
+                actualDeleteCount = 0;
+            }
+            else if (arguments.Length == 1)
+            {
+                insertCount = 0;
+                actualDeleteCount = len - actualStart;
+            }
+            else
+            {
+                insertCount = (ulong) (arguments.Length - 2);
+                var dc = TypeConverter.ToIntegerOrInfinity(deleteCount);
+                actualDeleteCount = (ulong) System.Math.Min(System.Math.Max(dc,0), len - actualStart);
+
+                items = System.Array.Empty<JsValue>();
+                if (arguments.Length > 2)
+                {
+                    items = new JsValue[arguments.Length - 2];
+                    System.Array.Copy(arguments, 2, items, 0, items.Length);
+                }
+            }
+
+            var newLen = len + insertCount - actualDeleteCount;
+            if (newLen > ArrayOperations.MaxArrayLikeLength)
+            {
+                ExceptionHelper.ThrowTypeError(_realm, "Invalid input length");
+            }
+
+            ValidateArrayLength(newLen);
+
+            var r = actualStart + actualDeleteCount;
+            var a = new JsArray(_engine, (uint) newLen);
+            uint i = 0;
+
+            while (i < actualStart)
+            {
+                a.SetIndexValue(i, o.Get(i), updateLength: false);
+                i++;
+            }
+            a.SetLength((uint) actualStart);
+
+            foreach (var item in items)
+            {
+                a.SetIndexValue(i++, item, updateLength: false);
+            }
+
+            while (i < newLen)
+            {
+                var fromValue = o.Get(r);
+                a.SetIndexValue(i, fromValue, updateLength: false);
+
+                i++;
+                r++;
+            }
+
+            a.SetLength(i);
+            return a;
+        }
+
+        private JsValue[] SortArray(IEnumerable<JsValue> array, ICallable? compareFn)
+        {
+            var comparer = ArrayComparer.WithFunction(_engine, compareFn);
+
+            try
+            {
+                return array.OrderBy(x => x, comparer).ToArray();
+            }
+            catch (InvalidOperationException e)
+            {
+                throw e.InnerException ?? e;
+            }
+        }
+
+        private ICallable? GetCompareFunction(JsValue compareArg)
+        {
+            ICallable? compareFn = null;
+            if (!compareArg.IsUndefined())
+            {
+                if (compareArg is not ICallable callable)
+                {
+                    ExceptionHelper.ThrowTypeError(_realm, "The comparison function must be either a function or undefined");
+                    return null;
+                }
+                compareFn = callable;
+            }
+
+            return compareFn;
+        }
+
         /// <summary>
         /// https://tc39.es/ecma262/#sec-array.prototype.reduceright
         /// </summary>
@@ -1551,6 +1737,20 @@ namespace Jint.Native.Array
             return result;
         }
 
+        private object[] CreateBackingArray(ulong length)
+        {
+            ValidateArrayLength(length);
+            return new object[length];
+        }
+
+        private void ValidateArrayLength(ulong length)
+        {
+            if (length > ArrayOperations.MaxArrayLength)
+            {
+                ExceptionHelper.ThrowRangeError(_engine.Realm, "Invalid array length " + length);
+            }
+        }
+
         internal sealed class ArrayComparer : IComparer<JsValue>
         {
             /// <summary>
@@ -1558,14 +1758,9 @@ namespace Jint.Native.Array
             /// </summary>
             public static readonly ArrayComparer Default = new(null, null);
 
-            public static ArrayComparer WithFunction(Engine engine, ICallable compare)
+            public static ArrayComparer WithFunction(Engine engine, ICallable? compare)
             {
-                if (compare == null)
-                {
-                    return Default;
-                }
-
-                return new ArrayComparer(engine, compare);
+                return compare is null ? Default : new ArrayComparer(engine, compare);
             }
 
             private readonly Engine? _engine;

+ 1 - 4
Jint/Native/Function/FunctionPrototype.cs

@@ -174,10 +174,7 @@ namespace Jint.Native.Function
                 ExceptionHelper.ThrowTypeError(realm);
             }
             var operations = ArrayOperations.For(argArrayObj);
-            var allowedTypes = elementTypes ??
-                               Types.Undefined | Types.Null | Types.Boolean | Types.String | Types.Symbol | Types.Number | Types.Object;
-
-            var argList = operations.GetAll(allowedTypes);
+            var argList = elementTypes is null ? operations.GetAll() : operations.GetAll(elementTypes.Value);
             return argList;
         }
 

+ 4 - 0
Jint/Native/JsArray.cs

@@ -30,4 +30,8 @@ public sealed class JsArray : ArrayInstance
     public JsArray(Engine engine, PropertyDescriptor[] items) : base(engine, items)
     {
     }
+
+    internal JsArray(Engine engine, object[] items) : base(engine, items)
+    {
+    }
 }

+ 7 - 7
Jint/Native/TypedArray/IntrinsicTypedArrayConstructor.cs

@@ -73,7 +73,7 @@ namespace Jint.Native.TypedArray
             {
                 var values = TypedArrayConstructor.IterableToList(_realm, source, usingIterator);
                 var iteratorLen = values.Count;
-                var iteratorTarget = TypedArrayCreate((IConstructor) c, new JsValue[] { iteratorLen });
+                var iteratorTarget = TypedArrayCreate(_realm, (IConstructor) c, new JsValue[] { iteratorLen });
                 for (var k = 0; k < iteratorLen; ++k)
                 {
                     var kValue = values[k];
@@ -95,7 +95,7 @@ namespace Jint.Native.TypedArray
             var len = arrayLike.Length;
 
             var argumentList = new JsValue[] { JsNumber.Create(len) };
-            var targetObj = TypedArrayCreate((IConstructor) c, argumentList);
+            var targetObj = TypedArrayCreate(_realm, (IConstructor) c, argumentList);
 
             var mappingArgs = mapping ? new JsValue[2] : null;
             for (uint k = 0; k < len; ++k)
@@ -132,7 +132,7 @@ namespace Jint.Native.TypedArray
                 ExceptionHelper.ThrowTypeError(_realm);
             }
 
-            var newObj = TypedArrayCreate((IConstructor) thisObj, new JsValue[] { len });
+            var newObj = TypedArrayCreate(_realm, (IConstructor) thisObj, new JsValue[] { len });
 
             var k = 0;
             while (k < len)
@@ -152,7 +152,7 @@ namespace Jint.Native.TypedArray
         {
             var defaultConstructor = exemplar._arrayElementType.GetConstructor(_realm.Intrinsics)!;
             var constructor = SpeciesConstructor(exemplar, defaultConstructor);
-            var result = TypedArrayCreate(constructor, argumentList);
+            var result = TypedArrayCreate(_realm, constructor, argumentList);
             if (result._contentType != exemplar._contentType)
             {
                 ExceptionHelper.ThrowTypeError(_realm);
@@ -164,14 +164,14 @@ namespace Jint.Native.TypedArray
         /// <summary>
         /// https://tc39.es/ecma262/#typedarray-create
         /// </summary>
-        private TypedArrayInstance TypedArrayCreate(IConstructor constructor, JsValue[] argumentList)
+        internal static TypedArrayInstance TypedArrayCreate(Realm realm, IConstructor constructor, JsValue[] argumentList)
         {
-            var newTypedArray = Construct(constructor, argumentList).ValidateTypedArray(_realm);
+            var newTypedArray = Construct(constructor, argumentList).ValidateTypedArray(realm);
             if (argumentList.Length == 1 && argumentList[0] is JsNumber number)
             {
                 if (newTypedArray.Length < number._value)
                 {
-                    ExceptionHelper.ThrowTypeError(_realm);
+                    ExceptionHelper.ThrowTypeError(realm);
                 }
             }
 

+ 155 - 61
Jint/Native/TypedArray/IntrinsicTypedArrayPrototype.cs

@@ -31,54 +31,54 @@ namespace Jint.Native.TypedArray
 
         protected override void Initialize()
         {
-            const PropertyFlag lengthFlags = PropertyFlag.Configurable;
-            const PropertyFlag propertyFlags = PropertyFlag.Writable | PropertyFlag.Configurable;
-            var properties = new PropertyDictionary(33, false)
-            {
-                ["buffer"] = new GetSetPropertyDescriptor(new ClrFunctionInstance(_engine, "get buffer", Buffer, 0, lengthFlags), Undefined, PropertyFlag.Configurable),
-                ["byteLength"] = new GetSetPropertyDescriptor(new ClrFunctionInstance(_engine, "get byteLength", ByteLength, 0, lengthFlags), Undefined, PropertyFlag.Configurable),
-                ["byteOffset"] = new GetSetPropertyDescriptor(new ClrFunctionInstance(Engine, "get byteOffset", ByteOffset, 0, lengthFlags), Undefined, PropertyFlag.Configurable),
-                ["length"] = new GetSetPropertyDescriptor(new ClrFunctionInstance(Engine, "get length", GetLength, 0, lengthFlags), Undefined, PropertyFlag.Configurable),
+            const PropertyFlag LengthFlags = PropertyFlag.Configurable;
+            const PropertyFlag PropertyFlags = PropertyFlag.Writable | PropertyFlag.Configurable;
+            var properties = new PropertyDictionary(36, false)
+            {
+                ["at"] = new(new ClrFunctionInstance(Engine, "at", At, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["buffer"] = new GetSetPropertyDescriptor(new ClrFunctionInstance(_engine, "get buffer", Buffer, 0, LengthFlags), Undefined, PropertyFlag.Configurable),
+                ["byteLength"] = new GetSetPropertyDescriptor(new ClrFunctionInstance(_engine, "get byteLength", ByteLength, 0, LengthFlags), Undefined, PropertyFlag.Configurable),
+                ["byteOffset"] = new GetSetPropertyDescriptor(new ClrFunctionInstance(Engine, "get byteOffset", ByteOffset, 0, LengthFlags), Undefined, PropertyFlag.Configurable),
                 ["constructor"] = new(_constructor, PropertyFlag.NonEnumerable),
-                ["copyWithin"] = new(new ClrFunctionInstance(Engine, "copyWithin", CopyWithin, 2, PropertyFlag.Configurable), propertyFlags),
-                ["entries"] = new(new ClrFunctionInstance(Engine, "entries", Entries, 0, PropertyFlag.Configurable), propertyFlags),
-                ["every"] = new(new ClrFunctionInstance(Engine, "every", Every, 1, PropertyFlag.Configurable), propertyFlags),
-                ["fill"] = new(new ClrFunctionInstance(Engine, "fill", Fill, 1, PropertyFlag.Configurable), propertyFlags),
-                ["filter"] = new(new ClrFunctionInstance(Engine, "filter", Filter, 1, PropertyFlag.Configurable), propertyFlags),
-                ["find"] = new(new ClrFunctionInstance(Engine, "find", Find, 1, PropertyFlag.Configurable), propertyFlags),
-                ["findIndex"] = new(new ClrFunctionInstance(Engine, "findIndex", FindIndex, 1, PropertyFlag.Configurable), propertyFlags),
-                ["findLast"] = new(new ClrFunctionInstance(Engine, "findLast", FindLast, 1, PropertyFlag.Configurable), propertyFlags),
-                ["findLastIndex"] = new(new ClrFunctionInstance(Engine, "findLastIndex", FindLastIndex, 1, PropertyFlag.Configurable), propertyFlags),
-                ["forEach"] = new(new ClrFunctionInstance(Engine, "forEach", ForEach, 1, PropertyFlag.Configurable), propertyFlags),
-                ["includes"] = new(new ClrFunctionInstance(Engine, "includes", Includes, 1, PropertyFlag.Configurable), propertyFlags),
-                ["indexOf"] = new(new ClrFunctionInstance(Engine, "indexOf", IndexOf, 1, PropertyFlag.Configurable), propertyFlags),
-                ["join"] = new(new ClrFunctionInstance(Engine, "join", Join, 1, PropertyFlag.Configurable), propertyFlags),
-                ["keys"] = new(new ClrFunctionInstance(Engine, "keys", Keys, 0, PropertyFlag.Configurable), propertyFlags),
-                ["lastIndexOf"] = new(new ClrFunctionInstance(Engine, "lastIndexOf", LastIndexOf, 1, PropertyFlag.Configurable), propertyFlags),
-                ["map"] = new(new ClrFunctionInstance(Engine, "map", Map, 1, PropertyFlag.Configurable), propertyFlags),
-                ["reduce"] = new(new ClrFunctionInstance(Engine, "reduce", Reduce, 1, PropertyFlag.Configurable), propertyFlags),
-                ["reduceRight"] = new(new ClrFunctionInstance(Engine, "reduceRight", ReduceRight, 1, PropertyFlag.Configurable), propertyFlags),
-                ["reverse"] = new(new ClrFunctionInstance(Engine, "reverse", Reverse, 0, PropertyFlag.Configurable), propertyFlags),
-                ["set"] = new(new ClrFunctionInstance(Engine, "set", Set, 1, PropertyFlag.Configurable), propertyFlags),
-                ["slice"] = new(new ClrFunctionInstance(Engine, "slice", Slice, 2, PropertyFlag.Configurable), propertyFlags),
-                ["some"] = new(new ClrFunctionInstance(Engine, "some", Some, 1, PropertyFlag.Configurable), propertyFlags),
-                ["sort"] = new(new ClrFunctionInstance(Engine, "sort", Sort, 1, PropertyFlag.Configurable), propertyFlags),
-                ["subarray"] = new(new ClrFunctionInstance(Engine, "subarray", Subarray, 2, PropertyFlag.Configurable), propertyFlags),
-                ["toLocaleString"] = new(new ClrFunctionInstance(Engine, "toLocaleString", ToLocaleString, 0, PropertyFlag.Configurable), propertyFlags),
-                ["toString"] = new(new ClrFunctionInstance(Engine, "toLocaleString", _realm.Intrinsics.Array.PrototypeObject.ToString, 0, PropertyFlag.Configurable), propertyFlags),
-                ["values"] = new(new ClrFunctionInstance(Engine, "values", Values, 0, PropertyFlag.Configurable), propertyFlags),
-                ["at"] = new(new ClrFunctionInstance(Engine, "at", At, 1, PropertyFlag.Configurable), propertyFlags),
+                ["copyWithin"] = new(new ClrFunctionInstance(Engine, "copyWithin", CopyWithin, 2, PropertyFlag.Configurable), PropertyFlags),
+                ["entries"] = new(new ClrFunctionInstance(Engine, "entries", Entries, 0, PropertyFlag.Configurable), PropertyFlags),
+                ["every"] = new(new ClrFunctionInstance(Engine, "every", Every, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["fill"] = new(new ClrFunctionInstance(Engine, "fill", Fill, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["filter"] = new(new ClrFunctionInstance(Engine, "filter", Filter, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["find"] = new(new ClrFunctionInstance(Engine, "find", Find, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["findIndex"] = new(new ClrFunctionInstance(Engine, "findIndex", FindIndex, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["findLast"] = new(new ClrFunctionInstance(Engine, "findLast", FindLast, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["findLastIndex"] = new(new ClrFunctionInstance(Engine, "findLastIndex", FindLastIndex, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["forEach"] = new(new ClrFunctionInstance(Engine, "forEach", ForEach, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["includes"] = new(new ClrFunctionInstance(Engine, "includes", Includes, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["indexOf"] = new(new ClrFunctionInstance(Engine, "indexOf", IndexOf, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["join"] = new(new ClrFunctionInstance(Engine, "join", Join, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["keys"] = new(new ClrFunctionInstance(Engine, "keys", Keys, 0, PropertyFlag.Configurable), PropertyFlags),
+                ["lastIndexOf"] = new(new ClrFunctionInstance(Engine, "lastIndexOf", LastIndexOf, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["length"] = new GetSetPropertyDescriptor(new ClrFunctionInstance(Engine, "get length", GetLength, 0, LengthFlags), Undefined, PropertyFlag.Configurable),
+                ["map"] = new(new ClrFunctionInstance(Engine, "map", Map, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["reduce"] = new(new ClrFunctionInstance(Engine, "reduce", Reduce, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["reduceRight"] = new(new ClrFunctionInstance(Engine, "reduceRight", ReduceRight, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["reverse"] = new(new ClrFunctionInstance(Engine, "reverse", Reverse, 0, PropertyFlag.Configurable), PropertyFlags),
+                ["set"] = new(new ClrFunctionInstance(Engine, "set", Set, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["slice"] = new(new ClrFunctionInstance(Engine, "slice", Slice, 2, PropertyFlag.Configurable), PropertyFlags),
+                ["some"] = new(new ClrFunctionInstance(Engine, "some", Some, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["sort"] = new(new ClrFunctionInstance(Engine, "sort", Sort, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["subarray"] = new(new ClrFunctionInstance(Engine, "subarray", Subarray, 2, PropertyFlag.Configurable), PropertyFlags),
+                ["toLocaleString"] = new(new ClrFunctionInstance(Engine, "toLocaleString", ToLocaleString, 0, PropertyFlag.Configurable), PropertyFlags),
+                ["toReversed"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toReversed", ToReversed, 0, PropertyFlag.Configurable), PropertyFlags),
+                ["toSorted"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toSorted", ToSorted, 1, PropertyFlag.Configurable), PropertyFlags),
+                ["toString"] = new(new ClrFunctionInstance(Engine, "toLocaleString", _realm.Intrinsics.Array.PrototypeObject.ToString, 0, PropertyFlag.Configurable), PropertyFlags),
+                ["values"] = new(new ClrFunctionInstance(Engine, "values", Values, 0, PropertyFlag.Configurable), PropertyFlags),
+                ["with"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "with", With, 2, PropertyFlag.Configurable), PropertyFlags),
             };
             SetProperties(properties);
 
             _originalIteratorFunction = new ClrFunctionInstance(Engine, "iterator", Values, 1);
             var symbols = new SymbolDictionary(2)
             {
-                [GlobalSymbolRegistry.Iterator] = new(_originalIteratorFunction, propertyFlags),
-                [GlobalSymbolRegistry.ToStringTag] = new GetSetPropertyDescriptor(
-                    new ClrFunctionInstance(Engine, "get [Symbol.toStringTag]", ToStringTag, 0, PropertyFlag.Configurable),
-                    Undefined,
-                    PropertyFlag.Configurable)
+                [GlobalSymbolRegistry.Iterator] = new(_originalIteratorFunction, PropertyFlags),
+                [GlobalSymbolRegistry.ToStringTag] = new GetSetPropertyDescriptor(new ClrFunctionInstance(Engine, "get [Symbol.toStringTag]", ToStringTag, 0, PropertyFlag.Configurable), Undefined, PropertyFlag.Configurable)
             };
             SetSymbols(symbols);
         }
@@ -1012,7 +1012,7 @@ namespace Jint.Native.TypedArray
                 k = (int) relativeStart;
             }
 
-            if(k < 0 || k >= len)
+            if (k < 0 || k >= len)
             {
                 return Undefined;
             }
@@ -1147,31 +1147,14 @@ namespace Jint.Native.TypedArray
             var buffer = obj._viewedArrayBuffer;
             var len = obj.Length;
 
-            var compareArg = arguments.At(0);
-            ICallable? compareFn = null;
-            if (!compareArg.IsUndefined())
-            {
-                compareFn = GetCallable(compareArg);
-            }
+            var compareFn = GetCompareFunction(arguments.At(0));
 
             if (len <= 1)
             {
                 return obj;
             }
 
-            JsValue[] array;
-            try
-            {
-                var comparer = TypedArrayComparer.WithFunction(buffer, compareFn);
-                var operations = ArrayOperations.For(obj);
-                array = operations
-                    .OrderBy(x => x, comparer)
-                    .ToArray();
-            }
-            catch (InvalidOperationException e)
-            {
-                throw e.InnerException ?? e;
-            }
+            var array = SortArray(buffer, compareFn, obj);
 
             for (var i = 0; i < (uint) array.Length; ++i)
             {
@@ -1323,6 +1306,117 @@ namespace Jint.Native.TypedArray
             return o._arrayElementType.GetTypedArrayName();
         }
 
+        private JsValue ToReversed(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj.ValidateTypedArray(_realm);
+            var length = o._arrayLength;
+
+            var a = TypedArrayCreateSameType(o, new [] { JsNumber.Create(length) });
+            uint k = 0;
+            while (k < length)
+            {
+                var from = length - k - 1;
+                a[k++] = o.Get(from);
+            }
+
+            return a;
+        }
+
+        private JsValue ToSorted(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj.ValidateTypedArray(_realm);
+            var compareFn = GetCompareFunction(arguments.At(0));
+
+            var buffer = o._viewedArrayBuffer;
+            var length = o.Length;
+
+            var a = TypedArrayCreateSameType(o, new [] { JsNumber.Create(length) });
+
+            var array = SortArray(buffer, compareFn, o);
+            for (var i = 0; (uint) i < (uint) array.Length; ++i)
+            {
+                a[i] = array[i];
+            }
+
+            return a;
+        }
+
+        private ObjectInstance With(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj.ValidateTypedArray(_realm);
+            var value = arguments.At(1);
+
+            var length = o._arrayLength;
+            var relativeIndex = TypeConverter.ToIntegerOrInfinity(arguments.At(0));
+
+            long actualIndex;
+            if (relativeIndex >= 0)
+            {
+                actualIndex = (long) relativeIndex;
+            }
+            else
+            {
+                actualIndex = (long) (length + relativeIndex);
+            }
+
+            value = o._contentType == TypedArrayContentType.BigInt
+                ? TypeConverter.ToJsBigInt(value)
+                : TypeConverter.ToJsNumber(value);
+
+            if (!o.IsValidIntegerIndex(actualIndex))
+            {
+                ExceptionHelper.ThrowRangeError(_realm, "Invalid start index");
+            }
+
+            var a = TypedArrayCreateSameType(o, new [] { JsNumber.Create(length) });
+
+            var k = 0;
+            while (k < length)
+            {
+                a[k] = k == (int) actualIndex ? value : o.Get(k);
+                k++;
+            }
+
+            return a;
+        }
+
+        private TypedArrayInstance TypedArrayCreateSameType(TypedArrayInstance exemplar, JsValue[] argumentList)
+        {
+            var constructor = exemplar._arrayElementType.GetConstructor(_realm.Intrinsics);
+            var result = IntrinsicTypedArrayConstructor.TypedArrayCreate(_realm, constructor, argumentList);
+            return result;
+        }
+
+        private ICallable? GetCompareFunction(JsValue compareArg)
+        {
+            ICallable? compareFn = null;
+            if (!compareArg.IsUndefined())
+            {
+                if (compareArg is not ICallable callable)
+                {
+                    ExceptionHelper.ThrowTypeError(_realm, "The comparison function must be either a function or undefined");
+                    return null;
+                }
+                compareFn = callable;
+            }
+
+            return compareFn;
+        }
+
+        private static JsValue[] SortArray(ArrayBufferInstance buffer, ICallable? compareFn, TypedArrayInstance obj)
+        {
+            var comparer = TypedArrayComparer.WithFunction(buffer, compareFn);
+            var operations = ArrayOperations.For(obj);
+            try
+            {
+                return operations.OrderBy(x => x, comparer).ToArray();
+            }
+            catch (InvalidOperationException e)
+            {
+                throw e.InnerException ?? e;
+            }
+        }
+
         private sealed class TypedArrayComparer : IComparer<JsValue>
         {
             public static TypedArrayComparer WithFunction(ArrayBufferInstance buffer, ICallable? compare)

+ 11 - 12
Jint/Native/TypedArray/TypeArrayHelper.cs

@@ -1,21 +1,20 @@
 using Jint.Runtime;
 
-namespace Jint.Native.TypedArray
+namespace Jint.Native.TypedArray;
+
+internal static class TypeArrayHelper
 {
-    internal static class TypeArrayHelper
+    internal static TypedArrayInstance ValidateTypedArray(this JsValue o, Realm realm)
     {
-        internal static TypedArrayInstance ValidateTypedArray(this JsValue o, Realm realm)
+        var typedArrayInstance = o as TypedArrayInstance;
+        if (typedArrayInstance is null)
         {
-            var typedArrayInstance = o as TypedArrayInstance;
-            if (typedArrayInstance is null)
-            {
-                ExceptionHelper.ThrowTypeError(realm);
-            }
+            ExceptionHelper.ThrowTypeError(realm);
+        }
 
-            var buffer = typedArrayInstance._viewedArrayBuffer;
-            buffer.AssertNotDetached();
+        var buffer = typedArrayInstance._viewedArrayBuffer;
+        buffer.AssertNotDetached();
 
-            return typedArrayInstance;
-        }
+        return typedArrayInstance;
     }
 }

+ 2 - 2
Jint/Native/TypedArray/TypedArrayElementType.cs

@@ -59,7 +59,7 @@ namespace Jint.Native.TypedArray
             };
         }
 
-        internal static IConstructor? GetConstructor(this TypedArrayElementType type, Intrinsics intrinsics)
+        internal static IConstructor GetConstructor(this TypedArrayElementType type, Intrinsics intrinsics)
         {
             return type switch
             {
@@ -74,7 +74,7 @@ namespace Jint.Native.TypedArray
                 TypedArrayElementType.BigUint64 => intrinsics.BigUint64Array,
                 TypedArrayElementType.Float32 => intrinsics.Float32Array,
                 TypedArrayElementType.Float64 => intrinsics.Float64Array,
-                _ => null
+                _ => null!
             };
         }
 

+ 15 - 14
Jint/Native/TypedArray/TypedArrayInstance.cs

@@ -18,29 +18,30 @@ namespace Jint.Native.TypedArray
         private readonly Intrinsics _intrinsics;
         internal uint _arrayLength;
 
-        private TypedArrayInstance(
-            Engine engine,
-            Intrinsics intrinsics) : base(engine)
-        {
-            _intrinsics = intrinsics;
-            _viewedArrayBuffer = new ArrayBufferInstance(engine, 0);
-        }
-
         internal TypedArrayInstance(
             Engine engine,
             Intrinsics intrinsics,
             TypedArrayElementType type,
-            uint length) : this(engine, intrinsics)
+            uint length) : base(engine)
         {
-            _arrayElementType = type;
-            _arrayLength = length;
+            _intrinsics = intrinsics;
+            _viewedArrayBuffer = new ArrayBufferInstance(engine, 0);
 
+            _arrayElementType = type;
             _contentType = type != TypedArrayElementType.BigInt64 && type != TypedArrayElementType.BigUint64
                 ? TypedArrayContentType.Number
                 : TypedArrayContentType.BigInt;
+
+            _arrayLength = length;
+        }
+
+        public JsValue this[int index]
+        {
+            get => IntegerIndexedElementGet(index);
+            set => IntegerIndexedElementSet(index, value);
         }
 
-        internal JsValue this[int index]
+        public JsValue this[uint index]
         {
             get => IntegerIndexedElementGet(index);
             set => IntegerIndexedElementSet(index, value);
@@ -329,7 +330,7 @@ namespace Jint.Native.TypedArray
         /// https://tc39.es/ecma262/#sec-isvalidintegerindex
         /// </summary>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private bool IsValidIntegerIndex(double index)
+        internal bool IsValidIntegerIndex(double index)
         {
             if (_viewedArrayBuffer.IsDetachedBuffer)
             {
@@ -358,7 +359,7 @@ namespace Jint.Native.TypedArray
         /// https://tc39.es/ecma262/#sec-isvalidintegerindex
         /// </summary>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private bool IsValidIntegerIndex(int index)
+        internal bool IsValidIntegerIndex(int index)
         {
             return !_viewedArrayBuffer.IsDetachedBuffer && (uint) index < _arrayLength;
         }

+ 29 - 26
Jint/Native/TypedArray/TypedArrayPrototype.cs

@@ -1,35 +1,38 @@
 using Jint.Collections;
+using Jint.Native.Array;
+using Jint.Native.Object;
+using Jint.Runtime;
 using Jint.Runtime.Descriptors;
+using Jint.Runtime.Interop;
 
-namespace Jint.Native.TypedArray
+namespace Jint.Native.TypedArray;
+
+/// <summary>
+/// https://tc39.es/ecma262/#sec-properties-of-typedarray-prototype-objects
+/// </summary>
+internal sealed class TypedArrayPrototype : Prototype
 {
-    /// <summary>
-    /// https://tc39.es/ecma262/#sec-properties-of-typedarray-prototype-objects
-    /// </summary>
-    internal sealed class TypedArrayPrototype : Prototype
-    {
-        private readonly TypedArrayConstructor _constructor;
-        private readonly TypedArrayElementType _arrayElementType;
+    private readonly TypedArrayConstructor _constructor;
+    private readonly TypedArrayElementType _arrayElementType;
 
-        internal TypedArrayPrototype(
-            Engine engine,
-            IntrinsicTypedArrayPrototype objectPrototype,
-            TypedArrayConstructor constructor,
-            TypedArrayElementType type) : base(engine, engine.Realm)
-        {
-            _prototype = objectPrototype;
-            _constructor = constructor;
-            _arrayElementType = type;
-        }
+    internal TypedArrayPrototype(
+        Engine engine,
+        IntrinsicTypedArrayPrototype objectPrototype,
+        TypedArrayConstructor constructor,
+        TypedArrayElementType type) : base(engine, engine.Realm)
+    {
+        _prototype = objectPrototype;
+        _constructor = constructor;
+        _arrayElementType = type;
+    }
 
-        protected override void Initialize()
+    protected override void Initialize()
+    {
+        var properties = new PropertyDictionary(2, false)
         {
-            var properties = new PropertyDictionary(2, false)
-            {
-                ["BYTES_PER_ELEMENT"] = new(JsNumber.Create(_arrayElementType.GetElementSize()), PropertyFlag.AllForbidden),
-                ["constructor"] = new(_constructor, PropertyFlag.NonEnumerable)
-            };
-            SetProperties(properties);
-        }
+            ["BYTES_PER_ELEMENT"] = new(JsNumber.Create(_arrayElementType.GetElementSize()), PropertyFlag.AllForbidden),
+            ["constructor"] = new(_constructor, PropertyFlag.NonEnumerable),
+        };
+        SetProperties(properties);
     }
 }

+ 55 - 1
Jint/Runtime/TypeConverter.cs

@@ -56,9 +56,11 @@ namespace Jint.Runtime
 
         // the object doesn't override important GetOwnProperty etc which change behavior
         PlainObject = 4096,
+        // our native array
+        Array = 8192,
 
         Primitive = Boolean | String | Number | Integer | BigInt | Symbol,
-        InternalFlags = ObjectEnvironmentRecord | RequiresCloning | PlainObject | Module
+        InternalFlags = ObjectEnvironmentRecord | RequiresCloning | PlainObject | Array | Module
     }
 
     public static class TypeConverter
@@ -248,6 +250,36 @@ namespace Jint.Runtime
             }
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static JsNumber ToJsNumber(JsValue o)
+        {
+            return o.IsNumber() ? (JsNumber) o : ToJsNumberUnlikely(o);
+        }
+
+        private static JsNumber ToJsNumberUnlikely(JsValue o)
+        {
+            var type = o._type & ~InternalTypes.InternalFlags;
+
+            switch (type)
+            {
+                case InternalTypes.Undefined:
+                    return JsNumber.DoubleNaN;
+                case InternalTypes.Null:
+                    return JsNumber.PositiveZero;
+                case InternalTypes.Boolean:
+                    return ((JsBoolean) o)._value ? JsNumber.PositiveOne : JsNumber.PositiveZero;
+                case InternalTypes.String:
+                    return new JsNumber(ToNumber(o.ToString()));
+                case InternalTypes.Symbol:
+                case InternalTypes.BigInt:
+                    // TODO proper TypeError would require Engine instance and a lot of API changes
+                    ExceptionHelper.ThrowTypeErrorNoEngine("Cannot convert a " + type + " value to a number");
+                    return JsNumber.PositiveZero;
+                default:
+                    return new JsNumber(ToNumber(ToPrimitive(o, Types.Number)));
+            }
+        }
+
         private static double ToNumber(string input)
         {
             // eager checks to save time and trimming
@@ -627,6 +659,28 @@ namespace Jint.Runtime
             }
         }
 
+        public static JsBigInt ToJsBigInt(JsValue value)
+        {
+            return value as JsBigInt ?? ToJsBigIntUnlikely(value);
+        }
+
+        private static JsBigInt ToJsBigIntUnlikely(JsValue value)
+        {
+            var prim = ToPrimitive(value, Types.Number);
+            switch (prim.Type)
+            {
+                case Types.BigInt:
+                    return (JsBigInt) prim;
+                case Types.Boolean:
+                    return ((JsBoolean) prim)._value ? JsBigInt.One : JsBigInt.Zero;
+                case Types.String:
+                    return new JsBigInt(StringToBigInt(prim.ToString()));
+                default:
+                    ExceptionHelper.ThrowTypeErrorNoEngine("Cannot convert a " + prim.Type + " to a BigInt");
+                    return JsBigInt.One;
+            }
+        }
+
         internal static BigInteger StringToBigInt(string str)
         {
             if (!TryStringToBigInt(str, out var result))

+ 1 - 0
README.md

@@ -110,6 +110,7 @@ The entire execution engine was rebuild with performance in mind, in many cases
 
 - ✔ Array find from last
 - ✔ Array.group and Array.groupToMap
+- ✔ Change Array by copy
 - ✔ ShadowRealm
 - ✔ Symbols as WeakMap keys