Selaa lähdekoodia

ArrayBuffer and DataView (#930)

Marko Lahma 4 vuotta sitten
vanhempi
commit
7080fdf7de

+ 15 - 0
Jint.Tests.Test262/BuiltIns/ArrayBufferTests.cs

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

+ 15 - 0
Jint.Tests.Test262/BuiltIns/DataViewTests.cs

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

+ 0 - 15
Jint.Tests.Test262/BuiltIns/StringIteratorPrototypeTests.cs

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

+ 8 - 0
Jint.Tests.Test262/BuiltIns/StringTests.cs

@@ -11,5 +11,13 @@ namespace Jint.Tests.Test262.BuiltIns
         {
             RunTestInternal(sourceFile);
         }
+
+        [Theory(DisplayName = "built-ins\\StringIteratorPrototype")]
+        [MemberData(nameof(SourceFiles), "built-ins\\StringIteratorPrototype", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\StringIteratorPrototype", true, Skip = "Skipped")]
+        protected void StringIteratorPrototype(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
     }
 }

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

@@ -7,6 +7,8 @@ using System.Reflection;
 using System.Text.RegularExpressions;
 using Esprima;
 using Esprima.Ast;
+using Jint.Native;
+using Jint.Native.ArrayBuffer;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
@@ -67,7 +69,8 @@ namespace Jint.Tests.Test262
                 "wellKnownIntrinsicObjects.js",
                 "fnGlobalObject.js",
                 "testTypedArray.js",
-                "detachArrayBuffer.js"
+                "detachArrayBuffer.js",
+                "byteConversionValues.js"
             };
 
             Sources = new Dictionary<string, Script>(files.Length);
@@ -126,6 +129,14 @@ namespace Jint.Tests.Test262
                     return realm.GlobalObject;
                 }), true, true, true));
 
+            o.FastSetProperty("detachArrayBuffer", new PropertyDescriptor(new ClrFunctionInstance(engine, "detachArrayBuffer",
+                (thisObj, args) =>
+                {
+                    var buffer = (ArrayBufferInstance) args.At(0);
+                    buffer.DetachArrayBuffer();
+                    return JsValue.Undefined;
+                }), true, true, true));
+
             engine.SetValue("$262", o);
 
             var includes = Regex.Match(code, @"includes: \[(.+?)\]");
@@ -282,6 +293,26 @@ namespace Jint.Tests.Test262
                                 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";
+                                break;
+                            case "resizable-arraybuffer":
+                                skip = true;
+                                reason = "resizable-arraybuffer not implemented";
+                                break;
+                            case "TypedArray.prototype.at":
+                                skip = true;
+                                reason = "TypedArray.prototype.at not implemented";
+                                break;
                         }
                     }
                 }
@@ -310,30 +341,12 @@ namespace Jint.Tests.Test262
                     reason = "Unicode support and its special cases need more work";
                 }
 
-                if (name.StartsWith("language/statements/class/subclass/builtin-objects/Promise"))
-                {
-                    skip = true;
-                    reason = "Promise not implemented";
-                }
-
                 if (name.StartsWith("language/statements/class/subclass/builtin-objects/TypedArray"))
                 {
                     skip = true;
                     reason = "TypedArray not implemented";
                 }
 
-                if (name.StartsWith("language/statements/class/subclass/builtin-objects/ArrayBuffer/"))
-                {
-                    skip = true;
-                    reason = "ArrayBuffer not implemented";
-                }
-
-                if (name.StartsWith("language/statements/class/subclass/builtin-objects/DataView"))
-                {
-                    skip = true;
-                    reason = "DataView not implemented";
-                }
-
                 if (name.StartsWith("language/statements/class/subclass/builtins.js"))
                 {
                     skip = true;

+ 0 - 4
Jint.Tests.Test262/test/skipped.json

@@ -114,10 +114,6 @@
     "source": "built-ins/Proxy/enumerate/removed-does-not-trigger.js",
     "reason": "for-of not implemented"
   },
