Browse Source

Typed arrays (#925)

Marko Lahma 4 years ago
parent
commit
1a9cd0ede6
47 changed files with 3118 additions and 326 deletions
  1. 1 1
      Jint.Benchmark/Jint.Benchmark.csproj
  2. 98 0
      Jint.Benchmark/TypedArrayBenchmark.cs
  3. 1 1
      Jint.Tests.CommonScripts/Jint.Tests.CommonScripts.csproj
  4. 1 1
      Jint.Tests.Ecma/Jint.Tests.Ecma.csproj
  5. 23 0
      Jint.Tests.Test262/BuiltIns/TypedArrayTests.cs
  6. 1 1
      Jint.Tests.Test262/Jint.Tests.Test262.csproj
  7. 0 32
      Jint.Tests.Test262/Test262Test.cs
  8. 8 8
      Jint.Tests.Test262/test/skipped.json
  9. 1 1
      Jint.Tests/Jint.Tests.csproj
  10. 86 0
      Jint.Tests/Runtime/TypedArrayInteropTests.cs
  11. 1 1
      Jint/Engine.cs
  12. 1 4
      Jint/Jint.csproj
  13. 10 5
      Jint/Native/Argument/ArgumentsInstance.cs
  14. 1 1
      Jint/Native/Array/ArrayInstance.cs
  15. 81 42
      Jint/Native/Array/ArrayOperations.cs
  16. 21 31
      Jint/Native/Array/ArrayPrototype.cs
  17. 4 3
      Jint/Native/ArrayBuffer/ArrayBufferConstructor.cs
  18. 51 24
      Jint/Native/ArrayBuffer/ArrayBufferInstance.cs
  19. 1 1
      Jint/Native/Boolean/BooleanConstructor.cs
  20. 3 3
      Jint/Native/DataView/DataViewPrototype.cs
  21. 2 2
      Jint/Native/Date/DateConstructor.cs
  22. 1 1
      Jint/Native/Function/FunctionConstructor.cs
  23. 1 1
      Jint/Native/Function/FunctionInstance.cs
  24. 12 0
      Jint/Native/Global/GlobalObject.cs
  25. 9 0
      Jint/Native/Iterator/ArrayIteratorType.cs
  26. 12 32
      Jint/Native/Iterator/IteratorConstructor.cs
  27. 53 102
      Jint/Native/Iterator/IteratorInstance.cs
  28. 4 4
      Jint/Native/JsNumber.cs
  29. 1 0
      Jint/Native/JsString.cs
  30. 1 1
      Jint/Native/Number/NumberConstructor.cs
  31. 4 4
      Jint/Native/Object/ObjectInstance.cs
  32. 1 1
      Jint/Native/String/StringConstructor.cs
  33. 198 0
      Jint/Native/TypedArray/IntrinsicTypedArrayConstructor.cs
  34. 1362 0
      Jint/Native/TypedArray/IntrinsicTypedArrayPrototype.cs
  35. 21 0
      Jint/Native/TypedArray/TypeArrayHelper.cs
  36. 202 0
      Jint/Native/TypedArray/TypedArrayConstructor.Types.cs
  37. 290 0
      Jint/Native/TypedArray/TypedArrayConstructor.cs
  38. 8 0
      Jint/Native/TypedArray/TypedArrayContentType.cs
  39. 48 3
      Jint/Native/TypedArray/TypedArrayElementType.cs
  40. 334 0
      Jint/Native/TypedArray/TypedArrayInstance.cs
  41. 36 0
      Jint/Native/TypedArray/TypedArrayPrototype.cs
  42. 1 3
      Jint/Runtime/Interpreter/Expressions/JintArrayExpression.cs
  43. 5 0
      Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs
  44. 17 5
      Jint/Runtime/Interpreter/Expressions/JintIdentifierExpression.cs
  45. 50 0
      Jint/Runtime/Intrinsics.cs
  46. 50 6
      Jint/Runtime/TypeConverter.cs
  47. 1 1
      README.md

+ 1 - 1
Jint.Benchmark/Jint.Benchmark.csproj

@@ -24,7 +24,7 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
+    <PackageReference Include="BenchmarkDotNet" Version="0.13.0" />
     <PackageReference Include="Jurassic" Version="3.1.0" />
     <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
     <PackageReference Include="NiL.JS.NetCore" Version="2.5.1419" />

+ 98 - 0
Jint.Benchmark/TypedArrayBenchmark.cs

@@ -0,0 +1,98 @@
+using BenchmarkDotNet.Attributes;
+
+namespace Jint.Benchmark
+{
+    [MemoryDiagnoser]
+    public class TypedArrayBenchmark
+    {
+        private const string script = @"
+var testArray = new Int32Array([29, 27, 28, 838, 22, 2882, 2, 93, 84, 74, 7, 933, 3754, 3874, 22838, 38464, 3837, 82424, 2927, 2625, 63, 27, 28, 838, 22, 2882, 2, 93, 84, 74, 7, 933, 3754, 3874, 22838, 38464, 3837, 82424, 2927, 2625, 63, 27, 28, 838, 22, 2882, 2, 93, 84, 74, 7, 933, 3754, 3874, 22838, 38464, 3837, 82424, 2927, 2625, 63, 27, 28, 838, 22, 2882, 2, 93, 84, 74, 7, 933, 3754, 3874, 22838, 38464, 3837, 82424, 2927, 2625, 63]);
+";
+
+        private Engine engine;
+
+
+        [GlobalSetup]
+        public void Setup()
+        {
+            engine = new Engine();
+            engine.Execute(script);
+        }
+
+        [Params(100)]
+        public int N { get; set; }
+
+        [Benchmark]
+        public void Slice()
+        {
+            for (var i = 0; i < N; ++i)
+            {
+                engine.Execute("testArray.slice();");
+            }
+        }
+
+        [Benchmark]
+        public void Concat()
+        {
+            // tests conversion performance as TypedArray does not have concat
+            for (var i = 0; i < N; ++i)
+            {
+                engine.Execute("[].concat(testArray);");
+            }
+        }
+
+        [Benchmark]
+        public void Index()
+        {
+            for (var i = 0; i < N; ++i)
+            {
+                engine.Execute(@"
+var obj2 = new Int32Array(testArray.length);
+for (var i = 0, l = testArray.length; i < l; i++) {
+  obj2[i] = testArray[i];
+}
+");
+            }
+        }
+
+        [Benchmark]
+        public void Map()
+        {
+            for (var i = 0; i < N; ++i)
+            {
+                engine.Execute(@"
+var obj2 = testArray.map(function(i) {
+  return i;
+});
+");
+            }
+        }
+
+        [Benchmark]
+        public void Apply()
+        {
+            for (var i = 0; i < N; ++i)
+            {
+                engine.Execute("Array.apply(undefined, testArray);");
+            }
+        }
+
+        [Benchmark]
+        public void JsonStringifyParse()
+        {
+            for (var i = 0; i < N; ++i)
+            {
+                engine.Execute("JSON.parse(JSON.stringify(testArray));");
+            }
+        }
+
+        [Benchmark]
+        public void FilterWithNumber()
+        {
+            for (var i = 0; i < N; ++i)
+            {
+                engine.Execute("testArray.filter(function(i) { return i > 55; });");
+            }
+        }
+    }
+}

+ 1 - 1
Jint.Tests.CommonScripts/Jint.Tests.CommonScripts.csproj

@@ -11,7 +11,7 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.analyzers" Version="0.10.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />

+ 1 - 1
Jint.Tests.Ecma/Jint.Tests.Ecma.csproj

@@ -8,7 +8,7 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.analyzers" Version="0.10.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />

+ 23 - 0
Jint.Tests.Test262/BuiltIns/TypedArrayTests.cs

@@ -0,0 +1,23 @@
+using Xunit;
+
+namespace Jint.Tests.Test262.BuiltIns
+{
+    public class TypedArrayTests : Test262Test
+    {
+        [Theory(DisplayName = "built-ins\\TypedArray")]
+        [MemberData(nameof(SourceFiles), "built-ins\\TypedArray", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\TypedArray", true, Skip = "Skipped")]
+        protected void TypedArray(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+
+        [Theory(DisplayName = "built-ins\\TypedArrayConstructors")]
+        [MemberData(nameof(SourceFiles), "built-ins\\TypedArrayConstructors", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\TypedArrayConstructors", true, Skip = "Skipped")]
+        protected void TypedArrayConstructors(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 1 - 1
Jint.Tests.Test262/Jint.Tests.Test262.csproj

@@ -10,7 +10,7 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
     <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.analyzers" Version="0.10.0" />

+ 0 - 32
Jint.Tests.Test262/Test262Test.cs

@@ -261,10 +261,6 @@ namespace Jint.Tests.Test262
                                 skip = true;
                                 reason = "private/public class fields not implemented in esprima";
                                 break;
-                            case "super":
-                                skip = true;
-                                reason = "super not implemented";
-                                break;
                             case "String.prototype.replaceAll":
                                 skip = true;
                                 reason = "not in spec yet";
@@ -285,22 +281,6 @@ namespace Jint.Tests.Test262
                                 skip = true;
                                 reason = "regexp-lookbehind not implemented";
                                 break;
-                            case "TypedArray":
-                                skip = true;
-                                reason = "TypedArray not implemented";
-                                break;
-                            case "Int32Array":
-                                skip = true;
-                                reason = "Int32Array not implemented";
-                                break;
-                            case "Int8Array":
-                                skip = true;
-                                reason = "Int8Array not implemented";
-                                break;
-                            case "Uint8Array":
-                                skip = true;
-                                reason = "Uint8Array not implemented";
-                                break;
                             case "SharedArrayBuffer":
                                 skip = true;
                                 reason = "SharedArrayBuffer not implemented";
@@ -341,18 +321,6 @@ namespace Jint.Tests.Test262
                     reason = "Unicode support and its special cases need more work";
                 }
 
-                if (name.StartsWith("language/statements/class/subclass/builtin-objects/TypedArray"))
-                {
-                    skip = true;
-                    reason = "TypedArray not implemented";
-                }
-
-                if (name.StartsWith("language/statements/class/subclass/builtins.js"))
-                {
-                    skip = true;
-                    reason = "Uint8Array not implemented";
-                }
-
                 if (name.StartsWith("built-ins/RegExp/CharacterClassEscapes/"))
                 {
                     skip = true;

+ 8 - 8
Jint.Tests.Test262/test/skipped.json

@@ -114,14 +114,6 @@
     "source": "built-ins/Proxy/enumerate/removed-does-not-trigger.js",
     "reason": "for-of not implemented"
   },
-  {
-    "source": "built-ins/Array/prototype/concat/Array.prototype.concat_large-typed-array.js",
-    "reason": "Uint8Array not implemented"
-  },
-  {
-    "source": "built-ins/Array/prototype/concat/Array.prototype.concat_small-typed-array.js",
-    "reason": "Uint8Array not implemented"
-  },
   {
     "source": "built-ins/Map/prototype/forEach/iterates-values-deleted-then-readded.js",
     "reason": "delete/add detection not implemented for map iterator during iteration"
@@ -190,6 +182,10 @@
     "source": "language/expressions/object/accessor-name-computed.js",
     "reason": "yield not implemented"
   },
+  {
+    "source": "built-ins/TypedArrayConstructors/ctors/object-arg/as-generator-iterable-returns.js",
+    "reason": "yield not implemented"
+  },
   {
     "source": "language/expressions/object/prop-dup-set-get-set.js",
     "reason": "accessor not implemented"
@@ -274,6 +270,10 @@
 
   // Esprima problems
 
+  {
+    "source": "language/expressions/object/method-definition/name-super-prop-param.js",
+    "reason": "Esprima problem"
+  },
   {
     "source": "language/expressions/optional-chaining/member-expression.js",
     "reason": "Esprima problem"

+ 1 - 1
Jint.Tests/Jint.Tests.csproj

@@ -17,7 +17,7 @@
   </ItemGroup>
   <ItemGroup>
     <PackageReference Include="Flurl.Http.Signed" Version="3.0.0" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
     <PackageReference Include="MongoDB.Bson.signed" Version="2.11.2" />
     <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
     <PackageReference Include="xunit" Version="2.4.1" />

+ 86 - 0
Jint.Tests/Runtime/TypedArrayInteropTests.cs

@@ -0,0 +1,86 @@
+using Xunit;
+
+namespace Jint.Tests.Runtime
+{
+    public class TypedArrayInteropTests
+    {
+        [Fact]
+        public void CanInteropWithInt8()
+        {
+            var engine = new Engine();
+            engine.SetValue("testSubject", engine.Realm.Intrinsics.Int8Array.Construct(new sbyte[] { 42 }));
+            ValidateCreatedTypeArray(engine, "Int8Array");
+        }
+
+        [Fact]
+        public void CanInteropWithUint8()
+        {
+            var engine = new Engine();
+            engine.SetValue("testSubject", engine.Realm.Intrinsics.Uint8Array.Construct(new byte[] { 42 }));
+            ValidateCreatedTypeArray(engine, "Uint8Array");
+        }
+
+        [Fact]
+        public void CanInteropWithUint8Clamped()
+        {
+            var engine = new Engine();
+            engine.SetValue("testSubject", engine.Realm.Intrinsics.Uint8ClampedArray.Construct(new byte[] { 42 }));
+            ValidateCreatedTypeArray(engine, "Uint8ClampedArray");
+        }
+
+        [Fact]
+        public void CanInteropWithInt16()
+        {
+            var engine = new Engine();
+            engine.SetValue("testSubject", engine.Realm.Intrinsics.Int16Array.Construct(new short[] { 42 }));
+            ValidateCreatedTypeArray(engine, "Int16Array");
+        }
+
+        [Fact]
+        public void CanInteropWithUint16()
+        {
+            var engine = new Engine();
+            engine.SetValue("testSubject", engine.Realm.Intrinsics.Uint16Array.Construct(new ushort[] { 42 }));
+            ValidateCreatedTypeArray(engine, "Uint16Array");
+        }
+
+        [Fact]
+        public void CanInteropWithInt32()
+        {
+            var engine = new Engine();
+            engine.SetValue("testSubject", engine.Realm.Intrinsics.Int32Array.Construct(new int[] { 42 }));
+            ValidateCreatedTypeArray(engine, "Int32Array");
+        }
+
+        [Fact]
+        public void CanInteropWithUint32()
+        {
+            var engine = new Engine();
+            engine.SetValue("testSubject", engine.Realm.Intrinsics.Uint32Array.Construct(new uint[] { 42 }));
+            ValidateCreatedTypeArray(engine, "Uint32Array");
+        }
+
+        [Fact(Skip = "BigInt not implemented")]
+        public void CanInteropWithBigInt64()
+        {
+            var engine = new Engine();
+            engine.SetValue("testSubject", engine.Realm.Intrinsics.BigInt64Array.Construct(new long[] { 42 }));
+            ValidateCreatedTypeArray(engine, "BigInt64Array");
+        }
+
+        [Fact(Skip = "BigInt not implemented")]
+        public void CanInteropWithBigUint64()
+        {
+            var engine = new Engine();
+            engine.SetValue("testSubject", engine.Realm.Intrinsics.BigUint64Array.Construct(new ulong[] { 42 }));
+            ValidateCreatedTypeArray(engine, "BigUint64Array");
+        }
+
+        private static void ValidateCreatedTypeArray(Engine engine, string arrayName)
+        {
+            Assert.Equal(arrayName, engine.Evaluate("testSubject.constructor.name").AsString());
+            Assert.Equal(1, engine.Evaluate("testSubject.length").AsNumber());
+            Assert.Equal(42, engine.Evaluate("testSubject[0]").AsNumber());
+        }
+    }
+}

+ 1 - 1
Jint/Engine.cs

@@ -422,7 +422,7 @@ namespace Jint
         {
             var baseValue = reference.GetBase();
 
-            if (baseValue._type == InternalTypes.Undefined)
+            if (baseValue.IsUndefined())
             {
                 if (_referenceResolver.TryUnresolvableReference(this, reference, out JsValue val))
                 {

+ 1 - 4
Jint/Jint.csproj

@@ -9,10 +9,7 @@
   </PropertyGroup>
   <ItemGroup>
     <PackageReference Include="Esprima" Version="2.0.0" />
-    <PackageReference Include="IsExternalInit" Version="1.0.0">
-      <PrivateAssets>all</PrivateAssets>
-      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
-    </PackageReference>
+    <PackageReference Include="IsExternalInit" Version="1.0.1" PrivateAssets="all" />
     <PackageReference Include="Nullable" Version="1.3.0" PrivateAssets="all" />
   </ItemGroup>
 </Project>

+ 10 - 5
Jint/Native/Argument/ArgumentsInstance.cs

@@ -25,9 +25,10 @@ namespace Jint.Native.Argument
         private DeclarativeEnvironmentRecord _env;
         private bool _canReturnToPool;
         private bool _hasRestParameter;
+        private bool _materialized;
 
         internal ArgumentsInstance(Engine engine)
-            : base(engine, ObjectClass.Arguments, InternalTypes.Object | InternalTypes.RequiresCloning)
+            : base(engine, ObjectClass.Arguments)
         {
         }
 
@@ -236,9 +237,15 @@ namespace Jint.Native.Argument
             return base.Delete(property);
         }
 
-        internal override JsValue DoClone()
+        internal void Materialize()
         {
-            // there's an assignment or return value of function, need to create persistent state
+            if (_materialized)
+            {
+                // already done
+                return;
+            }
+
+            _materialized = true;
 
             EnsureInitialized();
 
@@ -248,8 +255,6 @@ namespace Jint.Native.Argument
             _args = copiedArgs;
 
             _canReturnToPool = false;
-
-            return this;
         }
 
         internal void FunctionWasCalled()

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

@@ -421,7 +421,7 @@ namespace Jint.Native.Array
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal static bool IsArrayIndex(JsValue p, out uint index)
+        private static bool IsArrayIndex(JsValue p, out uint index)
         {
             if (p is JsNumber number)
             {

+ 81 - 42
Jint/Native/Array/ArrayOperations.cs

@@ -2,6 +2,7 @@ using System.Collections;
 using System.Collections.Generic;
 using Jint.Native.Number;
 using Jint.Native.Object;
+using Jint.Native.TypedArray;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 
@@ -19,6 +20,11 @@ namespace Jint.Native.Array
                 return new ArrayInstanceOperations(arrayInstance);
             }
 
+            if (instance is TypedArrayInstance typedArrayInstance)
+            {
+                return new TypedArrayInstanceOperations(typedArrayInstance);
+            }
+
             return new ObjectInstanceOperations(instance);
         }
 
@@ -199,18 +205,14 @@ namespace Jint.Native.Array
             }
 
             public override void SetLength(ulong length)
-            {
-                _target.Set(CommonProperties.Length, length, true);
-            }
+                => _target.Set(CommonProperties.Length, length, true);
 
             public override void EnsureCapacity(ulong capacity)
             {
             }
 
             public override JsValue Get(ulong index)
-            {
-                return _target.Get(JsString.Create(index), _target);
-            }
+                => _target.Get(JsString.Create(index), _target);
 
             public override bool TryGetValue(ulong index, out JsValue value)
             {
@@ -222,19 +224,13 @@ namespace Jint.Native.Array
             }
 
             public override void CreateDataPropertyOrThrow(ulong index, JsValue value)
-            {
-                _target.CreateDataPropertyOrThrow(JsString.Create(index), value);
-            }
+                => _target.CreateDataPropertyOrThrow(JsString.Create(index), value);
 
             public override void Set(ulong index, JsValue value, bool updateLength, bool throwOnError)
-            {
-                _target.Set(JsString.Create(index), value, throwOnError);
-            }
+                => _target.Set(JsString.Create(index), value, throwOnError);
 
             public override void DeletePropertyOrThrow(ulong index)
-            {
-                _target.DeletePropertyOrThrow(JsString.Create(index));
-            }
+                => _target.DeletePropertyOrThrow(JsString.Create(index));
         }
 
         private sealed class ArrayInstanceOperations : ArrayOperations<ArrayInstance>
@@ -244,40 +240,25 @@ namespace Jint.Native.Array
             }
 
             public override ulong GetSmallestIndex(ulong length)
-            {
-                return _target.GetSmallestIndex();
-            }
+                => _target.GetSmallestIndex();
 
             public override uint GetLength()
-            {
-                return (uint) ((JsNumber) _target._length._value)._value;
-            }
+                => (uint) ((JsNumber) _target._length._value)._value;
 
             public override ulong GetLongLength()
-            {
-                return (ulong) ((JsNumber) _target._length._value)._value;
-            }
+                => (ulong) ((JsNumber) _target._length._value)._value;
 
             public override void SetLength(ulong length)
-            {
-                _target.Set(CommonProperties.Length, length, true);
-            }
+                => _target.Set(CommonProperties.Length, length, true);
 
             public override void EnsureCapacity(ulong capacity)
-            {
-                _target.EnsureCapacity((uint) capacity);
-            }
+                => _target.EnsureCapacity((uint) capacity);
 
             public override bool TryGetValue(ulong index, out JsValue value)
-            {
                 // array max size is uint
-                return _target.TryGetValue((uint) index, out value);
-            }
+                => _target.TryGetValue((uint) index, out value);
 
-            public override JsValue Get(ulong index)
-            {
-                return _target.Get((uint) index);
-            }
+            public override JsValue Get(ulong index) => _target.Get((uint) index);
 
             public override JsValue[] GetAll(Types elementTypes)
             {
@@ -311,20 +292,78 @@ namespace Jint.Native.Array
             }
 
             public override void DeletePropertyOrThrow(ulong index)
+                => _target.DeletePropertyOrThrow((uint) index);
+
+            public override void CreateDataPropertyOrThrow(ulong index, JsValue value)
+                => _target.SetIndexValue((uint) index, value, updateLength: false);
+
+            public override void Set(ulong index, JsValue value, bool updateLength, bool throwOnError)
+                => _target.SetIndexValue((uint) index, value, updateLength);
+        }
+
+        private sealed class TypedArrayInstanceOperations : ArrayOperations
+        {
+            private readonly TypedArrayInstance _target;
+
+            public TypedArrayInstanceOperations(TypedArrayInstance target)
             {
-                _target.DeletePropertyOrThrow((uint) index);
+                _target = target;
             }
 
-            public override void CreateDataPropertyOrThrow(ulong index, JsValue value)
+            public override ObjectInstance Target => _target;
+
+            public override ulong GetSmallestIndex(ulong length) => 0;
+
+            public override uint GetLength()
             {
-                _target.SetIndexValue((uint) index, value, updateLength: false);
+                if (!_target.IsConcatSpreadable)
+                {
+                    return _target.Length;
+                }
+
+                var descValue = _target.Get(CommonProperties.Length, _target);
+                if (!ReferenceEquals(descValue, null))
+                {
+                    return (uint) TypeConverter.ToInteger(descValue);
+                }
+
+                return 0;
             }
 
-            public override void Set(ulong index, JsValue value, bool updateLength, bool throwOnError)
+            public override ulong GetLongLength() => GetLength();
+
+            public override void SetLength(ulong length)
             {
-                _target.SetIndexValue((uint) index, value, updateLength);
             }
+
+            public override void EnsureCapacity(ulong capacity)
+            {
+            }
+
+            public override JsValue Get(ulong index) => _target[(int) index];
+
+            public override bool TryGetValue(ulong index, out JsValue value)
+            {
+                if (index < _target.Length)
+                {
+                    value = _target[(int) index];
+                    return true;
+                }
+
+                value = JsValue.Undefined;
+                return false;
+            }
+
+            public override void CreateDataPropertyOrThrow(ulong index, JsValue value)
+                => _target.CreateDataPropertyOrThrow(index, value);
+
+            public override void Set(ulong index, JsValue value, bool updateLength, bool throwOnError)
+                => _target[(int) index] = value;
+
+            public override void DeletePropertyOrThrow(ulong index)
+                => _target.DeletePropertyOrThrow(index);
         }
+
     }
 
     /// <summary>

+ 21 - 31
Jint/Native/Array/ArrayPrototype.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Linq;
 using Jint.Collections;
+using Jint.Native.Iterator;
 using Jint.Native.Number;
 using Jint.Native.Object;
 using Jint.Native.Symbol;
@@ -105,7 +106,7 @@ namespace Jint.Native.Array
         {
             if (thisObj is ObjectInstance oi && oi.IsArrayLike)
             {
-                return _realm.Intrinsics.Iterator.ConstructArrayLikeKeyIterator(oi);
+                return _realm.Intrinsics.Iterator.CreateArrayLikeIterator(oi, ArrayIteratorType.Key);
             }
 
             ExceptionHelper.ThrowTypeError(_realm, "cannot construct iterator");
@@ -116,7 +117,7 @@ namespace Jint.Native.Array
         {
             if (thisObj is ObjectInstance oi && oi.IsArrayLike)
             {
-                return _realm.Intrinsics.Iterator.ConstructArrayLikeValueIterator(oi);
+                return _realm.Intrinsics.Iterator.CreateArrayLikeIterator(oi, ArrayIteratorType.Value);
             }
 
             ExceptionHelper.ThrowTypeError(_realm, "cannot construct iterator");
@@ -127,7 +128,7 @@ namespace Jint.Native.Array
         {
             if (thisObj is ObjectInstance oi && oi.IsArrayLike)
             {
-                return _realm.Intrinsics.Iterator.ConstructArrayLikeEntriesIterator(oi);
+                return _realm.Intrinsics.Iterator.CreateArrayLikeIterator(oi, ArrayIteratorType.KeyAndValue);
             }
 
             ExceptionHelper.ThrowTypeError(_realm, "cannot construct iterator");
@@ -614,15 +615,10 @@ namespace Jint.Native.Array
                     ? n
                     : len - System.Math.Abs(n), 0);
 
-            static bool SameValueZero(JsValue x, JsValue y)
-            {
-                return x == y || (x is JsNumber xNum && y is JsNumber yNum && double.IsNaN(xNum._value) && double.IsNaN(yNum._value));
-            }
-
             while (k < len)
             {
                 var value = o.Get(k);
-                if (SameValueZero(value, searchElement))
+                if (JintBinaryExpression.SameValueZero(value, searchElement))
                 {
                     return true;
                 }
@@ -908,7 +904,7 @@ namespace Jint.Native.Array
         {
             if (!thisObj.IsObject())
             {
-                ExceptionHelper.ThrowTypeError(_realm, "Array.prorotype.sort can only be applied on objects");
+                ExceptionHelper.ThrowTypeError(_realm, "Array.prototype.sort can only be applied on objects");
             }
 
             var obj = ArrayOperations.For(thisObj.AsObject());
@@ -946,17 +942,17 @@ namespace Jint.Native.Array
                     {
                         obj.DeletePropertyOrThrow(i);
                     }
-            }
+                }
             }
             catch (InvalidOperationException e)
             {
-                throw e.InnerException;
+                throw e.InnerException ?? e;
             }
 
             return obj.Target;
         }
 
-        internal JsValue Slice(JsValue thisObj, JsValue[] arguments)
+        private JsValue Slice(JsValue thisObj, JsValue[] arguments)
         {
             var start = arguments.At(0);
             var end = arguments.At(1);
@@ -989,7 +985,7 @@ namespace Jint.Native.Array
                 }
                 else
                 {
-                    final = (ulong) System.Math.Min(TypeConverter.ToInteger(relativeEnd), len);
+                    final = (ulong) System.Math.Min(relativeEnd, len);
                 }
             }
 
@@ -1091,17 +1087,13 @@ namespace Jint.Native.Array
             return o.Target;
         }
 
-        private JsValue Join(JsValue thisObj, JsValue[] arguments)
+        internal JsValue Join(JsValue thisObj, JsValue[] arguments)
         {
             var separator = arguments.At(0);
             var o = ArrayOperations.For(_realm, thisObj);
             var len = o.GetLength();
-            if (separator.IsUndefined())
-            {
-                separator = ",";
-            }
 
-            var sep = TypeConverter.ToString(separator);
+            var sep = TypeConverter.ToString(separator.IsUndefined() ? JsString.CommaString : separator);
 
             // as per the spec, this has to be called after ToString(separator)
             if (len == 0)
@@ -1109,7 +1101,7 @@ namespace Jint.Native.Array
                 return JsString.Empty;
             }
 
-            string StringFromJsValue(JsValue value)
+            static string StringFromJsValue(JsValue value)
             {
                 return value.IsNullOrUndefined()
                     ? ""
@@ -1122,17 +1114,15 @@ namespace Jint.Native.Array
                 return s;
             }
 
-            using (var sb = StringBuilderPool.Rent())
+            using var sb = StringBuilderPool.Rent();
+            sb.Builder.Append(s);
+            for (uint k = 1; k < len; k++)
             {
-                sb.Builder.Append(s);
-                for (uint k = 1; k < len; k++)
-                {
-                    sb.Builder.Append(sep);
-                    sb.Builder.Append(StringFromJsValue(o.Get(k)));
-                }
-
-                return sb.ToString();
+                sb.Builder.Append(sep);
+                sb.Builder.Append(StringFromJsValue(o.Get(k)));
             }
+
+            return sb.ToString();
         }
 
         private JsValue ToLocaleString(JsValue thisObj, JsValue[] arguments)
@@ -1198,7 +1188,7 @@ namespace Jint.Native.Array
             for (var i = 0; i < items.Count; i++)
             {
                 ulong increment;
-                if (!(items[i] is ObjectInstance objectInstance))
+                if (items[i] is not ObjectInstance objectInstance)
                 {
                     increment = 1;
                 }

+ 4 - 3
Jint/Native/ArrayBuffer/ArrayBufferConstructor.cs

@@ -3,6 +3,7 @@ using Jint.Native.DataView;
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Native.Symbol;
+using Jint.Native.TypedArray;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
@@ -53,7 +54,7 @@ namespace Jint.Native.ArrayBuffer
         private static JsValue IsView(JsValue thisObject, JsValue[] arguments)
         {
             var arg = arguments.At(0);
-            return arg is DataViewInstance;
+            return arg is DataViewInstance or TypedArrayInstance;
         }
 
         /// <summary>
@@ -81,12 +82,12 @@ namespace Jint.Native.ArrayBuffer
             return AllocateArrayBuffer(newTarget, byteLength);
         }
 
-        internal ArrayBufferInstance AllocateArrayBuffer(JsValue constructor, uint byteLength)
+        internal ArrayBufferInstance AllocateArrayBuffer(JsValue constructor, ulong byteLength)
         {
             var obj = OrdinaryCreateFromConstructor(
                 constructor,
                 static intrinsics => intrinsics.ArrayBuffer.PrototypeObject,
-                static (engine, realm, state) => new ArrayBufferInstance(engine, (uint) ((JsNumber) state)._value),
+                static (engine, realm, state) => new ArrayBufferInstance(engine, (ulong) ((JsNumber) state)._value),
                 JsNumber.Create(byteLength));
 
             return obj;

+ 51 - 24
Jint/Native/ArrayBuffer/ArrayBufferInstance.cs

@@ -11,20 +11,20 @@ namespace Jint.Native.ArrayBuffer
     public sealed class ArrayBufferInstance : ObjectInstance
     {
         // so that we don't need to allocate while or reading setting values
-        private byte[] _workBuffer = new byte[8];
+        private readonly byte[] _workBuffer = new byte[8];
 
         private byte[] _arrayBufferData;
-        private JsValue _arrayBufferDetachKey = Undefined;
+        private readonly JsValue _arrayBufferDetachKey = Undefined;
 
         internal ArrayBufferInstance(
             Engine engine,
-            uint byteLength) : base(engine)
+            ulong byteLength) : base(engine)
         {
-            var block = CreateByteDataBlock(byteLength);
+            var block = byteLength > 0 ? CreateByteDataBlock(byteLength) : System.Array.Empty<byte>();
             _arrayBufferData = block;
         }
 
-        private byte[] CreateByteDataBlock(uint byteLength)
+        private byte[] CreateByteDataBlock(ulong byteLength)
         {
             if (byteLength > int.MaxValue)
             {
@@ -55,15 +55,42 @@ namespace Jint.Native.ArrayBuffer
             _arrayBufferData = null;
         }
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-clonearraybuffer
+        /// </summary>
+        internal ArrayBufferInstance CloneArrayBuffer(
+            ArrayBufferConstructor constructor,
+            int srcByteOffset,
+            uint srcLength,
+            JsValue cloneConstructor)
+        {
+            var targetBuffer = constructor.AllocateArrayBuffer(cloneConstructor, srcLength);
+            AssertNotDetached();
+            var srcBlock = _arrayBufferData;
+            var targetBlock = targetBuffer.ArrayBufferData;
+
+            // TODO SharedArrayBuffer would use this
+            //CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, srcLength).
+
+            System.Array.Copy(srcBlock, srcByteOffset, targetBlock, 0, srcLength);
+
+            return targetBuffer;
+        }
+
         /// <summary>
         /// https://tc39.es/ecma262/#sec-getvaluefrombuffer
         /// </summary>
-        internal JsValue GetValueFromBuffer(uint byteIndex, TypedArrayElementType type, bool isTypedArray, ArrayBufferOrder order, bool? isLittleEndian = null)
+        internal double GetValueFromBuffer(
+            int byteIndex,
+            TypedArrayElementType type,
+            bool isTypedArray,
+            ArrayBufferOrder order,
+            bool? isLittleEndian = null)
         {
             if (!IsSharedArrayBuffer)
             {
                 // If isLittleEndian is not present, set isLittleEndian to the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
-                return RawBytesToNumeric(type, (int) byteIndex, isLittleEndian ?? BitConverter.IsLittleEndian);
+                return RawBytesToNumeric(type, byteIndex, isLittleEndian ?? BitConverter.IsLittleEndian);
             }
 
             /*
@@ -77,13 +104,13 @@ namespace Jint.Native.ArrayBuffer
                 h. Append Chosen Value EsprimaExtensions.Record { [[Event]]: readEvent, [[ChosenValue]]: rawValue } to execution.[[ChosenValues]].
             */
             ExceptionHelper.ThrowNotImplementedException("SharedArrayBuffer not implemented");
-            return Undefined;
+            return 0;
         }
 
         /// <summary>
         /// https://tc39.es/ecma262/#sec-rawbytestonumeric
         /// </summary>
-        private JsValue RawBytesToNumeric(TypedArrayElementType type, int byteIndex, bool isLittleEndian)
+        private double RawBytesToNumeric(TypedArrayElementType type, int byteIndex, bool isLittleEndian)
         {
             var elementSize = type.GetElementSize();
             var rawBytes = _arrayBufferData;
@@ -106,17 +133,17 @@ namespace Jint.Native.ArrayBuffer
                 // If value is an IEEE 754-2019 binary32 NaN value, return the NaN Number value.
                 if (float.IsNaN(value))
                 {
-                    return JsNumber.DoubleNaN;
+                    return double.NaN;
                 }
 
-                return JsNumber.Create(value);
+                return value;
             }
 
             if (type == TypedArrayElementType.Float64)
             {
                 // rawBytes concatenated and interpreted as a little-endian bit string encoding of an IEEE 754-2019 binary64 value.
                 var value = BitConverter.ToDouble(rawBytes, byteIndex);
-                return JsNumber.Create(value);
+                return value;
             }
 
             if (type.IsBigIntElementType())
@@ -125,24 +152,24 @@ namespace Jint.Native.ArrayBuffer
                 ExceptionHelper.ThrowNotImplementedException("BigInt not implemented");
             }
 
-            var intValue = type switch
+            long? intValue = type switch
             {
-                TypedArrayElementType.Int8 => JsNumber.Create((sbyte) rawBytes[byteIndex]),
-                TypedArrayElementType.Uint8 => JsNumber.Create(rawBytes[byteIndex]),
-                TypedArrayElementType.Uint8C => JsNumber.Create(rawBytes[byteIndex]),
-                TypedArrayElementType.Int16 => JsNumber.Create(isLittleEndian
+                TypedArrayElementType.Int8 => ((sbyte) rawBytes[byteIndex]),
+                TypedArrayElementType.Uint8 => (rawBytes[byteIndex]),
+                TypedArrayElementType.Uint8C =>(rawBytes[byteIndex]),
+                TypedArrayElementType.Int16 => (isLittleEndian
                     ? (short) (rawBytes[byteIndex] | (rawBytes[byteIndex + 1] << 8))
                     : (short) (rawBytes[byteIndex + 1] | (rawBytes[byteIndex] << 8))
                 ),
-                TypedArrayElementType.Uint16 => JsNumber.Create(isLittleEndian
+                TypedArrayElementType.Uint16 => (isLittleEndian
                     ? (ushort) (rawBytes[byteIndex] | (rawBytes[byteIndex + 1] << 8))
                     : (ushort) (rawBytes[byteIndex + 1] | (rawBytes[byteIndex] << 8))
                 ),
-                TypedArrayElementType.Int32 => JsNumber.Create(isLittleEndian
+                TypedArrayElementType.Int32 => (isLittleEndian
                     ? rawBytes[byteIndex] | (rawBytes[byteIndex + 1] << 8) | (rawBytes[byteIndex + 2] << 16) | (rawBytes[byteIndex + 3] << 24)
                     : rawBytes[byteIndex + 3] | (rawBytes[byteIndex + 2] << 8) | (rawBytes[byteIndex + 1] << 16) | (rawBytes[byteIndex + 0] << 24)
                 ),
-                TypedArrayElementType.Uint32 => JsNumber.Create(isLittleEndian
+                TypedArrayElementType.Uint32 => (isLittleEndian
                     ? (uint) (rawBytes[byteIndex] | (rawBytes[byteIndex + 1] << 8) | (rawBytes[byteIndex + 2] << 16) | (rawBytes[byteIndex + 3] << 24))
                     : (uint) (rawBytes[byteIndex + 3] | (rawBytes[byteIndex + 2] << 8) | (rawBytes[byteIndex + 1] << 16) | (rawBytes[byteIndex] << 24))
                 ),
@@ -154,14 +181,14 @@ namespace Jint.Native.ArrayBuffer
                 ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(type), type.ToString());
             }
 
-            return intValue;
+            return (double) intValue;
         }
 
         /// <summary>
         /// https://tc39.es/ecma262/#sec-setvalueinbuffer
         /// </summary>
         internal void SetValueInBuffer(
-            uint byteIndex,
+            int byteIndex,
             TypedArrayElementType type,
             double value,
             bool isTypedArray,
@@ -173,7 +200,7 @@ namespace Jint.Native.ArrayBuffer
             {
                 // If isLittleEndian is not present, set isLittleEndian to the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
                 var rawBytes = NumericToRawBytes(type, value, isLittleEndian ?? BitConverter.IsLittleEndian);
-                System.Array.Copy(rawBytes, 0, block, (int) byteIndex, type.GetElementSize());
+                System.Array.Copy(rawBytes, 0, block,  byteIndex, type.GetElementSize());
             }
             else
             {
@@ -214,7 +241,7 @@ namespace Jint.Native.ArrayBuffer
                         rawBytes[0] = (byte) intValue;
                         break;
                     case TypedArrayElementType.Uint8C:
-                        rawBytes[0] = System.Math.Min(System.Math.Max((byte) intValue, (byte) 0), (byte) 255);
+                        rawBytes[0] = (byte) TypeConverter.ToUint8Clamp(value);
                         break;
                     case TypedArrayElementType.Int16:
 #if !NETSTANDARD2_1

+ 1 - 1
Jint/Native/Boolean/BooleanConstructor.cs

@@ -18,7 +18,7 @@ namespace Jint.Native.Boolean
         {
             _prototype = functionPrototype;
             PrototypeObject = new BooleanPrototype(engine, realm, this, objectPrototype);
-            _length = new PropertyDescriptor(JsNumber.One, PropertyFlag.Configurable);
+            _length = new PropertyDescriptor(JsNumber.PositiveOne, PropertyFlag.Configurable);
             _prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
         }
 

+ 3 - 3
Jint/Native/DataView/DataViewPrototype.cs

@@ -231,7 +231,7 @@ namespace Jint.Native.DataView
                 ExceptionHelper.ThrowTypeError(_realm, "Method called on incompatible receiver " + view);
             }
 
-            var getIndex = TypeConverter.ToIndex(_realm, requestIndex);
+            var getIndex = (int) TypeConverter.ToIndex(_realm, requestIndex);
             var isLittleEndianBoolean = TypeConverter.ToBoolean(isLittleEndian);
             var buffer = dataView._viewedArrayBuffer;
 
@@ -245,7 +245,7 @@ namespace Jint.Native.DataView
                 ExceptionHelper.ThrowRangeError(_realm, "Offset is outside the bounds of the DataView");
             }
 
-            var bufferIndex = getIndex + viewOffset;
+            var bufferIndex = (int) (getIndex + viewOffset);
             return buffer.GetValueFromBuffer(bufferIndex, type, false, ArrayBufferOrder.Unordered, isLittleEndianBoolean);
         }
 
@@ -291,7 +291,7 @@ namespace Jint.Native.DataView
                 ExceptionHelper.ThrowRangeError(_realm, "Offset is outside the bounds of the DataView");
             }
 
-            var bufferIndex = getIndex + viewOffset;
+            var bufferIndex = (int) (getIndex + viewOffset);
             buffer.SetValueInBuffer(bufferIndex, type, numberValue, false, ArrayBufferOrder.Unordered, isLittleEndianBoolean);
             return Undefined;
         }

+ 2 - 2
Jint/Native/Date/DateConstructor.cs

@@ -107,7 +107,7 @@ namespace Jint.Native.Date
         {
             var y = TypeConverter.ToNumber(arguments.At(0));
             var m = TypeConverter.ToNumber(arguments.At(1, JsNumber.PositiveZero));
-            var dt = TypeConverter.ToNumber(arguments.At(2, JsNumber.One));
+            var dt = TypeConverter.ToNumber(arguments.At(2, JsNumber.PositiveOne));
             var h = TypeConverter.ToNumber(arguments.At(3, JsNumber.PositiveZero));
             var min = TypeConverter.ToNumber(arguments.At(4, JsNumber.PositiveZero));
             var s = TypeConverter.ToNumber(arguments.At(5, JsNumber.PositiveZero));
@@ -165,7 +165,7 @@ namespace Jint.Native.Date
             {
                 var y = TypeConverter.ToNumber(arguments.At(0));
                 var m = TypeConverter.ToNumber(arguments.At(1));
-                var dt = TypeConverter.ToNumber(arguments.At(2, JsNumber.One));
+                var dt = TypeConverter.ToNumber(arguments.At(2, JsNumber.PositiveOne));
                 var h = TypeConverter.ToNumber(arguments.At(3, JsNumber.PositiveZero));
                 var min = TypeConverter.ToNumber(arguments.At(4, JsNumber.PositiveZero));
                 var s = TypeConverter.ToNumber(arguments.At(5, JsNumber.PositiveZero));

+ 1 - 1
Jint/Native/Function/FunctionConstructor.cs

@@ -22,7 +22,7 @@ namespace Jint.Native.Function
             PrototypeObject = new FunctionPrototype(engine, realm, objectPrototype);
             _prototype = PrototypeObject;
             _prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
-            _length = new PropertyDescriptor(JsNumber.One, PropertyFlag.Configurable);
+            _length = new PropertyDescriptor(JsNumber.PositiveOne, PropertyFlag.Configurable);
         }
 
         public FunctionPrototype PrototypeObject { get; }

+ 1 - 1
Jint/Native/Function/FunctionInstance.cs

@@ -25,7 +25,7 @@ namespace Jint.Native.Function
         internal ConstructorKind _constructorKind = ConstructorKind.Base;
 
         internal Realm _realm;
-        private PrivateEnvironmentRecord  _privateEnvironment;
+        private PrivateEnvironmentRecord _privateEnvironment;
 
         protected FunctionInstance(
             Engine engine,

+ 12 - 0
Jint/Native/Global/GlobalObject.cs

@@ -41,6 +41,18 @@ namespace Jint.Native.Global
                 ["Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Array, propertyFlags),
                 ["ArrayBuffer"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.ArrayBuffer, propertyFlags),
                 ["DataView"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.DataView, propertyFlags),
+                ["TypedArray"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.TypedArray, propertyFlags),
+                ["Int8Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Int8Array, propertyFlags),
+                ["Uint8Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Uint8Array, propertyFlags),
+                ["Uint8ClampedArray"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Uint8ClampedArray, propertyFlags),
+                ["Int16Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Int16Array, propertyFlags),
+                ["Uint16Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Uint16Array, propertyFlags),
+                ["Int32Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Int32Array, propertyFlags),
+                ["Uint32Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Uint32Array, propertyFlags),
+                ["BigInt64Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.BigInt64Array, propertyFlags),
+                ["BigUint64Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.BigUint64Array, propertyFlags),
+                ["Float32Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Float32Array, propertyFlags),
+                ["Float64Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Float64Array, propertyFlags),
                 ["Map"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Map, propertyFlags),
                 ["Set"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Set, propertyFlags),
                 ["WeakMap"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.WeakMap, propertyFlags),

+ 9 - 0
Jint/Native/Iterator/ArrayIteratorType.cs

@@ -0,0 +1,9 @@
+namespace Jint.Native.Iterator
+{
+    internal enum ArrayIteratorType
+    {
+        Key,
+        Value,
+        KeyAndValue
+    }
+}

+ 12 - 32
Jint/Native/Iterator/IteratorConstructor.cs

@@ -44,7 +44,7 @@ namespace Jint.Native.Iterator
             return Construct(Enumerable.Empty<JsValue>());
         }
 
-        internal ObjectInstance Construct(IEnumerable<JsValue> enumerable)
+        internal IteratorInstance Construct(IEnumerable<JsValue> enumerable)
         {
             var instance = new IteratorInstance(Engine, enumerable)
             {
@@ -54,7 +54,7 @@ namespace Jint.Native.Iterator
             return instance;
         }
 
-        internal ObjectInstance Construct(List<JsValue> enumerable)
+        internal IteratorInstance Construct(List<JsValue> enumerable)
         {
             var instance = new IteratorInstance.ListIterator(Engine, enumerable)
             {
@@ -64,9 +64,9 @@ namespace Jint.Native.Iterator
             return instance;
         }
 
-        internal ObjectInstance Construct(ObjectInstance array, Func<Intrinsics, Prototype> prototypeSelector)
+        internal IteratorInstance Construct(ObjectInstance array, Func<Intrinsics, Prototype> prototypeSelector)
         {
-            var instance = new IteratorInstance.ArrayLikeIterator(Engine, array)
+            var instance = new IteratorInstance.ArrayLikeIterator(Engine, array, ArrayIteratorType.KeyAndValue)
             {
                 _prototype = prototypeSelector(_realm.Intrinsics)
             };
@@ -74,7 +74,7 @@ namespace Jint.Native.Iterator
             return instance;
         }
 
-        internal ObjectInstance ConstructEntryIterator(MapInstance map)
+        internal IteratorInstance ConstructEntryIterator(MapInstance map)
         {
             var instance = new IteratorInstance.MapIterator(Engine, map)
             {
@@ -84,7 +84,7 @@ namespace Jint.Native.Iterator
             return instance;
         }
 
-        internal ObjectInstance ConstructKeyIterator(MapInstance map)
+        internal IteratorInstance ConstructKeyIterator(MapInstance map)
         {
             var instance = new IteratorInstance(Engine, map._map.Keys)
             {
@@ -94,7 +94,7 @@ namespace Jint.Native.Iterator
             return instance;
         }
 
-        internal ObjectInstance ConstructValueIterator(MapInstance map)
+        internal IteratorInstance ConstructValueIterator(MapInstance map)
         {
             var instance = new IteratorInstance(Engine, map._map.Values)
             {
@@ -104,7 +104,7 @@ namespace Jint.Native.Iterator
             return instance;
         }
 
-        internal ObjectInstance ConstructEntryIterator(SetInstance set)
+        internal IteratorInstance ConstructEntryIterator(SetInstance set)
         {
             var instance = new IteratorInstance.SetEntryIterator(Engine, set)
             {
@@ -114,7 +114,7 @@ namespace Jint.Native.Iterator
             return instance;
         }
 
-        internal ObjectInstance ConstructValueIterator(SetInstance set)
+        internal IteratorInstance ConstructValueIterator(SetInstance set)
         {
             var instance = new IteratorInstance.ListIterator(Engine, set._set._list)
             {
@@ -124,9 +124,9 @@ namespace Jint.Native.Iterator
             return instance;
         }
 
-        internal ObjectInstance ConstructArrayLikeKeyIterator(ObjectInstance array)
+        internal IteratorInstance CreateArrayLikeIterator(ObjectInstance array, ArrayIteratorType kind)
         {
-            var instance = new IteratorInstance.ArrayLikeKeyIterator(Engine, array)
+            var instance = new IteratorInstance.ArrayLikeIterator(Engine, array, kind)
             {
                 _prototype = ArrayIteratorPrototypeObject
             };
@@ -134,27 +134,7 @@ namespace Jint.Native.Iterator
             return instance;
         }
 
-        internal ObjectInstance ConstructArrayLikeValueIterator(ObjectInstance array)
-        {
-            var instance = new IteratorInstance.ArrayLikeValueIterator(Engine, array)
-            {
-                _prototype = ArrayIteratorPrototypeObject
-            };
-
-            return instance;
-        }
-
-        internal ObjectInstance ConstructArrayLikeEntriesIterator(ObjectInstance array)
-        {
-            var instance = new IteratorInstance.ArrayLikeEntriesIterator(Engine, array)
-            {
-                _prototype = ArrayIteratorPrototypeObject
-            };
-
-            return instance;
-        }
-
-        internal ObjectInstance CreateRegExpStringIterator(ObjectInstance iteratingRegExp, string iteratedString, bool global, bool unicode)
+        internal IteratorInstance CreateRegExpStringIterator(ObjectInstance iteratingRegExp, string iteratedString, bool global, bool unicode)
         {
             var instance = new IteratorInstance.RegExpStringIterator(Engine, iteratingRegExp, iteratedString, global, unicode)
             {

+ 53 - 102
Jint/Native/Iterator/IteratorInstance.cs

@@ -7,6 +7,7 @@ using Jint.Native.Map;
 using Jint.Native.Object;
 using Jint.Native.RegExp;
 using Jint.Native.Set;
+using Jint.Native.TypedArray;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 
@@ -62,7 +63,7 @@ namespace Jint.Native.Iterator
             return obj;
         }
 
-        private class KeyValueIteratorPosition : ObjectInstance
+        private sealed class KeyValueIteratorPosition : ObjectInstance
         {
             internal static readonly ObjectInstance Done = new KeyValueIteratorPosition(null, null, null);
 
@@ -80,7 +81,7 @@ namespace Jint.Native.Iterator
             }
         }
 
-        private class ValueIteratorPosition : ObjectInstance
+        private sealed class ValueIteratorPosition : ObjectInstance
         {
             internal static readonly ObjectInstance Done = new KeyValueIteratorPosition(null, null, null);
 
@@ -95,7 +96,7 @@ namespace Jint.Native.Iterator
             }
         }
 
-        public class MapIterator : IteratorInstance
+        public sealed class MapIterator : IteratorInstance
         {
             private readonly MapInstance _map;
 
@@ -124,44 +125,7 @@ namespace Jint.Native.Iterator
             }
         }
 
-        public class ArrayLikeIterator : IteratorInstance
-        {
-            private readonly ArrayOperations _array;
-            private uint? _end;
-            private uint _position;
-
-            public ArrayLikeIterator(Engine engine, JsValue target) : base(engine)
-            {
-                var objectInstance = target as ObjectInstance;
-                if (objectInstance is null)
-                {
-                    ExceptionHelper.ThrowTypeError(engine.Realm, "Target must be an object");
-                }
-
-                _array = ArrayOperations.For(objectInstance);
-                _position = 0;
-            }
-
-            public override bool TryIteratorStep(out ObjectInstance nextItem)
-            {
-                if (_end == null)
-                {
-                    _end = _array.GetLength();
-                }
-
-                if (_position < _end.Value)
-                {
-                    _array.TryGetValue(_position, out var value);
-                    nextItem = new KeyValueIteratorPosition(_engine, _position++, value);
-                    return true;
-                }
-
-                nextItem = KeyValueIteratorPosition.Done;
-                return false;
-            }
-        }
-
-        public class SetEntryIterator : IteratorInstance
+        public sealed class SetEntryIterator : IteratorInstance
         {
             private readonly SetInstance _set;
             private int _position;
@@ -187,7 +151,7 @@ namespace Jint.Native.Iterator
             }
         }
 
-        public class ListIterator : IteratorInstance
+        public sealed class ListIterator : IteratorInstance
         {
             private readonly List<JsValue> _values;
             private int _position;
@@ -215,80 +179,67 @@ namespace Jint.Native.Iterator
             }
         }
 
-        public class ArrayLikeKeyIterator : IteratorInstance
+        internal sealed class ArrayLikeIterator : IteratorInstance
         {
+            private readonly ArrayIteratorType _kind;
+            private readonly TypedArrayInstance _typedArray;
             private readonly ArrayOperations _operations;
             private uint _position;
             private bool _closed;
 
-            public ArrayLikeKeyIterator(Engine engine, ObjectInstance objectInstance) : base(engine)
+            public ArrayLikeIterator(Engine engine, ObjectInstance objectInstance, ArrayIteratorType kind) : base(engine)
             {
-                _operations = ArrayOperations.For(objectInstance);
-                _position = 0;
-            }
-
-            public override bool TryIteratorStep(out ObjectInstance nextItem)
-            {
-                var length = _operations.GetLength();
-                if (!_closed && _position < length)
+                _kind = kind;
+                _typedArray = objectInstance as TypedArrayInstance;
+                if (_typedArray is null)
                 {
-                    nextItem = new ValueIteratorPosition(_engine, _position++);
-                    return true;
+                    _operations = ArrayOperations.For(objectInstance);
                 }
-
-                _closed = true;
-                nextItem = KeyValueIteratorPosition.Done;
-                return false;
-            }
-        }
-
-        public class ArrayLikeValueIterator : IteratorInstance
-        {
-            private readonly ArrayOperations _operations;
-            private uint _position;
-            private bool _closed;
-
-            public ArrayLikeValueIterator(Engine engine, ObjectInstance objectInstance) : base(engine)
-            {
-                _operations = ArrayOperations.For(objectInstance);
                 _position = 0;
             }
 
             public override bool TryIteratorStep(out ObjectInstance nextItem)
             {
-                var length = _operations.GetLength();
-                if (!_closed && _position < length)
+                uint len;
+                if (_typedArray is not null)
                 {
-                    _operations.TryGetValue(_position++, out var value);
-                    nextItem = new ValueIteratorPosition(_engine, value);
-                    return true;
+                    _typedArray._viewedArrayBuffer.AssertNotDetached();
+                    len = _typedArray.Length;
+                }
+                else
+                {
+                    len = _operations.GetLength();
                 }
 
-                _closed = true;
-                nextItem = KeyValueIteratorPosition.Done;
-                return false;
-            }
-        }
-
-        public class ArrayLikeEntriesIterator : IteratorInstance
-        {
-            private readonly ArrayOperations _operations;
-            private uint _position;
-            private bool _closed;
-
-            public ArrayLikeEntriesIterator(Engine engine, ObjectInstance objectInstance) : base(engine)
-            {
-                _operations = ArrayOperations.For(objectInstance);
-                _position = 0;
-            }
-
-            public override bool TryIteratorStep(out ObjectInstance nextItem)
-            {
-                var length = _operations.GetLength();
-                if (!_closed && _position < length)
+                if (!_closed && _position < len)
                 {
-                    _operations.TryGetValue(_position, out var value);
-                    nextItem = new KeyValueIteratorPosition(_engine, _position, value);
+                    JsValue value;
+                    if (_typedArray is not null)
+                    {
+                        nextItem = _kind switch
+                        {
+                            ArrayIteratorType.Key => new ValueIteratorPosition(_engine, _position),
+                            ArrayIteratorType.Value => new ValueIteratorPosition(_engine, _typedArray[(int) _position]),
+                            _ => new KeyValueIteratorPosition(_engine, _position, _typedArray[(int) _position])
+                        };
+                    }
+                    else
+                    {
+                        _operations.TryGetValue(_position, out value);
+                        if (_kind == ArrayIteratorType.Key)
+                        {
+                            nextItem = new ValueIteratorPosition(_engine, _position);
+                        }
+                        else if (_kind == ArrayIteratorType.Value)
+                        {
+                            nextItem = new ValueIteratorPosition(_engine, value);
+                        }
+                        else
+                        {
+                            nextItem = new KeyValueIteratorPosition(_engine, _position, value);
+                        }
+                    }
+
                     _position++;
                     return true;
                 }
@@ -299,7 +250,7 @@ namespace Jint.Native.Iterator
             }
         }
 
-        internal class ObjectIterator : IIterator
+        internal sealed class ObjectIterator : IIterator
         {
             private readonly ObjectInstance _target;
             private readonly ICallable _nextMethod;
@@ -372,7 +323,7 @@ namespace Jint.Native.Iterator
             }
         }
 
-        internal class StringIterator : IteratorInstance
+        internal sealed class StringIterator : IteratorInstance
         {
             private readonly TextElementEnumerator _iterator;
 
@@ -394,7 +345,7 @@ namespace Jint.Native.Iterator
             }
         }
 
-        internal class RegExpStringIterator : IteratorInstance
+        internal sealed class RegExpStringIterator : IteratorInstance
         {
             private readonly RegExpInstance _iteratingRegExp;
             private readonly string _s;

+ 4 - 4
Jint/Native/JsNumber.cs

@@ -25,12 +25,12 @@ namespace Jint.Native
         internal static readonly JsNumber DoubleNegativeOne = new JsNumber((double) -1);
         internal static readonly JsNumber DoublePositiveInfinity = new JsNumber(double.PositiveInfinity);
         internal static readonly JsNumber DoubleNegativeInfinity = new JsNumber(double.NegativeInfinity);
-        private static readonly JsNumber IntegerNegativeOne = new JsNumber(-1);
+        internal static readonly JsNumber IntegerNegativeOne = new JsNumber(-1);
         internal static readonly JsNumber NegativeZero = new JsNumber(-0d);
         internal static readonly JsNumber PositiveZero = new JsNumber(+0);
-        internal static readonly JsNumber One = new JsNumber(1);
-        internal static readonly JsNumber Two = new JsNumber(2);
-        internal static readonly JsNumber Three = new JsNumber(3);
+        internal static readonly JsNumber PositiveOne = new JsNumber(1);
+        internal static readonly JsNumber PositiveTwo = new JsNumber(2);
+        internal static readonly JsNumber PositiveThree = new JsNumber(3);
 
         internal static readonly JsNumber PI = new JsNumber(System.Math.PI);
 

+ 1 - 0
Jint/Native/JsString.cs

@@ -27,6 +27,7 @@ namespace Jint.Native
         internal static readonly JsString TrueString = new JsString("true");
         internal static readonly JsString FalseString = new JsString("false");
         internal static readonly JsString LengthString = new JsString("length");
+        internal static readonly JsValue CommaString = new JsString(",");
 
         internal string _value;
 

+ 1 - 1
Jint/Native/Number/NumberConstructor.cs

@@ -24,7 +24,7 @@ namespace Jint.Native.Number
         {
             _prototype = functionPrototype;
             PrototypeObject = new NumberPrototype(engine, realm, this, objectPrototype);
-            _length = new PropertyDescriptor(JsNumber.One, PropertyFlag.Configurable);
+            _length = new PropertyDescriptor(JsNumber.PositiveOne, PropertyFlag.Configurable);
             _prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
         }
 

+ 4 - 4
Jint/Native/Object/ObjectInstance.cs

@@ -83,7 +83,7 @@ namespace Jint.Native.Object
         /// <summary>
         /// https://tc39.es/ecma262/#sec-construct
         /// </summary>
-        internal ObjectInstance Construct(IConstructor f, JsValue[] argumentsList = null, IConstructor newTarget = null)
+        internal static ObjectInstance Construct(IConstructor f, JsValue[] argumentsList = null, IConstructor newTarget = null)
         {
             newTarget ??= f;
             argumentsList ??= System.Array.Empty<JsValue>();
@@ -893,15 +893,15 @@ namespace Jint.Native.Object
                 case ObjectClass.Array:
                     if (this is ArrayInstance arrayInstance)
                     {
-                        var len = TypeConverter.ToInt32(arrayInstance.Get(CommonProperties.Length, arrayInstance));
+                        var len = arrayInstance.Length;
                         var result = new object[len];
-                        for (var k = 0; k < len; k++)
+                        for (uint k = 0; k < len; k++)
                         {
                             var pk = TypeConverter.ToJsString(k);
                             var kpresent = arrayInstance.HasProperty(pk);
                             if (kpresent)
                             {
-                                var kvalue = arrayInstance.Get(pk, arrayInstance);
+                                var kvalue = arrayInstance.Get(k);
                                 result[k] = kvalue.ToObject();
                             }
                             else

+ 1 - 1
Jint/Native/String/StringConstructor.cs

@@ -25,7 +25,7 @@ namespace Jint.Native.String
         {
             _prototype = functionPrototype;
             PrototypeObject = new StringPrototype(engine, realm, this, objectPrototype);
-            _length = new PropertyDescriptor(JsNumber.One, PropertyFlag.Configurable);
+            _length = new PropertyDescriptor(JsNumber.PositiveOne, PropertyFlag.Configurable);
             _prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
         }
 

+ 198 - 0
Jint/Native/TypedArray/IntrinsicTypedArrayConstructor.cs

@@ -0,0 +1,198 @@
+using Jint.Collections;
+using Jint.Native.Function;
+using Jint.Native.Object;
+using Jint.Native.Symbol;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Interop;
+
+namespace Jint.Native.TypedArray
+{
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-%typedarray%-intrinsic-object
+    /// </summary>
+    internal class IntrinsicTypedArrayConstructor : FunctionInstance, IConstructor
+    {
+        internal IntrinsicTypedArrayConstructor(
+            Engine engine,
+            Realm realm,
+            ObjectInstance functionPrototype,
+            ObjectInstance objectPrototype,
+            string functionName) : base(engine, realm, new JsString(functionName))
+        {
+            _prototype = functionPrototype;
+            PrototypeObject = new IntrinsicTypedArrayPrototype(engine, realm, objectPrototype, this);
+            _length = new PropertyDescriptor(JsNumber.PositiveZero, PropertyFlag.Configurable);
+            _prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
+        }
+
+        public IntrinsicTypedArrayPrototype PrototypeObject { get; }
+
+        protected override void Initialize()
+        {
+            var properties = new PropertyDictionary(2, false)
+            {
+                ["from"] = new(new PropertyDescriptor(new ClrFunctionInstance(Engine, "from", From, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable)),
+                ["of"] = new(new PropertyDescriptor(new ClrFunctionInstance(Engine, "of", Of, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable))
+            };
+            SetProperties(properties);
+
+            var symbols = new SymbolDictionary(1)
+            {
+                [GlobalSymbolRegistry.Species] = new GetSetPropertyDescriptor(new ClrFunctionInstance(Engine, "get [Symbol.species]", Species, 0, PropertyFlag.Configurable), Undefined, PropertyFlag.Configurable)
+            };
+            SetSymbols(symbols);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.from
+        /// </summary>
+        private JsValue From(JsValue thisObj, JsValue[] arguments)
+        {
+            var c = thisObj;
+            if (!c.IsConstructor)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            var source = arguments.At(0);
+            var mapFunction = arguments.At(1);
+            var thisArg = arguments.At(2);
+
+            var mapping = !mapFunction.IsUndefined();
+            if (mapping)
+            {
+                if (!mapFunction.IsCallable)
+                {
+                    ExceptionHelper.ThrowTypeError(_realm);
+                }
+            }
+
+            var usingIterator = GetMethod(_realm, source, GlobalSymbolRegistry.Iterator);
+            if (usingIterator is not null)
+            {
+                var values = TypedArrayConstructor.IterableToList(_realm, source, usingIterator);
+                var iteratorLen = values.Count;
+                var iteratorTarget = TypedArrayCreate((IConstructor) c, new JsValue[] { iteratorLen });
+                for (var k = 0; k < iteratorLen; ++k)
+                {
+                    var kValue = values[k];
+                    var mappedValue = mapping
+                        ? ((ICallable) mapFunction).Call(thisArg, new[] { kValue, k })
+                        : kValue;
+                    iteratorTarget[k] = mappedValue;
+                }
+
+                return iteratorTarget;
+            }
+
+            if (source.IsNullOrUndefined())
+            {
+                ExceptionHelper.ThrowTypeError(_realm, "Cannot convert undefined or null to object");
+            }
+
+            var arrayLike = TypeConverter.ToObject(_realm, source);
+            var len = arrayLike.Length;
+
+            var argumentList = new JsValue[] { JsNumber.Create(len) };
+            var targetObj = TypedArrayCreate((IConstructor) c, argumentList);
+
+            var mappingArgs = mapping ? new JsValue[2] : null;
+            for (uint k = 0; k < len; ++k)
+            {
+                var Pk = JsNumber.Create(k);
+                var kValue = arrayLike.Get(Pk);
+                JsValue mappedValue;
+                if (mapping)
+                {
+                    mappingArgs[0] = kValue;
+                    mappingArgs[1] = Pk;
+                    mappedValue = ((ICallable) mapFunction).Call(thisArg, mappingArgs);
+                }
+                else
+                {
+                    mappedValue = kValue;
+                }
+
+                targetObj.Set(Pk, mappedValue, true);
+            }
+
+            return targetObj;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.of
+        /// </summary>
+        private JsValue Of(JsValue thisObj, JsValue[] items)
+        {
+            var len = items.Length;
+
+            if (!thisObj.IsConstructor)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            var newObj = TypedArrayCreate((IConstructor) thisObj, new JsValue[] { len });
+
+            var k = 0;
+            while (k < len)
+            {
+                var kValue = items[k];
+                newObj[k] = kValue;
+                k++;
+            }
+
+            return newObj;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#typedarray-species-create
+        /// </summary>
+        internal TypedArrayInstance TypedArraySpeciesCreate(TypedArrayInstance exemplar, JsValue[] argumentList)
+        {
+            var defaultConstructor = exemplar._arrayElementType.GetConstructor(_realm.Intrinsics);
+            var constructor = SpeciesConstructor(exemplar, defaultConstructor);
+            var result = TypedArrayCreate(constructor, argumentList);
+            if (result._contentType != exemplar._contentType)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            return result;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#typedarray-create
+        /// </summary>
+        private TypedArrayInstance TypedArrayCreate(IConstructor constructor, JsValue[] argumentList)
+        {
+            var newTypedArray = Construct(constructor, argumentList).ValidateTypedArray(_realm);
+            if (argumentList.Length == 1 && argumentList[0] is JsNumber number)
+            {
+                if (newTypedArray.Length < number._value)
+                {
+                    ExceptionHelper.ThrowTypeError(_realm);
+                }
+            }
+
+            return newTypedArray;
+        }
+
+        private static JsValue Species(JsValue thisObject, JsValue[] arguments)
+        {
+            return thisObject;
+        }
+
+        public override JsValue Call(JsValue thisObject, JsValue[] arguments)
+        {
+            ExceptionHelper.ThrowTypeError(_realm, "Abstract class TypedArray not directly constructable");
+            return Undefined;
+        }
+
+        public ObjectInstance Construct(JsValue[] args, JsValue newTarget)
+        {
+            ExceptionHelper.ThrowTypeError(_realm, "Abstract class TypedArray not directly constructable");
+            return null;
+        }
+    }
+}

+ 1362 - 0
Jint/Native/TypedArray/IntrinsicTypedArrayPrototype.cs

@@ -0,0 +1,1362 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Jint.Collections;
+using Jint.Native.Array;
+using Jint.Native.ArrayBuffer;
+using Jint.Native.Iterator;
+using Jint.Native.Number;
+using Jint.Native.Object;
+using Jint.Native.Symbol;
+using Jint.Pooling;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Interop;
+using Jint.Runtime.Interpreter.Expressions;
+
+namespace Jint.Native.TypedArray
+{
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-properties-of-the-%typedarrayprototype%-object
+    /// </summary>
+    internal sealed class IntrinsicTypedArrayPrototype : ObjectInstance
+    {
+        private readonly Realm _realm;
+        private readonly IntrinsicTypedArrayConstructor _constructor;
+        private ClrFunctionInstance _originalIteratorFunction;
+
+        internal IntrinsicTypedArrayPrototype(
+            Engine engine,
+            Realm realm,
+            ObjectInstance objectPrototype,
+            IntrinsicTypedArrayConstructor constructor) : base(engine)
+        {
+            _prototype = objectPrototype;
+            _realm = realm;
+            _constructor = constructor;
+        }
+
+        protected override void Initialize()
+        {
+            const PropertyFlag lengthFlags = PropertyFlag.Configurable;
+            const PropertyFlag propertyFlags = PropertyFlag.Writable | PropertyFlag.Configurable;
+            var properties = new PropertyDictionary(31, 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),
+                ["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),
+                ["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)
+            };
+            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)
+            };
+            SetSymbols(symbols);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.buffer
+        /// </summary>
+        private JsValue Buffer(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj as TypedArrayInstance;
+            if (o is null)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            return o._viewedArrayBuffer;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.bytelength
+        /// </summary>
+        private JsValue ByteLength(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj as TypedArrayInstance;
+            if (o is null)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            if (o._viewedArrayBuffer.IsDetachedBuffer)
+            {
+                return JsNumber.PositiveZero;
+            }
+
+            return JsNumber.Create(o._byteLength);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.byteoffset
+        /// </summary>
+        private JsValue ByteOffset(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj as TypedArrayInstance;
+            if (o is null)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            if (o._viewedArrayBuffer.IsDetachedBuffer)
+            {
+                return JsNumber.PositiveZero;
+            }
+
+            return JsNumber.Create(o._byteOffset);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.length
+        /// </summary>
+        private JsValue GetLength(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj as TypedArrayInstance;
+            if (o is null)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            var buffer = o._viewedArrayBuffer;
+            if (buffer.IsDetachedBuffer)
+            {
+                return JsNumber.PositiveZero;
+            }
+
+            return JsNumber.Create(o.Length);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.copywithin
+        /// </summary>
+        private JsValue CopyWithin(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj.ValidateTypedArray(_realm);
+
+            var target = arguments.At(0);
+            var start = arguments.At(1);
+            var end = arguments.At(2);
+
+            long len = o.Length;
+
+            var relativeTarget = TypeConverter.ToIntegerOrInfinity(target);
+
+            long to;
+            if (double.IsNegativeInfinity(relativeTarget))
+            {
+                to = 0;
+            }
+            else if (relativeTarget < 0)
+            {
+                to = (long) System.Math.Max(len + relativeTarget, 0);
+            }
+            else
+            {
+                to = (long) System.Math.Min(relativeTarget, len);
+            }
+
+            var relativeStart = TypeConverter.ToIntegerOrInfinity(start);
+
+            long from;
+            if (double.IsNegativeInfinity(relativeStart))
+            {
+                from = 0;
+            }
+            else if (relativeStart < 0)
+            {
+                from = (long) System.Math.Max(len + relativeStart, 0);
+            }
+            else
+            {
+                from = (long) System.Math.Min(relativeStart, len);
+            }
+
+            var relativeEnd = end.IsUndefined()
+                ? len
+                : TypeConverter.ToIntegerOrInfinity(end);
+
+            long final;
+            if (double.IsNegativeInfinity(relativeEnd))
+            {
+                final = 0;
+            }
+            else if (relativeEnd < 0)
+            {
+                final = (long) System.Math.Max(len + relativeEnd, 0);
+            }
+            else
+            {
+                final = (long) System.Math.Min(relativeEnd, len);
+            }
+
+            var count = System.Math.Min(final - from, len - to);
+
+            if (count > 0)
+            {
+                var buffer = o._viewedArrayBuffer;
+                buffer.AssertNotDetached();
+
+                var elementSize = o._arrayElementType.GetElementSize();
+                var byteOffset = o._byteOffset;
+                var toByteIndex = to * elementSize + byteOffset;
+                var fromByteIndex = from * elementSize + byteOffset;
+                var countBytes = count * elementSize;
+
+                int direction;
+                if (fromByteIndex < toByteIndex && toByteIndex < fromByteIndex + countBytes)
+                {
+                    direction = -1;
+                    fromByteIndex = fromByteIndex + countBytes - 1;
+                    toByteIndex = toByteIndex + countBytes - 1;
+                }
+                else
+                {
+                    direction = 1;
+                }
+
+                while (countBytes > 0)
+                {
+                    var value = buffer.GetValueFromBuffer((int) fromByteIndex, TypedArrayElementType.Uint8, true, ArrayBufferOrder.Unordered);
+                    buffer.SetValueInBuffer((int) toByteIndex, TypedArrayElementType.Uint8, value, true, ArrayBufferOrder.Unordered);
+                    fromByteIndex += direction;
+                    toByteIndex += direction;
+                    countBytes--;
+                }
+            }
+
+            return o;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.entries
+        /// </summary>
+        private JsValue Entries(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj.ValidateTypedArray(_realm);
+            return _realm.Intrinsics.Iterator.CreateArrayLikeIterator(o, ArrayIteratorType.KeyAndValue);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.every
+        /// </summary>
+        private JsValue Every(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj.ValidateTypedArray(_realm);
+            var len = o.Length;
+
+            if (len == 0)
+            {
+                return JsBoolean.True;
+            }
+
+            var predicate = GetCallable(arguments.At(0));
+            var thisArg = arguments.At(1);
+
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = o;
+            for (var k = 0; k < len; k++)
+            {
+                args[0] = o[k];
+                args[1] = k;
+                if (!TypeConverter.ToBoolean(predicate.Call(thisArg, args)))
+                {
+                    return JsBoolean.False;
+                }
+            }
+
+            _engine._jsValueArrayPool.ReturnArray(args);
+
+            return JsBoolean.True;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.fill
+        /// </summary>
+        private JsValue Fill(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj.ValidateTypedArray(_realm);
+
+            var start = arguments.At(1);
+            var end = arguments.At(2);
+
+            var value = o._contentType == TypedArrayContentType.BigInt
+                ? TypeConverter.ToBigInt(arguments.At(0))
+                : TypeConverter.ToNumber(arguments.At(0));
+
+            var len = o.Length;
+
+            int k;
+            var relativeStart = TypeConverter.ToIntegerOrInfinity(start);
+            if (double.IsNegativeInfinity(relativeStart))
+            {
+                k = 0;
+            }
+            else if (relativeStart < 0)
+            {
+                k = (int) System.Math.Max(len + relativeStart, 0);
+            }
+            else
+            {
+                k = (int) System.Math.Min(relativeStart, len);
+            }
+
+            uint final;
+            var relativeEnd = end.IsUndefined() ? len : TypeConverter.ToIntegerOrInfinity(end);
+            if (double.IsNegativeInfinity(relativeEnd))
+            {
+                final = 0;
+            }
+            else if (relativeEnd < 0)
+            {
+                final = (uint) System.Math.Max(len + relativeEnd, 0);
+            }
+            else
+            {
+                final = (uint) System.Math.Min(relativeEnd, len);
+            }
+
+            o._viewedArrayBuffer.AssertNotDetached();
+
+            for (var i = k; i < final; ++i)
+            {
+                o[i] = value;
+            }
+
+            return thisObj;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.filter
+        /// </summary>
+        private JsValue Filter(JsValue thisObj, JsValue[] arguments)
+        {
+            var callbackfn = GetCallable(arguments.At(0));
+            var thisArg = arguments.At(1);
+
+            var o = thisObj.ValidateTypedArray(_realm);
+            var len = o.Length;
+
+            var kept = new List<JsValue>();
+            var captured = 0;
+
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = o;
+            for (var k = 0; k < len; k++)
+            {
+                var kValue = o[k];
+                args[0] = kValue;
+                args[1] = k;
+                var selected = callbackfn.Call(thisArg, args);
+                if (TypeConverter.ToBoolean(selected))
+                {
+                    kept.Add(kValue);
+                    captured++;
+                }
+            }
+
+            _engine._jsValueArrayPool.ReturnArray(args);
+
+            var a = _realm.Intrinsics.TypedArray.TypedArraySpeciesCreate(o, new JsValue[] { captured });
+            for (var n = 0; n < captured; ++n)
+            {
+                a[n] = kept[n];
+            }
+
+            return a;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.find
+        /// </summary>
+        private JsValue Find(JsValue thisObj, JsValue[] arguments)
+        {
+            return DoFind(thisObj, arguments).Value;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.findindex
+        /// </summary>
+        private JsValue FindIndex(JsValue thisObj, JsValue[] arguments)
+        {
+            return DoFind(thisObj, arguments).Key;
+        }
+
+        private KeyValuePair<JsValue, JsValue> DoFind(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj.ValidateTypedArray(_realm);
+            var len = o.Length;
+
+            var predicate = GetCallable(arguments.At(0));
+            var thisArg = arguments.At(1);
+
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = o;
+            for (var k = 0; k < len; k++)
+            {
+                var kNumber = JsNumber.Create(k);
+                var kValue = o[k];
+                args[0] = kValue;
+                args[1] = kNumber;
+                if (TypeConverter.ToBoolean(predicate.Call(thisArg, args)))
+                {
+                    return new KeyValuePair<JsValue, JsValue>(kNumber, kValue);
+                }
+            }
+
+            return new KeyValuePair<JsValue, JsValue>(JsNumber.IntegerNegativeOne, Undefined);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.foreach
+        /// </summary>
+        private JsValue ForEach(JsValue thisObj, JsValue[] arguments)
+        {
+            var callbackfn = GetCallable(arguments.At(0));
+            var thisArg = arguments.At(1);
+
+            var o = thisObj.ValidateTypedArray(_realm);
+            var len = o.Length;
+
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = o;
+            for (var k = 0; k < len; k++)
+            {
+                var kValue = o[k];
+                args[0] = kValue;
+                args[1] = k;
+                callbackfn.Call(thisArg, args);
+            }
+
+            _engine._jsValueArrayPool.ReturnArray(args);
+
+            return Undefined;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.includes
+        /// </summary>
+        private JsValue Includes(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj.ValidateTypedArray(_realm);
+            var len = o.Length;
+
+            if (len == 0)
+            {
+                return false;
+            }
+
+            var searchElement = arguments.At(0);
+            var fromIndex = arguments.At(1, 0);
+
+            var n = TypeConverter.ToIntegerOrInfinity(fromIndex);
+            if (double.IsPositiveInfinity(n))
+            {
+                return JsBoolean.False;
+            }
+            else if (double.IsNegativeInfinity(n))
+            {
+                n = 0;
+            }
+
+            long k;
+            if (n >= 0)
+            {
+                k = (long) n;
+            }
+            else
+            {
+                k = (long) (len + n);
+                if (k < 0)
+                {
+                    k = 0;
+                }
+            }
+
+            while (k < len)
+            {
+                var value = o[(int) k];
+                if (JintBinaryExpression.SameValueZero(value, searchElement))
+                {
+                    return JsBoolean.True;
+                }
+
+                k++;
+            }
+
+            return JsBoolean.False;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.indexof
+        /// </summary>
+        private JsValue IndexOf(JsValue thisObj, JsValue[] arguments)
+        {
+            var searchElement = arguments.At(0);
+            var fromIndex = arguments.At(1);
+
+            var o = thisObj.ValidateTypedArray(_realm);
+            var len = o.Length;
+            if (len == 0)
+            {
+                return JsNumber.IntegerNegativeOne;
+            }
+
+            var n = TypeConverter.ToIntegerOrInfinity(fromIndex);
+            if (double.IsPositiveInfinity(n))
+            {
+                return JsNumber.IntegerNegativeOne;
+            }
+            else if (double.IsNegativeInfinity(n))
+            {
+                n = 0;
+            }
+
+            long k;
+            if (n >= 0)
+            {
+                k = (long) n;
+            }
+            else
+            {
+                k = (long) (len + n);
+                if (k < 0)
+                {
+                    k = 0;
+                }
+            }
+
+            for (; k < len; k++)
+            {
+                var kPresent = o.HasProperty(k);
+                if (kPresent)
+                {
+                    var elementK = o[(int) k];
+                    var same = JintBinaryExpression.StrictlyEqual(elementK, searchElement);
+                    if (same)
+                    {
+                        return k;
+                    }
+                }
+            }
+
+            return JsNumber.IntegerNegativeOne;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.join
+        /// </summary>
+        private JsValue Join(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj.ValidateTypedArray(_realm);
+
+            var separator = arguments.At(0);
+            var len = o.Length;
+
+            var sep = TypeConverter.ToString(separator.IsUndefined() ? JsString.CommaString : separator);
+            // as per the spec, this has to be called after ToString(separator)
+            if (len == 0)
+            {
+                return JsString.Empty;
+            }
+
+            static string StringFromJsValue(JsValue value)
+            {
+                return value.IsUndefined()
+                    ? ""
+                    : TypeConverter.ToString(value);
+            }
+
+            var s = StringFromJsValue(o[0]);
+            if (len == 1)
+            {
+                return s;
+            }
+
+            using var sb = StringBuilderPool.Rent();
+            sb.Builder.Append(s);
+            for (var k = 1; k < len; k++)
+            {
+                sb.Builder.Append(sep);
+                sb.Builder.Append(StringFromJsValue(o[k]));
+            }
+
+            return sb.ToString();
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.keys
+        /// </summary>
+        private JsValue Keys(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj.ValidateTypedArray(_realm);
+            return _realm.Intrinsics.Iterator.CreateArrayLikeIterator(o, ArrayIteratorType.Key);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.lastindexof
+        /// </summary>
+        private JsValue LastIndexOf(JsValue thisObj, JsValue[] arguments)
+        {
+            var searchElement = arguments.At(0);
+
+            var o = thisObj.ValidateTypedArray(_realm);
+            var len = o.Length;
+            if (len == 0)
+            {
+                return JsNumber.IntegerNegativeOne;
+            }
+
+            var fromIndex = arguments.At(1, len - 1);
+            var n = TypeConverter.ToIntegerOrInfinity(fromIndex);
+
+            if (double.IsNegativeInfinity(n))
+            {
+                return JsNumber.IntegerNegativeOne;
+            }
+
+            long k;
+            if (n >= 0)
+            {
+                k = (long) System.Math.Min(n, len - 1);
+            }
+            else
+            {
+                k = (long) (len + n);
+            }
+
+            for (; k >= 0; k--)
+            {
+                var kPresent = o.HasProperty(k);
+                if (kPresent)
+                {
+                    var elementK = o[(int) k];
+                    var same = JintBinaryExpression.StrictlyEqual(elementK, searchElement);
+                    if (same)
+                    {
+                        return k;
+                    }
+                }
+            }
+
+            return JsNumber.IntegerNegativeOne;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.map
+        /// </summary>
+        private ObjectInstance Map(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj.ValidateTypedArray(_realm);
+            var len = o.Length;
+
+            var thisArg = arguments.At(1);
+            var callable = GetCallable(arguments.At(0));
+
+            var a = _realm.Intrinsics.TypedArray.TypedArraySpeciesCreate(o, new JsValue[] { len });
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = o;
+            for (var k = 0; k < len; k++)
+            {
+                args[0] = o[k];
+                args[1] = k;
+                var mappedValue = callable.Call(thisArg, args);
+                a[k] = mappedValue;
+            }
+
+            _engine._jsValueArrayPool.ReturnArray(args);
+            return a;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.reduce
+        /// </summary>
+        private JsValue Reduce(JsValue thisObj, JsValue[] arguments)
+        {
+            var callbackfn = GetCallable(arguments.At(0));
+            var initialValue = arguments.At(1);
+
+            var o = thisObj.ValidateTypedArray(_realm);
+            var len = o.Length;
+
+            if (len == 0 && arguments.Length < 2)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            var k = 0;
+            var accumulator = Undefined;
+            if (!initialValue.IsUndefined())
+            {
+                accumulator = initialValue;
+            }
+            else
+            {
+                accumulator = o[k];
+                k++;
+            }
+
+            var args = _engine._jsValueArrayPool.RentArray(4);
+            args[3] = o;
+            while (k < len)
+            {
+                var kValue = o[k];
+                args[0] = accumulator;
+                args[1] = kValue;
+                args[2] = k;
+                accumulator = callbackfn.Call(Undefined, args);
+                k++;
+            }
+
+            _engine._jsValueArrayPool.ReturnArray(args);
+
+            return accumulator;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.reduceright
+        /// </summary>
+        private JsValue ReduceRight(JsValue thisObj, JsValue[] arguments)
+        {
+            var callbackfn = GetCallable(arguments.At(0));
+            var initialValue = arguments.At(1);
+
+            var o = thisObj.ValidateTypedArray(_realm);
+            var len = (int) o.Length;
+
+            if (len == 0 && arguments.Length < 2)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            var k = len - 1;
+            JsValue accumulator;
+            if (arguments.Length > 1)
+            {
+                accumulator = initialValue;
+            }
+            else
+            {
+                accumulator = o[k];
+                k--;
+            }
+
+            var jsValues = _engine._jsValueArrayPool.RentArray(4);
+            jsValues[3] = o;
+            for (; k >= 0; k--)
+            {
+                jsValues[0] = accumulator;
+                jsValues[1] = o[k];
+                jsValues[2] = k;
+                accumulator = callbackfn.Call(Undefined, jsValues);
+            }
+
+            _engine._jsValueArrayPool.ReturnArray(jsValues);
+            return accumulator;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.reverse
+        /// </summary>
+        private ObjectInstance Reverse(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj.ValidateTypedArray(_realm);
+            var len = (int) o.Length;
+            var middle = (int) System.Math.Floor(len / 2.0);
+            var lower = 0;
+            while (lower != middle)
+            {
+                var upper = len - lower - 1;
+
+                var lowerValue = o[lower];
+                var upperValue = o[upper];
+
+                o[lower] = upperValue;
+                o[upper] = lowerValue;
+
+                lower++;
+            }
+
+            return o;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set
+        /// </summary>
+        private JsValue Set(JsValue thisObj, JsValue[] arguments)
+        {
+            var target = thisObj as TypedArrayInstance;
+            if (target is null)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            var source = arguments.At(0);
+            var offset = arguments.At(1);
+
+            var targetOffset = TypeConverter.ToIntegerOrInfinity(offset);
+            if (targetOffset < 0)
+            {
+                ExceptionHelper.ThrowRangeError(_realm, "Invalid offset");
+            }
+
+            if (source is TypedArrayInstance typedArrayInstance)
+            {
+                SetTypedArrayFromTypedArray(target, targetOffset, typedArrayInstance);
+            }
+            else
+            {
+                SetTypedArrayFromArrayLike(target, targetOffset, source);
+            }
+
+            return Undefined;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-settypedarrayfromtypedarray
+        /// </summary>
+        private void SetTypedArrayFromTypedArray(TypedArrayInstance target, double targetOffset, TypedArrayInstance source)
+        {
+            var targetBuffer = target._viewedArrayBuffer;
+            targetBuffer.AssertNotDetached();
+
+            var targetLength = target._arrayLength;
+            var srcBuffer = source._viewedArrayBuffer;
+            srcBuffer.AssertNotDetached();
+
+            var targetType = target._arrayElementType;
+            var targetElementSize = targetType.GetElementSize();
+            var targetByteOffset = target._byteOffset;
+
+            var srcType = source._arrayElementType;
+            var srcElementSize = srcType.GetElementSize();
+            var srcLength = source._arrayLength;
+            var srcByteOffset = source._byteOffset;
+
+            if (double.IsNegativeInfinity(targetOffset))
+            {
+                ExceptionHelper.ThrowRangeError(_realm, "Invalid target offset");
+            }
+
+            if (srcLength + targetOffset > targetLength)
+            {
+                ExceptionHelper.ThrowRangeError(_realm, "Invalid target offset");
+            }
+
+            if (target._contentType != source._contentType)
+            {
+                ExceptionHelper.ThrowTypeError(_realm, "Content type mismatch");
+            }
+
+            bool same;
+            if (srcBuffer.IsSharedArrayBuffer && targetBuffer.IsSharedArrayBuffer)
+            {
+                // a. If srcBuffer.[[ArrayBufferData]] and targetBuffer.[[ArrayBufferData]] are the same Shared Data Block values, let same be true; else let same be false.
+                ExceptionHelper.ThrowNotImplementedException("SharedBuffer not implemented");
+                same = false;
+            }
+            else
+            {
+                same = SameValue(srcBuffer, targetBuffer);
+            }
+
+            int srcByteIndex;
+            if (same)
+            {
+                var srcByteLength = source._byteLength;
+                srcBuffer = srcBuffer.CloneArrayBuffer(_realm.Intrinsics.ArrayBuffer, srcByteOffset, srcByteLength, _realm.Intrinsics.ArrayBuffer);
+                // %ArrayBuffer% is used to clone srcBuffer because is it known to not have any observable side-effects.
+                srcByteIndex = 0;
+            }
+            else
+            {
+                srcByteIndex = srcByteOffset;
+            }
+
+            var targetByteIndex = (int) (targetOffset * targetElementSize + targetByteOffset);
+            var limit = targetByteIndex + targetElementSize * srcLength;
+
+            if (srcType == targetType)
+            {
+                // NOTE: If srcType and targetType are the same, the transfer must be performed in a manner that preserves the bit-level encoding of the source data.
+                while (targetByteIndex < limit)
+                {
+                    var value = srcBuffer.GetValueFromBuffer(srcByteIndex, TypedArrayElementType.Uint8, true, ArrayBufferOrder.Unordered);
+                    targetBuffer.SetValueInBuffer(targetByteIndex, TypedArrayElementType.Uint8, value, true, ArrayBufferOrder.Unordered);
+                    srcByteIndex += 1;
+                    targetByteIndex += 1;
+                }
+            }
+            else
+            {
+                while (targetByteIndex < limit)
+                {
+                    var value = srcBuffer.GetValueFromBuffer(srcByteIndex, srcType, true, ArrayBufferOrder.Unordered);
+                    targetBuffer.SetValueInBuffer(targetByteIndex, targetType, value, true, ArrayBufferOrder.Unordered);
+                    srcByteIndex += srcElementSize;
+                    targetByteIndex += targetElementSize;
+                }
+            }
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-settypedarrayfromarraylike
+        /// </summary>
+        private void SetTypedArrayFromArrayLike(TypedArrayInstance target, double targetOffset, JsValue source)
+        {
+            var targetBuffer = target._viewedArrayBuffer;
+            targetBuffer.AssertNotDetached();
+
+            var targetLength = target._arrayLength;
+            var targetElementSize = target._arrayElementType.GetElementSize();
+            var targetType = target._arrayElementType;
+            var targetByteOffset = target._byteOffset;
+            var src = ArrayOperations.For(TypeConverter.ToObject(_realm, source));
+            var srcLength = src.GetLength();
+
+            if (double.IsNegativeInfinity(targetOffset))
+            {
+                ExceptionHelper.ThrowRangeError(_realm, "Invalid target offset");
+            }
+
+            if (srcLength + targetOffset > targetLength)
+            {
+                ExceptionHelper.ThrowRangeError(_realm, "Invalid target offset");
+            }
+
+            var targetByteIndex = targetOffset * targetElementSize + targetByteOffset;
+            ulong k = 0;
+            var limit = targetByteIndex + targetElementSize * srcLength;
+
+            while (targetByteIndex < limit)
+            {
+                double value;
+                if (target._contentType == TypedArrayContentType.BigInt)
+                {
+                    value = TypeConverter.ToBigInt(src.Get(k));
+                }
+                else
+                {
+                    value = TypeConverter.ToNumber(src.Get(k));
+                }
+
+                targetBuffer.AssertNotDetached();
+                targetBuffer.SetValueInBuffer((int) targetByteIndex, targetType, value, true, ArrayBufferOrder.Unordered);
+                k++;
+                targetByteIndex += targetElementSize;
+            }
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.slice
+        /// </summary>
+        private JsValue Slice(JsValue thisObj, JsValue[] arguments)
+        {
+            var start = arguments.At(0);
+            var end = arguments.At(1);
+
+            var o = thisObj.ValidateTypedArray(_realm);
+            long len = o.Length;
+
+            var relativeStart = TypeConverter.ToIntegerOrInfinity(start);
+            int k;
+            if (double.IsNegativeInfinity(relativeStart))
+            {
+                k = 0;
+            }
+            else if (relativeStart < 0)
+            {
+                k = (int) System.Math.Max(len + relativeStart, 0);
+            }
+            else
+            {
+                k = (int) System.Math.Min(relativeStart, len);
+            }
+
+            var relativeEnd = end.IsUndefined()
+                ? len
+                : TypeConverter.ToIntegerOrInfinity(end);
+
+            long final;
+            if (double.IsNegativeInfinity(relativeEnd))
+            {
+                final = 0;
+            }
+            else if (relativeEnd < 0)
+            {
+                final = (long) System.Math.Max(len + relativeEnd, 0);
+            }
+            else
+            {
+                final = (long) System.Math.Min(relativeEnd, len);
+            }
+
+            var count = System.Math.Max(final - k, 0);
+            var a = _realm.Intrinsics.TypedArray.TypedArraySpeciesCreate(o, new JsValue[] { count });
+
+            if (count > 0)
+            {
+                o._viewedArrayBuffer.AssertNotDetached();
+                var srcType = o._arrayElementType;
+                var targetType = a._arrayElementType;
+                if (srcType != targetType)
+                {
+                    var n = 0;
+                    while (k < final)
+                    {
+                        var kValue = o[k];
+                        a[n] = kValue;
+                        k++;
+                        n++;
+                    }
+                }
+                else
+                {
+                    var srcBuffer = o._viewedArrayBuffer;
+                    var targetBuffer = a._viewedArrayBuffer;
+                    var elementSize = srcType.GetElementSize();
+                    var srcByteOffset = o._byteOffset;
+                    var targetByteIndex = a._byteOffset;
+                    var srcByteIndex = (int) k * elementSize + srcByteOffset;
+                    var limit = targetByteIndex + count * elementSize;
+                    while (targetByteIndex < limit)
+                    {
+                        var value = (JsNumber) srcBuffer.GetValueFromBuffer(srcByteIndex, TypedArrayElementType.Uint8, true, ArrayBufferOrder.Unordered);
+                        targetBuffer.SetValueInBuffer(targetByteIndex, TypedArrayElementType.Uint8, value._value, true, ArrayBufferOrder.Unordered);
+                        srcByteIndex++;
+                        targetByteIndex++;
+                    }
+                }
+            }
+
+            return a;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.some
+        /// </summary>
+        private JsValue Some(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj.ValidateTypedArray(_realm);
+            var len = o.Length;
+            var callbackfn = GetCallable(arguments.At(0));
+            var thisArg = arguments.At(1);
+
+            var args = _engine._jsValueArrayPool.RentArray(3);
+            args[2] = o;
+            for (var k = 0; k < len; k++)
+            {
+                args[0] = o[k];
+                args[1] = k;
+                if (TypeConverter.ToBoolean(callbackfn.Call(thisArg, args)))
+                {
+                    return JsBoolean.True;
+                }
+            }
+
+            _engine._jsValueArrayPool.ReturnArray(args);
+            return JsBoolean.False;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.sort
+        /// </summary>
+        private JsValue Sort(JsValue thisObj, JsValue[] arguments)
+        {
+            /*
+             * %TypedArray%.prototype.sort is a distinct function that, except as described below,
+             * implements the same requirements as those of Array.prototype.sort as defined in 23.1.3.27.
+             * The implementation of the %TypedArray%.prototype.sort specification may be optimized with the knowledge that the this value is
+             * an object that has a fixed length and whose integer-indexed properties are not sparse.
+             */
+
+            var obj = thisObj.ValidateTypedArray(_realm);
+            var buffer = obj._viewedArrayBuffer;
+            var len = obj.Length;
+
+            var compareArg = arguments.At(0);
+            ICallable compareFn = null;
+            if (!compareArg.IsUndefined())
+            {
+                compareFn = GetCallable(compareArg);
+            }
+
+            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;
+            }
+
+            for (var i = 0; i < (uint) array.Length; ++i)
+            {
+                obj[i] = array[i];
+            }
+
+            return obj;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.subarray
+        /// </summary>
+        private JsValue Subarray(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj as TypedArrayInstance;
+            if (o is null)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            var begin = arguments.At(0);
+            var end = arguments.At(1);
+
+            var buffer = o._viewedArrayBuffer;
+            var srcLength = o.Length;
+            var relativeBegin = TypeConverter.ToIntegerOrInfinity(begin);
+
+            double beginIndex;
+            if (double.IsNegativeInfinity(relativeBegin))
+            {
+                beginIndex = 0;
+            }
+            else if (relativeBegin < 0)
+            {
+                beginIndex = System.Math.Max(srcLength + relativeBegin, 0);
+            }
+            else
+            {
+                beginIndex = System.Math.Min(relativeBegin, srcLength);
+            }
+
+            double relativeEnd;
+            if (end.IsUndefined())
+            {
+                relativeEnd = srcLength;
+            }
+            else
+            {
+                relativeEnd = TypeConverter.ToIntegerOrInfinity(end);
+            }
+
+            double endIndex;
+            if (double.IsNegativeInfinity(relativeEnd))
+            {
+                endIndex = 0;
+            }
+            else if (relativeEnd < 0)
+            {
+                endIndex = System.Math.Max(srcLength + relativeEnd, 0);
+            }
+            else
+            {
+                endIndex = System.Math.Min(relativeEnd, srcLength);
+            }
+
+            var newLength = System.Math.Max(endIndex - beginIndex, 0);
+            var elementSize = o._arrayElementType.GetElementSize();
+            var srcByteOffset = o._byteOffset;
+            var beginByteOffset = srcByteOffset + beginIndex * elementSize;
+            var argumentsList = new JsValue[] { buffer, beginByteOffset, newLength };
+            return _realm.Intrinsics.TypedArray.TypedArraySpeciesCreate(o, argumentsList);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.tolocalestring
+        /// </summary>
+        private JsValue ToLocaleString(JsValue thisObj, JsValue[] arguments)
+        {
+            /*
+             * %TypedArray%.prototype.toLocaleString is a distinct function that implements the same algorithm as Array.prototype.toLocaleString
+             * as defined in 23.1.3.29 except that the this value's [[ArrayLength]] internal slot is accessed in place of performing
+             * a [[Get]] of "length". The implementation of the algorithm may be optimized with the knowledge that the this value is an object
+             * that has a fixed length and whose integer-indexed properties are not sparse. However, such optimization must not introduce
+             * any observable changes in the specified behaviour of the algorithm.
+             */
+
+            var array = thisObj.ValidateTypedArray(_realm);
+            var len = array.Length;
+            const string separator = ",";
+            if (len == 0)
+            {
+                return JsString.Empty;
+            }
+
+            JsValue r;
+            if (!array.TryGetValue(0, out var firstElement) || firstElement.IsNull() || firstElement.IsUndefined())
+            {
+                r = JsString.Empty;
+            }
+            else
+            {
+                var elementObj = TypeConverter.ToObject(_realm, firstElement);
+                var func = elementObj.Get("toLocaleString", elementObj) as ICallable;
+                if (func is null)
+                {
+                    ExceptionHelper.ThrowTypeError(_realm);
+                }
+
+                r = func.Call(elementObj, Arguments.Empty);
+            }
+
+            for (var k = 1; k < len; k++)
+            {
+                var s = r + separator;
+                var elementObj = TypeConverter.ToObject(_realm, array[k]);
+                var func = elementObj.Get("toLocaleString", elementObj) as ICallable;
+                if (func is null)
+                {
+                    ExceptionHelper.ThrowTypeError(_realm);
+                }
+
+                r = func.Call(elementObj, Arguments.Empty);
+
+                r = s + r;
+            }
+
+            return r;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-%typedarray%.prototype.values
+        /// </summary>
+        private JsValue Values(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj.ValidateTypedArray(_realm);
+            return _realm.Intrinsics.Iterator.CreateArrayLikeIterator(o, ArrayIteratorType.Value);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-get-%typedarray%.prototype-@@tostringtag
+        /// </summary>
+        private static JsValue ToStringTag(JsValue thisObj, JsValue[] arguments)
+        {
+            if (thisObj is not TypedArrayInstance o)
+            {
+                return Undefined;
+            }
+
+            return o._arrayElementType.GetTypedArrayName();
+        }
+
+        private sealed class TypedArrayComparer : IComparer<JsValue>
+        {
+            public static TypedArrayComparer WithFunction(ArrayBufferInstance buffer, ICallable compare)
+            {
+                return new TypedArrayComparer(buffer, compare);
+            }
+
+            private readonly ArrayBufferInstance _buffer;
+            private readonly ICallable _compare;
+            private readonly JsValue[] _comparableArray = new JsValue[2];
+
+            private TypedArrayComparer(ArrayBufferInstance buffer, ICallable compare)
+            {
+                _buffer = buffer;
+                _compare = compare;
+            }
+
+            public int Compare(JsValue x, JsValue y)
+            {
+                if (_compare is not null)
+                {
+                    _comparableArray[0] = x;
+                    _comparableArray[1] = y;
+
+                    var v = TypeConverter.ToNumber(_compare.Call(Undefined, _comparableArray));
+                    _buffer.AssertNotDetached();
+
+                    if (double.IsNaN(v))
+                    {
+                        return 0;
+                    }
+
+                    return (int) v;
+                }
+
+                var xValue = x.AsNumber();
+                var yValue = y.AsNumber();
+
+                if (double.IsNaN(xValue) && double.IsNaN(yValue))
+                {
+                    return 0;
+                }
+
+                if (double.IsNaN(xValue))
+                {
+                    return 1;
+                }
+
+                if (double.IsNaN(yValue))
+                {
+                    return -1;
+                }
+
+                if (xValue < yValue)
+                {
+                    return -1;
+                }
+
+                if (xValue > yValue)
+                {
+                    return 1;
+                }
+
+                if (NumberInstance.IsNegativeZero(xValue) && yValue == 0)
+                {
+                    return -1;
+                }
+
+                if (xValue == 0 && NumberInstance.IsNegativeZero(yValue))
+                {
+                    return 1;
+                }
+
+                return 0;
+            }
+        }
+    }
+}

+ 21 - 0
Jint/Native/TypedArray/TypeArrayHelper.cs

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

+ 202 - 0
Jint/Native/TypedArray/TypedArrayConstructor.Types.cs

@@ -0,0 +1,202 @@
+using Jint.Runtime;
+
+namespace Jint.Native.TypedArray
+{
+    public sealed class Int8ArrayConstructor : TypedArrayConstructor
+    {
+        internal Int8ArrayConstructor(
+            Engine engine,
+            Realm realm,
+            IntrinsicTypedArrayConstructor functionPrototype,
+            IntrinsicTypedArrayPrototype objectPrototype) : base(engine, realm, functionPrototype, objectPrototype, TypedArrayElementType.Int8)
+        {
+        }
+
+        public TypedArrayInstance Construct(sbyte[] values)
+        {
+            var array = (TypedArrayInstance) base.Construct(new JsValue[] { values.Length }, this);
+            FillTypedArrayInstance(array, values);
+            return array;
+        }
+    }
+
+    public sealed class Uint8ArrayConstructor : TypedArrayConstructor
+    {
+        internal Uint8ArrayConstructor(
+            Engine engine,
+            Realm realm,
+            IntrinsicTypedArrayConstructor functionPrototype,
+            IntrinsicTypedArrayPrototype objectPrototype) : base(engine, realm, functionPrototype, objectPrototype, TypedArrayElementType.Uint8)
+        {
+        }
+
+        public TypedArrayInstance Construct(byte[] values)
+        {
+            var array = (TypedArrayInstance) base.Construct(new JsValue[] { values.Length }, this);
+            FillTypedArrayInstance(array, values);
+            return array;
+        }
+    }
+
+    public sealed class Uint8ClampedArrayConstructor : TypedArrayConstructor
+    {
+        internal Uint8ClampedArrayConstructor(
+            Engine engine,
+            Realm realm,
+            IntrinsicTypedArrayConstructor functionPrototype,
+            IntrinsicTypedArrayPrototype objectPrototype) : base(engine, realm, functionPrototype, objectPrototype, TypedArrayElementType.Uint8C)
+        {
+        }
+
+        public TypedArrayInstance Construct(byte[] values)
+        {
+            var array = (TypedArrayInstance) base.Construct(new JsValue[] { values.Length }, this);
+            FillTypedArrayInstance(array, values);
+            return array;
+        }
+    }
+
+    public sealed class Int16ArrayConstructor : TypedArrayConstructor
+    {
+        internal Int16ArrayConstructor(
+            Engine engine,
+            Realm realm,
+            IntrinsicTypedArrayConstructor functionPrototype,
+            IntrinsicTypedArrayPrototype objectPrototype) : base(engine, realm, functionPrototype, objectPrototype, TypedArrayElementType.Int16)
+        {
+        }
+
+        public TypedArrayInstance Construct(short[] values)
+        {
+            var array = (TypedArrayInstance) base.Construct(new JsValue[] { values.Length }, this);
+            FillTypedArrayInstance(array, values);
+            return array;
+        }
+    }
+
+    public sealed class Uint16ArrayConstructor : TypedArrayConstructor
+    {
+        internal Uint16ArrayConstructor(
+            Engine engine,
+            Realm realm,
+            IntrinsicTypedArrayConstructor functionPrototype,
+            IntrinsicTypedArrayPrototype objectPrototype) : base(engine, realm, functionPrototype, objectPrototype, TypedArrayElementType.Uint16)
+        {
+        }
+
+        public TypedArrayInstance Construct(ushort[] values)
+        {
+            var array = (TypedArrayInstance) base.Construct(new JsValue[] { values.Length }, this);
+            FillTypedArrayInstance(array, values);
+            return array;
+        }
+    }
+
+    public sealed class Int32ArrayConstructor : TypedArrayConstructor
+    {
+        internal Int32ArrayConstructor(
+            Engine engine,
+            Realm realm,
+            IntrinsicTypedArrayConstructor functionPrototype,
+            IntrinsicTypedArrayPrototype objectPrototype) : base(engine, realm, functionPrototype, objectPrototype, TypedArrayElementType.Int32)
+        {
+        }
+
+        public TypedArrayInstance Construct(int[] values)
+        {
+            var array = (TypedArrayInstance) base.Construct(new JsValue[] { values.Length }, this);
+            FillTypedArrayInstance(array, values);
+            return array;
+        }
+    }
+
+    public sealed class Uint32ArrayConstructor : TypedArrayConstructor
+    {
+        internal Uint32ArrayConstructor(
+            Engine engine,
+            Realm realm,
+            IntrinsicTypedArrayConstructor functionPrototype,
+            IntrinsicTypedArrayPrototype objectPrototype) : base(engine, realm, functionPrototype, objectPrototype, TypedArrayElementType.Uint32)
+        {
+        }
+
+        public TypedArrayInstance Construct(uint[] values)
+        {
+            var array = (TypedArrayInstance) base.Construct(new JsValue[] { values.Length }, this);
+            FillTypedArrayInstance(array, values);
+            return array;
+        }
+    }
+
+    public sealed class Float32ArrayConstructor : TypedArrayConstructor
+    {
+        internal Float32ArrayConstructor(
+            Engine engine,
+            Realm realm,
+            IntrinsicTypedArrayConstructor functionPrototype,
+            IntrinsicTypedArrayPrototype objectPrototype) : base(engine, realm, functionPrototype, objectPrototype, TypedArrayElementType.Float32)
+        {
+        }
+
+        public TypedArrayInstance Construct(float[] values)
+        {
+            var array = (TypedArrayInstance) base.Construct(new JsValue[] { values.Length }, this);
+            FillTypedArrayInstance(array, values);
+            return array;
+        }
+    }
+
+    public sealed class Float64ArrayConstructor : TypedArrayConstructor
+    {
+        internal Float64ArrayConstructor(
+            Engine engine,
+            Realm realm,
+            IntrinsicTypedArrayConstructor functionPrototype,
+            IntrinsicTypedArrayPrototype objectPrototype) : base(engine, realm, functionPrototype, objectPrototype, TypedArrayElementType.Float64)
+        {
+        }
+
+        public TypedArrayInstance Construct(double[] values)
+        {
+            var array = (TypedArrayInstance) base.Construct(new JsValue[] { values.Length }, this);
+            FillTypedArrayInstance(array, values);
+            return array;
+        }
+    }
+
+    public sealed class BigInt64ArrayConstructor : TypedArrayConstructor
+    {
+        internal BigInt64ArrayConstructor(
+            Engine engine,
+            Realm realm,
+            IntrinsicTypedArrayConstructor functionPrototype,
+            IntrinsicTypedArrayPrototype objectPrototype) : base(engine, realm, functionPrototype, objectPrototype, TypedArrayElementType.BigInt64)
+        {
+        }
+
+        public TypedArrayInstance Construct(long[] values)
+        {
+            var array = (TypedArrayInstance) base.Construct(new JsValue[] { values.Length }, this);
+            FillTypedArrayInstance(array, values);
+            return array;
+        }
+    }
+
+    public sealed class BigUint64ArrayConstructor : TypedArrayConstructor
+    {
+        internal BigUint64ArrayConstructor(
+            Engine engine,
+            Realm realm,
+            IntrinsicTypedArrayConstructor functionPrototype,
+            IntrinsicTypedArrayPrototype objectPrototype) : base(engine, realm, functionPrototype, objectPrototype, TypedArrayElementType.BigUint64)
+        {
+        }
+
+        public TypedArrayInstance Construct(ulong[] values)
+        {
+            var array = (TypedArrayInstance) base.Construct(new JsValue[] { values.Length }, this);
+            FillTypedArrayInstance(array, values);
+            return array;
+        }
+    }
+}

+ 290 - 0
Jint/Native/TypedArray/TypedArrayConstructor.cs

@@ -0,0 +1,290 @@
+using System;
+using System.Collections.Generic;
+using Jint.Collections;
+using Jint.Native.Array;
+using Jint.Native.ArrayBuffer;
+using Jint.Native.Function;
+using Jint.Native.Object;
+using Jint.Native.Symbol;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+
+namespace Jint.Native.TypedArray
+{
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-typedarray-constructors
+    /// </summary>
+    public abstract class TypedArrayConstructor : FunctionInstance, IConstructor
+    {
+        private readonly TypedArrayElementType _arrayElementType;
+
+        internal TypedArrayConstructor(
+            Engine engine,
+            Realm realm,
+            IntrinsicTypedArrayConstructor functionPrototype,
+            IntrinsicTypedArrayPrototype objectPrototype,
+            TypedArrayElementType type) : base(engine, realm, new JsString(type.GetTypedArrayName()))
+        {
+            _arrayElementType = type;
+
+            _prototype = functionPrototype;
+            PrototypeObject = new TypedArrayPrototype(engine, objectPrototype, this, type);
+            _length = new PropertyDescriptor(JsNumber.PositiveThree, PropertyFlag.Configurable);
+            _prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
+        }
+
+        public TypedArrayPrototype PrototypeObject { get; }
+
+        protected override void Initialize()
+        {
+            var properties = new PropertyDictionary(1, false)
+            {
+                ["BYTES_PER_ELEMENT"] = new(new PropertyDescriptor(JsNumber.Create(_arrayElementType.GetElementSize()), PropertyFlag.AllForbidden))
+            };
+            SetProperties(properties);
+        }
+
+        public override JsValue Call(JsValue thisObject, JsValue[] arguments)
+        {
+            ExceptionHelper.ThrowTypeError(_realm, "Abstract class TypedArray not directly constructable");
+            return Undefined;
+        }
+
+        public ObjectInstance Construct(JsValue[] args, JsValue newTarget)
+        {
+            if (newTarget.IsUndefined())
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            Func<Intrinsics, ObjectInstance> proto = _arrayElementType switch
+            {
+                TypedArrayElementType.Float32 => static intrinsics => intrinsics.Float32Array.PrototypeObject,
+                TypedArrayElementType.Int8 => static intrinsics => intrinsics.Int8Array.PrototypeObject,
+                TypedArrayElementType.Int16 => static intrinsics => intrinsics.Int16Array.PrototypeObject,
+                TypedArrayElementType.Int32 => static intrinsics => intrinsics.Int32Array.PrototypeObject,
+                TypedArrayElementType.BigInt64 => static intrinsics => intrinsics.BigInt64Array.PrototypeObject,
+                TypedArrayElementType.Float64 => static intrinsics => intrinsics.Float64Array.PrototypeObject,
+                TypedArrayElementType.Uint8 => static intrinsics => intrinsics.Uint8Array.PrototypeObject,
+                TypedArrayElementType.Uint8C => static intrinsics => intrinsics.Uint8ClampedArray.PrototypeObject,
+                TypedArrayElementType.Uint16 => static intrinsics => intrinsics.Uint16Array.PrototypeObject,
+                TypedArrayElementType.Uint32 => static intrinsics => intrinsics.Uint32Array.PrototypeObject,
+                TypedArrayElementType.BigUint64 => static intrinsics => intrinsics.BigUint64Array.PrototypeObject,
+                _ => null
+            };
+
+            var numberOfArgs = args.Length;
+            if (numberOfArgs == 0)
+            {
+                return AllocateTypedArray(newTarget, proto, 0);
+            }
+
+            var firstArgument = args[0];
+            if (firstArgument.IsObject())
+            {
+                var o = AllocateTypedArray(newTarget, proto);
+                if (firstArgument is TypedArrayInstance typedArrayInstance)
+                {
+                    InitializeTypedArrayFromTypedArray(o, typedArrayInstance);
+                }
+                else if (firstArgument is ArrayBufferInstance arrayBuffer)
+                {
+                    var byteOffset = numberOfArgs > 1 ? args[1] : Undefined;
+                    var length = numberOfArgs > 2 ? args[2] : Undefined;
+                    InitializeTypedArrayFromArrayBuffer(o, arrayBuffer, byteOffset, length);
+                }
+                else
+                {
+                    var usingIterator = GetMethod(_realm, firstArgument, GlobalSymbolRegistry.Iterator);
+                    if (usingIterator is not null)
+                    {
+                        var values = IterableToList(_realm, firstArgument, usingIterator);
+                        InitializeTypedArrayFromList(o, values);
+                    }
+                    else
+                    {
+                        InitializeTypedArrayFromArrayLike(o, (ObjectInstance) firstArgument);
+                    }
+                }
+
+                return o;
+            }
+
+            var elementLength = TypeConverter.ToIndex(_realm, firstArgument);
+            return AllocateTypedArray(newTarget, proto, elementLength);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-iterabletolist
+        /// </summary>
+        internal static List<JsValue> IterableToList(Realm realm, JsValue items, ICallable usingIterator)
+        {
+            var iteratorRecord = items.GetIterator(realm);
+            var values = new List<JsValue>();
+            while (iteratorRecord.TryIteratorStep(out var nextItem))
+            {
+                values.Add(nextItem.Get(CommonProperties.Value));
+            }
+
+            return values;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-initializetypedarrayfromtypedarray
+        /// </summary>
+        private void InitializeTypedArrayFromTypedArray(TypedArrayInstance o, TypedArrayInstance srcArray)
+        {
+            var srcData = srcArray._viewedArrayBuffer;
+            srcData.AssertNotDetached();
+
+            var elementType = o._arrayElementType;
+            var elementLength = srcArray._arrayLength;
+            var srcType = srcArray._arrayElementType;
+            var srcElementSize = srcType.GetElementSize();
+            var srcByteOffset = srcArray._byteOffset;
+            var elementSize = elementType.GetElementSize();
+            var byteLength = elementSize * elementLength;
+
+            var bufferConstructor = (JsValue) (!srcData.IsSharedArrayBuffer
+                ? SpeciesConstructor(srcData, _realm.Intrinsics.ArrayBuffer)
+                : _realm.Intrinsics.ArrayBuffer);
+
+            ArrayBufferInstance data;
+            if (elementType == srcType)
+            {
+                data = srcData.CloneArrayBuffer(_realm.Intrinsics.ArrayBuffer, srcByteOffset, byteLength, bufferConstructor);
+            }
+            else
+            {
+                data = _realm.Intrinsics.ArrayBuffer.AllocateArrayBuffer(bufferConstructor, byteLength);
+                srcData.AssertNotDetached();
+                if (srcArray._contentType != o._contentType)
+                {
+                    ExceptionHelper.ThrowTypeError(_realm, "Content types differ");
+                }
+
+                var srcByteIndex = srcByteOffset;
+                var targetByteIndex = 0;
+                var count = elementLength;
+                while (count > 0)
+                {
+                    var value = srcData.GetValueFromBuffer(srcByteIndex, srcType, true, ArrayBufferOrder.Unordered);
+                    data.SetValueInBuffer(targetByteIndex, elementType, value, true, ArrayBufferOrder.Unordered);
+                    srcByteIndex += srcElementSize;
+                    targetByteIndex += elementSize;
+                    count--;
+                }
+            }
+
+            o._viewedArrayBuffer = data;
+            o._arrayLength = elementLength;
+            o._byteLength = byteLength;
+            o._byteOffset = 0;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-initializetypedarrayfromarraybuffer
+        /// </summary>
+        private void InitializeTypedArrayFromArrayBuffer(
+            TypedArrayInstance o,
+            ArrayBufferInstance buffer,
+            JsValue byteOffset,
+            JsValue length)
+        {
+            var elementSize = o._arrayElementType.GetElementSize();
+            var offset = (int) TypeConverter.ToIndex(_realm, byteOffset);
+            if (offset % elementSize != 0)
+            {
+                ExceptionHelper.ThrowRangeError(_realm, "Invalid offset");
+            }
+
+            int newByteLength;
+            var newLength = 0;
+            if (!length.IsUndefined())
+            {
+                newLength = (int) TypeConverter.ToIndex(_realm, length);
+            }
+
+            buffer.AssertNotDetached();
+
+            var bufferByteLength = buffer.ArrayBufferByteLength;
+            if (length.IsUndefined())
+            {
+                if (bufferByteLength % elementSize != 0)
+                {
+                    ExceptionHelper.ThrowRangeError(_realm, "Invalid buffer byte length");
+                }
+
+                newByteLength = bufferByteLength - offset;
+                if (newByteLength < 0)
+                {
+                    ExceptionHelper.ThrowRangeError(_realm, "Invalid buffer byte length");
+                }
+            }
+            else
+            {
+                newByteLength = newLength * elementSize;
+                if (offset + newByteLength > bufferByteLength)
+                {
+                    ExceptionHelper.ThrowRangeError(_realm, "Invalid buffer byte length");
+                }
+            }
+
+            o._viewedArrayBuffer = buffer;
+            o._arrayLength = (uint) (newByteLength / elementSize);
+            o._byteLength = (uint) newByteLength;
+            o._byteOffset = offset;
+        }
+
+        private static void InitializeTypedArrayFromList(TypedArrayInstance o, List<JsValue> values)
+        {
+            var len = values.Count;
+            o.AllocateTypedArrayBuffer((uint) len);
+            for (var k = 0; k < len; ++k)
+            {
+                o[k] = values[k];
+            }
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-initializetypedarrayfromarraylike
+        /// </summary>
+        private static void InitializeTypedArrayFromArrayLike(TypedArrayInstance o, ObjectInstance arrayLike)
+        {
+            var operations = ArrayOperations.For(arrayLike);
+            var len = operations.GetLongLength();
+            o.AllocateTypedArrayBuffer(len);
+            for (uint k = 0; k < len; ++k)
+            {
+                o[(int) k] = operations.Get(k);
+            }
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-allocatetypedarray
+        /// </summary>
+        private TypedArrayInstance AllocateTypedArray(JsValue newTarget, Func<Intrinsics, ObjectInstance> defaultProto, uint length = 0)
+        {
+            var proto = GetPrototypeFromConstructor(newTarget, defaultProto);
+            var realm = GetFunctionRealm(newTarget);
+            var obj = new TypedArrayInstance(_engine, realm.Intrinsics, _arrayElementType, length)
+            {
+                _prototype = proto
+            };
+            if (length > 0)
+            {
+                obj.AllocateTypedArrayBuffer(length);
+            }
+
+            return obj;
+        }
+
+        internal static void FillTypedArrayInstance(TypedArrayInstance target, System.Array values)
+        {
+            for (var i = 0; i < values.Length; ++i)
+            {
+                target.DoIntegerIndexedElementSet(i, Convert.ToDouble(values.GetValue(i)));
+            }
+        }
+    }
+}

+ 8 - 0
Jint/Native/TypedArray/TypedArrayContentType.cs

@@ -0,0 +1,8 @@
+namespace Jint.Native.TypedArray
+{
+    internal enum TypedArrayContentType : byte
+    {
+        Number,
+        BigInt
+    }
+}

+ 48 - 3
Jint/Native/TypedArray/TypedArrayElementType.cs

@@ -1,6 +1,8 @@
+using Jint.Runtime;
+
 namespace Jint.Native.TypedArray
 {
-    internal enum TypedArrayElementType
+    internal enum TypedArrayElementType : byte
     {
         // we have signed first to make comparison vaster to check if signed or unsigned type
         Int8,
@@ -19,7 +21,7 @@ namespace Jint.Native.TypedArray
 
     internal static class TypedArrayExtensions
     {
-        internal static int GetElementSize(this TypedArrayElementType type)
+        internal static byte GetElementSize(this TypedArrayElementType type)
         {
             return type switch
             {
@@ -34,7 +36,45 @@ namespace Jint.Native.TypedArray
                 TypedArrayElementType.BigUint64 => 8,
                 TypedArrayElementType.Float32 => 4,
                 TypedArrayElementType.Float64 => 8,
-                _ => -1
+                _ => 0
+            };
+        }
+
+        internal static string GetTypedArrayName(this TypedArrayElementType type)
+        {
+            return type switch
+            {
+                TypedArrayElementType.Int8 => "Int8Array",
+                TypedArrayElementType.Uint8 => "Uint8Array",
+                TypedArrayElementType.Uint8C => "Uint8ClampedArray",
+                TypedArrayElementType.Int16 => "Int16Array",
+                TypedArrayElementType.Uint16 => "Uint16Array",
+                TypedArrayElementType.Int32 => "Int32Array",
+                TypedArrayElementType.Uint32 => "Uint32Array",
+                TypedArrayElementType.BigInt64 => "BigInt64Array",
+                TypedArrayElementType.BigUint64 => "BigUint64Array",
+                TypedArrayElementType.Float32 => "Float32Array",
+                TypedArrayElementType.Float64 => "Float64Array",
+                _ => null
+            };
+        }
+
+        internal static IConstructor GetConstructor(this TypedArrayElementType type, Intrinsics intrinsics)
+        {
+            return type switch
+            {
+                TypedArrayElementType.Int8 => intrinsics.Int8Array,
+                TypedArrayElementType.Uint8 => intrinsics.Uint8Array,
+                TypedArrayElementType.Uint8C => intrinsics.Uint8ClampedArray,
+                TypedArrayElementType.Int16 => intrinsics.Int16Array,
+                TypedArrayElementType.Uint16 => intrinsics.Uint16Array,
+                TypedArrayElementType.Int32 => intrinsics.Int32Array,
+                TypedArrayElementType.Uint32 => intrinsics.Uint32Array,
+                TypedArrayElementType.BigInt64 => intrinsics.BigInt64Array,
+                TypedArrayElementType.BigUint64 => intrinsics.BigUint64Array,
+                TypedArrayElementType.Float32 => intrinsics.Float32Array,
+                TypedArrayElementType.Float64 => intrinsics.Float64Array,
+                _ => null
             };
         }
 
@@ -43,6 +83,11 @@ namespace Jint.Native.TypedArray
             return type > TypedArrayElementType.Float64;
         }
 
+        internal static bool FitsInt32(this TypedArrayElementType type)
+        {
+            return type <= TypedArrayElementType.Int32;
+        }
+
         internal static bool IsBigIntElementType(this TypedArrayElementType type)
         {
             return type is TypedArrayElementType.BigUint64 or TypedArrayElementType.BigInt64;

+ 334 - 0
Jint/Native/TypedArray/TypedArrayInstance.cs

@@ -0,0 +1,334 @@
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using Jint.Native.ArrayBuffer;
+using Jint.Native.Number;
+using Jint.Native.Object;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+
+namespace Jint.Native.TypedArray
+{
+    public sealed class TypedArrayInstance : ObjectInstance
+    {
+        internal TypedArrayContentType _contentType;
+        internal readonly TypedArrayElementType _arrayElementType;
+        internal ArrayBufferInstance _viewedArrayBuffer;
+        internal uint _byteLength;
+        internal int _byteOffset;
+        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)
+        {
+            _arrayElementType = type;
+            _arrayLength = length;
+        }
+
+        internal JsValue this[int index]
+        {
+            get => IntegerIndexedElementGet(index);
+            set => IntegerIndexedElementSet(index, value);
+        }
+
+        public override uint Length => _viewedArrayBuffer.IsDetachedBuffer ? 0 : _arrayLength;
+
+        internal override bool IsIntegerIndexedArray => true;
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-allocatetypedarraybuffer
+        /// </summary>
+        internal void AllocateTypedArrayBuffer(ulong len)
+        {
+            var elementSize = _arrayElementType.GetElementSize();
+            var byteLength = elementSize * len;
+
+            var data = _intrinsics.ArrayBuffer.AllocateArrayBuffer(_intrinsics.ArrayBuffer, byteLength);
+
+            _byteLength = (uint) byteLength;
+            _arrayLength = (uint) len;
+            _viewedArrayBuffer = data;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal bool HasProperty(long numericIndex)
+        {
+            return IsValidIntegerIndex(numericIndex);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-hasproperty-p
+        /// </summary>
+        public override bool HasProperty(JsValue property)
+        {
+            if (property.IsString())
+            {
+                var numericIndex = TypeConverter.CanonicalNumericIndexString(property);
+                if (numericIndex is not null)
+                {
+                    return IsValidIntegerIndex(numericIndex.Value);
+                }
+            }
+
+            return base.HasProperty(property);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-getownproperty-p
+        /// </summary>
+        public override PropertyDescriptor GetOwnProperty(JsValue property)
+        {
+            var numericIndex = TypeConverter.CanonicalNumericIndexString(property);
+            if (numericIndex is not null)
+            {
+                var value = IntegerIndexedElementGet(numericIndex.Value);
+                if (value.IsUndefined())
+                {
+                    return PropertyDescriptor.Undefined;
+                }
+
+                return new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
+            }
+
+            return base.GetOwnProperty(property);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-get-p-receiver
+        /// </summary>
+        public override JsValue Get(JsValue property, JsValue receiver)
+        {
+            var numericIndex = TypeConverter.CanonicalNumericIndexString(property);
+            if (numericIndex is not null)
+            {
+                return IntegerIndexedElementGet(numericIndex.Value);
+            }
+
+            return base.Get(property, receiver);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-set-p-v-receiver
+        /// </summary>
+        public override bool Set(JsValue property, JsValue value, JsValue receiver)
+        {
+            var numericIndex = TypeConverter.CanonicalNumericIndexString(property);
+            if (numericIndex is not null)
+            {
+                IntegerIndexedElementSet(numericIndex.Value, value);
+                return true;
+            }
+
+            return base.Set(property, value, receiver);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-defineownproperty-p-desc
+        /// </summary>
+        public override bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
+        {
+            if (property.IsNumber() || property.IsString())
+            {
+                var numericIndex = TypeConverter.CanonicalNumericIndexString(property);
+                if (numericIndex is not null)
+                {
+                    if (!IsValidIntegerIndex(numericIndex.Value))
+                    {
+                        return false;
+                    }
+
+                    if (desc.ConfigurableSet && !desc.Configurable)
+                    {
+                        return false;
+                    }
+
+                    if (desc.EnumerableSet && !desc.Enumerable)
+                    {
+                        return false;
+                    }
+
+                    if (desc.IsAccessorDescriptor())
+                    {
+                        return false;
+                    }
+
+                    if (desc.WritableSet && !desc.Writable)
+                    {
+                        return false;
+                    }
+
+                    IntegerIndexedElementSet(numericIndex.Value, desc.Value);
+                    return true;
+                }
+            }
+
+            return base.DefineOwnProperty(property, desc);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-ownpropertykeys
+        /// </summary>
+        public override List<JsValue> GetOwnPropertyKeys(Types types = Types.None | Types.String | Types.Symbol)
+        {
+            var keys = new List<JsValue>();
+            if (!_viewedArrayBuffer.IsDetachedBuffer)
+            {
+                var length = Length;
+                for (uint i = 0; i < length; ++i)
+                {
+                    keys.Add(JsString.Create(i));
+                }
+            }
+
+            if (_properties is not null)
+            {
+                foreach (var pair in _properties)
+                {
+                    keys.Add(pair.Key.Name);
+                }
+            }
+
+            if (_symbols is not null)
+            {
+                foreach (var pair in _symbols)
+                {
+                    keys.Add(pair.Key);
+                }
+            }
+
+            return keys;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-delete-p
+        /// </summary>
+        public override bool Delete(JsValue property)
+        {
+            var numericIndex = TypeConverter.CanonicalNumericIndexString(property);
+            if (numericIndex is not null)
+            {
+                return !IsValidIntegerIndex(numericIndex.Value);
+            }
+
+            return base.Delete(property);
+        }
+
+        // helper to prevent floating points
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private JsValue IntegerIndexedElementGet(int index)
+        {
+            if (!IsValidIntegerIndex(index))
+            {
+                return Undefined;
+            }
+
+            return DoIntegerIndexedElementGet(index);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-integerindexedelementget
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private JsValue IntegerIndexedElementGet(double index)
+        {
+            if (!IsValidIntegerIndex(index))
+            {
+                return Undefined;
+            }
+
+            return DoIntegerIndexedElementGet((int) index);
+        }
+
+        private JsValue DoIntegerIndexedElementGet(int index)
+        {
+            var offset = _byteOffset;
+            var elementType = _arrayElementType;
+            var elementSize = elementType.GetElementSize();
+            var indexedPosition = index * elementSize + offset;
+            var value = _viewedArrayBuffer.GetValueFromBuffer(indexedPosition, elementType, true, ArrayBufferOrder.Unordered);
+            return _arrayElementType.FitsInt32() ? JsNumber.Create((int) value) : JsNumber.Create(value);
+        }
+
+        // helper tot prevent floating point
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private void IntegerIndexedElementSet(int index, JsValue value)
+        {
+            var numValue = _contentType == TypedArrayContentType.BigInt
+                ? TypeConverter.ToBigInt(value)
+                : TypeConverter.ToNumber(value);
+
+            if (IsValidIntegerIndex(index))
+            {
+                DoIntegerIndexedElementSet(index, numValue);
+            }
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-integerindexedelementset
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private void IntegerIndexedElementSet(double index, JsValue value)
+        {
+            var numValue = _contentType == TypedArrayContentType.BigInt
+                ? TypeConverter.ToBigInt(value)
+                : TypeConverter.ToNumber(value);
+
+            if (IsValidIntegerIndex(index))
+            {
+                DoIntegerIndexedElementSet((int) index, numValue);
+            }
+        }
+
+        internal void DoIntegerIndexedElementSet(int index, double numValue)
+        {
+            var offset = _byteOffset;
+            var elementType = _arrayElementType;
+            var elementSize = elementType.GetElementSize();
+            var indexedPosition = index * elementSize + offset;
+            _viewedArrayBuffer.SetValueInBuffer(indexedPosition, elementType, numValue, true, ArrayBufferOrder.Unordered);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-isvalidintegerindex
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private bool IsValidIntegerIndex(double index)
+        {
+            return !_viewedArrayBuffer.IsDetachedBuffer
+                   && IsIntegralNumber(index)
+                   && !NumberInstance.IsNegativeZero(index)
+                   && (uint) index < _arrayLength;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-isvalidintegerindex
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private bool IsValidIntegerIndex(int index)
+        {
+            return !_viewedArrayBuffer.IsDetachedBuffer && (uint) index < _arrayLength;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-isintegralnumber
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private static bool IsIntegralNumber(double value)
+        {
+            return !double.IsNaN(value)
+                   && !double.IsInfinity(value)
+                   && System.Math.Floor(System.Math.Abs(value)) == System.Math.Abs(value);
+        }
+    }
+}

+ 36 - 0
Jint/Native/TypedArray/TypedArrayPrototype.cs

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

+ 1 - 3
Jint/Runtime/Interpreter/Expressions/JintArrayExpression.cs

@@ -37,12 +37,10 @@ namespace Jint.Runtime.Interpreter.Expressions
         protected override object EvaluateInternal()
         {
             var a = _engine.Realm.Intrinsics.Array.ConstructFast(_hasSpreads ? 0 : (uint) _expressions.Length);
-            var expressions = _expressions;
 
             uint arrayIndexCounter = 0;
-            for (uint i = 0; i < (uint) expressions.Length; i++)
+            foreach (var expr in _expressions)
             {
-                var expr = expressions[i];
                 if (expr == null)
                 {
                     arrayIndexCounter++;

+ 5 - 0
Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs

@@ -160,6 +160,11 @@ namespace Jint.Runtime.Interpreter.Expressions
             return (JsValue) EvaluateInternal();
         }
 
+        public static bool SameValueZero(JsValue x, JsValue y)
+        {
+            return x == y || (x is JsNumber xNum && y is JsNumber yNum && double.IsNaN(xNum._value) && double.IsNaN(yNum._value));
+        }
+
         public static bool StrictlyEqual(JsValue x, JsValue y)
         {
             var typeX = x._type & ~InternalTypes.InternalFlags;

+ 17 - 5
Jint/Runtime/Interpreter/Expressions/JintIdentifierExpression.cs

@@ -1,5 +1,6 @@
 using Esprima.Ast;
 using Jint.Native;
+using Jint.Native.Argument;
 using Jint.Runtime.Environments;
 
 namespace Jint.Runtime.Interpreter.Expressions
@@ -37,30 +38,41 @@ namespace Jint.Runtime.Interpreter.Expressions
             // need to notify correct node when taking shortcut
             _engine._lastSyntaxNode = _expression;
 
-            if (!(_calculatedValue is null))
+            if (_calculatedValue is not null)
             {
                 return _calculatedValue;
             }
 
             var strict = StrictModeScope.IsStrictModeCode;
             var env = _engine.ExecutionContext.LexicalEnvironment;
+
+            JsValue value;
             if (JintEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
                 _engine,
                 env,
                 _expressionName,
                 strict,
                 out _,
-                out var value))
+                out value))
             {
                 if (value is null)
                 {
                     ExceptionHelper.ThrowReferenceError(_engine.Realm, _expressionName.Key.Name + " has not been initialized");
                 }
-                return value;
+            }
+            else
+            {
+                var reference = _engine._referencePool.Rent(JsValue.Undefined, _expressionName.StringValue, strict, thisValue: null);
+                value = _engine.GetValue(reference, true);
+            }
+
+            // make sure arguments access freezes state
+            if (value is ArgumentsInstance argumentsInstance)
+            {
+                argumentsInstance.Materialize();
             }
 
-            var reference = _engine._referencePool.Rent(JsValue.Undefined, _expressionName.StringValue, strict, thisValue: null);
-            return _engine.GetValue(reference, true);
+            return value;
         }
     }
 }

+ 50 - 0
Jint/Runtime/Intrinsics.cs

@@ -19,6 +19,7 @@ using Jint.Native.RegExp;
 using Jint.Native.Set;
 using Jint.Native.String;
 using Jint.Native.Symbol;
+using Jint.Native.TypedArray;
 using Jint.Native.WeakMap;
 using Jint.Native.WeakSet;
 
@@ -66,6 +67,19 @@ namespace Jint.Runtime
         private ArrayBufferConstructor _arrayBufferConstructor;
         private DataViewConstructor _dataView;
 
+        private IntrinsicTypedArrayConstructor _typedArray;
+        private Int8ArrayConstructor _int8Array;
+        private Uint8ArrayConstructor _uint8Array;
+        private Uint8ClampedArrayConstructor _uint8ClampedArray;
+        private Int16ArrayConstructor _int16Array;
+        private Uint16ArrayConstructor _uint16Array;
+        private Int32ArrayConstructor _int32Array;
+        private Uint32ArrayConstructor _uint32Array;
+        private BigInt64ArrayConstructor _bigInt64Array;
+        private BigUint64ArrayConstructor _bigUint64Array;
+        private Float32ArrayConstructor _float32Array;
+        private Float64ArrayConstructor _float64Array;
+
         internal Intrinsics(Engine engine, Realm realm)
         {
             _engine = engine;
@@ -94,6 +108,42 @@ namespace Jint.Runtime
         public ArrayBufferConstructor ArrayBuffer =>
             _arrayBufferConstructor ??= new ArrayBufferConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject);
 
+        internal IntrinsicTypedArrayConstructor TypedArray =>
+            _typedArray ??= new IntrinsicTypedArrayConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject, "TypedArray");
+
+        public Int8ArrayConstructor Int8Array =>
+            _int8Array ??= new Int8ArrayConstructor(_engine, _realm, TypedArray, TypedArray.PrototypeObject);
+
+        public Uint8ArrayConstructor Uint8Array =>
+            _uint8Array ??= new Uint8ArrayConstructor(_engine, _realm, TypedArray, TypedArray.PrototypeObject);
+
+        public Uint8ClampedArrayConstructor Uint8ClampedArray =>
+            _uint8ClampedArray ??= new Uint8ClampedArrayConstructor(_engine, _realm, TypedArray, TypedArray.PrototypeObject);
+
+        public Int16ArrayConstructor Int16Array =>
+            _int16Array ??= new Int16ArrayConstructor(_engine, _realm, TypedArray, TypedArray.PrototypeObject);
+
+        public Uint16ArrayConstructor Uint16Array =>
+            _uint16Array ??= new Uint16ArrayConstructor(_engine, _realm, TypedArray, TypedArray.PrototypeObject);
+
+        public Int32ArrayConstructor Int32Array =>
+            _int32Array ??= new Int32ArrayConstructor(_engine, _realm, TypedArray, TypedArray.PrototypeObject);
+
+        public Uint32ArrayConstructor Uint32Array =>
+            _uint32Array ??= new Uint32ArrayConstructor(_engine, _realm, TypedArray, TypedArray.PrototypeObject);
+
+        public BigInt64ArrayConstructor BigInt64Array =>
+            _bigInt64Array ??= new BigInt64ArrayConstructor(_engine, _realm, TypedArray, TypedArray.PrototypeObject);
+
+        public BigUint64ArrayConstructor BigUint64Array =>
+            _bigUint64Array ??= new BigUint64ArrayConstructor(_engine, _realm, TypedArray, TypedArray.PrototypeObject);
+
+        public Float32ArrayConstructor Float32Array =>
+            _float32Array ??= new Float32ArrayConstructor(_engine, _realm, TypedArray, TypedArray.PrototypeObject);
+
+        public Float64ArrayConstructor Float64Array =>
+            _float64Array ??= new Float64ArrayConstructor(_engine, _realm, TypedArray, TypedArray.PrototypeObject);
+
         public MapConstructor Map =>
             _map ??= new MapConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject);
 

+ 50 - 6
Jint/Runtime/TypeConverter.cs

@@ -493,17 +493,22 @@ namespace Jint.Runtime
         /// <summary>
         /// https://tc39.es/ecma262/#sec-touint8clamp
         /// </summary>
-        internal static double ToUint8Clamp(JsValue o)
+        internal static byte ToUint8Clamp(JsValue o)
         {
             if (o._type == InternalTypes.Integer)
             {
                 var intValue = o.AsInteger();
                 if (intValue is > -1 and < 256)
                 {
-                    return intValue;
+                    return (byte) intValue;
                 }
             }
 
+            return ToUint8ClampUnlikely(o);
+        }
+
+        private static byte ToUint8ClampUnlikely(JsValue o)
+        {
             var number = ToNumber(o);
             if (double.IsNaN(number))
             {
@@ -523,20 +528,29 @@ namespace Jint.Runtime
             var f = Math.Floor(number);
             if (f + 0.5 < number)
             {
-                return f + 1;
+                return (byte) (f + 1);
             }
 
             if (number < f + 0.5)
             {
-                return f;
+                return (byte) f;
             }
 
             if (f % 2 != 0)
             {
-                return f + 1;
+                return (byte) (f + 1);
             }
 
-            return f;
+            return (byte) f;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-tobigint
+        /// </summary>
+        public static double ToBigInt(JsValue value)
+        {
+            ExceptionHelper.ThrowNotImplementedException("BigInt not implemented");
+            return 0;
         }
 
         /// <summary>
@@ -557,6 +571,36 @@ namespace Jint.Runtime
             return 0;
         }
 
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-canonicalnumericindexstring
+        /// </summary>
+        internal static double? CanonicalNumericIndexString(JsValue value)
+        {
+            if (value is JsNumber jsNumber)
+            {
+                return jsNumber._value;
+            }
+
+            if (value is JsString jsString)
+            {
+                if (jsString.ToString() == "-0")
+                {
+                    return -0d;
+                }
+
+                var n = ToNumber(value);
+                if (!JsValue.SameValue(ToString(n), value))
+                {
+                    return null;
+                }
+
+                return n;
+            }
+
+            return null;
+        }
+
         /// <summary>
         /// https://tc39.es/ecma262/#sec-toindex
         /// </summary>

+ 1 - 1
README.md

@@ -43,7 +43,7 @@ The entire execution engine was rebuild with performance in mind, in many cases
 - ✔ Reflect
 - ✔ Symbols
 - ❌ Tail calls
--  Typed arrays
+-  Typed arrays
 - ❌ Unicode
 - ✔ Weakmap and Weakset