-  {
-    "source": "built-ins/Array/from/items-is-arraybuffer.js",
-    "reason": "ArrayBuffer not implemented"
-  },
   {
     "source": "built-ins/Array/prototype/concat/Array.prototype.concat_large-typed-array.js",
     "reason": "Uint8Array not implemented"

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

@@ -60,6 +60,8 @@ namespace Jint.Native.Array
 
         public override bool IsArrayLike => true;
 
+        public override bool IsArray() => true;
+
         internal override bool HasOriginalIterator
             => ReferenceEquals(Get(GlobalSymbolRegistry.Iterator), _engine.Realm.Intrinsics.Array.PrototypeObject._originalIteratorFunction);
 
@@ -419,7 +421,7 @@ namespace Jint.Native.Array
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private static bool IsArrayIndex(JsValue p, out uint index)
+        internal static bool IsArrayIndex(JsValue p, out uint index)
         {
             if (p is JsNumber number)
             {

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

@@ -1263,7 +1263,7 @@ namespace Jint.Native.Array
             return a;
         }
 
-        private JsValue ToString(JsValue thisObj, JsValue[] arguments)
+        internal JsValue ToString(JsValue thisObj, JsValue[] arguments)
         {
             var array = TypeConverter.ToObject(_realm, thisObj);
 

+ 95 - 0
Jint/Native/ArrayBuffer/ArrayBufferConstructor.cs

@@ -0,0 +1,95 @@
+using Jint.Collections;
+using Jint.Native.DataView;
+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.ArrayBuffer
+{
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-properties-of-the-arraybuffer-constructor
+    /// </summary>
+    public sealed class ArrayBufferConstructor : FunctionInstance, IConstructor
+    {
+        private static readonly JsString _functionName = new("ArrayBuffer");
+
+        internal ArrayBufferConstructor(
+            Engine engine,
+            Realm realm,
+            FunctionPrototype functionPrototype,
+            ObjectPrototype objectPrototype)
+            : base(engine, realm, _functionName)
+        {
+            _prototype = functionPrototype;
+            PrototypeObject = new ArrayBufferPrototype(engine, realm, this, objectPrototype);
+            _length = new PropertyDescriptor(1, PropertyFlag.Configurable);
+            _prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
+        }
+
+        public ArrayBufferPrototype PrototypeObject { get; }
+
+        protected override void Initialize()
+        {
+            const PropertyFlag lengthFlags = PropertyFlag.Configurable;
+            var properties = new PropertyDictionary(1, checkExistingKeys: false)
+            {
+                ["isView"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "isView", IsView, 1, lengthFlags), PropertyFlag.Configurable | PropertyFlag.Writable)),
+            };
+            SetProperties(properties);
+
+            var symbols = new SymbolDictionary(1)
+            {
+                [GlobalSymbolRegistry.Species] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(Engine, "get [Symbol.species]", Species, 0, lengthFlags), set: Undefined,PropertyFlag.Configurable),
+            };
+            SetSymbols(symbols);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-arraybuffer.isview
+        /// </summary>
+        private static JsValue IsView(JsValue thisObject, JsValue[] arguments)
+        {
+            var arg = arguments.At(0);
+            return arg is DataViewInstance;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-get-arraybuffer-@@species
+        /// </summary>
+        private static JsValue Species(JsValue thisObject, JsValue[] arguments)
+        {
+            return thisObject;
+        }
+
+        public override JsValue Call(JsValue thisObject, JsValue[] arguments)
+        {
+            ExceptionHelper.ThrowTypeError(_realm, "Constructor ArrayBuffer requires 'new'");
+            return Undefined;
+        }
+
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
+        {
+            if (newTarget.IsUndefined())
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            var byteLength = TypeConverter.ToIndex(_realm, arguments.At(0));
+            return AllocateArrayBuffer(newTarget, byteLength);
+        }
+
+        internal ArrayBufferInstance AllocateArrayBuffer(JsValue constructor, uint byteLength)
+        {
+            var obj = OrdinaryCreateFromConstructor(
+                constructor,
+                static intrinsics => intrinsics.ArrayBuffer.PrototypeObject,
+                static (engine, realm, state) => new ArrayBufferInstance(engine, (uint) ((JsNumber) state)._value),
+                JsNumber.Create(byteLength));
+
+            return obj;
+        }
+    }
+}

+ 270 - 0
Jint/Native/ArrayBuffer/ArrayBufferInstance.cs

@@ -0,0 +1,270 @@
+using System;
+using Jint.Native.Object;
+using Jint.Native.TypedArray;
+using Jint.Runtime;
+
+namespace Jint.Native.ArrayBuffer
+{
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-arraybuffer-objects
+    /// </summary>
+    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 byte[] _arrayBufferData;
+        private JsValue _arrayBufferDetachKey = Undefined;
+
+        internal ArrayBufferInstance(
+            Engine engine,
+            uint byteLength) : base(engine)
+        {
+            var block = CreateByteDataBlock(byteLength);
+            _arrayBufferData = block;
+        }
+
+        private byte[] CreateByteDataBlock(uint byteLength)
+        {
+            if (byteLength > int.MaxValue)
+            {
+                ExceptionHelper.ThrowRangeError(_engine.Realm, "Array buffer allocation failed");
+            }
+
+            return new byte[byteLength];
+        }
+
+        internal int ArrayBufferByteLength => _arrayBufferData.Length;
+        internal byte[] ArrayBufferData => _arrayBufferData;
+
+        internal bool IsDetachedBuffer => _arrayBufferData is null;
+        internal bool IsSharedArrayBuffer => false; // TODO SharedArrayBuffer
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-detacharraybuffer
+        /// </summary>
+        internal void DetachArrayBuffer(JsValue key = null)
+        {
+            key ??= Undefined;
+
+            if (!SameValue(_arrayBufferDetachKey, key))
+            {
+                ExceptionHelper.ThrowTypeError(_engine.Realm);
+            }
+
+            _arrayBufferData = null;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-getvaluefrombuffer
+        /// </summary>
+        internal JsValue GetValueFromBuffer(uint 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);
+            }
+
+            /*
+                Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent EsprimaExtensions.Record.
+                b. Let eventList be the [[EventList]] field of the element in execution.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier().
+                c. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, let noTear be true; otherwise let noTear be false.
+                d. Let rawValue be a List of length elementSize whose elements are nondeterministically chosen byte values.
+                e. NOTE: In implementations, rawValue is the result of a non-atomic or atomic read instruction on the underlying hardware. The nondeterminism is a semantic prescription of the memory model to describe observable behaviour of hardware with weak consistency.
+                f. Let readEvent be ReadSharedMemory { [[Order]]: order, [[NoTear]]: noTear, [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize }.
+                g. Append readEvent to eventList.
+                h. Append Chosen Value EsprimaExtensions.Record { [[Event]]: readEvent, [[ChosenValue]]: rawValue } to execution.[[ChosenValues]].
+            */
+            ExceptionHelper.ThrowNotImplementedException("SharedArrayBuffer not implemented");
+            return Undefined;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-rawbytestonumeric
+        /// </summary>
+        private JsValue RawBytesToNumeric(TypedArrayElementType type, int byteIndex, bool isLittleEndian)
+        {
+            var elementSize = type.GetElementSize();
+            var rawBytes = _arrayBufferData;
+
+            // floats require a little more at the moment
+            var needsReverse = !isLittleEndian && elementSize > 1 && (type == TypedArrayElementType.Float32 || type == TypedArrayElementType.Float64);
+            if (needsReverse)
+            {
+                System.Array.Copy(rawBytes, byteIndex, _workBuffer, 0, elementSize);
+                byteIndex = 0;
+                System.Array.Reverse(_workBuffer, 0, elementSize);
+                rawBytes = _workBuffer;
+            }
+
+            if (type == TypedArrayElementType.Float32)
+            {
+                // rawBytes concatenated and interpreted as a little-endian bit string encoding of an IEEE 754-2019 binary32 value.
+                var value = BitConverter.ToSingle(rawBytes, byteIndex);
+
+                // If value is an IEEE 754-2019 binary32 NaN value, return the NaN Number value.
+                if (float.IsNaN(value))
+                {
+                    return JsNumber.DoubleNaN;
+                }
+
+                return JsNumber.Create(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);
+            }
+
+            if (type.IsBigIntElementType())
+            {
+                // return the BigInt value that corresponds to intValue
+                ExceptionHelper.ThrowNotImplementedException("BigInt not implemented");
+            }
+
+            var 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
+                    ? (short) (rawBytes[byteIndex] | (rawBytes[byteIndex + 1] << 8))
+                    : (short) (rawBytes[byteIndex + 1] | (rawBytes[byteIndex] << 8))
+                ),
+                TypedArrayElementType.Uint16 => JsNumber.Create(isLittleEndian
+                    ? (ushort) (rawBytes[byteIndex] | (rawBytes[byteIndex + 1] << 8))
+                    : (ushort) (rawBytes[byteIndex + 1] | (rawBytes[byteIndex] << 8))
+                ),
+                TypedArrayElementType.Int32 => JsNumber.Create(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
+                    ? (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))
+                ),
+                _ => null
+            };
+
+            if (intValue is null)
+            {
+                ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(type), type.ToString());
+            }
+
+            return intValue;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-setvalueinbuffer
+        /// </summary>
+        internal void SetValueInBuffer(
+            uint byteIndex,
+            TypedArrayElementType type,
+            double value,
+            bool isTypedArray,
+            ArrayBufferOrder order,
+            bool? isLittleEndian = null)
+        {
+            var block = _arrayBufferData;
+            if (!IsSharedArrayBuffer)
+            {
+                // 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());
+            }
+            else
+            {
+                /*
+                    a. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record.
+                    b. Let eventList be the [[EventList]] field of the element in execution.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier().
+                    c. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, let noTear be true; otherwise let noTear be false.
+                    d. Append WriteSharedMemory { [[Order]]: order, [[NoTear]]: noTear, [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize, [[Payload]]: rawBytes } to eventList.
+                */
+                ExceptionHelper.ThrowNotImplementedException("SharedArrayBuffer not implemented");
+            }
+        }
+
+        private byte[] NumericToRawBytes(TypedArrayElementType type, double value, bool isLittleEndian)
+        {
+            byte[] rawBytes;
+            if (type == TypedArrayElementType.Float32)
+            {
+                // Let rawBytes be a List whose elements are the 4 bytes that are the result of converting value to IEEE 754-2019 binary32 format using roundTiesToEven mode. If isLittleEndian is false, the bytes are arranged in big endian order. Otherwise, the bytes are arranged in little endian order. If value is NaN, rawBytes may be set to any implementation chosen IEEE 754-2019 binary32 format Not-a-Number encoding. An implementation must always choose the same encoding for each implementation distinguishable NaN value.
+                rawBytes = BitConverter.GetBytes((float) value);
+            }
+            else if (type == TypedArrayElementType.Float64)
+            {
+                // Let rawBytes be a List whose elements are the 8 bytes that are the IEEE 754-2019 binary64 format encoding of value. If isLittleEndian is false, the bytes are arranged in big endian order. Otherwise, the bytes are arranged in little endian order. If value is NaN, rawBytes may be set to any implementation chosen IEEE 754-2019 binary64 format Not-a-Number encoding. An implementation must always choose the same encoding for each implementation distinguishable NaN value.
+                rawBytes = BitConverter.GetBytes(value);
+            }
+            else
+            {
+                // inlined conversion for faster speed instead of getting the method in spec
+                var intValue = (long) value;
+                rawBytes = _workBuffer;
+                switch (type)
+                {
+                    case TypedArrayElementType.Int8:
+                        rawBytes[0] = (byte) (sbyte) intValue;
+                        break;
+                    case TypedArrayElementType.Uint8:
+                        rawBytes[0] = (byte) intValue;
+                        break;
+                    case TypedArrayElementType.Uint8C:
+                        rawBytes[0] = System.Math.Min(System.Math.Max((byte) intValue, (byte) 0), (byte) 255);
+                        break;
+                    case TypedArrayElementType.Int16:
+#if !NETSTANDARD2_1
+                        rawBytes = BitConverter.GetBytes((short) intValue);
+#else
+                        BitConverter.TryWriteBytes(rawBytes, (short) intValue);
+#endif
+                        break;
+                    case TypedArrayElementType.Uint16:
+#if !NETSTANDARD2_1
+                        rawBytes = BitConverter.GetBytes((ushort) intValue);
+#else
+                        BitConverter.TryWriteBytes(rawBytes, (ushort) intValue);
+#endif
+                        break;
+                    case TypedArrayElementType.Int32:
+#if !NETSTANDARD2_1
+                        rawBytes = BitConverter.GetBytes((int) intValue);
+#else
+                        BitConverter.TryWriteBytes(rawBytes, (int) intValue);
+#endif
+                        break;
+                    case TypedArrayElementType.Uint32:
+#if !NETSTANDARD2_1
+                        rawBytes = BitConverter.GetBytes((uint) intValue);
+#else
+                        BitConverter.TryWriteBytes(rawBytes, (uint) intValue);
+#endif
+                        break;
+                    default:
+                        ExceptionHelper.ThrowArgumentOutOfRangeException();
+                        return null;
+                }
+            }
+
+            var elementSize = type.GetElementSize();
+            if (!isLittleEndian && elementSize > 1)
+            {
+                System.Array.Reverse(rawBytes, 0, elementSize);
+            }
+
+            return rawBytes;
+        }
+
+        internal void AssertNotDetached()
+        {
+            if (IsDetachedBuffer)
+            {
+                ExceptionHelper.ThrowTypeError(_engine.Realm, "ArrayBuffer has been detached");
+            }
+        }
+    }
+}

+ 9 - 0
Jint/Native/ArrayBuffer/ArrayBufferOrder.cs

@@ -0,0 +1,9 @@
+namespace Jint.Native.ArrayBuffer
+{
+    internal enum ArrayBufferOrder
+    {
+        Init,
+        SecCst,
+        Unordered
+    }
+}

+ 201 - 0
Jint/Native/ArrayBuffer/ArrayBufferPrototype.cs

@@ -0,0 +1,201 @@
+using Jint.Collections;
+using Jint.Native.Object;
+using Jint.Native.Symbol;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Interop;
+
+namespace Jint.Native.ArrayBuffer
+{
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-properties-of-the-arraybuffer-prototype-object
+    /// </summary>
+    public sealed class ArrayBufferPrototype : ObjectInstance
+    {
+        private readonly Realm _realm;
+        private readonly ArrayBufferConstructor _constructor;
+
+        internal ArrayBufferPrototype(
+            Engine engine,
+            Realm realm,
+            ArrayBufferConstructor constructor,
+            ObjectPrototype objectPrototype) : base(engine, 0)
+        {
+            _prototype = objectPrototype;
+            _realm = realm;
+            _constructor = constructor;
+        }
+
+        protected override void Initialize()
+        {
+            const PropertyFlag lengthFlags = PropertyFlag.Configurable;
+            var properties = new PropertyDictionary(3, checkExistingKeys: false)
+            {
+                ["byteLength"] = new GetSetPropertyDescriptor(new ClrFunctionInstance(_engine, "get byteLength", ByteLength, 0, lengthFlags), Undefined, PropertyFlag.Configurable),
+                ["constructor"] = new PropertyDescriptor(_constructor, PropertyFlag.NonEnumerable),
+                ["slice"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "slice", Slice, 2, lengthFlags), PropertyFlag.Configurable | PropertyFlag.Writable)
+            };
+            SetProperties(properties);
+
+            var symbols = new SymbolDictionary(1)
+            {
+                [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("ArrayBuffer", PropertyFlag.Configurable)
+            };
+            SetSymbols(symbols);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.bytelength
+        /// </summary>
+        private JsValue ByteLength(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj as ArrayBufferInstance;
+            if (o is null)
+            {
+                ExceptionHelper.ThrowTypeError(_realm, "Method ArrayBuffer.prototype.byteLength called on incompatible receiver " + thisObj);
+            }
+
+            if (o.IsSharedArrayBuffer)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            if (o.IsDetachedBuffer)
+            {
+                return JsNumber.PositiveZero;
+            }
+
+            return JsNumber.Create(o.ArrayBufferByteLength);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-arraybuffer.prototype.slice
+        /// </summary>
+        private JsValue Slice(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj as ArrayBufferInstance;
+            if (o is null)
+            {
+                ExceptionHelper.ThrowTypeError(_realm, "Method ArrayBuffer.prototype.slice called on incompatible receiver " + thisObj);
+            }
+
+            if (o.IsSharedArrayBuffer)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            o.AssertNotDetached();
+
+            var start = arguments.At(0);
+            var end = arguments.At(1);
+
+            var len = o.ArrayBufferByteLength;
+            var relativeStart = TypeConverter.ToIntegerOrInfinity(start);
+            var first = relativeStart switch
+            {
+                double.NegativeInfinity => 0,
+                < 0 => (int) System.Math.Max(len + relativeStart, 0),
+                _ => (int) System.Math.Min(relativeStart, len)
+            };
+
+            double relativeEnd;
+            if (end.IsUndefined())
+            {
+                relativeEnd = len;
+            }
+            else
+            {
+                relativeEnd = TypeConverter.ToIntegerOrInfinity(end);
+            }
+
+            var final = relativeEnd switch
+            {
+                double.NegativeInfinity => 0,
+                < 0 => (int) System.Math.Max(len + relativeEnd, 0),
+                _ => (int) System.Math.Min(relativeEnd, len)
+            };
+
+            var newLen = System.Math.Max(final - first, 0);
+            var ctor = SpeciesConstructor(o, _realm.Intrinsics.ArrayBuffer);
+            var bufferInstance = Construct(ctor, new JsValue[] { JsNumber.Create(newLen) }) as ArrayBufferInstance;
+
+            if (bufferInstance is null)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+            if (bufferInstance.IsSharedArrayBuffer)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+            if (bufferInstance.IsDetachedBuffer)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            if (ReferenceEquals(bufferInstance, o))
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            if (bufferInstance.ArrayBufferByteLength < newLen)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            // NOTE: Side-effects of the above steps may have detached O.
+
+            if (bufferInstance.IsDetachedBuffer)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            var fromBuf = o.ArrayBufferData;
+            var toBuf = bufferInstance.ArrayBufferData;
+            System.Array.Copy(fromBuf, first, toBuf, 0, newLen);
+            return bufferInstance;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-copydatablockbytes
+        /// </summary>
+        /// <remarks>
+        /// Here only to support algorithm of shared view buffer.
+        /// </remarks>
+        private void CopyDataBlockBytes(byte[] toBlock, int toIndex, byte[] fromBlock, int fromIndex, int count)
+        {
+            var fromSize = Length;
+            var toSize = Length;
+
+            bool isSharedDataBlock = false;
+
+            while (count > 0)
+            {
+                if (isSharedDataBlock)
+                {
+                    /*
+                    i. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record.
+                        ii. Let eventList be the [[EventList]] field of the element in execution.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier().
+                        iii. Let bytes be a List whose sole element is a nondeterministically chosen byte value.
+                        iv. NOTE: In implementations, bytes is the result of a non-atomic read instruction on the underlying hardware. The nondeterminism is a semantic prescription of the memory model to describe observable behaviour of hardware with weak consistency.
+                        v. Let readEvent be ReadSharedMemory { [[Order]]: Unordered, [[NoTear]]: true, [[Block]]: fromBlock, [[ByteIndex]]: fromIndex, [[ElementSize]]: 1 }.
+                    vi. Append readEvent to eventList.
+                        vii. Append Chosen Value Record { [[Event]]: readEvent, [[ChosenValue]]: bytes } to execution.[[ChosenValues]].
+                    viii. If toBlock is a Shared Data Block, then
+                    1. Append WriteSharedMemory { [[Order]]: Unordered, [[NoTear]]: true, [[Block]]: toBlock, [[ByteIndex]]: toIndex, [[ElementSize]]: 1, [[Payload]]: bytes } to eventList.
+                        ix. Else,
+                    1. Set toBlock[toIndex] to bytes[0].
+                    */
+                    ExceptionHelper.ThrowNotImplementedException();
+                }
+                else
+                {
+                    toBlock[toIndex] = fromBlock[fromIndex];
+                }
+
+                toIndex++;
+                fromIndex++;
+                count--;
+            }
+        }
+    }
+}

+ 97 - 0
Jint/Native/DataView/DataViewConstructor.cs

@@ -0,0 +1,97 @@
+using Jint.Native.ArrayBuffer;
+using Jint.Native.Function;
+using Jint.Native.Object;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+
+namespace Jint.Native.DataView
+{
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-dataview-constructor
+    /// </summary>
+    public sealed class DataViewConstructor : FunctionInstance, IConstructor
+    {
+        private static readonly JsString _functionName = new("DataView");
+
+        internal DataViewConstructor(
+            Engine engine,
+            Realm realm,
+            FunctionPrototype functionPrototype,
+            ObjectPrototype objectPrototype)
+            : base(engine, realm, _functionName)
+        {
+            _prototype = functionPrototype;
+            PrototypeObject = new DataViewPrototype(engine, realm, this, objectPrototype);
+            _length = new PropertyDescriptor(1, PropertyFlag.Configurable);
+            _prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
+        }
+
+        public DataViewPrototype PrototypeObject { get; }
+
+        public override JsValue Call(JsValue thisObject, JsValue[] arguments)
+        {
+            ExceptionHelper.ThrowTypeError(_realm, "Constructor DataView requires 'new'");
+            return Undefined;
+        }
+
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
+        {
+            if (newTarget.IsUndefined())
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            var buffer = arguments.At(0) as ArrayBufferInstance;
+            var byteOffset = arguments.At(1);
+            var byteLength = arguments.At(2);
+
+            if (buffer is null)
+            {
+                ExceptionHelper.ThrowTypeError(_realm, "First argument to DataView constructor must be an ArrayBuffer");
+            }
+
+            var offset = TypeConverter.ToIndex(_realm, byteOffset);
+
+            if (buffer.IsDetachedBuffer)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            var bufferByteLength = (uint) buffer.ArrayBufferByteLength;
+            if (offset > bufferByteLength)
+            {
+                ExceptionHelper.ThrowRangeError(_realm, "Start offset " + offset + " is outside the bounds of the buffer");
+            }
+
+            uint viewByteLength;
+            if (byteLength.IsUndefined())
+            {
+                viewByteLength = bufferByteLength - offset;
+            }
+            else
+            {
+                viewByteLength = TypeConverter.ToIndex(_realm, byteLength);
+                if (offset + viewByteLength > bufferByteLength)
+                {
+                    ExceptionHelper.ThrowRangeError(_realm, "Invalid DataView length");
+                }
+            }
+
+            var o = OrdinaryCreateFromConstructor(
+                newTarget,
+                static intrinsics => intrinsics.DataView.PrototypeObject,
+                static (engine, realm, state) => new DataViewInstance(engine));
+
+            if (buffer.IsDetachedBuffer)
+            {
+                ExceptionHelper.ThrowTypeError(_realm);
+            }
+
+            o._viewedArrayBuffer = buffer;
+            o._byteLength = viewByteLength;
+            o._byteOffset = offset;
+
+            return o;
+        }
+    }
+}

+ 19 - 0
Jint/Native/DataView/DataViewInstance.cs

@@ -0,0 +1,19 @@
+using Jint.Native.ArrayBuffer;
+using Jint.Native.Object;
+
+namespace Jint.Native.DataView
+{
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-properties-of-dataview-instances
+    /// </summary>
+    public sealed class DataViewInstance : ObjectInstance
+    {
+        internal ArrayBufferInstance _viewedArrayBuffer;
+        internal uint _byteLength;
+        internal uint _byteOffset;
+
+        internal DataViewInstance(Engine engine) : base(engine)
+        {
+        }
+    }
+}

+ 299 - 0
Jint/Native/DataView/DataViewPrototype.cs

@@ -0,0 +1,299 @@
+using Jint.Collections;
+using Jint.Native.ArrayBuffer;
+using Jint.Native.Object;
+using Jint.Native.Symbol;
+using Jint.Native.TypedArray;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Interop;
+
+namespace Jint.Native.DataView
+{
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-properties-of-the-dataview-prototype-object
+    /// </summary>
+    public sealed class DataViewPrototype : ObjectInstance
+    {
+        private readonly Realm _realm;
+        private readonly DataViewConstructor _constructor;
+
+        internal DataViewPrototype(
+            Engine engine,
+            Realm realm,
+            DataViewConstructor constructor,
+            ObjectPrototype objectPrototype) : base(engine)
+        {
+            _prototype = objectPrototype;
+            _realm = realm;
+            _constructor = constructor;
+        }
+
+        protected override void Initialize()
+        {
+            const PropertyFlag lengthFlags = PropertyFlag.Configurable;
+            const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
+            var properties = new PropertyDictionary(24, checkExistingKeys: 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),
+                ["constructor"] = new PropertyDescriptor(_constructor, PropertyFlag.NonEnumerable),
+                ["getBigInt64"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getBigInt64", GetBigInt64, length: 1, lengthFlags), propertyFlags),
+                ["getBigUint64"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getBigUint64", GetBigUint64, length: 1, lengthFlags), propertyFlags),
+                ["getFloat32"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getFloat32", GetFloat32, length: 1, lengthFlags), propertyFlags),
+                ["getFloat64"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getFloat64", GetFloat64, length: 1, lengthFlags), propertyFlags),
+                ["getInt8"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getInt8", GetInt8, length: 1, lengthFlags), propertyFlags),
+                ["getInt16"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getInt16", GetInt16, length: 1, lengthFlags), propertyFlags),
+                ["getInt32"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getInt32", GetInt32, length: 1, lengthFlags), propertyFlags),
+                ["getUint8"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUint8", GetUint8, length: 1, lengthFlags), propertyFlags),
+                ["getUint16"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUint16", GetUint16, length: 1, lengthFlags), propertyFlags),
+                ["getUint32"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getUint32", GetUint32, length: 1, lengthFlags), propertyFlags),
+                ["setBigInt64"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setBigInt64", SetBigInt64, length: 2, lengthFlags), propertyFlags),
+                ["setBigUint64"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setBigUint64", SetBigUint64, length: 2, lengthFlags), propertyFlags),
+                ["setFloat32"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setFloat32", SetFloat32, length: 2, lengthFlags), propertyFlags),
+                ["setFloat64"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setFloat64", SetFloat64, length: 2, lengthFlags), propertyFlags),
+                ["setInt8"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setInt8", SetInt8, length: 2, lengthFlags), propertyFlags),
+                ["setInt16"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setInt16", SetInt16, length: 2, lengthFlags), propertyFlags),
+                ["setInt32"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setInt32", SetInt32, length: 2, lengthFlags), propertyFlags),
+                ["setUint8"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUint8", SetUint8, length: 2, lengthFlags), propertyFlags),
+                ["setUint16"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUint16", SetUint16, length: 2, lengthFlags), propertyFlags),
+                ["setUint32"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setUint32", SetUint32, length: 2, lengthFlags), propertyFlags)
+            };
+            SetProperties(properties);
+
+            var symbols = new SymbolDictionary(1)
+            {
+                [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("DataView", PropertyFlag.Configurable)
+            };
+            SetSymbols(symbols);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-get-dataview.prototype.buffer
+        /// </summary>
+        private JsValue Buffer(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj as DataViewInstance;
+            if (o is null)
+            {
+                ExceptionHelper.ThrowTypeError(_realm, "Method get DataView.prototype.buffer called on incompatible receiver " + thisObj);
+            }
+
+            return o._viewedArrayBuffer;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-get-dataview.prototype.bytelength
+        /// </summary>
+        private JsValue ByteLength(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj as DataViewInstance;
+            if (o is null)
+            {
+                ExceptionHelper.ThrowTypeError(_realm, "Method get DataView.prototype.byteLength called on incompatible receiver " + thisObj);
+            }
+
+            var buffer = o._viewedArrayBuffer;
+            buffer.AssertNotDetached();
+
+            return JsNumber.Create(o._byteLength);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-get-dataview.prototype.byteoffset
+        /// </summary>
+        private JsValue ByteOffset(JsValue thisObj, JsValue[] arguments)
+        {
+            var o = thisObj as DataViewInstance;
+            if (o is null)
+            {
+                ExceptionHelper.ThrowTypeError(_realm, "Method get DataView.prototype.byteOffset called on incompatible receiver " + thisObj);
+            }
+
+            var buffer = o._viewedArrayBuffer;
+            buffer.AssertNotDetached();
+
+            return JsNumber.Create(o._byteOffset);
+        }
+
+        private JsValue GetBigInt64(JsValue thisObj, JsValue[] arguments)
+        {
+            return GetViewValue(thisObj, arguments.At(0), arguments.At(1), TypedArrayElementType.BigInt64);
+        }
+
+        private JsValue GetBigUint64(JsValue thisObj, JsValue[] arguments)
+        {
+            return GetViewValue(thisObj, arguments.At(0), arguments.At(1), TypedArrayElementType.BigUint64);
+        }
+
+        private JsValue GetFloat32(JsValue thisObj, JsValue[] arguments)
+        {
+            return GetViewValue(thisObj, arguments.At(0), arguments.At(1, JsBoolean.False), TypedArrayElementType.Float32);
+        }
+
+        private JsValue GetFloat64(JsValue thisObj, JsValue[] arguments)
+        {
+            return GetViewValue(thisObj, arguments.At(0), arguments.At(1, JsBoolean.False), TypedArrayElementType.Float64);
+        }
+
+        private JsValue GetInt8(JsValue thisObj, JsValue[] arguments)
+        {
+            return GetViewValue(thisObj, arguments.At(0), JsBoolean.True, TypedArrayElementType.Int8);
+        }
+
+        private JsValue GetInt16(JsValue thisObj, JsValue[] arguments)
+        {
+            return GetViewValue(thisObj, arguments.At(0), arguments.At(1, JsBoolean.False), TypedArrayElementType.Int16);
+        }
+
+        private JsValue GetInt32(JsValue thisObj, JsValue[] arguments)
+        {
+            return GetViewValue(thisObj, arguments.At(0), arguments.At(1, JsBoolean.False), TypedArrayElementType.Int32);
+        }
+
+        private JsValue GetUint8(JsValue thisObj, JsValue[] arguments)
+        {
+            return GetViewValue(thisObj, arguments.At(0), JsBoolean.True, TypedArrayElementType.Uint8);
+        }
+
+        private JsValue GetUint16(JsValue thisObj, JsValue[] arguments)
+        {
+            return GetViewValue(thisObj, arguments.At(0), arguments.At(1, JsBoolean.False), TypedArrayElementType.Uint16);
+        }
+
+        private JsValue GetUint32(JsValue thisObj, JsValue[] arguments)
+        {
+            return GetViewValue(thisObj, arguments.At(0), arguments.At(1, JsBoolean.False), TypedArrayElementType.Uint32);
+        }
+
+        private JsValue SetBigInt64(JsValue thisObj, JsValue[] arguments)
+        {
+            return SetViewValue(thisObj, arguments.At(0), arguments.At(2), TypedArrayElementType.BigInt64, arguments.At(1));
+        }
+
+        private JsValue SetBigUint64(JsValue thisObj, JsValue[] arguments)
+        {
+            return SetViewValue(thisObj, arguments.At(0), arguments.At(2), TypedArrayElementType.BigUint64, arguments.At(1));
+        }
+
+        private JsValue SetFloat32 (JsValue thisObj, JsValue[] arguments)
+        {
+            return SetViewValue(thisObj, arguments.At(0), arguments.At(2, JsBoolean.False), TypedArrayElementType.Float32, arguments.At(1));
+        }
+
+        private JsValue SetFloat64(JsValue thisObj, JsValue[] arguments)
+        {
+            return SetViewValue(thisObj, arguments.At(0), arguments.At(2, JsBoolean.False), TypedArrayElementType.Float64, arguments.At(1));
+        }
+
+        private JsValue SetInt8 (JsValue thisObj, JsValue[] arguments)
+        {
+            return SetViewValue(thisObj, arguments.At(0), JsBoolean.True, TypedArrayElementType.Int8, arguments.At(1));
+        }
+
+        private JsValue SetInt16(JsValue thisObj, JsValue[] arguments)
+        {
+            return SetViewValue(thisObj, arguments.At(0), arguments.At(2, JsBoolean.False), TypedArrayElementType.Int16, arguments.At(1));
+        }
+
+        private JsValue SetInt32(JsValue thisObj, JsValue[] arguments)
+        {
+            return SetViewValue(thisObj, arguments.At(0), arguments.At(2, JsBoolean.False), TypedArrayElementType.Int32, arguments.At(1));
+        }
+
+        private JsValue SetUint8(JsValue thisObj, JsValue[] arguments)
+        {
+            return SetViewValue(thisObj, arguments.At(0), JsBoolean.True, TypedArrayElementType.Uint8, arguments.At(1));
+        }
+
+        private JsValue SetUint16(JsValue thisObj, JsValue[] arguments)
+        {
+            return SetViewValue(thisObj, arguments.At(0), arguments.At(2, JsBoolean.False), TypedArrayElementType.Uint16, arguments.At(1));
+        }
+
+        private JsValue SetUint32(JsValue thisObj, JsValue[] arguments)
+        {
+            return SetViewValue(thisObj, arguments.At(0), arguments.At(2, JsBoolean.False), TypedArrayElementType.Uint32, arguments.At(1));
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-getviewvalue
+        /// </summary>
+        private JsValue GetViewValue(
+            JsValue view,
+            JsValue requestIndex,
+            JsValue isLittleEndian,
+            TypedArrayElementType type)
+        {
+            var dataView = view as DataViewInstance;
+            if (dataView is null)
+            {
+                ExceptionHelper.ThrowTypeError(_realm, "Method called on incompatible receiver " + view);
+            }
+
+            var getIndex = TypeConverter.ToIndex(_realm, requestIndex);
+            var isLittleEndianBoolean = TypeConverter.ToBoolean(isLittleEndian);
+            var buffer = dataView._viewedArrayBuffer;
+
+            buffer.AssertNotDetached();
+
+            var viewOffset = dataView._byteOffset;
+            var viewSize = dataView._byteLength;
+            var elementSize = type.GetElementSize();
+            if (getIndex + elementSize > viewSize)
+            {
+                ExceptionHelper.ThrowRangeError(_realm, "Offset is outside the bounds of the DataView");
+            }
+
+            var bufferIndex = getIndex + viewOffset;
+            return buffer.GetValueFromBuffer(bufferIndex, type, false, ArrayBufferOrder.Unordered, isLittleEndianBoolean);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-setviewvalue
+        /// </summary>
+        private JsValue SetViewValue(
+            JsValue view,
+            JsValue requestIndex,
+            JsValue isLittleEndian,
+            TypedArrayElementType type,
+            JsValue value)
+        {
+            var dataView = view as DataViewInstance;
+            if (dataView is null)
+            {
+                ExceptionHelper.ThrowTypeError(_realm, "Method called on incompatible receiver " + view);
+            }
+
+            var getIndex = TypeConverter.ToIndex(_realm, requestIndex);
+
+            double numberValue;
+            if (type.IsBigIntElementType())
+            {
+                // let numberValue be ? ToBigInt(value).
+                ExceptionHelper.ThrowNotImplementedException("BigInt not implemented");
+                return Undefined;
+            }
+            else
+            {
+                numberValue = TypeConverter.ToNumber(value);
+            }
+
+            var isLittleEndianBoolean = TypeConverter.ToBoolean(isLittleEndian);
+            var buffer = dataView._viewedArrayBuffer;
+            buffer.AssertNotDetached();
+
+            var viewOffset = dataView._byteOffset;
+            var viewSize = dataView._byteLength;
+            var elementSize = type.GetElementSize();
+            if (getIndex + elementSize > viewSize)
+            {
+                ExceptionHelper.ThrowRangeError(_realm, "Offset is outside the bounds of the DataView");
+            }
+
+            var bufferIndex = getIndex + viewOffset;
+            buffer.SetValueInBuffer(bufferIndex, type, numberValue, false, ArrayBufferOrder.Unordered, isLittleEndianBoolean);
+            return Undefined;
+        }
+    }
+}

+ 3 - 1
Jint/Native/Global/GlobalObject.cs

@@ -33,12 +33,14 @@ namespace Jint.Native.Global
             const PropertyFlag lengthFlags = PropertyFlag.Configurable;
             const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
 
-            var properties = new PropertyDictionary(40, checkExistingKeys: false)
+            var properties = new PropertyDictionary(54, checkExistingKeys: false)
             {
                 ["Object"] = new PropertyDescriptor(_realm.Intrinsics.Object, propertyFlags),
                 ["Function"] = new PropertyDescriptor(_realm.Intrinsics.Function, propertyFlags),
                 ["Symbol"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Symbol, propertyFlags),
                 ["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),
                 ["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),

+ 2 - 3
Jint/Native/Iterator/IteratorProtocol.cs

@@ -1,5 +1,4 @@
-using Jint.Native.Array;
-using Jint.Native.Object;
+using Jint.Native.Object;
 using Jint.Runtime;
 
 namespace Jint.Native.Iterator
@@ -9,7 +8,7 @@ namespace Jint.Native.Iterator
     /// </summary>
     internal abstract class IteratorProtocol
     {
-        protected readonly Engine _engine;
+        private readonly Engine _engine;
         private readonly IIterator _iterator;
         private readonly int _argCount;
 

+ 17 - 0
Jint/Native/JsNumber.cs

@@ -29,6 +29,8 @@ namespace Jint.Native
         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 PI = new JsNumber(System.Math.PI);
 
@@ -134,6 +136,21 @@ namespace Jint.Native
             return new JsNumber(value);
         }
 
+        internal static JsNumber Create(byte value)
+        {
+            return _intToJsValue[value];
+        }
+
+        internal static JsNumber Create(sbyte value)
+        {
+            var temp = _intToJsValue;
+            if ((uint) value < (uint) temp.Length)
+            {
+                return temp[value];
+            }
+            return new JsNumber(value);
+        }
+
         internal static JsNumber Create(int value)
         {
             var temp = _intToJsValue;

+ 1 - 4
Jint/Native/JsValue.cs

@@ -57,10 +57,7 @@ namespace Jint.Native
         }
 
         [Pure]
-        public virtual bool IsArray()
-        {
-            return this is ArrayInstance;
-        }
+        public virtual bool IsArray() => false;
 
         [Pure]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]

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

@@ -1197,10 +1197,10 @@ namespace Jint.Native.Object
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal ICallable GetMethod(JsValue property)
         {
-            return GetMethod(_engine, this, property);
+            return GetMethod(_engine.Realm, this, property);
         }
 
-        internal static ICallable GetMethod(Engine engine, JsValue v, JsValue p)
+        internal static ICallable GetMethod(Realm realm, JsValue v, JsValue p)
         {
             var jsValue = v.Get(p);
             if (jsValue.IsNullOrUndefined())
@@ -1211,7 +1211,7 @@ namespace Jint.Native.Object
             var callable = jsValue as ICallable;
             if (callable is null)
             {
-                ExceptionHelper.ThrowTypeError(engine.Realm, "Value returned for property '" + p + "' of object is not a function");
+                ExceptionHelper.ThrowTypeError(realm, "Value returned for property '" + p + "' of object is not a function");
             }
             return callable;
         }

+ 5 - 5
Jint/Native/String/StringPrototype.cs

@@ -332,7 +332,7 @@ namespace Jint.Native.String
 
             if (separator is ObjectInstance oi)
             {
-                var splitter = GetMethod(_engine, oi, GlobalSymbolRegistry.Split);
+                var splitter = GetMethod(_realm, oi, GlobalSymbolRegistry.Split);
                 if (splitter != null)
                 {
                     return splitter.Call(separator, new[] { thisObj, limit });
@@ -452,7 +452,7 @@ namespace Jint.Native.String
 
             if (regex is ObjectInstance oi)
             {
-                var searcher = GetMethod(_engine, oi, GlobalSymbolRegistry.Search);
+                var searcher = GetMethod(_realm, oi, GlobalSymbolRegistry.Search);
                 if (searcher != null)
                 {
                     return searcher.Call(regex, new[] { thisObj });
@@ -473,7 +473,7 @@ namespace Jint.Native.String
 
             if (!searchValue.IsNullOrUndefined())
             {
-                var replacer = GetMethod(_engine, searchValue, GlobalSymbolRegistry.Replace);
+                var replacer = GetMethod(_realm, searchValue, GlobalSymbolRegistry.Replace);
                 if (replacer != null)
                 {
                     return replacer.Call(searchValue, new[] { thisObj, replaceValue});
@@ -521,7 +521,7 @@ namespace Jint.Native.String
             var regex = arguments.At(0);
             if (regex is ObjectInstance oi)
             {
-                var matcher = GetMethod(_engine, oi, GlobalSymbolRegistry.Match);
+                var matcher = GetMethod(_realm, oi, GlobalSymbolRegistry.Match);
                 if (matcher != null)
                 {
                     return matcher.Call(regex, new[] { thisObj });
@@ -550,7 +550,7 @@ namespace Jint.Native.String
                         ExceptionHelper.ThrowTypeError(_realm);
                     }
                 }
-                var matcher = GetMethod(_engine, (ObjectInstance) regex, GlobalSymbolRegistry.MatchAll);
+                var matcher = GetMethod(_realm, (ObjectInstance) regex, GlobalSymbolRegistry.MatchAll);
                 if (matcher != null)
                 {
                     return matcher.Call(regex, new[] { thisObj });

+ 51 - 0
Jint/Native/TypedArray/TypedArrayElementType.cs

@@ -0,0 +1,51 @@
+namespace Jint.Native.TypedArray
+{
+    internal enum TypedArrayElementType
+    {
+        // we have signed first to make comparison vaster to check if signed or unsigned type
+        Int8,
+        Int16,
+        Int32,
+        BigInt64,
+        Float32,
+        Float64,
+
+        Uint8,
+        Uint8C,
+        Uint16,
+        Uint32,
+        BigUint64
+    }
+
+    internal static class TypedArrayExtensions
+    {
+        internal static int GetElementSize(this TypedArrayElementType type)
+        {
+            return type switch
+            {
+                TypedArrayElementType.Int8 => 1,
+                TypedArrayElementType.Uint8 => 1,
+                TypedArrayElementType.Uint8C => 1,
+                TypedArrayElementType.Int16 => 2,
+                TypedArrayElementType.Uint16 => 2,
+                TypedArrayElementType.Int32 => 4,
+                TypedArrayElementType.Uint32 => 4,
+                TypedArrayElementType.BigInt64 => 8,
+                TypedArrayElementType.BigUint64 => 8,
+                TypedArrayElementType.Float32 => 4,
+                TypedArrayElementType.Float64 => 8,
+                _ => -1
+            };
+        }
+
+        internal static bool IsUnsignedElementType(this TypedArrayElementType type)
+        {
+            return type > TypedArrayElementType.Float64;
+        }
+
+        internal static bool IsBigIntElementType(this TypedArrayElementType type)
+        {
+            return type is TypedArrayElementType.BigUint64 or TypedArrayElementType.BigInt64;
+        }
+    }
+}

+ 11 - 1
Jint/Runtime/Intrinsics.cs

@@ -1,6 +1,8 @@
 using Jint.Native;
 using Jint.Native.Array;
+using Jint.Native.ArrayBuffer;
 using Jint.Native.Boolean;
+using Jint.Native.DataView;
 using Jint.Native.Date;
 using Jint.Native.Error;
 using Jint.Native.Function;
@@ -61,6 +63,8 @@ namespace Jint.Runtime
         private SetConstructor _set;
         private ArrayConstructor _array;
         private BooleanConstructor _boolean;
+        private ArrayBufferConstructor _arrayBufferConstructor;
+        private DataViewConstructor _dataView;
 
         internal Intrinsics(Engine engine, Realm realm)
         {
@@ -84,6 +88,12 @@ namespace Jint.Runtime
         public ArrayConstructor Array =>
             _array ??= new ArrayConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject);
 
+        public DataViewConstructor DataView =>
+            _dataView ??= new DataViewConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject);
+
+        public ArrayBufferConstructor ArrayBuffer =>
+            _arrayBufferConstructor ??= new ArrayBufferConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject);
+
         public MapConstructor Map =>
             _map ??= new MapConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject);
 
@@ -117,7 +127,7 @@ namespace Jint.Runtime
         public DateConstructor Date =>
             _date ??= new DateConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject);
 
-        public MathInstance Math=>
+        public MathInstance Math =>
             _math ??= new MathInstance(_engine, Object.PrototypeObject);
 
         public JsonInstance Json =>

+ 153 - 30
Jint/Runtime/TypeConverter.cs

@@ -163,7 +163,7 @@ namespace Jint.Runtime
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.2
+        /// https://tc39.es/ecma262/#sec-toboolean
         /// </summary>
         public static bool ToBoolean(JsValue o)
         {
@@ -188,7 +188,7 @@ namespace Jint.Runtime
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.3
+        /// https://tc39.es/ecma262/#sec-tonumber
         /// </summary>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static double ToNumber(JsValue o)
@@ -211,7 +211,7 @@ namespace Jint.Runtime
                 case InternalTypes.Object when o is IPrimitiveInstance p:
                     return ToNumber(ToPrimitive(p.PrimitiveValue, Types.Number));
                 case InternalTypes.Boolean:
-                    return (((JsBoolean)o)._value ? 1 : 0);
+                    return ((JsBoolean) o)._value ? 1 : 0;
                 case InternalTypes.String:
                     return ToNumber(o.ToString());
                 case InternalTypes.Symbol:
@@ -231,7 +231,7 @@ namespace Jint.Runtime
                 return 0;
             }
 
-            char first = input[0];
+            var first = input[0];
             if (input.Length == 1 && first >= '0' && first <= '9')
             {
                 // simple constant number
@@ -263,7 +263,7 @@ namespace Jint.Runtime
             {
                 if (s.Length > 2 && s[0] == '0' && char.IsLetter(s[1]))
                 {
-                    int fromBase = 0;
+                    var fromBase = 0;
                     if (s[1] == 'x' || s[1] == 'X')
                     {
                         fromBase = 16;
@@ -291,7 +291,7 @@ namespace Jint.Runtime
                     return double.NaN;
                 }
 
-                double n = double.Parse(s,
+                var n = double.Parse(s,
                     NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign |
                     NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite |
                     NumberStyles.AllowExponent, CultureInfo.InvariantCulture);
@@ -392,11 +392,11 @@ namespace Jint.Runtime
         {
             // Computes the integral value of the number mod 2^32.
 
-            long doubleBits = BitConverter.DoubleToInt64Bits(o);
-            int sign = (int)(doubleBits >> 63);   // 0 if positive, -1 if negative
-            int exponent = (int)((doubleBits >> 52) & 0x7FF) - 1023;
+            var doubleBits = BitConverter.DoubleToInt64Bits(o);
+            var sign = (int) (doubleBits >> 63); // 0 if positive, -1 if negative
+            var exponent = (int) ((doubleBits >> 52) & 0x7FF) - 1023;
 
-            if ((uint)exponent >= 84)
+            if ((uint) exponent >= 84)
             {
                 // Anything with an exponent that is negative or >= 84 will convert to zero.
                 // This includes infinities and NaNs, which have exponent = 1024
@@ -404,8 +404,8 @@ namespace Jint.Runtime
                 return 0;
             }
 
-            long mantissa = (doubleBits & 0xFFFFFFFFFFFFFL) | 0x10000000000000L;
-            int int32Value = (exponent >= 52) ? (int)(mantissa << (exponent - 52)) : (int)(mantissa >> (52 - exponent));
+            var mantissa = (doubleBits & 0xFFFFFFFFFFFFFL) | 0x10000000000000L;
+            var int32Value = exponent >= 52 ? (int) (mantissa << (exponent - 52)) : (int) (mantissa >> (52 - exponent));
 
             return (int32Value + sign) ^ sign;
         }
@@ -420,11 +420,11 @@ namespace Jint.Runtime
                 return o.AsInteger();
             }
 
-            double doubleVal = ToNumber(o);
-            if (doubleVal >= -(double)int.MinValue && doubleVal <= (double)int.MaxValue)
+            var doubleVal = ToNumber(o);
+            if (doubleVal >= -(double) int.MinValue && doubleVal <= int.MaxValue)
             {
                 // Double-to-int cast is correct in this range
-                return (int)doubleVal;
+                return (int) doubleVal;
             }
 
             return DoubleToInt32Slow(doubleVal);
@@ -437,17 +437,17 @@ namespace Jint.Runtime
         {
             if (o._type == InternalTypes.Integer)
             {
-                return (uint)o.AsInteger();
+                return (uint) o.AsInteger();
             }
 
-            double doubleVal = ToNumber(o);
-            if (doubleVal >= 0.0 && doubleVal <= (double)uint.MaxValue)
+            var doubleVal = ToNumber(o);
+            if (doubleVal >= 0.0 && doubleVal <= uint.MaxValue)
             {
                 // Double-to-uint cast is correct in this range
-                return (uint)doubleVal;
+                return (uint) doubleVal;
             }
 
-            return (uint)DoubleToInt32Slow(doubleVal);
+            return (uint) DoubleToInt32Slow(doubleVal);
         }
 
         /// <summary>
@@ -460,6 +460,128 @@ namespace Jint.Runtime
                 : (ushort) (uint) ToNumber(o);
         }
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-toint16
+        /// </summary>
+        internal static double ToInt16(JsValue o)
+        {
+            return o._type == InternalTypes.Integer
+                ? (short) o.AsInteger()
+                : (short) (long) ToNumber(o);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-toint8
+        /// </summary>
+        internal static double ToInt8(JsValue o)
+        {
+            return o._type == InternalTypes.Integer
+                ? (sbyte) o.AsInteger()
+                : (sbyte) (long) ToNumber(o);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-touint8
+        /// </summary>
+        internal static double ToUint8(JsValue o)
+        {
+            return o._type == InternalTypes.Integer
+                ? (byte) o.AsInteger()
+                : (byte) (long) ToNumber(o);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-touint8clamp
+        /// </summary>
+        internal static double ToUint8Clamp(JsValue o)
+        {
+            if (o._type == InternalTypes.Integer)
+            {
+                var intValue = o.AsInteger();
+                if (intValue is > -1 and < 256)
+                {
+                    return intValue;
+                }
+            }
+
+            var number = ToNumber(o);
+            if (double.IsNaN(number))
+            {
+                return 0;
+            }
+
+            if (number <= 0)
+            {
+                return 0;
+            }
+
+            if (number >= 255)
+            {
+                return 255;
+            }
+
+            var f = Math.Floor(number);
+            if (f + 0.5 < number)
+            {
+                return f + 1;
+            }
+
+            if (number < f + 0.5)
+            {
+                return f;
+            }
+
+            if (f % 2 != 0)
+            {
+                return f + 1;
+            }
+
+            return f;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-tobigint64
+        /// </summary>
+        internal static double ToBigInt64(JsValue value)
+        {
+            ExceptionHelper.ThrowNotImplementedException("BigInt not implemented");
+            return 0;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-tobiguint64
+        /// </summary>
+        internal static double ToBigUint64(JsValue value)
+        {
+            ExceptionHelper.ThrowNotImplementedException("BigInt not implemented");
+            return 0;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-toindex
+        /// </summary>
+        public static uint ToIndex(Realm realm, JsValue value)
+        {
+            if (value.IsUndefined())
+            {
+                return 0;
+            }
+
+            var integerIndex = ToIntegerOrInfinity(value);
+            if (integerIndex < 0)
+            {
+                ExceptionHelper.ThrowRangeError(realm);
+            }
+
+            var index = ToLength(integerIndex);
+            if (integerIndex != index)
+            {
+                ExceptionHelper.ThrowRangeError(realm, "Invalid index");
+            }
+
+            return (uint) Math.Min(uint.MaxValue, index);
+        }
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static string ToString(long i)
         {
@@ -552,6 +674,7 @@ namespace Jint.Runtime
             {
                 return s;
             }
+
             return JsString.Create(ToStringNonString(o));
         }
 
@@ -561,11 +684,11 @@ namespace Jint.Runtime
             switch (type)
             {
                 case InternalTypes.Boolean:
-                    return ((JsBoolean)o)._value ? "true" : "false";
+                    return ((JsBoolean) o)._value ? "true" : "false";
                 case InternalTypes.Integer:
-                    return ToString((int)((JsNumber)o)._value);
+                    return ToString((int) ((JsNumber) o)._value);
                 case InternalTypes.Number:
-                    return ToString(((JsNumber)o)._value);
+                    return ToString(((JsNumber) o)._value);
                 case InternalTypes.Symbol:
                     ExceptionHelper.ThrowTypeErrorNoEngine("Cannot convert a Symbol value to a string");
                     return null;
@@ -599,14 +722,14 @@ namespace Jint.Runtime
             switch (type)
             {
                 case InternalTypes.Boolean:
-                    return realm.Intrinsics.Boolean.Construct((JsBoolean)value);
+                    return realm.Intrinsics.Boolean.Construct((JsBoolean) value);
                 case InternalTypes.Number:
                 case InternalTypes.Integer:
-                    return realm.Intrinsics.Number.Construct((JsNumber)value);
+                    return realm.Intrinsics.Number.Construct((JsNumber) value);
                 case InternalTypes.String:
                     return realm.Intrinsics.String.Construct(value.ToString());
                 case InternalTypes.Symbol:
-                    return realm.Intrinsics.Symbol.Construct((JsSymbol)value);
+                    return realm.Intrinsics.Symbol.Construct((JsSymbol) value);
                 case InternalTypes.Null:
                 case InternalTypes.Undefined:
                     ExceptionHelper.ThrowTypeError(realm, "Cannot convert undefined or null to object");
@@ -723,9 +846,9 @@ namespace Jint.Runtime
                             else
                             {
                                 if (argType.GetOperatorOverloadMethods()
-                                  .Any(m => paramType.IsAssignableFrom(m.ReturnType) &&
-                                    (m.Name == "op_Implicit" ||
-                                    m.Name == "op_Explicit")))
+                                    .Any(m => paramType.IsAssignableFrom(m.ReturnType) &&
+                                              (m.Name == "op_Implicit" ||
+                                               m.Name == "op_Explicit")))
                                 {
                                     score -= 100;
                                 }
@@ -764,4 +887,4 @@ namespace Jint.Runtime
             return !type.IsValueType || Nullable.GetUnderlyingType(type) != null;
         }
     }
-}
+}

+ 19 - 15
README.md

@@ -23,33 +23,37 @@ The entire execution engine was rebuild with performance in mind, in many cases
 
 #### ECMAScript 2015 (ES6)
 
+- ✔ ArrayBuffer
 - ✔ Arrow function expression
+- ✔ Binary and octal literals
 - ✔ Class support
-- ✔ Enhanced object literals
-- ✔ Template strings
+- ✔ DataView
 - ✔ Destructuring
 - ✔ Default, rest and spread
-- ✔ Lexical scoping of variables (let and const)
+- ✔ Enhanced object literals
 - ✔ `for...of`
+- ❌ Generators
+- ✔ Template strings
+- ✔ Lexical scoping of variables (let and const)
 - ✔ Map and Set
+- ❌ Modules and module loaders
+- ✔ Promises (Experimental, API is unstable)
+- ✔ Reflect
 - ✔ Proxies
-- ✔ Symbols
 - ✔ Reflect
-- ✔ Binary and octal literals
-- ❌ Generators
+- ✔ Symbols
+- ❌ Tail calls
+- ❌ Typed arrays
 - ❌ Unicode
-- ❌ Modules and module loaders
 - ✔ Weakmap and Weakset
-- ✔ Promises (Experimental, API is unstable)
-- ❌ Tail calls
 
 #### ECMAScript 2016
 
-- ✔ Block-scoping of variables and functions
-- ✔ Destructuring patterns (of variables)
-- ✔ Exponentiation operator `**`
 - ✔ `Array.prototype.includes`
 - ❌ `await`, `async`
+- ✔ Block-scoping of variables and functions
+- ✔ Exponentiation operator `**`
+- ✔ Destructuring patterns (of variables)
 
 ####  ECMAScript 2017
 
@@ -57,8 +61,8 @@ The entire execution engine was rebuild with performance in mind, in many cases
 
 #### ECMAScript 2018
 
-- ✔ Rest/spread operators for object literals (`...identifier`),
 - ✔ `Promise.prototype.finally`
+- ✔ Rest/spread operators for object literals (`...identifier`),
 
 #### ECMAScript 2019
 
@@ -66,9 +70,9 @@ The entire execution engine was rebuild with performance in mind, in many cases
 
 #### ECMAScript 2020
 
-- ✔ Nullish coalescing operator (`??`)
-- ✔ `globalThis` object
 - ❌ BigInt
+- ✔ `globalThis` object
+- ✔ Nullish coalescing operator (`??`)
 
 #### Other