2
0
Эх сурвалжийг харах

ES2020 BigInt (#1027)

Co-authored-by: Sébastien Ros <[email protected]>
Marko Lahma 3 жил өмнө
parent
commit
9d8432606d
76 өөрчлөгдсөн 2213 нэмэгдсэн , 550 устгасан
  1. 1 1
      Jint.Repl/Program.cs
  2. 15 0
      Jint.Tests.Test262/BuiltIns/BigIntTests.cs
  3. 2 5
      Jint.Tests.Test262/Test262Test.cs
  4. 9 0
      Jint.Tests.Test262/test/built-ins/BigInt/asIntN/bigint-tobigint-errors.js
  5. 2 1
      Jint.Tests.Test262/test/built-ins/BigInt/asIntN/bigint-tobigint-toprimitive.js
  6. 2 0
      Jint.Tests.Test262/test/built-ins/BigInt/asIntN/bits-toindex-errors.js
  7. 2 0
      Jint.Tests.Test262/test/built-ins/BigInt/asIntN/bits-toindex-toprimitive.js
  8. 26 0
      Jint.Tests.Test262/test/built-ins/BigInt/asIntN/not-a-constructor.js
  9. 9 0
      Jint.Tests.Test262/test/built-ins/BigInt/asUintN/bigint-tobigint-errors.js
  10. 2 0
      Jint.Tests.Test262/test/built-ins/BigInt/asUintN/bigint-tobigint-toprimitive.js
  11. 2 0
      Jint.Tests.Test262/test/built-ins/BigInt/asUintN/bits-toindex-errors.js
  12. 2 0
      Jint.Tests.Test262/test/built-ins/BigInt/asUintN/bits-toindex-toprimitive.js
  13. 26 0
      Jint.Tests.Test262/test/built-ins/BigInt/asUintN/not-a-constructor.js
  14. 33 0
      Jint.Tests.Test262/test/built-ins/BigInt/is-a-constructor.js
  15. 0 23
      Jint.Tests.Test262/test/built-ins/BigInt/new-target-throws.js
  16. 31 0
      Jint.Tests.Test262/test/built-ins/BigInt/prototype/toLocaleString/not-a-constructor.js
  17. 21 0
      Jint.Tests.Test262/test/built-ins/BigInt/prototype/toString/a-z.js
  18. 31 0
      Jint.Tests.Test262/test/built-ins/BigInt/prototype/toString/not-a-constructor.js
  19. 1 0
      Jint.Tests.Test262/test/built-ins/BigInt/prototype/toString/prototype-call.js
  20. 2 0
      Jint.Tests.Test262/test/built-ins/BigInt/prototype/toString/thisbigintvalue-not-valid-throws.js
  21. 31 0
      Jint.Tests.Test262/test/built-ins/BigInt/prototype/valueOf/not-a-constructor.js
  22. 1 0
      Jint.Tests.Test262/test/built-ins/BigInt/prototype/valueOf/this-value-invalid-object-throws.js
  23. 2 0
      Jint.Tests.Test262/test/built-ins/BigInt/prototype/valueOf/this-value-invalid-primitive-throws.js
  24. 72 0
      Jint.Tests.Test262/test/built-ins/BigInt/wrapper-object-ordinary-toprimitive.js
  25. 9 1
      Jint.Tests.Test262/test/skipped.json
  26. 2 0
      Jint.Tests/Runtime/Domain/JsUuid.cs
  27. 1 1
      Jint.Tests/Runtime/OperatorOverloadingTests.cs
  28. 1 1
      Jint.Tests/Runtime/RegExpTests.cs
  29. 13 5
      Jint.Tests/Runtime/TypedArrayInteropTests.cs
  30. 2 2
      Jint.Tests/Runtime/UuidTests.cs
  31. 1 1
      Jint/Jint.csproj
  32. 31 5
      Jint/JsValueExtensions.cs
  33. 4 10
      Jint/Native/Array/ArrayPrototype.cs
  34. 36 19
      Jint/Native/ArrayBuffer/ArrayBufferInstance.cs
  35. 0 43
      Jint/Native/ArrayBuffer/ArrayBufferPrototype.cs
  36. 134 0
      Jint/Native/BigInt/BigIntConstructor.cs
  37. 24 0
      Jint/Native/BigInt/BigIntInstance.cs
  38. 153 0
      Jint/Native/BigInt/BigIntPrototype.cs
  39. 1 1
      Jint/Native/Boolean/BooleanConstructor.cs
  40. 4 4
      Jint/Native/Boolean/BooleanInstance.cs
  41. 2 2
      Jint/Native/Boolean/BooleanPrototype.cs
  42. 3 5
      Jint/Native/DataView/DataViewPrototype.cs
  43. 2 1
      Jint/Native/Global/GlobalObject.cs
  44. 105 0
      Jint/Native/JsBigInt.cs
  45. 16 14
      Jint/Native/JsBoolean.cs
  46. 12 10
      Jint/Native/JsNull.cs
  47. 72 11
      Jint/Native/JsNumber.cs
  48. 47 27
      Jint/Native/JsString.cs
  49. 6 6
      Jint/Native/JsUndefined.cs
  50. 67 26
      Jint/Native/JsValue.cs
  51. 55 24
      Jint/Native/Json/JsonSerializer.cs
  52. 1 1
      Jint/Native/Map/MapInstance.cs
  53. 27 11
      Jint/Native/Number/NumberConstructor.cs
  54. 1 1
      Jint/Native/Number/NumberInstance.cs
  55. 1 1
      Jint/Native/Number/NumberPrototype.cs
  56. 5 16
      Jint/Native/Object/ObjectInstance.cs
  57. 25 0
      Jint/Native/SameValueZeroComparer.cs
  58. 1 1
      Jint/Native/Set/SetInstance.cs
  59. 1 1
      Jint/Native/String/StringConstructor.cs
  60. 5 5
      Jint/Native/String/StringInstance.cs
  61. 3 3
      Jint/Native/String/StringPrototype.cs
  62. 28 16
      Jint/Native/TypedArray/IntrinsicTypedArrayPrototype.cs
  63. 18 2
      Jint/Native/TypedArray/TypedArrayConstructor.cs
  64. 51 27
      Jint/Native/TypedArray/TypedArrayInstance.cs
  65. 159 0
      Jint/Native/TypedArray/TypedArrayValue.cs
  66. 49 6
      Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs
  67. 310 100
      Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs
  68. 94 59
      Jint/Runtime/Interpreter/Expressions/JintExpression.cs
  69. 6 0
      Jint/Runtime/Interpreter/Expressions/JintLiteralExpression.cs
  70. 22 10
      Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs
  71. 53 12
      Jint/Runtime/Interpreter/Expressions/JintUpdateExpression.cs
  72. 1 1
      Jint/Runtime/Interpreter/Statements/JintSwitchBlock.cs
  73. 5 0
      Jint/Runtime/Intrinsics.cs
  74. 2 2
      Jint/Runtime/OrderedSet.cs
  75. 216 25
      Jint/Runtime/TypeConverter.cs
  76. 1 1
      README.md

+ 1 - 1
Jint.Repl/Program.cs

@@ -66,7 +66,7 @@ namespace Jint.Repl
                 try
                 try
                 {
                 {
                     var result = engine.Evaluate(input, parserOptions);
                     var result = engine.Evaluate(input, parserOptions);
-                    if (!result.IsNull() && !result.IsUndefined())
+                    if (!result.IsPrimitive() && result is not IPrimitiveInstance)
                     {
                     {
                         var serializer = new JsonSerializer(engine);
                         var serializer = new JsonSerializer(engine);
                         var str = serializer.Serialize(result, Undefined.Instance, "  ");
                         var str = serializer.Serialize(result, Undefined.Instance, "  ");

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

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

+ 2 - 5
Jint.Tests.Test262/Test262Test.cs

@@ -73,6 +73,7 @@ namespace Jint.Tests.Test262
                 "detachArrayBuffer.js",
                 "detachArrayBuffer.js",
                 "byteConversionValues.js",
                 "byteConversionValues.js",
                 "hidden-constructors.js",
                 "hidden-constructors.js",
+                "testBigIntTypedArray.js"
             };
             };
 
 
             Sources = new Dictionary<string, Script>(files.Length);
             Sources = new Dictionary<string, Script>(files.Length);
@@ -174,7 +175,7 @@ namespace Jint.Tests.Test262
 
 
             if (!negative && !string.IsNullOrWhiteSpace(lastError))
             if (!negative && !string.IsNullOrWhiteSpace(lastError))
             {
             {
-                throw new XunitException(lastError);
+                throw new XunitException($"{Environment.NewLine}{fileName}{Environment.NewLine}{Environment.NewLine}{lastError}");
             }
             }
         }
         }
 
 
@@ -242,10 +243,6 @@ namespace Jint.Tests.Test262
                                 skip = true;
                                 skip = true;
                                 reason = "tail-calls not implemented";
                                 reason = "tail-calls not implemented";
                                 break;
                                 break;
-                            case "BigInt":
-                                skip = true;
-                                reason = "BigInt not implemented";
-                                break;
                             case "generators":
                             case "generators":
                                 skip = true;
                                 skip = true;
                                 reason = "generators not implemented";
                                 reason = "generators not implemented";

+ 9 - 0
Jint.Tests.Test262/test/built-ins/BigInt/asIntN/bigint-tobigint-errors.js

@@ -9,6 +9,15 @@ info: |
   2. Let bigint ? ToBigInt(bigint).
   2. Let bigint ? ToBigInt(bigint).
 features: [BigInt, computed-property-names, Symbol, Symbol.toPrimitive]
 features: [BigInt, computed-property-names, Symbol, Symbol.toPrimitive]
 ---*/
 ---*/
+assert.sameValue(typeof BigInt, 'function');
+assert.sameValue(typeof BigInt.asIntN, 'function');
+
+assert.throws(TypeError, function () {
+  BigInt.asIntN();
+}, "ToBigInt: no argument => undefined => TypeError");
+assert.throws(TypeError, function () {
+  BigInt.asIntN(0);
+}, "ToBigInt: no argument => undefined => TypeError");
 
 
 assert.throws(TypeError, function() {
 assert.throws(TypeError, function() {
   BigInt.asIntN(0, undefined);
   BigInt.asIntN(0, undefined);

+ 2 - 1
Jint.Tests.Test262/test/built-ins/BigInt/asIntN/bigint-tobigint-toprimitive.js

@@ -9,7 +9,8 @@ info: |
   2. Let bigint ? ToBigInt(bigint).
   2. Let bigint ? ToBigInt(bigint).
 features: [BigInt, computed-property-names, Symbol, Symbol.toPrimitive]
 features: [BigInt, computed-property-names, Symbol, Symbol.toPrimitive]
 ---*/
 ---*/
-
+assert.sameValue(typeof BigInt, 'function');
+assert.sameValue(typeof BigInt.asIntN, 'function');
 function err() {
 function err() {
   throw new Test262Error();
   throw new Test262Error();
 }
 }

+ 2 - 0
Jint.Tests.Test262/test/built-ins/BigInt/asIntN/bits-toindex-errors.js

@@ -9,6 +9,8 @@ info: |
   1. Let bits be ? ToIndex(bits).
   1. Let bits be ? ToIndex(bits).
 features: [BigInt, computed-property-names, Symbol, Symbol.toPrimitive]
 features: [BigInt, computed-property-names, Symbol, Symbol.toPrimitive]
 ---*/
 ---*/
+assert.sameValue(typeof BigInt, 'function');
+assert.sameValue(typeof BigInt.asIntN, 'function');
 
 
 assert.throws(RangeError, function() {
 assert.throws(RangeError, function() {
   BigInt.asIntN(-1, 0n);
   BigInt.asIntN(-1, 0n);

+ 2 - 0
Jint.Tests.Test262/test/built-ins/BigInt/asIntN/bits-toindex-toprimitive.js

@@ -9,6 +9,8 @@ info: |
   1. Let bits be ? ToIndex(bits).
   1. Let bits be ? ToIndex(bits).
 features: [BigInt, computed-property-names, Symbol, Symbol.toPrimitive]
 features: [BigInt, computed-property-names, Symbol, Symbol.toPrimitive]
 ---*/
 ---*/
+assert.sameValue(typeof BigInt, 'function');
+assert.sameValue(typeof BigInt.asIntN, 'function');
 
 
 function err() {
 function err() {
   throw new Test262Error();
   throw new Test262Error();

+ 26 - 0
Jint.Tests.Test262/test/built-ins/BigInt/asIntN/not-a-constructor.js

@@ -0,0 +1,26 @@
+// Copyright (C) 2020 Rick Waldron. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-ecmascript-standard-built-in-objects
+description: >
+  BigInt.asIntN does not implement [[Construct]], is not new-able
+info: |
+  ECMAScript Function Objects
+
+  Built-in function objects that are not identified as constructors do not
+  implement the [[Construct]] internal method unless otherwise specified in
+  the description of a particular function.
+
+  sec-evaluatenew
+
+  ...
+  7. If IsConstructor(constructor) is false, throw a TypeError exception.
+  ...
+includes: [isConstructor.js]
+features: [Reflect.construct, BigInt, arrow-function]
+---*/
+assert.sameValue(isConstructor(BigInt.asIntN), false, 'isConstructor(BigInt.asIntN) must return false');
+
+assert.throws(TypeError, () => {
+  new BigInt.asIntN(64, 1n);
+}, '`new BigInt.asIntN(64, 1n)` throws TypeError');

+ 9 - 0
Jint.Tests.Test262/test/built-ins/BigInt/asUintN/bigint-tobigint-errors.js

@@ -9,6 +9,15 @@ info: |
   2. Let bigint ? ToBigInt(bigint).
   2. Let bigint ? ToBigInt(bigint).
 features: [BigInt, computed-property-names, Symbol, Symbol.toPrimitive]
 features: [BigInt, computed-property-names, Symbol, Symbol.toPrimitive]
 ---*/
 ---*/
+assert.sameValue(typeof BigInt, 'function');
+assert.sameValue(typeof BigInt.asUintN, 'function');
+
+assert.throws(TypeError, function () {
+  BigInt.asUintN();
+}, "ToBigInt: no argument => undefined => TypeError");
+assert.throws(TypeError, function () {
+  BigInt.asUintN(0);
+}, "ToBigInt: no argument => undefined => TypeError");
 
 
 assert.throws(TypeError, function() {
 assert.throws(TypeError, function() {
   BigInt.asUintN(0, undefined);
   BigInt.asUintN(0, undefined);

+ 2 - 0
Jint.Tests.Test262/test/built-ins/BigInt/asUintN/bigint-tobigint-toprimitive.js

@@ -9,6 +9,8 @@ info: |
   2. Let bigint ? ToBigInt(bigint).
   2. Let bigint ? ToBigInt(bigint).
 features: [BigInt, computed-property-names, Symbol, Symbol.toPrimitive]
 features: [BigInt, computed-property-names, Symbol, Symbol.toPrimitive]
 ---*/
 ---*/
+assert.sameValue(typeof BigInt, 'function');
+assert.sameValue(typeof BigInt.asUintN, 'function');
 
 
 function err() {
 function err() {
   throw new Test262Error();
   throw new Test262Error();

+ 2 - 0
Jint.Tests.Test262/test/built-ins/BigInt/asUintN/bits-toindex-errors.js

@@ -9,6 +9,8 @@ info: |
   1. Let bits be ? ToIndex(bits).
   1. Let bits be ? ToIndex(bits).
 features: [BigInt, computed-property-names, Symbol, Symbol.toPrimitive]
 features: [BigInt, computed-property-names, Symbol, Symbol.toPrimitive]
 ---*/
 ---*/
+assert.sameValue(typeof BigInt, 'function');
+assert.sameValue(typeof BigInt.asUintN, 'function');
 
 
 assert.throws(RangeError, function() {
 assert.throws(RangeError, function() {
   BigInt.asUintN(-1, 0n);
   BigInt.asUintN(-1, 0n);

+ 2 - 0
Jint.Tests.Test262/test/built-ins/BigInt/asUintN/bits-toindex-toprimitive.js

@@ -9,6 +9,8 @@ info: |
   1. Let bits be ? ToIndex(bits).
   1. Let bits be ? ToIndex(bits).
 features: [BigInt, computed-property-names, Symbol, Symbol.toPrimitive]
 features: [BigInt, computed-property-names, Symbol, Symbol.toPrimitive]
 ---*/
 ---*/
+assert.sameValue(typeof BigInt, 'function');
+assert.sameValue(typeof BigInt.asUintN, 'function');
 
 
 function err() {
 function err() {
   throw new Test262Error();
   throw new Test262Error();

+ 26 - 0
Jint.Tests.Test262/test/built-ins/BigInt/asUintN/not-a-constructor.js

@@ -0,0 +1,26 @@
+// Copyright (C) 2020 Rick Waldron. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-ecmascript-standard-built-in-objects
+description: >
+  BigInt.asUintN does not implement [[Construct]], is not new-able
+info: |
+  ECMAScript Function Objects
+
+  Built-in function objects that are not identified as constructors do not
+  implement the [[Construct]] internal method unless otherwise specified in
+  the description of a particular function.
+
+  sec-evaluatenew
+
+  ...
+  7. If IsConstructor(constructor) is false, throw a TypeError exception.
+  ...
+includes: [isConstructor.js]
+features: [Reflect.construct, BigInt, arrow-function]
+---*/
+assert.sameValue(isConstructor(BigInt.asUintN), false, 'isConstructor(BigInt.asUintN) must return false');
+
+assert.throws(TypeError, () => {
+  new BigInt.asUintN(64, 1n);
+}, '`new BigInt.asUintN(64, 1n)` throws TypeError');

+ 33 - 0
Jint.Tests.Test262/test/built-ins/BigInt/is-a-constructor.js

@@ -0,0 +1,33 @@
+// Copyright (C) 2020 Rick Waldron. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-ecmascript-standard-built-in-objects
+description: >
+  BigInt is a constructor, and does implement [[Construct]], but is not new target
+info: |
+  sec-bigint-constructor
+
+  - is not intended to be used with the new operator or to be subclassed. It may be used as the value of an extends clause of a class definition but a super call to the BigInt constructor will cause an exception.
+
+  sec-bigint-constructor-number-value
+
+  1. If NewTarget is not undefined, throw a TypeError exception.
+includes: [isConstructor.js]
+features: [BigInt, Reflect.construct, arrow-function]
+---*/
+
+assert.sameValue(
+  isConstructor(BigInt),
+  true,
+  'isConstructor(BigInt) must return true'
+);
+
+assert.throws(TypeError, () => {
+  new BigInt({
+    valueOf() {
+      new Test262Error();
+    }
+  });
+}, '`new BigInt({ valueOf() {new Test262Error();}})` throws TypeError');
+

+ 0 - 23
Jint.Tests.Test262/test/built-ins/BigInt/new-target-throws.js

@@ -1,23 +0,0 @@
-// Copyright (C) 2017 Robin Templeton. All rights reserved.
-// This code is governed by the BSD license found in the LICENSE file.
-
-/*---
-description: Throws a TypeError if BigInt is called with a new target
-esid: sec-bigint-constructor
-info: |
-  1. If NewTarget is not undefined, throw a TypeError exception.
-  ...
-features: [BigInt]
----*/
-
-assert.throws(TypeError, function() {
-  new BigInt();
-});
-
-assert.throws(TypeError, function() {
-  new BigInt({
-    valueOf: function() {
-      throw new Test262Error("unreachable");
-    }
-  });
-});

+ 31 - 0
Jint.Tests.Test262/test/built-ins/BigInt/prototype/toLocaleString/not-a-constructor.js

@@ -0,0 +1,31 @@
+// Copyright (C) 2020 Rick Waldron. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-ecmascript-standard-built-in-objects
+description: >
+  BigInt.prototype.toLocaleString does not implement [[Construct]], is not new-able
+info: |
+  ECMAScript Function Objects
+
+  Built-in function objects that are not identified as constructors do not
+  implement the [[Construct]] internal method unless otherwise specified in
+  the description of a particular function.
+
+  sec-evaluatenew
+
+  ...
+  7. If IsConstructor(constructor) is false, throw a TypeError exception.
+  ...
+includes: [isConstructor.js]
+features: [Reflect.construct, BigInt, arrow-function]
+---*/
+assert.sameValue(
+  isConstructor(BigInt.prototype.toLocaleString),
+  false,
+  'isConstructor(BigInt.prototype.toLocaleString) must return false'
+);
+
+assert.throws(TypeError, () => {
+  let n = 1n;
+  new n.toLocaleString();
+}, '`let n = 1n; new n.toLocaleString()` throws TypeError');

+ 21 - 0
Jint.Tests.Test262/test/built-ins/BigInt/prototype/toString/a-z.js

@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Rick Waldron. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-bigint.prototype.tostring
+description: >
+  Letters a-z are used for digits with values 10 through 35
+info: |
+  6. Return the String representation of this Number value using
+  the radix specified by radixNumber. Letters a-z are used for
+  digits with values 10 through 35. The precise algorithm is
+  implementation-dependent, however the algorithm should be a
+  generalization of that specified in 6.1.6.2.23.
+features: [BigInt]
+---*/
+
+for (let radix = 11; radix <= 36; radix++) {
+  for (let i = 10n; i < radix; i++) {
+    assert.sameValue(i.toString(radix), String.fromCharCode(Number(i + 87n)));
+  }
+}

+ 31 - 0
Jint.Tests.Test262/test/built-ins/BigInt/prototype/toString/not-a-constructor.js

@@ -0,0 +1,31 @@
+// Copyright (C) 2020 Rick Waldron. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-ecmascript-standard-built-in-objects
+description: >
+  BigInt.prototype.toString does not implement [[Construct]], is not new-able
+info: |
+  ECMAScript Function Objects
+
+  Built-in function objects that are not identified as constructors do not
+  implement the [[Construct]] internal method unless otherwise specified in
+  the description of a particular function.
+
+  sec-evaluatenew
+
+  ...
+  7. If IsConstructor(constructor) is false, throw a TypeError exception.
+  ...
+includes: [isConstructor.js]
+features: [Reflect.construct, BigInt, arrow-function]
+---*/
+assert.sameValue(
+  isConstructor(BigInt.prototype.toString),
+  false,
+  'isConstructor(BigInt.prototype.toString) must return false'
+);
+
+assert.throws(TypeError, () => {
+  let n = 1n;
+  new n.toString();
+}, '`let n = 1n; new n.toString()` throws TypeError');

+ 1 - 0
Jint.Tests.Test262/test/built-ins/BigInt/prototype/toString/prototype-call.js

@@ -15,6 +15,7 @@ info: |
   [[BigIntData]] internal slot.
   [[BigIntData]] internal slot.
 features: [BigInt]
 features: [BigInt]
 ---*/
 ---*/
+assert.sameValue(typeof BigInt, 'function');
 
 
 assert.throws(TypeError, function() {
 assert.throws(TypeError, function() {
   BigInt.prototype.toString(1);
   BigInt.prototype.toString(1);

+ 2 - 0
Jint.Tests.Test262/test/built-ins/BigInt/prototype/toString/thisbigintvalue-not-valid-throws.js

@@ -21,6 +21,8 @@ features: [BigInt, Symbol, Symbol.toPrimitive]
 
 
 var toString = BigInt.prototype.toString;
 var toString = BigInt.prototype.toString;
 
 
+assert.sameValue(typeof toString, 'function');
+
 assert.throws(TypeError, function() {
 assert.throws(TypeError, function() {
   toString.call({
   toString.call({
     x: 1n
     x: 1n

+ 31 - 0
Jint.Tests.Test262/test/built-ins/BigInt/prototype/valueOf/not-a-constructor.js

@@ -0,0 +1,31 @@
+// Copyright (C) 2020 Rick Waldron. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-ecmascript-standard-built-in-objects
+description: >
+  BigInt.prototype.valueOf does not implement [[Construct]], is not new-able
+info: |
+  ECMAScript Function Objects
+
+  Built-in function objects that are not identified as constructors do not
+  implement the [[Construct]] internal method unless otherwise specified in
+  the description of a particular function.
+
+  sec-evaluatenew
+
+  ...
+  7. If IsConstructor(constructor) is false, throw a TypeError exception.
+  ...
+includes: [isConstructor.js]
+features: [Reflect.construct, BigInt, arrow-function]
+---*/
+assert.sameValue(
+  isConstructor(BigInt.prototype.valueOf),
+  false,
+  'isConstructor(BigInt.prototype.valueOf) must return false'
+);
+
+assert.throws(TypeError, () => {
+  let n = 1n;
+  new n.valueOf();
+}, '`let n = 1n; new n.valueOf()` throws TypeError');

+ 1 - 0
Jint.Tests.Test262/test/built-ins/BigInt/prototype/valueOf/this-value-invalid-object-throws.js

@@ -20,6 +20,7 @@ features: [BigInt]
 ---*/
 ---*/
 
 
 var valueOf = BigInt.prototype.valueOf;
 var valueOf = BigInt.prototype.valueOf;
+assert.sameValue(typeof valueOf, 'function');
 
 
 assert.throws(TypeError, function() {
 assert.throws(TypeError, function() {
   valueOf.call({});
   valueOf.call({});

+ 2 - 0
Jint.Tests.Test262/test/built-ins/BigInt/prototype/valueOf/this-value-invalid-primitive-throws.js

@@ -21,6 +21,8 @@ features: [BigInt, Symbol]
 
 
 var valueOf = BigInt.prototype.valueOf;
 var valueOf = BigInt.prototype.valueOf;
 
 
+assert.sameValue(typeof valueOf, 'function');
+
 assert.throws(TypeError, function() {
 assert.throws(TypeError, function() {
   valueOf.call(undefined);
   valueOf.call(undefined);
 }, "undefined");
 }, "undefined");

+ 72 - 0
Jint.Tests.Test262/test/built-ins/BigInt/wrapper-object-ordinary-toprimitive.js

@@ -0,0 +1,72 @@
+// Copyright (C) 2021 Alexey Shvayka. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-toprimitive
+description: >
+    BigInt wrapper object is converted to primitive via OrdinaryToPrimitive.
+info: |
+    ToPrimitive ( input [ , preferredType ] )
+
+    [...]
+    2. If Type(input) is Object, then
+        a. Let exoticToPrim be ? GetMethod(input, @@toPrimitive).
+        b. If exoticToPrim is not undefined, then
+            [...]
+        c. If preferredType is not present, let preferredType be number.
+        d. Return ? OrdinaryToPrimitive(input, preferredType).
+features: [BigInt]
+---*/
+
+const BigIntToString = BigInt.prototype.toString;
+let toStringGets = 0;
+let toStringCalls = 0;
+let toStringFunction = function() { ++toStringCalls; return `${BigIntToString.call(this)}foo`; };
+Object.defineProperty(BigInt.prototype, "toString", {
+    get: () => { ++toStringGets; return toStringFunction; },
+});
+
+assert.sameValue("" + Object(1n), "1", "hint: default");
+assert.throws(TypeError, () => { +Object(1n); }, "hint: number");
+assert.sameValue(`${Object(1n)}`, "1foo", "hint: string");
+
+assert.sameValue(toStringGets, 1);
+assert.sameValue(toStringCalls, 1);
+
+const BigIntValueOf = BigInt.prototype.valueOf;
+let valueOfGets = 0;
+let valueOfCalls = 0;
+let valueOfFunction = function() { ++valueOfCalls; return BigIntValueOf.call(this) * 2n; };
+Object.defineProperty(BigInt.prototype, "valueOf", {
+    get: () => { ++valueOfGets; return valueOfFunction; },
+});
+
+assert(Object(1n) == 2n, "hint: default");
+assert.sameValue(Object(1n) + 1n, 3n, "hint: number");
+assert.sameValue({ "1foo": 1, "2": 2 }[Object(1n)], 1, "hint: string");
+
+assert.sameValue(toStringGets, 2);
+assert.sameValue(toStringCalls, 2);
+assert.sameValue(valueOfGets, 2);
+assert.sameValue(valueOfCalls, 2);
+
+toStringFunction = undefined;
+
+assert.throws(TypeError, () => { 1 + Object(1n); }, "hint: default");
+assert.sameValue(Object(1n) * 1n, 2n, "hint: number");
+assert.sameValue("".concat(Object(1n)), "2", "hint: string");
+
+assert.sameValue(toStringGets, 3);
+assert.sameValue(toStringCalls, 2);
+assert.sameValue(valueOfGets, 5);
+assert.sameValue(valueOfCalls, 5);
+
+valueOfFunction = null;
+
+assert.throws(TypeError, () => { new Date(Object(1n)); }, "hint: default");
+assert.throws(TypeError, () => { Number(Object(1n)); }, "hint: number");
+assert.throws(TypeError, () => { String(Object(1n)); }, "hint: string");
+
+assert.sameValue(toStringGets, 6);
+assert.sameValue(toStringCalls, 2);
+assert.sameValue(valueOfGets, 8);
+assert.sameValue(valueOfCalls, 5);

+ 9 - 1
Jint.Tests.Test262/test/skipped.json

@@ -174,6 +174,14 @@
     "reason": "inner binding rejects modification (from parameters) Expected a Error to be thrown but no exception was thrown at all"
     "reason": "inner binding rejects modification (from parameters) Expected a Error to be thrown but no exception was thrown at all"
   },
   },
 
 
+  {
+    "source": "built-ins/TypedArrayConstructors/ctors-bigint/object-arg/as-generator-iterable-returns.js",
+    "reason": "yield not implemented"
+  },
+  {
+    "source": "built-ins/TypedArrayConstructors/ctors-bigint/object-arg/iterating-throws.js",
+    "reason": "yield not implemented"
+  },
   {
   {
     "source": "language/expressions/object/accessor-name-computed-yield-id.js",
     "source": "language/expressions/object/accessor-name-computed-yield-id.js",
     "reason": "accessor / yield not implemented"
     "reason": "accessor / yield not implemented"
@@ -363,5 +371,5 @@
   {
   {
     "source": "language/statements/for-of/dstr-obj-id-identifier-yield-ident-valid.js",
     "source": "language/statements/for-of/dstr-obj-id-identifier-yield-ident-valid.js",
     "reason": "Esprima problem"
     "reason": "Esprima problem"
-  },
+  }
 ]
 ]

+ 2 - 0
Jint.Tests/Runtime/Domain/JsUuid.cs

@@ -21,5 +21,7 @@ namespace Jint.Tests.Runtime.Domain
         public override object ToObject() => _value;
         public override object ToObject() => _value;
 
 
         public override string ToString() => _value.ToString();
         public override string ToString() => _value.ToString();
+
+        public override bool Equals(object obj) => Equals(obj as JsUuid);
     }
     }
 }
 }

+ 1 - 1
Jint.Tests/Runtime/OperatorOverloadingTests.cs

@@ -367,7 +367,7 @@ namespace Jint.Tests.Runtime
             engine.SetValue("log", new Action<object>(Console.WriteLine));
             engine.SetValue("log", new Action<object>(Console.WriteLine));
 
 
             engine.Evaluate("let v1 = new Vector2D(1, 2);");
             engine.Evaluate("let v1 = new Vector2D(1, 2);");
-            Assert.Equal("(1, 2)", engine.Evaluate("new String(v1)").As<StringInstance>().PrimitiveValue.ToString());
+            Assert.Equal("(1, 2)", engine.Evaluate("new String(v1)").As<StringInstance>().StringData.ToString());
             Assert.Equal("### (1, 2) ###", engine.Evaluate("'### ' + v1 + ' ###'"));
             Assert.Equal("### (1, 2) ###", engine.Evaluate("'### ' + v1 + ' ###'"));
         }
         }
     }
     }

+ 1 - 1
Jint.Tests/Runtime/RegExpTests.cs

@@ -87,7 +87,7 @@ namespace Jint.Tests.Runtime
         public void ShouldNotThrowErrorOnRegExNumericNegation()
         public void ShouldNotThrowErrorOnRegExNumericNegation()
         {
         {
             var engine = new Engine();
             var engine = new Engine();
-            Assert.Equal(JsNumber.DoubleNaN, engine.Evaluate("-/[]/"));
+            Assert.True(ReferenceEquals(JsNumber.DoubleNaN, engine.Evaluate("-/[]/")));
         }
         }
     }
     }
 }
 }

+ 13 - 5
Jint.Tests/Runtime/TypedArrayInteropTests.cs

@@ -73,7 +73,7 @@ namespace Jint.Tests.Runtime
         public void CanInteropWithInt32()
         public void CanInteropWithInt32()
         {
         {
             var engine = new Engine();
             var engine = new Engine();
-            var source = new int[] { 42, 12 };
+            var source = new[] { 42, 12 };
             engine.SetValue("testSubject", engine.Realm.Intrinsics.Int32Array.Construct(source));
             engine.SetValue("testSubject", engine.Realm.Intrinsics.Int32Array.Construct(source));
             ValidateCreatedTypeArray(engine, "Int32Array");
             ValidateCreatedTypeArray(engine, "Int32Array");
 
 
@@ -96,26 +96,26 @@ namespace Jint.Tests.Runtime
             Assert.Equal(source, fromEngine.AsUint32Array());
             Assert.Equal(source, fromEngine.AsUint32Array());
         }
         }
 
 
-        [Fact(Skip = "BigInt not implemented")]
+        [Fact]
         public void CanInteropWithBigInt64()
         public void CanInteropWithBigInt64()
         {
         {
             var engine = new Engine();
             var engine = new Engine();
             var source = new long[] { 42, 12 };
             var source = new long[] { 42, 12 };
             engine.SetValue("testSubject", engine.Realm.Intrinsics.BigInt64Array.Construct(source));
             engine.SetValue("testSubject", engine.Realm.Intrinsics.BigInt64Array.Construct(source));
-            ValidateCreatedTypeArray(engine, "BigInt64Array");
+            ValidateCreatedBigIntegerTypeArray(engine, "BigInt64Array");
 
 
             var fromEngine = engine.GetValue("testSubject");
             var fromEngine = engine.GetValue("testSubject");
             Assert.True(fromEngine.IsBigInt64Array());
             Assert.True(fromEngine.IsBigInt64Array());
             Assert.Equal(source, fromEngine.AsBigInt64Array());
             Assert.Equal(source, fromEngine.AsBigInt64Array());
         }
         }
 
 
-        [Fact(Skip = "BigInt not implemented")]
+        [Fact]
         public void CanInteropWithBigUint64()
         public void CanInteropWithBigUint64()
         {
         {
             var engine = new Engine();
             var engine = new Engine();
             var source = new ulong[] { 42, 12 };
             var source = new ulong[] { 42, 12 };
             engine.SetValue("testSubject", engine.Realm.Intrinsics.BigUint64Array.Construct(source));
             engine.SetValue("testSubject", engine.Realm.Intrinsics.BigUint64Array.Construct(source));
-            ValidateCreatedTypeArray(engine, "BigUint64Array");
+            ValidateCreatedBigIntegerTypeArray(engine, "BigUint64Array");
 
 
             var fromEngine = engine.GetValue("testSubject");
             var fromEngine = engine.GetValue("testSubject");
             Assert.True(fromEngine.IsBigUint64Array());
             Assert.True(fromEngine.IsBigUint64Array());
@@ -129,5 +129,13 @@ namespace Jint.Tests.Runtime
             Assert.Equal(42, engine.Evaluate("testSubject[0]").AsNumber());
             Assert.Equal(42, engine.Evaluate("testSubject[0]").AsNumber());
             Assert.Equal(12, engine.Evaluate("testSubject[1]").AsNumber());
             Assert.Equal(12, engine.Evaluate("testSubject[1]").AsNumber());
         }
         }
+
+        private static void ValidateCreatedBigIntegerTypeArray(Engine engine, string arrayName)
+        {
+            Assert.Equal(arrayName, engine.Evaluate("testSubject.constructor.name").AsString());
+            Assert.Equal(2, engine.Evaluate("testSubject.length").AsNumber());
+            Assert.Equal(42, engine.Evaluate("testSubject[0]").AsBigInt());
+            Assert.Equal(12, engine.Evaluate("testSubject[1]").AsBigInt());
+        }
     }
     }
 }
 }

+ 2 - 2
Jint.Tests/Runtime/UuidTests.cs

@@ -43,8 +43,8 @@ namespace Jint.Tests.Runtime
         [Fact]
         [Fact]
         public void Copy()
         public void Copy()
         {
         {
-            var actual = (bool)RunTest($"const g = new Uuid(); copy(g).toString() === g.toString()");
-            Assert.True(actual);
+            _engine.Evaluate("const g = new Uuid();");
+            Assert.Equal(_engine.Evaluate("copy(g).toString()").AsString(), _engine.Evaluate("g.toString()").AsString());
         }
         }
     }
     }
 }
 }

+ 1 - 1
Jint/Jint.csproj

@@ -8,7 +8,7 @@
     <IsPackable>true</IsPackable>
     <IsPackable>true</IsPackable>
   </PropertyGroup>
   </PropertyGroup>
   <ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Esprima" Version="2.0.4" />
+    <PackageReference Include="Esprima" Version="2.1.0" />
     <PackageReference Include="IsExternalInit" Version="1.0.1" PrivateAssets="all" />
     <PackageReference Include="IsExternalInit" Version="1.0.1" PrivateAssets="all" />
     <PackageReference Include="Nullable" Version="1.3.0" PrivateAssets="all" />
     <PackageReference Include="Nullable" Version="1.3.0" PrivateAssets="all" />
   </ItemGroup>
   </ItemGroup>

+ 31 - 5
Jint/JsValueExtensions.cs

@@ -1,6 +1,8 @@
 using System;
 using System;
 using System.Diagnostics.Contracts;
 using System.Diagnostics.Contracts;
+using System.Numerics;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
+using Esprima;
 using Jint.Native;
 using Jint.Native;
 using Jint.Native.Array;
 using Jint.Native.Array;
 using Jint.Native.Date;
 using Jint.Native.Date;
@@ -89,6 +91,13 @@ namespace Jint
             return (value._type & (InternalTypes.Number | InternalTypes.Integer)) != 0;
             return (value._type & (InternalTypes.Number | InternalTypes.Integer)) != 0;
         }
         }
 
 
+        [Pure]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static bool IsBigInt(this JsValue value)
+        {
+            return (value._type & InternalTypes.BigInt) != 0;
+        }
+
         [Pure]
         [Pure]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static bool IsInteger(this JsValue value)
         internal static bool IsInteger(this JsValue value)
@@ -204,6 +213,12 @@ namespace Jint
             return (int) ((JsNumber) value)._value;
             return (int) ((JsNumber) value)._value;
         }
         }
 
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static BigInteger AsBigInt(this JsValue value)
+        {
+            return ((JsBigInt) value)._value;
+        }
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static string AsString(this JsValue value)
         public static string AsString(this JsValue value)
         {
         {
@@ -343,13 +358,12 @@ namespace Jint
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static long[] AsBigInt64Array(this JsValue value)
         public static long[] AsBigInt64Array(this JsValue value)
         {
         {
-            if (!value.IsUint32Array())
+            if (!value.IsBigInt64Array())
             {
             {
                 ThrowWrongTypeException(value, "BigInt64Array");
                 ThrowWrongTypeException(value, "BigInt64Array");
             }
             }
 
 
-            ExceptionHelper.ThrowNotImplementedException();
-            return null;
+            return ((TypedArrayInstance) value).ToNativeArray<long>();
         }
         }
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -366,8 +380,7 @@ namespace Jint
                 ThrowWrongTypeException(value, "BigUint64Array");
                 ThrowWrongTypeException(value, "BigUint64Array");
             }
             }
 
 
-            ExceptionHelper.ThrowNotImplementedException();
-            return null;
+            return ((TypedArrayInstance) value).ToNativeArray<ulong>();
         }
         }
 
 
         [Pure]
         [Pure]
@@ -467,5 +480,18 @@ namespace Jint
         {
         {
             ExceptionHelper.ThrowArgumentException($"Expected {expectedType} but got {value._type}");
             ExceptionHelper.ThrowArgumentException($"Expected {expectedType} but got {value._type}");
         }
         }
+
+        internal static BigInteger ToBigInteger(this JsValue value, Engine engine)
+        {
+            try
+            {
+                return TypeConverter.ToBigInt(value);
+            }
+            catch (ParserException ex)
+            {
+                ExceptionHelper.ThrowSyntaxError(engine.Realm, ex.Message);
+                return default;
+            }
+        }
     }
     }
 }
 }

+ 4 - 10
Jint/Native/Array/ArrayPrototype.cs

@@ -10,9 +10,6 @@ using Jint.Pooling;
 using Jint.Runtime;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 using Jint.Runtime.Interop;
-using Jint.Runtime.Interpreter.Expressions;
-
-using static System.String;
 
 
 namespace Jint.Native.Array
 namespace Jint.Native.Array
 {
 {
@@ -299,8 +296,7 @@ namespace Jint.Native.Array
                 if (kPresent)
                 if (kPresent)
                 {
                 {
                     var elementK = o.Get(i);
                     var elementK = o.Get(i);
-                    var same = JintBinaryExpression.StrictlyEqual(elementK, searchElement);
-                    if (same)
+                    if (elementK == searchElement)
                     {
                     {
                         return i;
                         return i;
                     }
                     }
@@ -620,7 +616,7 @@ namespace Jint.Native.Array
             while (k < len)
             while (k < len)
             {
             {
                 var value = o.Get(k);
                 var value = o.Get(k);
-                if (JintBinaryExpression.SameValueZero(value, searchElement))
+                if (SameValueZeroComparer.Equals(value, searchElement))
                 {
                 {
                     return true;
                     return true;
                 }
                 }
@@ -722,8 +718,7 @@ namespace Jint.Native.Array
                 if (kPresent)
                 if (kPresent)
                 {
                 {
                     var elementK = o.Get(k);
                     var elementK = o.Get(k);
-                    var same = JintBinaryExpression.StrictlyEqual(elementK, searchElement);
-                    if (same)
+                    if (elementK == searchElement)
                     {
                     {
                         return k;
                         return k;
                     }
                     }
@@ -1484,8 +1479,7 @@ namespace Jint.Native.Array
                 var xString = TypeConverter.ToString(x);
                 var xString = TypeConverter.ToString(x);
                 var yString = TypeConverter.ToString(y);
                 var yString = TypeConverter.ToString(y);
 
 
-                var r = CompareOrdinal(xString, yString);
-                return r;
+                return string.CompareOrdinal(xString, yString);
             }
             }
         }
         }
     }
     }

+ 36 - 19
Jint/Native/ArrayBuffer/ArrayBufferInstance.cs

@@ -80,7 +80,7 @@ namespace Jint.Native.ArrayBuffer
         /// <summary>
         /// <summary>
         /// https://tc39.es/ecma262/#sec-getvaluefrombuffer
         /// https://tc39.es/ecma262/#sec-getvaluefrombuffer
         /// </summary>
         /// </summary>
-        internal double GetValueFromBuffer(
+        internal TypedArrayValue GetValueFromBuffer(
             int byteIndex,
             int byteIndex,
             TypedArrayElementType type,
             TypedArrayElementType type,
             bool isTypedArray,
             bool isTypedArray,
@@ -104,19 +104,22 @@ namespace Jint.Native.ArrayBuffer
                 h. Append Chosen Value EsprimaExtensions.Record { [[Event]]: readEvent, [[ChosenValue]]: rawValue } to execution.[[ChosenValues]].
                 h. Append Chosen Value EsprimaExtensions.Record { [[Event]]: readEvent, [[ChosenValue]]: rawValue } to execution.[[ChosenValues]].
             */
             */
             ExceptionHelper.ThrowNotImplementedException("SharedArrayBuffer not implemented");
             ExceptionHelper.ThrowNotImplementedException("SharedArrayBuffer not implemented");
-            return 0;
+            return default;
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// https://tc39.es/ecma262/#sec-rawbytestonumeric
         /// https://tc39.es/ecma262/#sec-rawbytestonumeric
         /// </summary>
         /// </summary>
-        internal double RawBytesToNumeric(TypedArrayElementType type, int byteIndex, bool isLittleEndian)
+        internal TypedArrayValue RawBytesToNumeric(TypedArrayElementType type, int byteIndex, bool isLittleEndian)
         {
         {
             var elementSize = type.GetElementSize();
             var elementSize = type.GetElementSize();
             var rawBytes = _arrayBufferData;
             var rawBytes = _arrayBufferData;
 
 
-            // floats require a little more at the moment
-            var needsReverse = !isLittleEndian && elementSize > 1 && (type == TypedArrayElementType.Float32 || type == TypedArrayElementType.Float64);
+            // 8 byte values require a little more at the moment
+            var needsReverse = !isLittleEndian
+                               && elementSize > 1
+                               && type is TypedArrayElementType.Float32 or TypedArrayElementType.Float64 or TypedArrayElementType.BigInt64 or TypedArrayElementType.BigUint64;
+
             if (needsReverse)
             if (needsReverse)
             {
             {
                 System.Array.Copy(rawBytes, byteIndex, _workBuffer, 0, elementSize);
                 System.Array.Copy(rawBytes, byteIndex, _workBuffer, 0, elementSize);
@@ -146,13 +149,19 @@ namespace Jint.Native.ArrayBuffer
                 return value;
                 return value;
             }
             }
 
 
-            if (type.IsBigIntElementType())
+            if (type == TypedArrayElementType.BigUint64)
+            {
+                var value = BitConverter.ToUInt64(rawBytes, byteIndex);
+                return value;
+            }
+
+            if (type == TypedArrayElementType.BigInt64)
             {
             {
-                // return the BigInt value that corresponds to intValue
-                ExceptionHelper.ThrowNotImplementedException("BigInt not implemented");
+                var value = BitConverter.ToInt64(rawBytes, byteIndex);
+                return value;
             }
             }
 
 
-            long? intValue = type switch
+            TypedArrayValue? arrayValue = type switch
             {
             {
                 TypedArrayElementType.Int8 => ((sbyte) rawBytes[byteIndex]),
                 TypedArrayElementType.Int8 => ((sbyte) rawBytes[byteIndex]),
                 TypedArrayElementType.Uint8 => (rawBytes[byteIndex]),
                 TypedArrayElementType.Uint8 => (rawBytes[byteIndex]),
@@ -176,12 +185,12 @@ namespace Jint.Native.ArrayBuffer
                 _ => null
                 _ => null
             };
             };
 
 
-            if (intValue is null)
+            if (arrayValue is null)
             {
             {
                 ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(type), type.ToString());
                 ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(type), type.ToString());
             }
             }
 
 
-            return (double) intValue;
+            return arrayValue.Value;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -190,7 +199,7 @@ namespace Jint.Native.ArrayBuffer
         internal void SetValueInBuffer(
         internal void SetValueInBuffer(
             int byteIndex,
             int byteIndex,
             TypedArrayElementType type,
             TypedArrayElementType type,
-            double value,
+            TypedArrayValue value,
             bool isTypedArray,
             bool isTypedArray,
             ArrayBufferOrder order,
             ArrayBufferOrder order,
             bool? isLittleEndian = null)
             bool? isLittleEndian = null)
@@ -214,23 +223,31 @@ namespace Jint.Native.ArrayBuffer
             }
             }
         }
         }
 
 
-        private byte[] NumericToRawBytes(TypedArrayElementType type, double value, bool isLittleEndian)
+        private byte[] NumericToRawBytes(TypedArrayElementType type, TypedArrayValue value, bool isLittleEndian)
         {
         {
             byte[] rawBytes;
             byte[] rawBytes;
             if (type == TypedArrayElementType.Float32)
             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.
                 // 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);
+                rawBytes = BitConverter.GetBytes((float) value.DoubleValue);
             }
             }
             else if (type == TypedArrayElementType.Float64)
             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.
                 // 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);
+                rawBytes = BitConverter.GetBytes(value.DoubleValue);
+            }
+            else if (type == TypedArrayElementType.BigInt64)
+            {
+                rawBytes = BitConverter.GetBytes(TypeConverter.ToBigInt64(value.BigInteger));
+            }
+            else if (type == TypedArrayElementType.BigUint64)
+            {
+                rawBytes = BitConverter.GetBytes(TypeConverter.ToBigUint64(value.BigInteger));
             }
             }
             else
             else
             {
             {
                 // inlined conversion for faster speed instead of getting the method in spec
                 // inlined conversion for faster speed instead of getting the method in spec
-                var intValue = (long) value;
+                var intValue = (long) value.DoubleValue;
                 rawBytes = _workBuffer;
                 rawBytes = _workBuffer;
                 switch (type)
                 switch (type)
                 {
                 {
@@ -241,7 +258,7 @@ namespace Jint.Native.ArrayBuffer
                         rawBytes[0] = (byte) intValue;
                         rawBytes[0] = (byte) intValue;
                         break;
                         break;
                     case TypedArrayElementType.Uint8C:
                     case TypedArrayElementType.Uint8C:
-                        rawBytes[0] = (byte) TypeConverter.ToUint8Clamp(value);
+                        rawBytes[0] = (byte) TypeConverter.ToUint8Clamp(value.DoubleValue);
                         break;
                         break;
                     case TypedArrayElementType.Int16:
                     case TypedArrayElementType.Int16:
 #if !NETSTANDARD2_1
 #if !NETSTANDARD2_1
@@ -259,9 +276,9 @@ namespace Jint.Native.ArrayBuffer
                         break;
                         break;
                     case TypedArrayElementType.Int32:
                     case TypedArrayElementType.Int32:
 #if !NETSTANDARD2_1
 #if !NETSTANDARD2_1
-                        rawBytes = BitConverter.GetBytes((int) intValue);
+                        rawBytes = BitConverter.GetBytes((uint) intValue);
 #else
 #else
-                        BitConverter.TryWriteBytes(rawBytes, (int) intValue);
+                        BitConverter.TryWriteBytes(rawBytes, (uint) intValue);
 #endif
 #endif
                         break;
                         break;
                     case TypedArrayElementType.Uint32:
                     case TypedArrayElementType.Uint32:

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

@@ -154,48 +154,5 @@ namespace Jint.Native.ArrayBuffer
             System.Array.Copy(fromBuf, first, toBuf, 0, newLen);
             System.Array.Copy(fromBuf, first, toBuf, 0, newLen);
             return bufferInstance;
             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--;
-            }
-        }
     }
     }
 }
 }

+ 134 - 0
Jint/Native/BigInt/BigIntConstructor.cs

@@ -0,0 +1,134 @@
+using System.Numerics;
+using Jint.Collections;
+using Jint.Native.Function;
+using Jint.Native.Object;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Interop;
+
+namespace Jint.Native.BigInt;
+
+/// <summary>
+/// https://tc39.es/ecma262/#sec-properties-of-the-bigint-constructor
+/// </summary>
+public sealed class BigIntConstructor : FunctionInstance, IConstructor
+{
+    private static readonly JsString _functionName = new("BigInt");
+
+    public BigIntConstructor(
+        Engine engine,
+        Realm realm,
+        FunctionPrototype functionPrototype,
+        ObjectPrototype objectPrototype)
+        : base(engine, realm, _functionName)
+    {
+        _prototype = functionPrototype;
+        PrototypeObject = new BigIntPrototype(engine, realm, this, objectPrototype);
+        _length = new PropertyDescriptor(JsNumber.PositiveOne, PropertyFlag.Configurable);
+        _prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
+    }
+
+    protected override void Initialize()
+    {
+        var properties = new PropertyDictionary(2, checkExistingKeys: false)
+        {
+            ["asIntN"] = new(new ClrFunctionInstance(Engine, "asIntN", AsIntN, 2, PropertyFlag.Configurable), true, false, true),
+            ["asUintN"] = new(new ClrFunctionInstance(Engine, "asUintN", AsUintN, 2, PropertyFlag.Configurable), true, false, true),
+        };
+        SetProperties(properties);
+    }
+
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-bigint.asintn
+    /// </summary>
+    private JsValue AsIntN(JsValue thisObj, JsValue[] arguments)
+    {
+        var bits = (int) TypeConverter.ToIndex(_realm, arguments.At(0));
+        var bigint = arguments.At(1).ToBigInteger(_engine);
+
+        var mod = TypeConverter.BigIntegerModulo(bigint, BigInteger.Pow(2, bits));
+        if (bits > 0 && mod >= BigInteger.Pow(2, bits - 1))
+        {
+            return (mod - BigInteger.Pow(2, bits));
+        }
+
+        return mod;
+    }
+
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-bigint.asuintn
+    /// </summary>
+    private JsValue AsUintN(JsValue thisObj, JsValue[] arguments)
+    {
+        var bits = (int) TypeConverter.ToIndex(_realm, arguments.At(0));
+        var bigint = arguments.At(1).ToBigInteger(_engine);
+
+        var result = TypeConverter.BigIntegerModulo(bigint, BigInteger.Pow(2, bits));
+
+        return result;
+    }
+
+    public override JsValue Call(JsValue thisObject, JsValue[] arguments)
+    {
+        if (arguments.Length == 0)
+        {
+            return JsBigInt.Zero;
+        }
+
+        var prim = TypeConverter.ToPrimitive(arguments.At(0), Types.Number);
+        if (prim.IsNumber())
+        {
+            return NumberToBigInt((JsNumber) prim);
+        }
+
+        return prim.ToBigInteger(_engine);
+    }
+
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-numbertobigint
+    /// </summary>
+    private JsBigInt NumberToBigInt(JsNumber value)
+    {
+        if (TypeConverter.IsIntegralNumber(value._value))
+        {
+            return JsBigInt.Create((long) value._value);
+        }
+
+        ExceptionHelper.ThrowRangeError(_realm, "The number " + value + " cannot be converted to a BigInt because it is not an integer");
+        return null;
+    }
+
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-bigint-constructor-number-value
+    /// </summary>
+    public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
+    {
+        var value = arguments.Length > 0
+            ? JsBigInt.Create(arguments[0].ToBigInteger(_engine))
+            : JsBigInt.Zero;
+
+        if (newTarget.IsUndefined())
+        {
+            return Construct(value);
+        }
+
+        var o = OrdinaryCreateFromConstructor(
+            newTarget,
+            static intrinsics => intrinsics.BigInt.PrototypeObject,
+            static (engine, realm, state) => new BigIntInstance(engine, (JsBigInt) state), value);
+        return o;
+    }
+
+    public BigIntPrototype PrototypeObject { get; }
+
+    public BigIntInstance Construct(JsBigInt value)
+    {
+        var instance = new BigIntInstance(Engine)
+        {
+            _prototype = PrototypeObject,
+            BigIntData = value
+        };
+
+        return instance;
+    }
+}

+ 24 - 0
Jint/Native/BigInt/BigIntInstance.cs

@@ -0,0 +1,24 @@
+using Jint.Native.Object;
+using Jint.Runtime;
+
+namespace Jint.Native.BigInt;
+
+public sealed class BigIntInstance : ObjectInstance, IPrimitiveInstance
+{
+    public BigIntInstance(Engine engine)
+        : base(engine, ObjectClass.Object)
+    {
+    }
+
+    public BigIntInstance(Engine engine, JsBigInt value)
+        : this(engine)
+    {
+        BigIntData = value;
+    }
+
+    Types IPrimitiveInstance.Type => Types.BigInt;
+
+    JsValue IPrimitiveInstance.PrimitiveValue => BigIntData;
+
+    public JsBigInt BigIntData { get; internal init; }
+}

+ 153 - 0
Jint/Native/BigInt/BigIntPrototype.cs

@@ -0,0 +1,153 @@
+using System.Numerics;
+using Jint.Collections;
+using Jint.Native.Object;
+using Jint.Native.Symbol;
+using Jint.Pooling;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Interop;
+
+namespace Jint.Native.BigInt;
+
+/// <summary>
+/// https://tc39.es/ecma262/#sec-properties-of-the-bigint-prototype-object
+/// </summary>
+public sealed class BigIntPrototype : ObjectInstance
+{
+    private readonly Realm _realm;
+    private readonly BigIntConstructor _constructor;
+
+    internal BigIntPrototype(
+        Engine engine,
+        Realm realm,
+        BigIntConstructor constructor,
+        ObjectPrototype objectPrototype)
+        : base(engine)
+    {
+        _prototype = objectPrototype;
+        _realm = realm;
+        _constructor = constructor;
+    }
+
+    protected override void Initialize()
+    {
+        var properties = new PropertyDictionary(4, checkExistingKeys: false)
+        {
+            ["constructor"] = new(_constructor, true, false, true),
+            ["toString"] = new(new ClrFunctionInstance(Engine, "toString", ToBigIntString, 0, PropertyFlag.Configurable), true, false, true),
+            ["toLocaleString"] = new(new ClrFunctionInstance(Engine, "toLocaleString", ToLocaleString, 0, PropertyFlag.Configurable), true, false, true),
+            ["valueOf"] = new(new ClrFunctionInstance(Engine, "valueOf", ValueOf, 0, PropertyFlag.Configurable), true, false, true),
+        };
+        SetProperties(properties);
+
+        var symbols = new SymbolDictionary(1)
+        {
+            [GlobalSymbolRegistry.ToStringTag] = new("BigInt", false, false, true)
+        };
+        SetSymbols(symbols);
+    }
+
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-bigint.prototype.tolocalestring
+    /// </summary>
+    private JsValue ToLocaleString(JsValue thisObject, JsValue[] arguments)
+    {
+        if (!thisObject.IsBigInt() && thisObject is not BigIntInstance)
+        {
+            ExceptionHelper.ThrowTypeError(_realm);
+        }
+
+        var m = TypeConverter.ToBigInt(thisObject);
+        return m.ToString("R");
+    }
+
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-bigint.prototype.valueof
+    /// </summary>
+    private JsValue ValueOf(JsValue thisObj, JsValue[] arguments)
+    {
+        if (thisObj is BigIntInstance ni)
+        {
+            return ni.BigIntData;
+        }
+
+        if (thisObj is JsBigInt)
+        {
+            return thisObj;
+        }
+
+        ExceptionHelper.ThrowTypeError(_realm);
+        return null;
+    }
+
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-bigint.prototype.tostring
+    /// </summary>
+    private JsValue ToBigIntString(JsValue thisObject, JsValue[] arguments)
+    {
+        var x = ThisBigIntValue(thisObject);
+
+        var radix = arguments.At(0);
+
+        var radixMV = radix.IsUndefined()
+            ? 10
+            : (int) TypeConverter.ToIntegerOrInfinity(radix);
+
+        if (radixMV is < 2 or > 36)
+        {
+            ExceptionHelper.ThrowRangeError(_realm, "radix must be between 2 and 36");
+        }
+
+        var value = x._value;
+        if (value == BigInteger.Zero)
+        {
+            return JsString.NumberZeroString;
+        }
+
+        if (radixMV == 10)
+        {
+            return value.ToString("R");
+        }
+
+        var negative = value < 0;
+
+        if (negative)
+        {
+            value = -value;
+        }
+
+        const string digits = "0123456789abcdefghijklmnopqrstuvwxyz";
+
+        using var builder = StringBuilderPool.Rent();
+        var sb = builder.Builder;
+
+        for (; value > 0; value /= radixMV)
+        {
+            var d = (int) (value % radixMV);
+            sb.Append(digits[d]);
+        }
+
+        var charArray = new char[sb.Length];
+        sb.CopyTo(0, charArray, 0, charArray.Length);
+        System.Array.Reverse(charArray);
+
+        return (negative ? "-" : "") + new string(charArray);
+    }
+
+    /// <summary>
+    /// https://tc39.es/ecma262/#thisbigintvalue
+    /// </summary>
+    private JsBigInt ThisBigIntValue(JsValue value)
+    {
+        switch (value)
+        {
+            case JsBigInt bigInt:
+                return bigInt;
+            case BigIntInstance bigIntInstance:
+                return bigIntInstance.BigIntData;
+            default:
+                ExceptionHelper.ThrowTypeError(_realm);
+                return JsBigInt.One;
+        }
+    }
+}

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

@@ -60,7 +60,7 @@ namespace Jint.Native.Boolean
             var instance = new BooleanInstance(Engine)
             var instance = new BooleanInstance(Engine)
             {
             {
                 _prototype = PrototypeObject,
                 _prototype = PrototypeObject,
-                PrimitiveValue = value,
+                BooleanData = value,
             };
             };
 
 
             return instance;
             return instance;

+ 4 - 4
Jint/Native/Boolean/BooleanInstance.cs

@@ -9,17 +9,17 @@ namespace Jint.Native.Boolean
             : base(engine, ObjectClass.Boolean)
             : base(engine, ObjectClass.Boolean)
         {
         {
         }
         }
-        
+
         public BooleanInstance(Engine engine, JsBoolean value)
         public BooleanInstance(Engine engine, JsBoolean value)
             : base(engine, ObjectClass.Boolean)
             : base(engine, ObjectClass.Boolean)
         {
         {
-            PrimitiveValue = value;
+            BooleanData = value;
         }
         }
 
 
         Types IPrimitiveInstance.Type => Types.Boolean;
         Types IPrimitiveInstance.Type => Types.Boolean;
 
 
-        JsValue IPrimitiveInstance.PrimitiveValue => PrimitiveValue;
+        JsValue IPrimitiveInstance.PrimitiveValue => BooleanData;
 
 
-        public JsValue PrimitiveValue { get; set; }
+        public JsValue BooleanData { get; internal init; }
     }
     }
 }
 }

+ 2 - 2
Jint/Native/Boolean/BooleanPrototype.cs

@@ -21,7 +21,7 @@ namespace Jint.Native.Boolean
             ObjectPrototype objectPrototype) : base(engine)
             ObjectPrototype objectPrototype) : base(engine)
         {
         {
             _prototype = objectPrototype;
             _prototype = objectPrototype;
-            PrimitiveValue = JsBoolean.False;
+            BooleanData = JsBoolean.False;
             _realm = realm;
             _realm = realm;
             _constructor = constructor;
             _constructor = constructor;
         }
         }
@@ -46,7 +46,7 @@ namespace Jint.Native.Boolean
 
 
             if (thisObj is BooleanInstance bi)
             if (thisObj is BooleanInstance bi)
             {
             {
-                return bi.PrimitiveValue;
+                return bi.BooleanData;
             }
             }
 
 
             ExceptionHelper.ThrowTypeError(_realm);
             ExceptionHelper.ThrowTypeError(_realm);

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

@@ -246,7 +246,7 @@ namespace Jint.Native.DataView
             }
             }
 
 
             var bufferIndex = (int) (getIndex + viewOffset);
             var bufferIndex = (int) (getIndex + viewOffset);
-            return buffer.GetValueFromBuffer(bufferIndex, type, false, ArrayBufferOrder.Unordered, isLittleEndianBoolean);
+            return buffer.GetValueFromBuffer(bufferIndex, type, false, ArrayBufferOrder.Unordered, isLittleEndianBoolean).ToJsValue();
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -267,12 +267,10 @@ namespace Jint.Native.DataView
 
 
             var getIndex = TypeConverter.ToIndex(_realm, requestIndex);
             var getIndex = TypeConverter.ToIndex(_realm, requestIndex);
 
 
-            double numberValue;
+            TypedArrayValue numberValue;
             if (type.IsBigIntElementType())
             if (type.IsBigIntElementType())
             {
             {
-                // let numberValue be ? ToBigInt(value).
-                ExceptionHelper.ThrowNotImplementedException("BigInt not implemented");
-                return Undefined;
+                numberValue = TypeConverter.ToBigInt(value);
             }
             }
             else
             else
             {
             {

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

@@ -33,7 +33,7 @@ namespace Jint.Native.Global
             const PropertyFlag lengthFlags = PropertyFlag.Configurable;
             const PropertyFlag lengthFlags = PropertyFlag.Configurable;
             const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
             const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
 
 
-            var properties = new PropertyDictionary(54, checkExistingKeys: false)
+            var properties = new PropertyDictionary(55, checkExistingKeys: false)
             {
             {
                 ["Object"] = new PropertyDescriptor(_realm.Intrinsics.Object, propertyFlags),
                 ["Object"] = new PropertyDescriptor(_realm.Intrinsics.Object, propertyFlags),
                 ["Function"] = new PropertyDescriptor(_realm.Intrinsics.Function, propertyFlags),
                 ["Function"] = new PropertyDescriptor(_realm.Intrinsics.Function, propertyFlags),
@@ -61,6 +61,7 @@ namespace Jint.Native.Global
                 ["String"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.String, propertyFlags),
                 ["String"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.String, propertyFlags),
                 ["RegExp"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.RegExp, propertyFlags),
                 ["RegExp"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.RegExp, propertyFlags),
                 ["Number"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Number, propertyFlags),
                 ["Number"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Number, propertyFlags),
+                ["BigInt"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.BigInt, propertyFlags),
                 ["Boolean"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Boolean, propertyFlags),
                 ["Boolean"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Boolean, propertyFlags),
                 ["Date"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Date, propertyFlags),
                 ["Date"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Date, propertyFlags),
                 ["Math"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Math, propertyFlags),
                 ["Math"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state)._realm.Intrinsics.Math, propertyFlags),

+ 105 - 0
Jint/Native/JsBigInt.cs

@@ -0,0 +1,105 @@
+using System;
+using System.Numerics;
+using Jint.Runtime;
+
+namespace Jint.Native;
+
+public sealed class JsBigInt : JsValue, IEquatable<JsBigInt>
+{
+    internal readonly BigInteger _value;
+
+    public static readonly JsBigInt Zero = new(0);
+    public static readonly JsBigInt One = new(1);
+
+    private static readonly JsBigInt[] _bigIntegerToJsValue;
+
+    static JsBigInt()
+    {
+        var bigIntegers = new JsBigInt[1024];
+        for (uint i = 0; i < bigIntegers.Length; i++)
+        {
+            bigIntegers[i] = new JsBigInt(i);
+        }
+        _bigIntegerToJsValue = bigIntegers;
+    }
+
+    public JsBigInt(BigInteger value) : base(Types.BigInt)
+    {
+        _value = value;
+    }
+
+    internal static JsBigInt Create(BigInteger bigInt)
+    {
+        var temp = _bigIntegerToJsValue;
+        if (bigInt >= 0 && bigInt < (uint) temp.Length)
+        {
+            return temp[(int) bigInt];
+        }
+
+        return new JsBigInt(bigInt);
+    }
+
+    internal static JsBigInt Create(JsValue value)
+    {
+        return value is JsBigInt jsBigInt
+            ? jsBigInt
+            : Create(TypeConverter.ToBigInt(value));
+    }
+
+
+    public override object ToObject()
+    {
+        return _value;
+    }
+
+    public static bool operator ==(JsBigInt a, double b)
+    {
+        return a is not null && TypeConverter.IsIntegralNumber(b) && a._value == (long) b;
+    }
+
+    public static bool operator !=(JsBigInt a, double b)
+    {
+        return !(a == b);
+    }
+
+    public override string ToString()
+    {
+        return TypeConverter.ToString(_value);
+    }
+
+    public override bool IsLooselyEqual(JsValue value)
+    {
+        if (value is JsBigInt bigInt)
+        {
+            return Equals(bigInt);
+        }
+
+        return value is JsNumber jsNumber && TypeConverter.IsIntegralNumber(jsNumber._value) && _value == new BigInteger(jsNumber._value)
+               || value is JsString jsString && TypeConverter.TryStringToBigInt(jsString.ToString(), out var temp) && temp == _value;
+    }
+
+    public override bool Equals(object other)
+    {
+        return Equals(other as JsBigInt);
+    }
+
+    public override bool Equals(JsValue other)
+    {
+        return Equals(other as JsBigInt);
+    }
+
+    public bool Equals(JsBigInt other)
+    {
+        if (ReferenceEquals(null, other))
+        {
+            return false;
+        }
+
+        return ReferenceEquals(this, other) || _value.Equals(other._value);
+    }
+
+    public override int GetHashCode()
+    {
+        return _value.GetHashCode();
+    }
+}

+ 16 - 14
Jint/Native/JsBoolean.cs

@@ -1,4 +1,6 @@
-using System;
+#nullable enable
+
+using System;
 using Jint.Runtime;
 using Jint.Runtime;
 
 
 namespace Jint.Native
 namespace Jint.Native
@@ -28,31 +30,31 @@ namespace Jint.Native
             return _value ? "true" : "false";
             return _value ? "true" : "false";
         }
         }
 
 
-        public override bool Equals(JsValue obj)
+        public override bool IsLooselyEqual(JsValue value)
         {
         {
-            if (ReferenceEquals(null, obj))
+            if (value is JsBoolean jsBoolean)
             {
             {
-                return false;
+                return Equals(jsBoolean);
             }
             }
 
 
-            if (!(obj is JsBoolean number))
-            {
-                return false;
-            }
+            return !value.IsNullOrUndefined() && base.IsLooselyEqual(value);
+        }
 
 
-            return Equals(number);
+        public override bool Equals(JsValue obj)
+        {
+            return Equals(obj as JsBoolean);
         }
         }
 
 
-        public bool Equals(JsBoolean other)
+        public bool Equals(JsBoolean? other)
         {
         {
-            if (ReferenceEquals(null, other))
+            if (ReferenceEquals(this, other))
             {
             {
-                return false;
+                return true;
             }
             }
 
 
-            if (ReferenceEquals(this, other))
+            if (other is null)
             {
             {
-                return true;
+                return false;
             }
             }
 
 
             return _value == other._value;
             return _value == other._value;

+ 12 - 10
Jint/Native/JsNull.cs

@@ -1,4 +1,6 @@
-using System;
+#nullable enable
+
+using System;
 using Jint.Runtime;
 using Jint.Runtime;
 
 
 namespace Jint.Native
 namespace Jint.Native
@@ -9,7 +11,7 @@ namespace Jint.Native
         {
         {
         }
         }
 
 
-        public override object ToObject()
+        public override object? ToObject()
         {
         {
             return null;
             return null;
         }
         }
@@ -19,19 +21,19 @@ namespace Jint.Native
             return "null";
             return "null";
         }
         }
 
 
-        public override bool Equals(JsValue obj)
+        public override bool IsLooselyEqual(JsValue value)
         {
         {
-            if (ReferenceEquals(this, obj))
-            {
-                return true;
-            }
+            return ReferenceEquals(Null, value) || ReferenceEquals(Undefined, value);
+        }
 
 
-            return obj is JsNull s && Equals(s);
+        public override bool Equals(JsValue obj)
+        {
+            return Equals(obj as JsNull);
         }
         }
 
 
-        public bool Equals(JsNull other)
+        public bool Equals(JsNull? other)
         {
         {
-            return !ReferenceEquals(null, other);
+            return other is not null;
         }
         }
     }
     }
 }
 }

+ 72 - 11
Jint/Native/JsNumber.cs

@@ -1,5 +1,9 @@
-using System;
+#nullable enable
+
+using System;
+using System.Numerics;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
+using Jint.Native.Number;
 using Jint.Runtime;
 using Jint.Runtime;
 
 
 namespace Jint.Native
 namespace Jint.Native
@@ -96,8 +100,7 @@ namespace Jint.Native
         {
         {
             // we expect zero to be on the fast path of integer mostly
             // we expect zero to be on the fast path of integer mostly
             var temp = _intToJsValue;
             var temp = _intToJsValue;
-            if (value >= 1 && value < temp.Length
-                && System.Math.Abs(value % 1) <= DoubleIsIntegerTolerance)
+            if (value >= 1 && value < temp.Length && TypeConverter.IsIntegralNumber(value))
             {
             {
                 return temp[(uint) value];
                 return temp[(uint) value];
             }
             }
@@ -198,29 +201,87 @@ namespace Jint.Native
             return new JsNumber(value);
             return new JsNumber(value);
         }
         }
 
 
+        public static JsNumber Create(JsValue jsValue)
+        {
+            if (jsValue is JsNumber number)
+            {
+                return number;
+            }
+
+            return Create(TypeConverter.ToNumber(jsValue));
+        }
+
         public override string ToString()
         public override string ToString()
         {
         {
             return TypeConverter.ToString(_value);
             return TypeConverter.ToString(_value);
         }
         }
 
 
-        public override bool Equals(JsValue obj)
+        internal bool IsNaN()
+        {
+            return double.IsNaN(_value);
+        }
+
+        /// <summary>
+        /// Either positive or negative zero.
+        /// </summary>
+        internal bool IsZero()
+        {
+            return IsNegativeZero() || IsPositiveZero();
+        }
+
+        internal bool IsNegativeZero()
+        {
+            return NumberInstance.IsNegativeZero(_value);
+        }
+
+        internal bool IsPositiveZero()
+        {
+            return NumberInstance.IsPositiveZero(_value);
+        }
+
+        internal bool IsPositiveInfinity()
         {
         {
-            if (ReferenceEquals(null, obj))
+            return double.IsPositiveInfinity(_value);
+        }
+
+        internal bool IsNegativeInfinity()
+        {
+            return double.IsNegativeInfinity(_value);
+        }
+
+        public override bool IsLooselyEqual(JsValue value)
+        {
+            if (value is JsNumber jsNumber)
             {
             {
-                return false;
+                return Equals(jsNumber);
             }
             }
 
 
-            if (!(obj is JsNumber number))
+            if (value.IsBigInt())
             {
             {
-                return false;
+                return TypeConverter.IsIntegralNumber(_value) && new BigInteger(_value) == value.AsBigInt();
             }
             }
 
 
-            return Equals(number);
+            return base.IsLooselyEqual(value);
         }
         }
 
 
-        public bool Equals(JsNumber other)
+        public override bool Equals(JsValue obj)
         {
         {
-            if (ReferenceEquals(null, other))
+            return Equals(obj as JsNumber);
+        }
+
+        public override bool Equals(object? obj)
+        {
+            return Equals(obj as JsNumber);
+        }
+
+        public bool Equals(JsNumber? other)
+        {
+            if (other is null)
+            {
+                return false;
+            }
+
+            if (double.IsNaN(_value) || double.IsNaN(other._value))
             {
             {
                 return false;
                 return false;
             }
             }

+ 47 - 27
Jint/Native/JsString.cs

@@ -1,4 +1,6 @@
-using System;
+#nullable enable
+
+using System;
 using System.Text;
 using System.Text;
 using Jint.Native.Array;
 using Jint.Native.Array;
 using Jint.Runtime;
 using Jint.Runtime;
@@ -20,6 +22,7 @@ namespace Jint.Native
         internal static readonly JsString BooleanString = new JsString("boolean");
         internal static readonly JsString BooleanString = new JsString("boolean");
         internal static readonly JsString StringString = new JsString("string");
         internal static readonly JsString StringString = new JsString("string");
         internal static readonly JsString NumberString = new JsString("number");
         internal static readonly JsString NumberString = new JsString("number");
+        internal static readonly JsString BigIntString = new JsString("bigint");
         internal static readonly JsString SymbolString = new JsString("symbol");
         internal static readonly JsString SymbolString = new JsString("symbol");
         internal static readonly JsString DefaultString = new JsString("default");
         internal static readonly JsString DefaultString = new JsString("default");
         internal static readonly JsString NumberZeroString = new JsString("0");
         internal static readonly JsString NumberZeroString = new JsString("0");
@@ -68,34 +71,34 @@ namespace Jint.Native
             _value = value.ToString();
             _value = value.ToString();
         }
         }
 
 
-        public static bool operator ==(JsValue a, JsString b)
+        public static bool operator ==(JsValue? a, JsString? b)
         {
         {
-            if (a is JsString s && b is object)
+            if (a is JsString s && b is not null)
             {
             {
                 return s.ToString() == b.ToString();
                 return s.ToString() == b.ToString();
             }
             }
 
 
-            if ((object) a == null)
+            if (a is null)
             {
             {
-                return (object) b == null;
+                return b is null;
             }
             }
 
 
-            return (object) b != null && a.Equals(b);
+            return b is not null && a.Equals(b);
         }
         }
 
 
-        public static bool operator ==(JsString a, JsValue b)
+        public static bool operator ==(JsString? a, JsValue? b)
         {
         {
-            if (a is object && b is JsString s)
+            if (a is not null && b is JsString s)
             {
             {
                 return s.ToString() == b.ToString();
                 return s.ToString() == b.ToString();
             }
             }
 
 
-            if ((object) a == null)
+            if (a is null)
             {
             {
-                return (object) b == null;
+                return b is null;
             }
             }
 
 
-            return (object) b != null && a.Equals(b);
+            return b is not null && a.Equals(b);
         }
         }
 
 
         public static bool operator !=(JsString a, JsValue b)
         public static bool operator !=(JsString a, JsValue b)
@@ -225,37 +228,42 @@ namespace Jint.Native
 
 
         public override bool Equals(JsValue obj)
         public override bool Equals(JsValue obj)
         {
         {
-            if (ReferenceEquals(null, obj))
+            return Equals(obj as JsString);
+        }
+
+        public bool Equals(JsString? other)
+        {
+            if (other is null)
             {
             {
                 return false;
                 return false;
             }
             }
 
 
-            if (!(obj is JsString s))
+            if (ReferenceEquals(this, other))
             {
             {
-                return false;
+                return true;
             }
             }
 
 
-            return Equals(s);
+            return _value == other.ToString();
         }
         }
 
 
-        public bool Equals(JsString other)
+        public override bool IsLooselyEqual(JsValue value)
         {
         {
-            if (ReferenceEquals(null, other))
+            if (value is JsString jsString)
             {
             {
-                return false;
+                return Equals(jsString);
             }
             }
 
 
-            if (ReferenceEquals(this, other))
+            if (value.IsBigInt())
             {
             {
-                return true;
+                return value.IsBigInt() && TypeConverter.TryStringToBigInt(ToString(), out var temp) && temp == value.AsBigInt();
             }
             }
 
 
-            return _value == other.ToString();
+            return base.IsLooselyEqual(value);
         }
         }
 
 
         public override bool Equals(object obj)
         public override bool Equals(object obj)
         {
         {
-            return ReferenceEquals(this, obj) || obj is JsString other && Equals(other);
+            return Equals(obj as JsString);
         }
         }
 
 
         public override int GetHashCode()
         public override int GetHashCode()
@@ -265,7 +273,7 @@ namespace Jint.Native
 
 
         internal sealed class ConcatenatedString : JsString
         internal sealed class ConcatenatedString : JsString
         {
         {
-            private StringBuilder _stringBuilder;
+            private StringBuilder? _stringBuilder;
             private bool _dirty;
             private bool _dirty;
 
 
             internal ConcatenatedString(string value, int capacity = 0)
             internal ConcatenatedString(string value, int capacity = 0)
@@ -285,7 +293,7 @@ namespace Jint.Native
             {
             {
                 if (_dirty)
                 if (_dirty)
                 {
                 {
-                    _value = _stringBuilder.ToString();
+                    _value = _stringBuilder!.ToString();
                     _dirty = false;
                     _dirty = false;
                 }
                 }
 
 
@@ -310,7 +318,7 @@ namespace Jint.Native
 
 
             internal override JsString EnsureCapacity(int capacity)
             internal override JsString EnsureCapacity(int capacity)
             {
             {
-                _stringBuilder.EnsureCapacity(capacity);
+                _stringBuilder!.EnsureCapacity(capacity);
                 return this;
                 return this;
             }
             }
 
 
@@ -328,9 +336,21 @@ namespace Jint.Native
             {
             {
                 if (other is ConcatenatedString cs)
                 if (other is ConcatenatedString cs)
                 {
                 {
-                    if (_stringBuilder != null && cs._stringBuilder != null)
+                    var stringBuilder = _stringBuilder;
+                    var csStringBuilder = cs._stringBuilder;
+
+                    // we cannot use StringBuilder.Equals as it also checks Capacity on full framework / pre .NET Core 3
+                    if (stringBuilder != null && csStringBuilder != null && stringBuilder.Length == csStringBuilder.Length)
                     {
                     {
-                        return _stringBuilder.Equals(cs._stringBuilder);
+                        for (var i = 0; i < stringBuilder.Length; ++i)
+                        {
+                            if (stringBuilder[i] != csStringBuilder[i])
+                            {
+                                return false;
+                            }
+                        }
+
+                        return true;
                     }
                     }
 
 
                     return ToString() == cs.ToString();
                     return ToString() == cs.ToString();

+ 6 - 6
Jint/Native/JsUndefined.cs

@@ -19,14 +19,14 @@ namespace Jint.Native
             return "undefined";
             return "undefined";
         }
         }
 
 
-        public override bool Equals(JsValue obj)
+        public override bool IsLooselyEqual(JsValue value)
         {
         {
-            if (ReferenceEquals(this, obj))
-            {
-                return true;
-            }
+            return ReferenceEquals(Undefined, value) || ReferenceEquals(Null, value);
+        }
 
 
-            return obj is JsUndefined s && Equals(s);
+        public override bool Equals(JsValue obj)
+        {
+            return Equals(obj as JsUndefined);
         }
         }
 
 
         public bool Equals(JsUndefined other)
         public bool Equals(JsUndefined other)

+ 67 - 26
Jint/Native/JsValue.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Diagnostics.Contracts;
 using System.Diagnostics.Contracts;
+using System.Numerics;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
 using Jint.Native.Generator;
 using Jint.Native.Generator;
 using Jint.Native.Iterator;
 using Jint.Native.Iterator;
@@ -213,32 +214,17 @@ namespace Jint.Native
 
 
         public static bool operator ==(JsValue a, JsValue b)
         public static bool operator ==(JsValue a, JsValue b)
         {
         {
-            if ((object) a == null)
+            if (a is null)
             {
             {
-                return (object) b == null;
+                return b is null;
             }
             }
 
 
-            return (object) b != null && a.Equals(b);
+            return b is not null && a.Equals(b);
         }
         }
 
 
         public static bool operator !=(JsValue a, JsValue b)
         public static bool operator !=(JsValue a, JsValue b)
         {
         {
-            if ((object) a == null)
-            {
-                if ((object) b == null)
-                {
-                    return false;
-                }
-
-                return true;
-            }
-
-            if ((object) b == null)
-            {
-                return true;
-            }
-
-            return !a.Equals(b);
+            return !(a == b);
         }
         }
 
 
         public static implicit operator JsValue(char value)
         public static implicit operator JsValue(char value)
@@ -271,6 +257,11 @@ namespace Jint.Native
             return JsNumber.Create(value);
             return JsNumber.Create(value);
         }
         }
 
 
+        public static implicit operator JsValue(BigInteger value)
+        {
+            return JsBigInt.Create(value);
+        }
+
         public static implicit operator JsValue(bool value)
         public static implicit operator JsValue(bool value)
         {
         {
             return value ? JsBoolean.True : JsBoolean.False;
             return value ? JsBoolean.True : JsBoolean.False;
@@ -287,22 +278,69 @@ namespace Jint.Native
             return JsString.Create(value);
             return JsString.Create(value);
         }
         }
 
 
-        public override bool Equals(object obj)
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-islooselyequal
+        /// </summary>
+        public virtual bool IsLooselyEqual(JsValue value)
         {
         {
-            if (ReferenceEquals(null, obj))
+            if (ReferenceEquals(this, value))
             {
             {
-                return false;
+                return true;
             }
             }
 
 
-            if (ReferenceEquals(this, obj))
+            var x = this;
+            var y = value;
+
+            if (x.IsNumber() && y.IsString())
             {
             {
-                return true;
+                return x.IsLooselyEqual(TypeConverter.ToNumber(y));
+            }
+
+            if (x.IsString() && y.IsNumber())
+            {
+                return y.IsLooselyEqual(TypeConverter.ToNumber(x));
             }
             }
 
 
-            return obj is JsValue value && Equals(value);
+            if (x.IsBoolean())
+            {
+                return y.IsLooselyEqual(TypeConverter.ToNumber(x));
+            }
+
+            if (y.IsBoolean())
+            {
+                return x.IsLooselyEqual(TypeConverter.ToNumber(y));
+            }
+
+            const InternalTypes stringOrNumber = InternalTypes.String | InternalTypes.Integer | InternalTypes.Number | InternalTypes.BigInt;
+
+            if (y.IsObject() && (x._type & stringOrNumber) != 0)
+            {
+                return x.IsLooselyEqual(TypeConverter.ToPrimitive(y));
+            }
+
+            if (x.IsObject() && (y._type & stringOrNumber) != 0)
+            {
+                return y.IsLooselyEqual(TypeConverter.ToPrimitive(x));
+            }
+
+            return false;
+        }
+
+        /// <summary>
+        /// Strict equality.
+        /// </summary>
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as JsValue);
         }
         }
 
 
-        public abstract bool Equals(JsValue other);
+        /// <summary>
+        /// Strict equality.
+        /// </summary>
+        public virtual bool Equals(JsValue other)
+        {
+            return ReferenceEquals(this, other);
+        }
 
 
         public override int GetHashCode()
         public override int GetHashCode()
         {
         {
@@ -335,6 +373,9 @@ namespace Jint.Native
                     case Types.Number:
                     case Types.Number:
                         Value = ((JsNumber) value)._value + " (number)";
                         Value = ((JsNumber) value)._value + " (number)";
                         break;
                         break;
+                    case Types.BigInt:
+                        Value = ((JsBigInt) value)._value + " (bigint)";
+                        break;
                     case Types.Object:
                     case Types.Object:
                         Value = value.AsObject().GetType().Name;
                         Value = value.AsObject().GetType().Name;
                         break;
                         break;

+ 55 - 24
Jint/Native/Json/JsonSerializer.cs

@@ -1,8 +1,12 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using Jint.Collections;
 using Jint.Collections;
+using Jint.Native.BigInt;
+using Jint.Native.Boolean;
 using Jint.Native.Global;
 using Jint.Native.Global;
+using Jint.Native.Number;
 using Jint.Native.Object;
 using Jint.Native.Object;
+using Jint.Native.String;
 using Jint.Pooling;
 using Jint.Pooling;
 using Jint.Runtime;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors;
@@ -18,6 +22,8 @@ namespace Jint.Native.Json
         private List<JsValue> _propertyList;
         private List<JsValue> _propertyList;
         private JsValue _replacerFunction = Undefined.Instance;
         private JsValue _replacerFunction = Undefined.Instance;
 
 
+        private static readonly JsString toJsonProperty = new("toJSON");
+
         public JsonSerializer(Engine engine)
         public JsonSerializer(Engine engine)
         {
         {
             _engine = engine;
             _engine = engine;
@@ -117,15 +123,23 @@ namespace Jint.Native.Json
             var wrapper = _engine.Realm.Intrinsics.Object.Construct(Arguments.Empty);
             var wrapper = _engine.Realm.Intrinsics.Object.Construct(Arguments.Empty);
             wrapper.DefineOwnProperty(JsString.Empty, new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable));
             wrapper.DefineOwnProperty(JsString.Empty, new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable));
 
 
-            return Str(JsString.Empty, wrapper);
+            return SerializeJSONProperty(JsString.Empty, wrapper);
         }
         }
 
 
-        private JsValue Str(JsValue key, JsValue holder)
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-serializejsonproperty
+        /// </summary>
+        private JsValue SerializeJSONProperty(JsValue key, JsValue holder)
         {
         {
             var value = holder.Get(key, holder);
             var value = holder.Get(key, holder);
-            if (value.IsObject())
+            var isBigInt = value is BigIntInstance || value.IsBigInt();
+            if (value.IsObject() || isBigInt)
             {
             {
-                var toJson = value.AsObject().Get("toJSON", value);
+                var toJson = value.Get(toJsonProperty, value);
+                if (toJson.IsUndefined() && isBigInt)
+                {
+                    toJson = _engine.Realm.Intrinsics.BigInt.PrototypeObject.Get(toJsonProperty);
+                }
                 if (toJson.IsObject())
                 if (toJson.IsObject())
                 {
                 {
                     if (toJson.AsObject() is ICallable callableToJson)
                     if (toJson.AsObject() is ICallable callableToJson)
@@ -135,30 +149,33 @@ namespace Jint.Native.Json
                 }
                 }
             }
             }
 
 
-            if (!ReferenceEquals(_replacerFunction, Undefined.Instance))
+            if (!_replacerFunction.IsUndefined())
             {
             {
-                var replacerFunctionCallable = (ICallable)_replacerFunction.AsObject();
+                var replacerFunctionCallable = (ICallable) _replacerFunction.AsObject();
                 value = replacerFunctionCallable.Call(holder, Arguments.From(key, value));
                 value = replacerFunctionCallable.Call(holder, Arguments.From(key, value));
             }
             }
 
 
             if (value.IsObject())
             if (value.IsObject())
             {
             {
                 var valueObj = value.AsObject();
                 var valueObj = value.AsObject();
-                switch (valueObj.Class)
+                switch (valueObj)
                 {
                 {
-                    case ObjectClass.Number:
-                        value = TypeConverter.ToNumber(value);
+                    case NumberInstance numberInstance:
+                        value = numberInstance.NumberData;
                         break;
                         break;
-                    case ObjectClass.String:
-                        value = TypeConverter.ToString(value);
+                    case StringInstance stringInstance:
+                        value = stringInstance.StringData;
                         break;
                         break;
-                    case ObjectClass.Boolean:
-                        value = TypeConverter.ToPrimitive(value);
+                    case BooleanInstance booleanInstance:
+                        value = booleanInstance.BooleanData;
+                        break;
+                    case BigIntInstance bigIntInstance:
+                        value = bigIntInstance.BigIntData;
                         break;
                         break;
                     default:
                     default:
                         value = SerializesAsArray(value)
                         value = SerializesAsArray(value)
-                            ? SerializeArray(value)
-                            : SerializeObject(value.AsObject());
+                            ? SerializeJSONArray(value)
+                            : SerializeJSONObject(value.AsObject());
                         return value;
                         return value;
                 }
                 }
             }
             }
@@ -175,7 +192,7 @@ namespace Jint.Native.Json
 
 
             if (value.IsString())
             if (value.IsString())
             {
             {
-                return Quote(value.ToString());
+                return QuoteJSONString(value.ToString());
             }
             }
 
 
             if (value.IsNumber())
             if (value.IsNumber())
@@ -189,13 +206,18 @@ namespace Jint.Native.Json
                 return JsString.NullString;
                 return JsString.NullString;
             }
             }
 
 
+            if (value.IsBigInt())
+            {
+                ExceptionHelper.ThrowTypeError(_engine.Realm, "Do not know how to serialize a BigInt");
+            }
+
             var isCallable = value.IsObject() && value.AsObject() is ICallable;
             var isCallable = value.IsObject() && value.AsObject() is ICallable;
 
 
             if (value.IsObject() && isCallable == false)
             if (value.IsObject() && isCallable == false)
             {
             {
                 return SerializesAsArray(value)
                 return SerializesAsArray(value)
-                    ? SerializeArray(value)
-                    : SerializeObject(value.AsObject());
+                    ? SerializeJSONArray(value)
+                    : SerializeJSONObject(value.AsObject());
             }
             }
 
 
             return JsValue.Undefined;
             return JsValue.Undefined;
@@ -206,7 +228,10 @@ namespace Jint.Native.Json
             return value.AsObject().Class == ObjectClass.Array || value is ObjectWrapper { IsArrayLike: true };
             return value.AsObject().Class == ObjectClass.Array || value is ObjectWrapper { IsArrayLike: true };
         }
         }
 
 
-        private static string Quote(string value)
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-quotejsonstring
+        /// </summary>
+        private static string QuoteJSONString(string value)
         {
         {
             using var stringBuilder = StringBuilderPool.Rent();
             using var stringBuilder = StringBuilderPool.Rent();
             var sb = stringBuilder.Builder;
             var sb = stringBuilder.Builder;
@@ -253,7 +278,10 @@ namespace Jint.Native.Json
             return sb.ToString();
             return sb.ToString();
         }
         }
 
 
-        private string SerializeArray(JsValue value)
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-serializejsonarray
+        /// </summary>
+        private string SerializeJSONArray(JsValue value)
         {
         {
             _stack.Enter(value);
             _stack.Enter(value);
             var stepback = _indent;
             var stepback = _indent;
@@ -262,7 +290,7 @@ namespace Jint.Native.Json
             var len = TypeConverter.ToUint32(value.Get(CommonProperties.Length, value));
             var len = TypeConverter.ToUint32(value.Get(CommonProperties.Length, value));
             for (int i = 0; i < len; i++)
             for (int i = 0; i < len; i++)
             {
             {
-                var strP = Str(i, value);
+                var strP = SerializeJSONProperty(i, value);
                 if (strP.IsUndefined())
                 if (strP.IsUndefined())
                 {
                 {
                     strP = JsString.NullString;
                     strP = JsString.NullString;
@@ -295,7 +323,10 @@ namespace Jint.Native.Json
             return final;
             return final;
         }
         }
 
 
-        private string SerializeObject(ObjectInstance value)
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-serializejsonobject
+        /// </summary>
+        private string SerializeJSONObject(ObjectInstance value)
         {
         {
             string final;
             string final;
 
 
@@ -310,10 +341,10 @@ namespace Jint.Native.Json
             var partial = new List<string>();
             var partial = new List<string>();
             foreach (var p in k)
             foreach (var p in k)
             {
             {
-                var strP = Str(p, value);
+                var strP = SerializeJSONProperty(p, value);
                 if (!strP.IsUndefined())
                 if (!strP.IsUndefined())
                 {
                 {
-                    var member = Quote(p.ToString()) + ":";
+                    var member = QuoteJSONString(p.ToString()) + ":";
                     if (_gap != "")
                     if (_gap != "")
                     {
                     {
                         member += " ";
                         member += " ";

+ 1 - 1
Jint/Native/Map/MapInstance.cs

@@ -14,7 +14,7 @@ namespace Jint.Native.Map
             : base(engine, objectClass: ObjectClass.Map)
             : base(engine, objectClass: ObjectClass.Map)
         {
         {
             _realm = realm;
             _realm = realm;
-            _map = new OrderedDictionary<JsValue, JsValue>();
+            _map = new OrderedDictionary<JsValue, JsValue>(SameValueZeroComparer.Instance);
         }
         }
 
 
         public override PropertyDescriptor GetOwnProperty(JsValue property)
         public override PropertyDescriptor GetOwnProperty(JsValue property)

+ 27 - 11
Jint/Native/Number/NumberConstructor.cs

@@ -8,6 +8,9 @@ using Jint.Runtime.Interop;
 
 
 namespace Jint.Native.Number
 namespace Jint.Native.Number
 {
 {
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-number-constructor
+    /// </summary>
     public sealed class NumberConstructor : FunctionInstance, IConstructor
     public sealed class NumberConstructor : FunctionInstance, IConstructor
     {
     {
         private static readonly JsString _functionName = new JsString("Number");
         private static readonly JsString _functionName = new JsString("Number");
@@ -111,12 +114,8 @@ namespace Jint.Native.Number
 
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
         {
-            if (arguments.Length == 0)
-            {
-                return 0d;
-            }
-
-            return TypeConverter.ToNumber(arguments[0]);
+            var n = ProcessFirstParameter(arguments);
+            return n;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -124,22 +123,39 @@ namespace Jint.Native.Number
         /// </summary>
         /// </summary>
         public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
         {
-            var value = arguments.Length > 0
-                ? JsNumber.Create(TypeConverter.ToNumber(arguments[0]))
-                : JsNumber.PositiveZero;
+            var n = ProcessFirstParameter(arguments);
 
 
             if (newTarget.IsUndefined())
             if (newTarget.IsUndefined())
             {
             {
-                return Construct(value);
+                return Construct(n);
             }
             }
 
 
             var o = OrdinaryCreateFromConstructor(
             var o = OrdinaryCreateFromConstructor(
                 newTarget,
                 newTarget,
                 static intrinsics => intrinsics.Number.PrototypeObject,
                 static intrinsics => intrinsics.Number.PrototypeObject,
-                static (engine, realm, state) => new NumberInstance(engine, (JsNumber) state), value);
+                static (engine, realm, state) => new NumberInstance(engine, (JsNumber) state), n);
             return o;
             return o;
         }
         }
 
 
+        private static JsNumber ProcessFirstParameter(JsValue[] arguments)
+        {
+            var n = JsNumber.PositiveZero;
+            if (arguments.Length > 0)
+            {
+                var prim = TypeConverter.ToNumeric(arguments[0]);
+                if (prim.IsBigInt())
+                {
+                    n = JsNumber.Create((long) ((JsBigInt) prim)._value);
+                }
+                else
+                {
+                    n = (JsNumber) prim;
+                }
+            }
+
+            return n;
+        }
+
         public NumberPrototype PrototypeObject { get; private set; }
         public NumberPrototype PrototypeObject { get; private set; }
 
 
         public NumberInstance Construct(JsNumber value)
         public NumberInstance Construct(JsNumber value)

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

@@ -24,7 +24,7 @@ namespace Jint.Native.Number
 
 
         JsValue IPrimitiveInstance.PrimitiveValue => NumberData;
         JsValue IPrimitiveInstance.PrimitiveValue => NumberData;
 
 
-        public JsNumber NumberData { get; set; }
+        public JsNumber NumberData { get; internal init; }
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static bool IsNegativeZero(double x)
         public static bool IsNegativeZero(double x)

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

@@ -12,7 +12,7 @@ using Jint.Runtime.Interop;
 namespace Jint.Native.Number
 namespace Jint.Native.Number
 {
 {
     /// <summary>
     /// <summary>
-    /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.7.4
+    /// https://tc39.es/ecma262/#sec-properties-of-the-number-prototype-object
     /// </summary>
     /// </summary>
     public sealed class NumberPrototype : NumberInstance
     public sealed class NumberPrototype : NumberInstance
     {
     {

+ 5 - 16
Jint/Native/Object/ObjectInstance.cs

@@ -16,7 +16,6 @@ using Jint.Native.Symbol;
 using Jint.Runtime;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 using Jint.Runtime.Interop;
-using Jint.Runtime.Interpreter.Expressions;
 
 
 namespace Jint.Native.Object
 namespace Jint.Native.Object
 {
 {
@@ -724,7 +723,7 @@ namespace Jint.Native.Object
                 current.Enumerable == desc.Enumerable && current.EnumerableSet == desc.EnumerableSet &&
                 current.Enumerable == desc.Enumerable && current.EnumerableSet == desc.EnumerableSet &&
                 ((ReferenceEquals(currentGet, null) && ReferenceEquals(descGet, null)) || (!ReferenceEquals(currentGet, null) && !ReferenceEquals(descGet, null) && SameValue(currentGet, descGet))) &&
                 ((ReferenceEquals(currentGet, null) && ReferenceEquals(descGet, null)) || (!ReferenceEquals(currentGet, null) && !ReferenceEquals(descGet, null) && SameValue(currentGet, descGet))) &&
                 ((ReferenceEquals(currentSet, null) && ReferenceEquals(descSet, null)) || (!ReferenceEquals(currentSet, null) && !ReferenceEquals(descSet, null) && SameValue(currentSet, descSet))) &&
                 ((ReferenceEquals(currentSet, null) && ReferenceEquals(descSet, null)) || (!ReferenceEquals(currentSet, null) && !ReferenceEquals(descSet, null) && SameValue(currentSet, descSet))) &&
-                ((ReferenceEquals(currentValue, null) && ReferenceEquals(descValue, null)) || (!ReferenceEquals(currentValue, null) && !ReferenceEquals(descValue, null) && JintBinaryExpression.StrictlyEqual(currentValue, descValue)))
+                ((ReferenceEquals(currentValue, null) && ReferenceEquals(descValue, null)) || (!ReferenceEquals(currentValue, null) && !ReferenceEquals(descValue, null) && currentValue == descValue))
             )
             )
             {
             {
                 return true;
                 return true;
@@ -924,7 +923,7 @@ namespace Jint.Native.Object
                 case ObjectClass.String:
                 case ObjectClass.String:
                     if (this is StringInstance stringInstance)
                     if (this is StringInstance stringInstance)
                     {
                     {
-                        converted = stringInstance.PrimitiveValue.ToString();
+                        converted = stringInstance.StringData.ToString();
                     }
                     }
                     break;
                     break;
 
 
@@ -938,7 +937,7 @@ namespace Jint.Native.Object
                 case ObjectClass.Boolean:
                 case ObjectClass.Boolean:
                     if (this is BooleanInstance booleanInstance)
                     if (this is BooleanInstance booleanInstance)
                     {
                     {
-                        converted = ((JsBoolean) booleanInstance.PrimitiveValue)._value
+                        converted = ((JsBoolean) booleanInstance.BooleanData)._value
                             ? JsBoolean.BoxedTrue
                             ? JsBoolean.BoxedTrue
                             : JsBoolean.BoxedFalse;
                             : JsBoolean.BoxedFalse;
                     }
                     }
@@ -1317,22 +1316,12 @@ namespace Jint.Native.Object
 
 
         public override bool Equals(JsValue obj)
         public override bool Equals(JsValue obj)
         {
         {
-            if (ReferenceEquals(null, obj))
-            {
-                return false;
-            }
-
-            if (!(obj is ObjectInstance s))
-            {
-                return false;
-            }
-
-            return Equals(s);
+            return Equals(obj as ObjectInstance);
         }
         }
 
 
         public bool Equals(ObjectInstance other)
         public bool Equals(ObjectInstance other)
         {
         {
-            if (ReferenceEquals(null, other))
+            if (other is null)
             {
             {
                 return false;
                 return false;
             }
             }

+ 25 - 0
Jint/Native/SameValueZeroComparer.cs

@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace Jint.Native;
+
+internal sealed class SameValueZeroComparer : IEqualityComparer<JsValue>
+{
+    public static readonly SameValueZeroComparer Instance = new();
+
+    bool IEqualityComparer<JsValue>.Equals(JsValue x, JsValue y)
+    {
+        return Equals(x, y);
+    }
+
+    public int GetHashCode(JsValue obj)
+    {
+        return obj.GetHashCode();
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    internal static bool Equals(JsValue x, JsValue y)
+    {
+        return x == y || x is JsNumber xNum && y is JsNumber yNum && double.IsNaN(xNum._value) && double.IsNaN(yNum._value);
+    }
+}

+ 1 - 1
Jint/Native/Set/SetInstance.cs

@@ -11,7 +11,7 @@ namespace Jint.Native.Set
         public SetInstance(Engine engine)
         public SetInstance(Engine engine)
             : base(engine, ObjectClass.Map)
             : base(engine, ObjectClass.Map)
         {
         {
-            _set = new OrderedSet<JsValue>();
+            _set = new OrderedSet<JsValue>(SameValueZeroComparer.Instance);
         }
         }
 
 
         public override PropertyDescriptor GetOwnProperty(JsValue property)
         public override PropertyDescriptor GetOwnProperty(JsValue property)

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

@@ -187,7 +187,7 @@ namespace Jint.Native.String
             var instance = new StringInstance(Engine)
             var instance = new StringInstance(Engine)
             {
             {
                 _prototype = prototype,
                 _prototype = prototype,
-                PrimitiveValue = value,
+                StringData = value,
                 _length = PropertyDescriptor.AllForbiddenDescriptor.ForNumber(value.Length)
                 _length = PropertyDescriptor.AllForbiddenDescriptor.ForNumber(value.Length)
             };
             };
 
 

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

@@ -16,9 +16,9 @@ namespace Jint.Native.String
 
 
         Types IPrimitiveInstance.Type => Types.String;
         Types IPrimitiveInstance.Type => Types.String;
 
 
-        JsValue IPrimitiveInstance.PrimitiveValue => PrimitiveValue;
+        JsValue IPrimitiveInstance.PrimitiveValue => StringData;
 
 
-        public JsString PrimitiveValue { get; set; }
+        public JsString StringData { get; internal init; }
 
 
         private static bool IsInt32(double d, out int intValue)
         private static bool IsInt32(double d, out int intValue)
         {
         {
@@ -55,7 +55,7 @@ namespace Jint.Native.String
                 return PropertyDescriptor.Undefined;
                 return PropertyDescriptor.Undefined;
             }
             }
 
 
-            var str = PrimitiveValue.ToString();
+            var str = StringData.ToString();
             var number = TypeConverter.ToNumber(property);
             var number = TypeConverter.ToNumber(property);
             if (!IsInt32(number, out var index) || index < 0 || index >= str.Length)
             if (!IsInt32(number, out var index) || index < 0 || index >= str.Length)
             {
             {
@@ -80,8 +80,8 @@ namespace Jint.Native.String
 
 
         public override List<JsValue> GetOwnPropertyKeys(Types types)
         public override List<JsValue> GetOwnPropertyKeys(Types types)
         {
         {
-            var keys = new List<JsValue>(PrimitiveValue.Length + 1);
-            for (uint i = 0; i < PrimitiveValue.Length; ++i)
+            var keys = new List<JsValue>(StringData.Length + 1);
+            for (uint i = 0; i < StringData.Length; ++i)
             {
             {
                 keys.Add(JsString.Create(i));
                 keys.Add(JsString.Create(i));
             }
             }

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

@@ -29,7 +29,7 @@ namespace Jint.Native.String
             : base(engine)
             : base(engine)
         {
         {
             _prototype = objectPrototype;
             _prototype = objectPrototype;
-            PrimitiveValue = JsString.Empty;
+            StringData = JsString.Empty;
             _length = PropertyDescriptor.AllForbiddenDescriptor.NumberZero;
             _length = PropertyDescriptor.AllForbiddenDescriptor.NumberZero;
             _realm = realm;
             _realm = realm;
             _constructor = constructor;
             _constructor = constructor;
@@ -109,7 +109,7 @@ namespace Jint.Native.String
                 ExceptionHelper.ThrowTypeError(_realm);
                 ExceptionHelper.ThrowTypeError(_realm);
             }
             }
 
 
-            return s.PrimitiveValue;
+            return s.StringData;
         }
         }
 
 
         // http://msdn.microsoft.com/en-us/library/system.char.iswhitespace(v=vs.110).aspx
         // http://msdn.microsoft.com/en-us/library/system.char.iswhitespace(v=vs.110).aspx
@@ -765,7 +765,7 @@ namespace Jint.Native.String
         {
         {
             if (thisObj is StringInstance si)
             if (thisObj is StringInstance si)
             {
             {
-                return si.PrimitiveValue;
+                return si.StringData;
             }
             }
 
 
             if (thisObj is JsString)
             if (thisObj is JsString)

+ 28 - 16
Jint/Native/TypedArray/IntrinsicTypedArrayPrototype.cs

@@ -12,7 +12,6 @@ using Jint.Pooling;
 using Jint.Runtime;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 using Jint.Runtime.Interop;
-using Jint.Runtime.Interpreter.Expressions;
 
 
 namespace Jint.Native.TypedArray
 namespace Jint.Native.TypedArray
 {
 {
@@ -310,12 +309,19 @@ namespace Jint.Native.TypedArray
         {
         {
             var o = thisObj.ValidateTypedArray(_realm);
             var o = thisObj.ValidateTypedArray(_realm);
 
 
+            var jsValue = arguments.At(0);
             var start = arguments.At(1);
             var start = arguments.At(1);
             var end = arguments.At(2);
             var end = arguments.At(2);
 
 
-            var value = o._contentType == TypedArrayContentType.BigInt
-                ? TypeConverter.ToBigInt(arguments.At(0))
-                : TypeConverter.ToNumber(arguments.At(0));
+            JsValue value;
+            if (o._contentType == TypedArrayContentType.BigInt)
+            {
+                value = JsBigInt.Create(jsValue.ToBigInteger(_engine));
+            }
+            else
+            {
+                value = JsNumber.Create(jsValue);
+            }
 
 
             var len = o.Length;
             var len = o.Length;
 
 
@@ -509,7 +515,7 @@ namespace Jint.Native.TypedArray
             while (k < len)
             while (k < len)
             {
             {
                 var value = o[(int) k];
                 var value = o[(int) k];
-                if (JintBinaryExpression.SameValueZero(value, searchElement))
+                if (SameValueZeroComparer.Equals(value, searchElement))
                 {
                 {
                     return JsBoolean.True;
                     return JsBoolean.True;
                 }
                 }
@@ -565,8 +571,7 @@ namespace Jint.Native.TypedArray
                 if (kPresent)
                 if (kPresent)
                 {
                 {
                     var elementK = o[(int) k];
                     var elementK = o[(int) k];
-                    var same = JintBinaryExpression.StrictlyEqual(elementK, searchElement);
-                    if (same)
+                    if (elementK == searchElement)
                     {
                     {
                         return k;
                         return k;
                     }
                     }
@@ -664,8 +669,7 @@ namespace Jint.Native.TypedArray
                 if (kPresent)
                 if (kPresent)
                 {
                 {
                     var elementK = o[(int) k];
                     var elementK = o[(int) k];
-                    var same = JintBinaryExpression.StrictlyEqual(elementK, searchElement);
-                    if (same)
+                    if (elementK == searchElement)
                     {
                     {
                         return k;
                         return k;
                     }
                     }
@@ -963,18 +967,19 @@ namespace Jint.Native.TypedArray
 
 
             while (targetByteIndex < limit)
             while (targetByteIndex < limit)
             {
             {
-                double value;
                 if (target._contentType == TypedArrayContentType.BigInt)
                 if (target._contentType == TypedArrayContentType.BigInt)
                 {
                 {
-                    value = TypeConverter.ToBigInt(src.Get(k));
+                    var value = src.Get(k).ToBigInteger(_engine);
+                    targetBuffer.AssertNotDetached();
+                    targetBuffer.SetValueInBuffer((int) targetByteIndex, targetType, value, true, ArrayBufferOrder.Unordered);
                 }
                 }
                 else
                 else
                 {
                 {
-                    value = TypeConverter.ToNumber(src.Get(k));
+                    var value = TypeConverter.ToNumber(src.Get(k));
+                    targetBuffer.AssertNotDetached();
+                    targetBuffer.SetValueInBuffer((int) targetByteIndex, targetType, value, true, ArrayBufferOrder.Unordered);
                 }
                 }
 
 
-                targetBuffer.AssertNotDetached();
-                targetBuffer.SetValueInBuffer((int) targetByteIndex, targetType, value, true, ArrayBufferOrder.Unordered);
                 k++;
                 k++;
                 targetByteIndex += targetElementSize;
                 targetByteIndex += targetElementSize;
             }
             }
@@ -1084,8 +1089,8 @@ namespace Jint.Native.TypedArray
                     var limit = targetByteIndex + count * elementSize;
                     var limit = targetByteIndex + count * elementSize;
                     while (targetByteIndex < limit)
                     while (targetByteIndex < limit)
                     {
                     {
-                        var value = (JsNumber) srcBuffer.GetValueFromBuffer(srcByteIndex, TypedArrayElementType.Uint8, true, ArrayBufferOrder.Unordered);
-                        targetBuffer.SetValueInBuffer(targetByteIndex, TypedArrayElementType.Uint8, value._value, true, ArrayBufferOrder.Unordered);
+                        var value = srcBuffer.GetValueFromBuffer(srcByteIndex, TypedArrayElementType.Uint8, true, ArrayBufferOrder.Unordered);
+                        targetBuffer.SetValueInBuffer(targetByteIndex, TypedArrayElementType.Uint8, value, true, ArrayBufferOrder.Unordered);
                         srcByteIndex++;
                         srcByteIndex++;
                         targetByteIndex++;
                         targetByteIndex++;
                     }
                     }
@@ -1348,6 +1353,13 @@ namespace Jint.Native.TypedArray
                     return (int) v;
                     return (int) v;
                 }
                 }
 
 
+                if (x.Type == Types.BigInt || y.Type == Types.BigInt)
+                {
+                    var xBigInt = TypeConverter.ToBigInt(x);
+                    var yBigInt = TypeConverter.ToBigInt(y);
+                    return xBigInt.CompareTo(yBigInt);
+                }
+
                 var xValue = x.AsNumber();
                 var xValue = x.AsNumber();
                 var yValue = y.AsNumber();
                 var yValue = y.AsNumber();
 
 

+ 18 - 2
Jint/Native/TypedArray/TypedArrayConstructor.cs

@@ -279,11 +279,27 @@ namespace Jint.Native.TypedArray
             return obj;
             return obj;
         }
         }
 
 
-        internal static void FillTypedArrayInstance(TypedArrayInstance target, System.Array values)
+        internal static void FillTypedArrayInstance<T>(TypedArrayInstance target, T[] values)
         {
         {
             for (var i = 0; i < values.Length; ++i)
             for (var i = 0; i < values.Length; ++i)
             {
             {
-                target.DoIntegerIndexedElementSet(i, Convert.ToDouble(values.GetValue(i)));
+                target.DoIntegerIndexedElementSet(i, Convert.ToDouble(values[i]));
+            }
+        }
+
+        internal static void FillTypedArrayInstance(TypedArrayInstance target, ulong[] values)
+        {
+            for (var i = 0; i < values.Length; ++i)
+            {
+                target.DoIntegerIndexedElementSet(i, values[i]);
+            }
+        }
+
+        internal static void FillTypedArrayInstance(TypedArrayInstance target, long[] values)
+        {
+            for (var i = 0; i < values.Length; ++i)
+            {
+                target.DoIntegerIndexedElementSet(i, values[i]);
             }
             }
         }
         }
     }
     }

+ 51 - 27
Jint/Native/TypedArray/TypedArrayInstance.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
+using Esprima;
 using Jint.Native.ArrayBuffer;
 using Jint.Native.ArrayBuffer;
 using Jint.Native.Number;
 using Jint.Native.Number;
 using Jint.Native.Object;
 using Jint.Native.Object;
@@ -11,7 +12,7 @@ namespace Jint.Native.TypedArray
 {
 {
     public sealed class TypedArrayInstance : ObjectInstance
     public sealed class TypedArrayInstance : ObjectInstance
     {
     {
-        internal TypedArrayContentType _contentType;
+        internal readonly TypedArrayContentType _contentType;
         internal readonly TypedArrayElementType _arrayElementType;
         internal readonly TypedArrayElementType _arrayElementType;
         internal ArrayBufferInstance _viewedArrayBuffer;
         internal ArrayBufferInstance _viewedArrayBuffer;
         internal uint _byteLength;
         internal uint _byteLength;
@@ -35,6 +36,10 @@ namespace Jint.Native.TypedArray
         {
         {
             _arrayElementType = type;
             _arrayElementType = type;
             _arrayLength = length;
             _arrayLength = length;
+
+            _contentType = type != TypedArrayElementType.BigInt64 && type != TypedArrayElementType.BigUint64
+                ? TypedArrayContentType.Number
+                : TypedArrayContentType.BigInt;
         }
         }
 
 
         internal JsValue this[int index]
         internal JsValue this[int index]
@@ -258,20 +263,35 @@ namespace Jint.Native.TypedArray
             var elementSize = elementType.GetElementSize();
             var elementSize = elementType.GetElementSize();
             var indexedPosition = index * elementSize + offset;
             var indexedPosition = index * elementSize + offset;
             var value = _viewedArrayBuffer.GetValueFromBuffer(indexedPosition, elementType, true, ArrayBufferOrder.Unordered);
             var value = _viewedArrayBuffer.GetValueFromBuffer(indexedPosition, elementType, true, ArrayBufferOrder.Unordered);
-            return _arrayElementType.FitsInt32() ? JsNumber.Create((int) value) : JsNumber.Create(value);
+            if (value.Type == Types.Number)
+            {
+                return _arrayElementType.FitsInt32()
+                    ? JsNumber.Create((int) value.DoubleValue)
+                    : JsNumber.Create(value.DoubleValue);
+            }
+
+            return JsBigInt.Create(value.BigInteger);
         }
         }
 
 
         // helper tot prevent floating point
         // helper tot prevent floating point
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private void IntegerIndexedElementSet(int index, JsValue value)
         private void IntegerIndexedElementSet(int index, JsValue value)
         {
         {
-            var numValue = _contentType == TypedArrayContentType.BigInt
-                ? TypeConverter.ToBigInt(value)
-                : TypeConverter.ToNumber(value);
-
-            if (IsValidIntegerIndex(index))
+            if (_contentType != TypedArrayContentType.BigInt)
+            {
+                var numValue = TypeConverter.ToNumber(value);
+                if (IsValidIntegerIndex(index))
+                {
+                    DoIntegerIndexedElementSet(index, numValue);
+                }
+            }
+            else
             {
             {
-                DoIntegerIndexedElementSet(index, numValue);
+                var numValue = value.ToBigInteger(_engine);
+                if (IsValidIntegerIndex(index))
+                {
+                    DoIntegerIndexedElementSet(index, numValue);
+                }
             }
             }
         }
         }
 
 
@@ -281,17 +301,32 @@ namespace Jint.Native.TypedArray
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private void IntegerIndexedElementSet(double index, JsValue value)
         private void IntegerIndexedElementSet(double index, JsValue value)
         {
         {
-            var numValue = _contentType == TypedArrayContentType.BigInt
-                ? TypeConverter.ToBigInt(value)
-                : TypeConverter.ToNumber(value);
-
-            if (IsValidIntegerIndex(index))
+            if (_contentType != TypedArrayContentType.BigInt)
+            {
+                var numValue = TypeConverter.ToNumber(value);
+                if (IsValidIntegerIndex(index))
+                {
+                    DoIntegerIndexedElementSet((int) index, numValue);
+                }
+            }
+            else
             {
             {
-                DoIntegerIndexedElementSet((int) index, numValue);
+                try
+                {
+                    var numValue = TypeConverter.ToBigInt(value);
+                    if (IsValidIntegerIndex(index))
+                    {
+                        DoIntegerIndexedElementSet((int) index, numValue);
+                    }
+                }
+                catch (ParserException ex)
+                {
+                    ExceptionHelper.ThrowSyntaxError(_engine.Realm, ex.Message);
+                }
             }
             }
         }
         }
 
 
-        internal void DoIntegerIndexedElementSet(int index, double numValue)
+        internal void DoIntegerIndexedElementSet(int index, TypedArrayValue numValue)
         {
         {
             var offset = _byteOffset;
             var offset = _byteOffset;
             var elementType = _arrayElementType;
             var elementType = _arrayElementType;
@@ -307,7 +342,7 @@ namespace Jint.Native.TypedArray
         private bool IsValidIntegerIndex(double index)
         private bool IsValidIntegerIndex(double index)
         {
         {
             return !_viewedArrayBuffer.IsDetachedBuffer
             return !_viewedArrayBuffer.IsDetachedBuffer
-                   && IsIntegralNumber(index)
+                   && TypeConverter.IsIntegralNumber(index)
                    && !NumberInstance.IsNegativeZero(index)
                    && !NumberInstance.IsNegativeZero(index)
                    && (uint) index < _arrayLength;
                    && (uint) index < _arrayLength;
         }
         }
@@ -321,17 +356,6 @@ namespace Jint.Native.TypedArray
             return !_viewedArrayBuffer.IsDetachedBuffer && (uint) index < _arrayLength;
             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);
-        }
-
         internal T[] ToNativeArray<T>()
         internal T[] ToNativeArray<T>()
         {
         {
             var conversionType = typeof(T);
             var conversionType = typeof(T);

+ 159 - 0
Jint/Native/TypedArray/TypedArrayValue.cs

@@ -0,0 +1,159 @@
+using System;
+using System.Numerics;
+using Jint.Runtime;
+
+namespace Jint.Native.TypedArray;
+
+/// <summary>
+/// Container for either double or BigInteger.
+/// </summary>
+internal readonly record struct TypedArrayValue(Types Type, double DoubleValue, BigInteger BigInteger) : IConvertible
+{
+    public static implicit operator TypedArrayValue(double value)
+    {
+        return new TypedArrayValue(Types.Number, value, default);
+    }
+
+    public static implicit operator TypedArrayValue(byte value)
+    {
+        return new TypedArrayValue(Types.Number, value, default);
+    }
+
+    public static implicit operator TypedArrayValue(int value)
+    {
+        return new TypedArrayValue(Types.Number, value, default);
+    }
+
+    public static implicit operator TypedArrayValue(ushort value)
+    {
+        return new TypedArrayValue(Types.Number, value, default);
+    }
+
+    public static implicit operator TypedArrayValue(short value)
+    {
+        return new TypedArrayValue(Types.Number, value, default);
+    }
+
+    public static implicit operator TypedArrayValue(uint value)
+    {
+        return new TypedArrayValue(Types.Number, value, default);
+    }
+
+    public static implicit operator TypedArrayValue(BigInteger value)
+    {
+        return new TypedArrayValue(Types.BigInt, default, value);
+    }
+
+    public static implicit operator TypedArrayValue(ulong value)
+    {
+        return new TypedArrayValue(Types.BigInt, default, value);
+    }
+
+    public static implicit operator TypedArrayValue(long value)
+    {
+        return new TypedArrayValue(Types.BigInt, default, value);
+    }
+
+    public JsValue ToJsValue()
+    {
+        return Type == Types.Number
+            ? JsNumber.Create(DoubleValue)
+            : JsBigInt.Create(BigInteger);
+    }
+
+    public TypeCode GetTypeCode()
+    {
+        ExceptionHelper.ThrowNotImplementedException();
+        return default;
+    }
+
+    public bool ToBoolean(IFormatProvider provider)
+    {
+        ExceptionHelper.ThrowNotImplementedException();
+        return default;
+    }
+
+    public char ToChar(IFormatProvider provider)
+    {
+        ExceptionHelper.ThrowNotImplementedException();
+        return default;
+    }
+
+    public sbyte ToSByte(IFormatProvider provider)
+    {
+        return (sbyte) DoubleValue;
+    }
+
+    public byte ToByte(IFormatProvider provider)
+    {
+        return (byte) DoubleValue;
+    }
+
+    public short ToInt16(IFormatProvider provider)
+    {
+        return (short) DoubleValue;
+    }
+
+    public ushort ToUInt16(IFormatProvider provider)
+    {
+        return (ushort) DoubleValue;
+    }
+
+    public int ToInt32(IFormatProvider provider)
+    {
+        return (int) DoubleValue;
+    }
+
+    public uint ToUInt32(IFormatProvider provider)
+    {
+        return (uint) DoubleValue;
+    }
+
+    public long ToInt64(IFormatProvider provider)
+    {
+        return (long) BigInteger;
+    }
+
+    public ulong ToUInt64(IFormatProvider provider)
+    {
+        return (ulong) BigInteger;
+    }
+
+    public float ToSingle(IFormatProvider provider)
+    {
+        return (float) DoubleValue;
+    }
+
+    public double ToDouble(IFormatProvider provider)
+    {
+        return DoubleValue;
+    }
+
+    public decimal ToDecimal(IFormatProvider provider)
+    {
+        return (decimal) DoubleValue;
+    }
+
+    public DateTime ToDateTime(IFormatProvider provider)
+    {
+        ExceptionHelper.ThrowNotImplementedException();
+        return default;
+    }
+
+    public string ToString(IFormatProvider provider)
+    {
+        ExceptionHelper.ThrowNotImplementedException();
+        return default;
+    }
+
+    public object ToType(Type conversionType, IFormatProvider provider)
+    {
+        if (conversionType == typeof(BigInteger) && Type == Types.BigInt)
+        {
+            return BigInteger;
+        }
+
+        ExceptionHelper.ThrowNotImplementedException();
+        return default;
+    }
+}

+ 49 - 6
Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs

@@ -1,3 +1,4 @@
+using System.Numerics;
 using Esprima.Ast;
 using Esprima.Ast;
 using Jint.Native;
 using Jint.Native;
 using Jint.Native.Function;
 using Jint.Native.Function;
@@ -126,10 +127,14 @@ namespace Jint.Runtime.Interpreter.Expressions
 
 
                                 lval = jsString.Append(rprim);
                                 lval = jsString.Append(rprim);
                             }
                             }
-                            else
+                            else if (!AreIntegerOperands(lval, rval))
                             {
                             {
                                 lval = TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim);
                                 lval = TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim);
                             }
                             }
+                            else
+                            {
+                                lval = TypeConverter.ToBigInt(lprim) + TypeConverter.ToBigInt(rprim);
+                            }
                         }
                         }
 
 
                         break;
                         break;
@@ -138,9 +143,19 @@ namespace Jint.Runtime.Interpreter.Expressions
                     case AssignmentOperator.MinusAssign:
                     case AssignmentOperator.MinusAssign:
                     {
                     {
                         var rval = _right.GetValue(context).Value;
                         var rval = _right.GetValue(context).Value;
-                        lval = AreIntegerOperands(lval, rval)
-                            ? JsNumber.Create(lval.AsInteger() - rval.AsInteger())
-                            : JsNumber.Create(TypeConverter.ToNumber(lval) - TypeConverter.ToNumber(rval));
+                        if (AreIntegerOperands(lval, rval))
+                        {
+                            lval = JsNumber.Create(lval.AsInteger() - rval.AsInteger());
+                        }
+                        else if (!AreIntegerOperands(lval, rval))
+                        {
+                            lval = JsNumber.Create(TypeConverter.ToNumber(lval) - TypeConverter.ToNumber(rval));
+                        }
+                        else
+                        {
+                            lval = JsNumber.Create(TypeConverter.ToBigInt(lval) - TypeConverter.ToBigInt(rval));
+                        }
+
                         break;
                         break;
                     }
                     }
 
 
@@ -155,10 +170,14 @@ namespace Jint.Runtime.Interpreter.Expressions
                         {
                         {
                             lval = Undefined.Instance;
                             lval = Undefined.Instance;
                         }
                         }
-                        else
+                        else if (!AreIntegerOperands(lval, rval))
                         {
                         {
                             lval = TypeConverter.ToNumber(lval) * TypeConverter.ToNumber(rval);
                             lval = TypeConverter.ToNumber(lval) * TypeConverter.ToNumber(rval);
                         }
                         }
+                        else
+                        {
+                            lval = TypeConverter.ToBigInt(lval) * TypeConverter.ToBigInt(rval);
+                        }
 
 
                         break;
                         break;
                     }
                     }
@@ -166,7 +185,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     case AssignmentOperator.DivideAssign:
                     case AssignmentOperator.DivideAssign:
                     {
                     {
                         var rval = _right.GetValue(context).Value;
                         var rval = _right.GetValue(context).Value;
-                        lval = Divide(lval, rval);
+                        lval = Divide(context, lval, rval);
                         break;
                         break;
                     }
                     }
 
 
@@ -177,6 +196,10 @@ namespace Jint.Runtime.Interpreter.Expressions
                         {
                         {
                             lval = Undefined.Instance;
                             lval = Undefined.Instance;
                         }
                         }
+                        else if (!AreIntegerOperands(lval, rval))
+                        {
+                            lval = TypeConverter.ToNumber(lval) % TypeConverter.ToNumber(rval);
+                        }
                         else
                         else
                         {
                         {
                             lval = TypeConverter.ToNumber(lval) % TypeConverter.ToNumber(rval);
                             lval = TypeConverter.ToNumber(lval) % TypeConverter.ToNumber(rval);
@@ -263,6 +286,26 @@ namespace Jint.Runtime.Interpreter.Expressions
                         break;
                         break;
                     }
                     }
 
 
+                    case AssignmentOperator.ExponentiationAssign:
+                    {
+                        var rval = _right.GetValue(context).Value;
+                        if (!lval.IsBigInt() && !rval.IsBigInt())
+                        {
+                            lval = JsNumber.Create(System.Math.Pow(TypeConverter.ToNumber(lval), TypeConverter.ToNumber(rval)));
+                        }
+                        else
+                        {
+                            var exponent = TypeConverter.ToBigInt(rval);
+                            if (exponent > int.MaxValue || exponent < int.MinValue)
+                            {
+                                ExceptionHelper.ThrowTypeError(context.Engine.Realm, "Cannot do exponentation with exponent not fitting int32");
+                            }
+                            lval = JsBigInt.Create(BigInteger.Pow(TypeConverter.ToBigInt(lval), (int) exponent));
+                        }
+
+                        break;
+                    }
+
                     default:
                     default:
                         ExceptionHelper.ThrowNotImplementedException();
                         ExceptionHelper.ThrowNotImplementedException();
                         return default;
                         return default;

+ 310 - 100
Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs

@@ -1,9 +1,12 @@
 using System;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
 using System.Linq;
 using System.Linq;
+using System.Numerics;
+using System.Runtime.CompilerServices;
 using Esprima.Ast;
 using Esprima.Ast;
 using Jint.Extensions;
 using Jint.Extensions;
 using Jint.Native;
 using Jint.Native;
+using Jint.Native.Number;
 using Jint.Native.Object;
 using Jint.Native.Object;
 using Jint.Runtime.Interop;
 using Jint.Runtime.Interop;
 
 
@@ -19,6 +22,8 @@ namespace Jint.Runtime.Interpreter.Expressions
 
 
         private JintBinaryExpression(Engine engine, BinaryExpression expression) : base(expression)
         private JintBinaryExpression(Engine engine, BinaryExpression expression) : base(expression)
         {
         {
+            // TODO check https://tc39.es/ecma262/#sec-applystringornumericbinaryoperator
+
             _left = Build(engine, expression.Left);
             _left = Build(engine, expression.Left);
             _right = Build(engine, expression.Right);
             _right = Build(engine, expression.Right);
         }
         }
@@ -155,70 +160,18 @@ namespace Jint.Runtime.Interpreter.Expressions
             return result;
             return result;
         }
         }
 
 
-        public static bool SameValueZero(JsValue x, JsValue y)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static bool AreNonBigIntOperands(JsValue left, JsValue right)
         {
         {
-            return x == y || (x is JsNumber xNum && y is JsNumber yNum && double.IsNaN(xNum._value) && double.IsNaN(yNum._value));
+            return left._type != InternalTypes.BigInt && right._type != InternalTypes.BigInt;
         }
         }
 
 
-        public static bool StrictlyEqual(JsValue x, JsValue y)
+        internal static void AssertValidBigIntArithmeticOperands(EvaluationContext context, JsValue left, JsValue right)
         {
         {
-            var typeX = x._type & ~InternalTypes.InternalFlags;
-            var typeY = y._type & ~InternalTypes.InternalFlags;
-
-            if (typeX != typeY)
-            {
-                if (typeX == InternalTypes.Integer)
-                {
-                    typeX = InternalTypes.Number;
-                }
-
-                if (typeY == InternalTypes.Integer)
-                {
-                    typeY = InternalTypes.Number;
-                }
-
-                if (typeX != typeY)
-                {
-                    return false;
-                }
-            }
-
-            if (typeX == InternalTypes.Undefined || typeX == InternalTypes.Null)
-            {
-                return true;
-            }
-
-            if (typeX == InternalTypes.Integer)
-            {
-                return x.AsInteger() == y.AsInteger();
-            }
-
-            if (typeX == InternalTypes.Number)
+            if (left.Type != right.Type)
             {
             {
-                var nx = ((JsNumber) x)._value;
-                var ny = ((JsNumber) y)._value;
-                return !double.IsNaN(nx) && !double.IsNaN(ny) && nx == ny;
+                ExceptionHelper.ThrowTypeError(context.Engine.Realm, "Cannot mix BigInt and other types, use explicit conversions");
             }
             }
-
-            if ((typeX & InternalTypes.String) != 0)
-            {
-                return x.ToString() == y.ToString();
-            }
-
-            if (typeX == InternalTypes.Boolean)
-            {
-                return ((JsBoolean) x)._value == ((JsBoolean) y)._value;
-            }
-
-            if ((typeX & InternalTypes.Object) != 0 && x.AsObject() is IObjectWrapper xw)
-            {
-                var yw = y.AsObject() as IObjectWrapper;
-                if (yw == null)
-                    return false;
-                return Equals(xw.Target, yw.Target);
-            }
-
-            return x == y;
         }
         }
 
 
         private sealed class StrictlyEqualBinaryExpression : JintBinaryExpression
         private sealed class StrictlyEqualBinaryExpression : JintBinaryExpression
@@ -231,7 +184,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
             {
                 var left = _left.GetValue(context).Value;
                 var left = _left.GetValue(context).Value;
                 var right = _right.GetValue(context).Value;
                 var right = _right.GetValue(context).Value;
-                var equal = StrictlyEqual(left, right);
+                var equal = left == right;
                 return NormalCompletion(equal ? JsBoolean.True : JsBoolean.False);
                 return NormalCompletion(equal ? JsBoolean.True : JsBoolean.False);
             }
             }
         }
         }
@@ -246,7 +199,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
             {
                 var left = _left.GetValue(context).Value;
                 var left = _left.GetValue(context).Value;
                 var right = _right.GetValue(context).Value;
                 var right = _right.GetValue(context).Value;
-                return NormalCompletion(StrictlyEqual(left, right) ? JsBoolean.False : JsBoolean.True);
+                return NormalCompletion(left == right ? JsBoolean.False : JsBoolean.True);
             }
             }
         }
         }
 
 
@@ -320,9 +273,20 @@ namespace Jint.Runtime.Interpreter.Expressions
 
 
                 var lprim = TypeConverter.ToPrimitive(left);
                 var lprim = TypeConverter.ToPrimitive(left);
                 var rprim = TypeConverter.ToPrimitive(right);
                 var rprim = TypeConverter.ToPrimitive(right);
-                JsValue result = lprim.IsString() || rprim.IsString()
-                    ? JsString.Create(TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim))
-                    : JsNumber.Create(TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim));
+                JsValue result;
+                if (lprim.IsString() || rprim.IsString())
+                {
+                    result = JsString.Create(TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim));
+                }
+                else if (AreNonBigIntOperands(left,right))
+                {
+                    result = JsNumber.Create(TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim));
+                }
+                else
+                {
+                    AssertValidBigIntArithmeticOperands(context, lprim, rprim);
+                    result = JsBigInt.Create(TypeConverter.ToBigInt(lprim) + TypeConverter.ToBigInt(rprim));
+                }
 
 
                 return NormalCompletion(result);
                 return NormalCompletion(result);
             }
             }
@@ -345,9 +309,22 @@ namespace Jint.Runtime.Interpreter.Expressions
                     return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                     return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                 }
                 }
 
 
-                var number = AreIntegerOperands(left, right)
-                    ? JsNumber.Create(left.AsInteger() - right.AsInteger())
-                    : JsNumber.Create(TypeConverter.ToNumber(left) - TypeConverter.ToNumber(right));
+                JsValue number;
+                left = TypeConverter.ToNumeric(left);
+                right = TypeConverter.ToNumeric(right);
+
+                if (AreIntegerOperands(left, right))
+                {
+                    number = JsNumber.Create(left.AsInteger() - right.AsInteger());
+                }
+                else if (AreNonBigIntOperands(left, right))
+                {
+                    number = JsNumber.Create(TypeConverter.ToNumber(left) - TypeConverter.ToNumber(right));
+                }
+                else
+                {
+                    number = JsBigInt.Create(TypeConverter.ToBigInt(left) - TypeConverter.ToBigInt(right));
+                }
 
 
                 return NormalCompletion(number);
                 return NormalCompletion(number);
             }
             }
@@ -364,23 +341,33 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var left = _left.GetValue(context).Value;
                 var left = _left.GetValue(context).Value;
                 var right = _right.GetValue(context).Value;
                 var right = _right.GetValue(context).Value;
 
 
+                JsValue result;
                 if (context.OperatorOverloadingAllowed
                 if (context.OperatorOverloadingAllowed
                     && TryOperatorOverloading(context, left, right, "op_Multiply", out var opResult))
                     && TryOperatorOverloading(context, left, right, "op_Multiply", out var opResult))
                 {
                 {
-                    return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
+                    result = JsValue.FromObject(context.Engine, opResult);
                 }
                 }
-
-                if (AreIntegerOperands(left, right))
+                else if (AreIntegerOperands(left, right))
                 {
                 {
-                    return NormalCompletion(JsNumber.Create((long) left.AsInteger() * right.AsInteger()));
+                    result = JsNumber.Create((long) left.AsInteger() * right.AsInteger());
                 }
                 }
-
-                if (left.IsUndefined() || right.IsUndefined())
+                else
                 {
                 {
-                    return NormalCompletion(Undefined.Instance);
+                    var leftNumeric = TypeConverter.ToNumeric(left);
+                    var rightNumeric = TypeConverter.ToNumeric(right);
+
+                    if (leftNumeric.IsNumber())
+                    {
+                        result = JsNumber.Create(TypeConverter.ToNumber(left) * TypeConverter.ToNumber(right));
+                    }
+                    else
+                    {
+                        AssertValidBigIntArithmeticOperands(context, leftNumeric, rightNumeric);
+                        result = JsBigInt.Create(leftNumeric.AsBigInt() * rightNumeric.AsBigInt());
+                    }
                 }
                 }
 
 
-                return NormalCompletion(JsNumber.Create(TypeConverter.ToNumber(left) * TypeConverter.ToNumber(right)));
+                return NormalCompletion(result);
             }
             }
         }
         }
 
 
@@ -401,7 +388,9 @@ namespace Jint.Runtime.Interpreter.Expressions
                     return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                     return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                 }
                 }
 
 
-                return NormalCompletion(Divide(left, right));
+                left = TypeConverter.ToNumeric(left);
+                right = TypeConverter.ToNumeric(right);
+                return NormalCompletion(Divide(context, left, right));
             }
             }
         }
         }
 
 
@@ -425,7 +414,12 @@ namespace Jint.Runtime.Interpreter.Expressions
                     return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                     return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                 }
                 }
 
 
-                return NormalCompletion(Equal(left, right) == !_invert ? JsBoolean.True : JsBoolean.False);
+                // if types match, we can take faster strict equality
+                var equality = left.Type == right.Type
+                    ? left.Equals(right)
+                    : left.IsLooselyEqual(right);
+
+                return NormalCompletion(equality == !_invert ? JsBoolean.True : JsBoolean.False);
             }
             }
         }
         }
 
 
@@ -479,10 +473,120 @@ namespace Jint.Runtime.Interpreter.Expressions
 
 
             protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
             {
-                var left = _left.GetValue(context).Value;
-                var right = _right.GetValue(context).Value;
+                var leftReference = _left.GetValue(context).Value;
+                var rightReference = _right.GetValue(context).Value;
+
+                var left = TypeConverter.ToNumeric(leftReference);
+                var right = TypeConverter.ToNumeric(rightReference);
+
+                JsValue result;
+                if (AreNonBigIntOperands(left,right))
+                {
+                    // validation
+                    var baseNumber = (JsNumber) left;
+                    var exponentNumber = (JsNumber) right;
+
+                    if (exponentNumber.IsNaN())
+                    {
+                        return NormalCompletion(JsNumber.DoubleNaN);
+                    }
+
+                    if (exponentNumber.IsZero())
+                    {
+                        return NormalCompletion(JsNumber.PositiveOne);
+                    }
+
+                    if (baseNumber.IsNaN())
+                    {
+                        return NormalCompletion(JsNumber.DoubleNaN);
+                    }
+
+                    var exponentValue = exponentNumber._value;
+                    if (baseNumber.IsPositiveInfinity())
+                    {
+                        return NormalCompletion(exponentValue > 0 ? JsNumber.DoublePositiveInfinity : JsNumber.PositiveZero);
+                    }
+
+                    static bool IsOddIntegral(double value) => TypeConverter.IsIntegralNumber(value) && value % 2 != 0;
+
+                    if (baseNumber.IsNegativeInfinity())
+                    {
+                        if (exponentValue > 0)
+                        {
+                            return NormalCompletion(IsOddIntegral(exponentValue) ? JsNumber.DoubleNegativeInfinity : JsNumber.DoublePositiveInfinity);
+                        }
+
+                        return NormalCompletion(IsOddIntegral(exponentValue) ? JsNumber.NegativeZero : JsNumber.PositiveZero);
+                    }
 
 
-                return NormalCompletion(JsNumber.Create(Math.Pow(TypeConverter.ToNumber(left), TypeConverter.ToNumber(right))));
+                    if (baseNumber.IsPositiveZero())
+                    {
+                        return NormalCompletion(exponentValue > 0 ? JsNumber.PositiveZero : JsNumber.DoublePositiveInfinity);
+                    }
+
+                    if (baseNumber.IsNegativeZero())
+                    {
+                        if (exponentValue > 0)
+                        {
+                            return NormalCompletion(IsOddIntegral(exponentValue) ? JsNumber.NegativeZero : JsNumber.PositiveZero);
+                        }
+                        return NormalCompletion(IsOddIntegral(exponentValue) ? JsNumber.DoubleNegativeInfinity : JsNumber.DoublePositiveInfinity);
+                    }
+
+                    var baseValue = baseNumber._value;
+                    if (exponentNumber.IsPositiveInfinity())
+                    {
+                        if (Math.Abs(baseValue) > 1)
+                        {
+                            return NormalCompletion(JsNumber.DoublePositiveInfinity);
+                        }
+                        if (Math.Abs(baseValue) == 1)
+                        {
+                            return NormalCompletion(JsNumber.DoubleNaN);
+                        }
+
+                        return NormalCompletion(JsNumber.PositiveZero);
+                    }
+
+                    if (exponentNumber.IsNegativeInfinity())
+                    {
+                        if (Math.Abs(baseValue) > 1)
+                        {
+                            return NormalCompletion(JsNumber.PositiveZero);
+                        }
+                        if (Math.Abs(baseValue) == 1)
+                        {
+                            return NormalCompletion(JsNumber.DoubleNaN);
+                        }
+
+                        return NormalCompletion(JsNumber.DoublePositiveInfinity);
+                    }
+
+                    if (baseValue < 0 && !TypeConverter.IsIntegralNumber(exponentValue))
+                    {
+                        return NormalCompletion(JsNumber.DoubleNaN);
+                    }
+
+                    result = JsNumber.Create(Math.Pow(baseNumber._value, exponentValue));
+                }
+                else
+                {
+                    AssertValidBigIntArithmeticOperands(context, left, right);
+
+                    var exponent = right.AsBigInt();
+                    if (exponent < 0)
+                    {
+                        ExceptionHelper.ThrowRangeError(context.Engine.Realm, "Exponent must be positive");
+                    }
+
+                    if (exponent > int.MaxValue || exponent < int.MinValue)
+                    {
+                        ExceptionHelper.ThrowTypeError(context.Engine.Realm, "Exponent does not fit 32bit range");
+                    }
+                    result = JsBigInt.Create(BigInteger.Pow(left.AsBigInt(), (int) exponent));
+                }
+
+                return NormalCompletion(result);
             }
             }
         }
         }
 
 
@@ -524,22 +628,80 @@ namespace Jint.Runtime.Interpreter.Expressions
                     return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                     return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                 }
                 }
 
 
+                var result = Undefined.Instance;
+                left = TypeConverter.ToNumeric(left);
+                right = TypeConverter.ToNumeric(right);
+
                 if (AreIntegerOperands(left, right))
                 if (AreIntegerOperands(left, right))
                 {
                 {
                     var leftInteger = left.AsInteger();
                     var leftInteger = left.AsInteger();
                     var rightInteger = right.AsInteger();
                     var rightInteger = right.AsInteger();
-                    if (leftInteger > 0 && rightInteger != 0)
+
+                    if (rightInteger == 0)
                     {
                     {
-                        return NormalCompletion(JsNumber.Create(leftInteger % rightInteger));
+                        result = JsNumber.DoubleNaN;
+                    }
+                    else
+                    {
+                        var modulo = leftInteger % rightInteger;
+                        if (modulo == 0 && leftInteger < 0)
+                        {
+                            result = JsNumber.NegativeZero;
+                        }
+                        else
+                        {
+                            result = JsNumber.Create(modulo);
+                        }
                     }
                     }
                 }
                 }
+                else if (AreNonBigIntOperands(left, right))
+                {
+                    var n = TypeConverter.ToNumber(left);
+                    var d = TypeConverter.ToNumber(right);
 
 
-                if (left.IsUndefined() || right.IsUndefined())
+                    if (double.IsNaN(n) || double.IsNaN(d) || double.IsInfinity(n))
+                    {
+                        result = JsNumber.DoubleNaN;
+                    }
+                    else if (double.IsInfinity(d))
+                    {
+                        result = n;
+                    }
+                    else if (NumberInstance.IsPositiveZero(d) || NumberInstance.IsNegativeZero(d))
+                    {
+                        result = JsNumber.DoubleNaN;
+                    }
+                    else if (NumberInstance.IsPositiveZero(n) || NumberInstance.IsNegativeZero(n))
+                    {
+                        result = n;
+                    }
+                    else
+                    {
+                        result = JsNumber.Create(n % d);
+                    }
+                }
+                else
                 {
                 {
-                    return NormalCompletion(Undefined.Instance);
+                    AssertValidBigIntArithmeticOperands(context, left, right);
+
+                    var n = TypeConverter.ToBigInt(left);
+                    var d = TypeConverter.ToBigInt(right);
+
+                    if (d == 0)
+                    {
+                        ExceptionHelper.ThrowRangeError(context.Engine.Realm, "Division by zero");
+                    }
+                    else if (n == 0)
+                    {
+                        result = JsBigInt.Zero;
+                    }
+                    else
+                    {
+                        result = JsBigInt.Create(n % d);
+                    }
                 }
                 }
 
 
-                return NormalCompletion(JsNumber.Create(TypeConverter.ToNumber(left) % TypeConverter.ToNumber(right)));
+                return NormalCompletion(result);
             }
             }
         }
         }
 
 
@@ -571,21 +733,29 @@ namespace Jint.Runtime.Interpreter.Expressions
 
 
             protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             protected override ExpressionResult EvaluateInternal(EvaluationContext context)
             {
             {
-                var left = _left.GetValue(context).Value;
-                var right = _right.GetValue(context).Value;
+                var lval = _left.GetValue(context).Value;
+                var rval = _right.GetValue(context).Value;
 
 
                 if (context.OperatorOverloadingAllowed
                 if (context.OperatorOverloadingAllowed
-                    && TryOperatorOverloading(context, left, right, OperatorClrName, out var opResult))
+                    && TryOperatorOverloading(context, lval, rval, OperatorClrName, out var opResult))
                 {
                 {
                     return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                     return NormalCompletion(JsValue.FromObject(context.Engine, opResult));
                 }
                 }
 
 
-                if (AreIntegerOperands(left, right))
+                var lnum = TypeConverter.ToNumeric(lval);
+                var rnum = TypeConverter.ToNumeric(rval);
+
+                if (lnum.Type != rnum.Type)
                 {
                 {
-                    int leftValue = left.AsInteger();
-                    int rightValue = right.AsInteger();
+                    ExceptionHelper.ThrowTypeError(context.Engine.Realm);
+                }
 
 
-                    JsValue result;
+                if (AreIntegerOperands(lnum, rnum))
+                {
+                    int leftValue = lnum.AsInteger();
+                    int rightValue = rnum.AsInteger();
+
+                    JsValue result = null;
                     switch (_operator)
                     switch (_operator)
                     {
                     {
                         case BinaryOperator.BitwiseAnd:
                         case BinaryOperator.BitwiseAnd:
@@ -608,40 +778,80 @@ namespace Jint.Runtime.Interpreter.Expressions
                             break;
                             break;
                         default:
                         default:
                             ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(_operator), "unknown shift operator");
                             ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(_operator), "unknown shift operator");
-                            result = null;
                             break;
                             break;
                     }
                     }
 
 
                     return NormalCompletion(result);
                     return NormalCompletion(result);
                 }
                 }
 
 
-                return NormalCompletion(EvaluateNonInteger(left, right));
+                return NormalCompletion(EvaluateNonInteger(context.Engine.Realm, lnum, rnum));
             }
             }
 
 
-            private JsNumber EvaluateNonInteger(JsValue left, JsValue right)
+            private JsValue EvaluateNonInteger(Realm realm, JsValue left, JsValue right)
             {
             {
                 switch (_operator)
                 switch (_operator)
                 {
                 {
                     case BinaryOperator.BitwiseAnd:
                     case BinaryOperator.BitwiseAnd:
-                        return JsNumber.Create(TypeConverter.ToInt32(left) & TypeConverter.ToInt32(right));
+                    {
+                        if (!left.IsBigInt())
+                        {
+                            return JsNumber.Create(TypeConverter.ToInt32(left) & TypeConverter.ToInt32(right));
+                        }
+
+                        return JsBigInt.Create(TypeConverter.ToBigInt(left) & TypeConverter.ToBigInt(right));
+                    }
 
 
                     case BinaryOperator.BitwiseOr:
                     case BinaryOperator.BitwiseOr:
-                        return JsNumber.Create(TypeConverter.ToInt32(left) | TypeConverter.ToInt32(right));
+                    {
+                        if (!left.IsBigInt())
+                        {
+                            return JsNumber.Create(TypeConverter.ToInt32(left) | TypeConverter.ToInt32(right));
+                        }
+                        return JsBigInt.Create(TypeConverter.ToBigInt(left) | TypeConverter.ToBigInt(right));
+                    }
 
 
                     case BinaryOperator.BitwiseXOr:
                     case BinaryOperator.BitwiseXOr:
-                        return JsNumber.Create(TypeConverter.ToInt32(left) ^ TypeConverter.ToInt32(right));
+                    {
+                        if (!left.IsBigInt())
+                        {
+                            return JsNumber.Create(TypeConverter.ToInt32(left) ^ TypeConverter.ToInt32(right));
+                        }
+                        return JsBigInt.Create(TypeConverter.ToBigInt(left) ^ TypeConverter.ToBigInt(right));
+                    }
 
 
                     case BinaryOperator.LeftShift:
                     case BinaryOperator.LeftShift:
-                        return JsNumber.Create(TypeConverter.ToInt32(left) << (int) (TypeConverter.ToUint32(right) & 0x1F));
+                    {
+                        if (!left.IsBigInt())
+                        {
+                            return JsNumber.Create(TypeConverter.ToInt32(left) << (int) (TypeConverter.ToUint32(right) & 0x1F));
+                        }
+                        return JsBigInt.Create(TypeConverter.ToBigInt(left) << (int) TypeConverter.ToBigInt(right));
+                    }
 
 
                     case BinaryOperator.RightShift:
                     case BinaryOperator.RightShift:
-                        return JsNumber.Create(TypeConverter.ToInt32(left) >> (int) (TypeConverter.ToUint32(right) & 0x1F));
+                    {
+                        if (!left.IsBigInt())
+                        {
+                            return JsNumber.Create(TypeConverter.ToInt32(left) >> (int) (TypeConverter.ToUint32(right) & 0x1F));
+                        }
+                        return JsBigInt.Create(TypeConverter.ToBigInt(left) >> (int) TypeConverter.ToBigInt(right));
+                    }
 
 
                     case BinaryOperator.UnsignedRightShift:
                     case BinaryOperator.UnsignedRightShift:
-                        return JsNumber.Create((uint) TypeConverter.ToInt32(left) >> (int) (TypeConverter.ToUint32(right) & 0x1F));
+                    {
+                        if (!left.IsBigInt())
+                        {
+                            return JsNumber.Create((uint) TypeConverter.ToInt32(left) >> (int) (TypeConverter.ToUint32(right) & 0x1F));
+                        }
+                        ExceptionHelper.ThrowTypeError(realm);
+                        return null;
+                    }
+
                     default:
                     default:
+                    {
                         ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(_operator), "unknown shift operator");
                         ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(_operator), "unknown shift operator");
                         return null;
                         return null;
+                    }
                 }
                 }
             }
             }
         }
         }

+ 94 - 59
Jint/Runtime/Interpreter/Expressions/JintExpression.cs

@@ -1,6 +1,7 @@
 #nullable enable
 #nullable enable
 
 
 using System.Diagnostics;
 using System.Diagnostics;
+using System.Numerics;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
 using Esprima;
 using Esprima;
 using Esprima.Ast;
 using Esprima.Ast;
@@ -209,11 +210,32 @@ namespace Jint.Runtime.Interpreter.Expressions
         }
         }
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        protected static JsValue Divide(JsValue lval, JsValue rval)
+        protected static JsValue Divide(EvaluationContext context, JsValue left, JsValue right)
         {
         {
-            return AreIntegerOperands(lval, rval)
-                ? DivideInteger(lval, rval)
-                : DivideComplex(lval, rval);
+            JsValue result;
+            if (AreIntegerOperands(left, right))
+            {
+                result = DivideInteger(left, right);
+            }
+            else if (JintBinaryExpression.AreNonBigIntOperands(left, right))
+            {
+                result = DivideComplex(left, right);
+            }
+            else
+            {
+                JintBinaryExpression.AssertValidBigIntArithmeticOperands(context, left, right);
+                var x = TypeConverter.ToBigInt(left);
+                var y = TypeConverter.ToBigInt(right);
+
+                if (y == 0)
+                {
+                    ExceptionHelper.ThrowRangeError(context.Engine.Realm, "Division by zero");
+                }
+
+                result = JsBigInt.Create(x / y);
+            }
+
+            return result;
         }
         }
 
 
         private static JsValue DivideInteger(JsValue lval, JsValue rval)
         private static JsValue DivideInteger(JsValue lval, JsValue rval)
@@ -290,60 +312,6 @@ namespace Jint.Runtime.Interpreter.Expressions
 
 
         }
         }
 
 
-        protected static bool Equal(JsValue x, JsValue y)
-        {
-            return x.Type == y.Type
-                ? JintBinaryExpression.StrictlyEqual(x, y)
-                : EqualUnlikely(x, y);
-        }
-
-        private static bool EqualUnlikely(JsValue x, JsValue y)
-        {
-            if (x._type == InternalTypes.Null && y._type == InternalTypes.Undefined)
-            {
-                return true;
-            }
-
-            if (x._type == InternalTypes.Undefined && y._type == InternalTypes.Null)
-            {
-                return true;
-            }
-
-            if (x.IsNumber() && y.IsString())
-            {
-                return Equal(x, TypeConverter.ToNumber(y));
-            }
-
-            if (x.IsString() && y.IsNumber())
-            {
-                return Equal(TypeConverter.ToNumber(x), y);
-            }
-
-            if (x.IsBoolean())
-            {
-                return Equal(TypeConverter.ToNumber(x), y);
-            }
-
-            if (y.IsBoolean())
-            {
-                return Equal(x, TypeConverter.ToNumber(y));
-            }
-
-            const InternalTypes stringOrNumber = InternalTypes.String | InternalTypes.Integer | InternalTypes.Number;
-
-            if (y.IsObject() && (x._type & stringOrNumber) != 0)
-            {
-                return Equal(x, TypeConverter.ToPrimitive(y));
-            }
-
-            if (x.IsObject() && ((y._type & stringOrNumber) != 0))
-            {
-                return Equal(TypeConverter.ToPrimitive(x), y);
-            }
-
-            return false;
-        }
-
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         protected static JsValue Compare(JsValue x, JsValue y, bool leftFirst = true) =>
         protected static JsValue Compare(JsValue x, JsValue y, bool leftFirst = true) =>
             x._type == y._type && x._type == InternalTypes.Integer
             x._type == y._type && x._type == InternalTypes.Integer
@@ -386,6 +354,73 @@ namespace Jint.Runtime.Interpreter.Expressions
 
 
             if (typea != Types.String || typeb != Types.String)
             if (typea != Types.String || typeb != Types.String)
             {
             {
+                if (typea == Types.BigInt || typeb == Types.BigInt)
+                {
+                    if (typea == typeb)
+                    {
+                        return TypeConverter.ToBigInt(px) < TypeConverter.ToBigInt(py);
+                    }
+
+                    if (typea == Types.BigInt)
+                    {
+                        if (py is JsString jsStringY)
+                        {
+                            if (!TypeConverter.TryStringToBigInt(jsStringY.ToString(), out var temp))
+                            {
+                                return JsValue.Undefined;
+                            }
+                            return TypeConverter.ToBigInt(px) < temp;
+                        }
+
+                        var numberB = TypeConverter.ToNumber(py);
+                        if (double.IsNaN(numberB))
+                        {
+                            return JsValue.Undefined;
+                        }
+
+                        if (double.IsPositiveInfinity(numberB))
+                        {
+                            return true;
+                        }
+
+                        if (double.IsNegativeInfinity(numberB))
+                        {
+                            return false;
+                        }
+
+                        var normalized = new BigInteger(System.Math.Ceiling(numberB));
+                        return TypeConverter.ToBigInt(px) < normalized;
+                    }
+
+                    if (px is JsString jsStringX)
+                    {
+                        if (!TypeConverter.TryStringToBigInt(jsStringX.ToString(), out var temp))
+                        {
+                            return JsValue.Undefined;
+                        }
+                        return temp < TypeConverter.ToBigInt(py);
+                    }
+
+                    var numberA = TypeConverter.ToNumber(px);
+                    if (double.IsNaN(numberA))
+                    {
+                        return JsValue.Undefined;
+                    }
+
+                    if (double.IsPositiveInfinity(numberA))
+                    {
+                        return false;
+                    }
+
+                    if (double.IsNegativeInfinity(numberA))
+                    {
+                        return true;
+                    }
+
+                    var normalizedA = new BigInteger(System.Math.Floor(numberA));
+                    return normalizedA < TypeConverter.ToBigInt(py);
+                }
+
                 var nx = TypeConverter.ToNumber(px);
                 var nx = TypeConverter.ToNumber(px);
                 var ny = TypeConverter.ToNumber(py);
                 var ny = TypeConverter.ToNumber(py);
 
 
@@ -435,7 +470,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
             }
         }
         }
 
 
-        protected JsValue[] BuildArgumentsWithSpreads(EvaluationContext context, JintExpression[] jintExpressions)
+        protected static JsValue[] BuildArgumentsWithSpreads(EvaluationContext context, JintExpression[] jintExpressions)
         {
         {
             var args = new System.Collections.Generic.List<JsValue>(jintExpressions.Length);
             var args = new System.Collections.Generic.List<JsValue>(jintExpressions.Length);
             for (var i = 0; i < jintExpressions.Length; i++)
             for (var i = 0; i < jintExpressions.Length; i++)

+ 6 - 0
Jint/Runtime/Interpreter/Expressions/JintLiteralExpression.cs

@@ -1,4 +1,5 @@
 using System;
 using System;
+using System.Numerics;
 using Esprima;
 using Esprima;
 using Esprima.Ast;
 using Esprima.Ast;
 using Jint.Native;
 using Jint.Native;
@@ -48,6 +49,11 @@ namespace Jint.Runtime.Interpreter.Expressions
                 return JsString.Create((string) literal.Value);
                 return JsString.Create((string) literal.Value);
             }
             }
 
 
+            if (literal.TokenType == TokenType.BigIntLiteral)
+            {
+                return JsBigInt.Create((BigInteger) literal.Value);
+            }
+
             return null;
             return null;
         }
         }
 
 

+ 22 - 10
Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs

@@ -6,6 +6,7 @@ using Jint.Runtime.Environments;
 using Jint.Runtime.Interop;
 using Jint.Runtime.Interop;
 using Jint.Runtime.References;
 using Jint.Runtime.References;
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
+using System.Numerics;
 using System.Reflection;
 using System.Reflection;
 
 
 namespace Jint.Runtime.Interpreter.Expressions
 namespace Jint.Runtime.Interpreter.Expressions
@@ -67,9 +68,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                         return result;
                         return result;
                     }
                     }
 
 
-                    return v.IsInteger() && v.AsInteger() != 0
-                        ? v
-                        : JsNumber.Create(TypeConverter.ToNumber(v));
+                    return TypeConverter.ToNumber(v);
                 }
                 }
                 case UnaryOperator.Minus:
                 case UnaryOperator.Minus:
                 {
                 {
@@ -91,7 +90,13 @@ namespace Jint.Runtime.Interpreter.Expressions
                         return result;
                         return result;
                     }
                     }
 
 
-                    return JsNumber.Create(~TypeConverter.ToInt32(v));
+                    var value = TypeConverter.ToNumeric(v);
+                    if (value.IsNumber())
+                    {
+                        return JsNumber.Create(~TypeConverter.ToInt32(value));
+                    }
+
+                    return JsBigInt.Create(~value.AsBigInt());
                 }
                 }
                 case UnaryOperator.LogicalNot:
                 case UnaryOperator.LogicalNot:
                 {
                 {
@@ -190,6 +195,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     {
                     {
                         case Types.Boolean: return JsString.BooleanString;
                         case Types.Boolean: return JsString.BooleanString;
                         case Types.Number: return JsString.NumberString;
                         case Types.Number: return JsString.NumberString;
+                        case Types.BigInt: return JsString.BigIntString;
                         case Types.String: return JsString.StringString;
                         case Types.String: return JsString.StringString;
                         case Types.Symbol: return JsString.SymbolString;
                         case Types.Symbol: return JsString.SymbolString;
                     }
                     }
@@ -207,20 +213,26 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
             }
         }
         }
 
 
-        private static JsNumber EvaluateMinus(JsValue value)
+        private static JsValue EvaluateMinus(JsValue value)
         {
         {
-            var minusValue = value;
-            if (minusValue.IsInteger())
+            if (value.IsInteger())
             {
             {
-                var asInteger = minusValue.AsInteger();
+                var asInteger = value.AsInteger();
                 if (asInteger != 0)
                 if (asInteger != 0)
                 {
                 {
                     return JsNumber.Create(asInteger * -1);
                     return JsNumber.Create(asInteger * -1);
                 }
                 }
             }
             }
 
 
-            var n = TypeConverter.ToNumber(minusValue);
-            return JsNumber.Create(double.IsNaN(n) ? double.NaN : n * -1);
+            value = TypeConverter.ToNumeric(value);
+            if (value.IsNumber())
+            {
+                var n = ((JsNumber) value)._value;
+                return double.IsNaN(n) ? JsNumber.DoubleNaN : JsNumber.Create(n * -1);
+            }
+
+            var bigInt = value.AsBigInt();
+            return JsBigInt.Create(BigInteger.Negate(bigInt));
         }
         }
 
 
         internal static bool TryOperatorOverloading(EvaluationContext context, JsValue value, string clrName, out JsValue result)
         internal static bool TryOperatorOverloading(EvaluationContext context, JsValue value, string clrName, out JsValue result)

+ 53 - 12
Jint/Runtime/Interpreter/Expressions/JintUpdateExpression.cs

@@ -78,17 +78,41 @@ namespace Jint.Runtime.Interpreter.Expressions
 
 
             if (!operatorOverloaded)
             if (!operatorOverloaded)
             {
             {
-                newValue = isInteger
-                    ? JsNumber.Create(value.AsInteger() + _change)
-                    : JsNumber.Create(TypeConverter.ToNumber(value) + _change);
+                if (isInteger)
+                {
+                    newValue = JsNumber.Create(value.AsInteger() + _change);
+                }
+                else if (!value.IsBigInt())
+                {
+                    newValue = JsNumber.Create(TypeConverter.ToNumber(value) + _change);
+                }
+                else
+                {
+                    newValue = JsBigInt.Create(TypeConverter.ToBigInt(value) + _change);
+                }
             }
             }
 
 
             engine.PutValue(reference, newValue);
             engine.PutValue(reference, newValue);
             engine._referencePool.Return(reference);
             engine._referencePool.Return(reference);
 
 
-            return _prefix
-                ? newValue
-                : (isInteger || operatorOverloaded ? value : JsNumber.Create(TypeConverter.ToNumber(value)));
+            if (_prefix)
+            {
+                return newValue;
+            }
+            else
+            {
+                if (isInteger || operatorOverloaded)
+                {
+                    return value;
+                }
+
+                if (!value.IsBigInt())
+                {
+                    return JsNumber.Create(TypeConverter.ToNumber(value));
+                }
+
+                return JsBigInt.Create(value);
+            }
         }
         }
 
 
         private JsValue UpdateIdentifier(EvaluationContext context)
         private JsValue UpdateIdentifier(EvaluationContext context)
@@ -126,15 +150,32 @@ namespace Jint.Runtime.Interpreter.Expressions
 
 
                 if (!operatorOverloaded)
                 if (!operatorOverloaded)
                 {
                 {
-                    newValue = isInteger
-                    ? JsNumber.Create(value.AsInteger() + _change)
-                    : JsNumber.Create(TypeConverter.ToNumber(value) + _change);
+                    if (isInteger)
+                    {
+                        newValue = JsNumber.Create(value.AsInteger() + _change);
+                    }
+                    else if (value._type != InternalTypes.BigInt)
+                    {
+                        newValue = JsNumber.Create(TypeConverter.ToNumber(value) + _change);
+                    }
+                    else
+                    {
+                        newValue = JsBigInt.Create(TypeConverter.ToBigInt(value) + _change);
+                    }
                 }
                 }
 
 
                 environmentRecord.SetMutableBinding(name.Key.Name, newValue, strict);
                 environmentRecord.SetMutableBinding(name.Key.Name, newValue, strict);
-                return _prefix
-                    ? newValue
-                    : (isInteger || operatorOverloaded ? value : JsNumber.Create(TypeConverter.ToNumber(value)));
+                if (_prefix)
+                {
+                    return newValue;
+                }
+
+                if (!value.IsBigInt() && !value.IsNumber() && !operatorOverloaded)
+                {
+                    return JsNumber.Create(TypeConverter.ToNumber(value));
+                }
+
+                return value;
             }
             }
 
 
             return null;
             return null;

+ 1 - 1
Jint/Runtime/Interpreter/Statements/JintSwitchBlock.cs

@@ -62,7 +62,7 @@ namespace Jint.Runtime.Interpreter.Statements
                 else
                 else
                 {
                 {
                     var clauseSelector = clause.Test.GetValue(context).Value;
                     var clauseSelector = clause.Test.GetValue(context).Value;
-                    if (JintBinaryExpression.StrictlyEqual(clauseSelector, input))
+                    if (clauseSelector == input)
                     {
                     {
                         hit = true;
                         hit = true;
                     }
                     }

+ 5 - 0
Jint/Runtime/Intrinsics.cs

@@ -1,6 +1,7 @@
 using Jint.Native;
 using Jint.Native;
 using Jint.Native.Array;
 using Jint.Native.Array;
 using Jint.Native.ArrayBuffer;
 using Jint.Native.ArrayBuffer;
+using Jint.Native.BigInt;
 using Jint.Native.Boolean;
 using Jint.Native.Boolean;
 using Jint.Native.DataView;
 using Jint.Native.DataView;
 using Jint.Native.Date;
 using Jint.Native.Date;
@@ -60,6 +61,7 @@ namespace Jint.Runtime
         private RegExpConstructor _regExp;
         private RegExpConstructor _regExp;
         private RegExpStringIteratorPrototype _regExpStringIteratorPrototype;
         private RegExpStringIteratorPrototype _regExpStringIteratorPrototype;
         private NumberConstructor _number;
         private NumberConstructor _number;
+        private BigIntConstructor _bigInt;
         private StringConstructor _string;
         private StringConstructor _string;
         private StringIteratorPrototype _stringIteratorPrototype;
         private StringIteratorPrototype _stringIteratorPrototype;
         private MapConstructor _map;
         private MapConstructor _map;
@@ -194,6 +196,9 @@ namespace Jint.Runtime
         public NumberConstructor Number =>
         public NumberConstructor Number =>
             _number ??= new NumberConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject);
             _number ??= new NumberConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject);
 
 
+        public BigIntConstructor BigInt =>
+            _bigInt ??= new BigIntConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject);
+
         public DateConstructor Date =>
         public DateConstructor Date =>
             _date ??= new DateConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject);
             _date ??= new DateConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject);
 
 

+ 2 - 2
Jint/Runtime/OrderedSet.cs

@@ -7,10 +7,10 @@ namespace Jint.Runtime
         internal readonly List<T> _list;
         internal readonly List<T> _list;
         private readonly HashSet<T> _set;
         private readonly HashSet<T> _set;
 
 
-        public OrderedSet()
+        public OrderedSet(IEqualityComparer<T> comparer)
         {
         {
             _list = new List<T>();
             _list = new List<T>();
-            _set = new HashSet<T>();
+            _set = new HashSet<T>(comparer);
         }
         }
 
 
         public T this[int index]
         public T this[int index]

+ 216 - 25
Jint/Runtime/TypeConverter.cs

@@ -1,7 +1,9 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
+using System.Numerics;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
+using Esprima;
 using Esprima.Ast;
 using Esprima.Ast;
 using Jint.Extensions;
 using Jint.Extensions;
 using Jint.Native;
 using Jint.Native;
@@ -25,7 +27,8 @@ namespace Jint.Runtime
         String = 8,
         String = 8,
         Number = 16,
         Number = 16,
         Symbol = 64,
         Symbol = 64,
-        Object = 128
+        BigInt = 128,
+        Object = 256
     }
     }
 
 
     [Flags]
     [Flags]
@@ -43,15 +46,16 @@ namespace Jint.Runtime
         Number = 16,
         Number = 16,
         Integer = 32,
         Integer = 32,
         Symbol = 64,
         Symbol = 64,
+        BigInt = 128,
 
 
         // primitive  types range end
         // primitive  types range end
-        Object = 128,
+        Object = 256,
 
 
         // internal usage
         // internal usage
         ObjectEnvironmentRecord = 512,
         ObjectEnvironmentRecord = 512,
         RequiresCloning = 1024,
         RequiresCloning = 1024,
 
 
-        Primitive = Boolean | String | Number | Integer | Symbol,
+        Primitive = Boolean | String | Number | Integer | BigInt | Symbol,
         InternalFlags = ObjectEnvironmentRecord | RequiresCloning
         InternalFlags = ObjectEnvironmentRecord | RequiresCloning
     }
     }
 
 
@@ -90,16 +94,16 @@ namespace Jint.Runtime
 
 
         private static JsValue ToPrimitiveObjectInstance(ObjectInstance oi, Types preferredType)
         private static JsValue ToPrimitiveObjectInstance(ObjectInstance oi, Types preferredType)
         {
         {
-            var hint = preferredType switch
-            {
-                Types.String => JsString.StringString,
-                Types.Number => JsString.NumberString,
-                _ => JsString.DefaultString
-            };
-
             var exoticToPrim = oi.GetMethod(GlobalSymbolRegistry.ToPrimitive);
             var exoticToPrim = oi.GetMethod(GlobalSymbolRegistry.ToPrimitive);
-            if (exoticToPrim is object)
+            if (exoticToPrim is not null)
             {
             {
+                var hint = preferredType switch
+                {
+                    Types.String => JsString.StringString,
+                    Types.Number => JsString.NumberString,
+                    _ => JsString.DefaultString
+                };
+
                 var str = exoticToPrim.Call(oi, new JsValue[] { hint });
                 var str = exoticToPrim.Call(oi, new JsValue[] { hint });
                 if (str.IsPrimitive())
                 if (str.IsPrimitive())
                 {
                 {
@@ -181,11 +185,32 @@ namespace Jint.Runtime
                     return n != 0 && !double.IsNaN(n);
                     return n != 0 && !double.IsNaN(n);
                 case InternalTypes.String:
                 case InternalTypes.String:
                     return !((JsString) o).IsNullOrEmpty();
                     return !((JsString) o).IsNullOrEmpty();
+                case InternalTypes.BigInt:
+                    return ((JsBigInt) o)._value != 0;
                 default:
                 default:
                     return true;
                     return true;
             }
             }
         }
         }
 
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-tonumeric
+        /// </summary>
+        public static JsValue ToNumeric(JsValue value)
+        {
+            if (value.IsNumber() || value.IsBigInt())
+            {
+                return value;
+            }
+
+            var primValue = ToPrimitive(value, Types.Number);
+            if (primValue.IsBigInt())
+            {
+                return primValue;
+            }
+
+            return ToNumber(primValue);
+        }
+
         /// <summary>
         /// <summary>
         /// https://tc39.es/ecma262/#sec-tonumber
         /// https://tc39.es/ecma262/#sec-tonumber
         /// </summary>
         /// </summary>
@@ -207,15 +232,14 @@ namespace Jint.Runtime
                     return double.NaN;
                     return double.NaN;
                 case InternalTypes.Null:
                 case InternalTypes.Null:
                     return 0;
                     return 0;
-                case InternalTypes.Object when o is IPrimitiveInstance p:
-                    return ToNumber(ToPrimitive(p.PrimitiveValue, Types.Number));
                 case InternalTypes.Boolean:
                 case InternalTypes.Boolean:
                     return ((JsBoolean) o)._value ? 1 : 0;
                     return ((JsBoolean) o)._value ? 1 : 0;
                 case InternalTypes.String:
                 case InternalTypes.String:
                     return ToNumber(o.ToString());
                     return ToNumber(o.ToString());
                 case InternalTypes.Symbol:
                 case InternalTypes.Symbol:
+                case InternalTypes.BigInt:
                     // TODO proper TypeError would require Engine instance and a lot of API changes
                     // TODO proper TypeError would require Engine instance and a lot of API changes
-                    ExceptionHelper.ThrowTypeErrorNoEngine("Cannot convert a Symbol value to a number");
+                    ExceptionHelper.ThrowTypeErrorNoEngine("Cannot convert a " + type + " value to a number");
                     return 0;
                     return 0;
                 default:
                 default:
                     return ToNumber(ToPrimitive(o, Types.Number));
                     return ToNumber(ToPrimitive(o, Types.Number));
@@ -546,30 +570,178 @@ namespace Jint.Runtime
         /// <summary>
         /// <summary>
         /// https://tc39.es/ecma262/#sec-tobigint
         /// https://tc39.es/ecma262/#sec-tobigint
         /// </summary>
         /// </summary>
-        public static double ToBigInt(JsValue value)
+        public static BigInteger ToBigInt(JsValue value)
+        {
+            return value is JsBigInt bigInt
+                ? bigInt._value
+                : ToBigIntUnlikely(value);
+        }
+
+        private static BigInteger ToBigIntUnlikely(JsValue value)
+        {
+            var prim = ToPrimitive(value, Types.Number);
+            switch (prim.Type)
+            {
+                case Types.BigInt:
+                    return ((JsBigInt) prim)._value;
+                case Types.Boolean:
+                    return ((JsBoolean) prim)._value ? BigInteger.One : BigInteger.Zero;
+                case Types.String:
+                    return StringToBigInt(prim.ToString());
+                default:
+                    ExceptionHelper.ThrowTypeErrorNoEngine("Cannot convert a " + prim.Type + " to a BigInt");
+                    return BigInteger.One;
+            }
+        }
+
+        internal static BigInteger StringToBigInt(string str)
+        {
+            if (!TryStringToBigInt(str, out var result))
+            {
+                throw new ParserException(" Cannot convert " + str + " to a BigInt");
+            }
+
+            return result;
+        }
+
+        internal static bool TryStringToBigInt(string str, out BigInteger result)
         {
         {
-            ExceptionHelper.ThrowNotImplementedException("BigInt not implemented");
-            return 0;
+            if (string.IsNullOrWhiteSpace(str))
+            {
+                result = BigInteger.Zero;
+                return true;
+            }
+
+            str = str.Trim();
+
+            for (var i = 0; i < str.Length; i++)
+            {
+                var c = str[i];
+                if (!char.IsDigit(c))
+                {
+                    if (i == 0 && (c == '-' || Character.IsDecimalDigit(c)))
+                    {
+                        // ok
+                        continue;
+                    }
+
+                    if (i != 1 && Character.IsHexDigit(c))
+                    {
+                        // ok
+                        continue;
+                    }
+
+                    if (i == 1 && (Character.IsDecimalDigit(c) || c is 'x' or 'X' or 'b' or 'B' or 'o' or 'O'))
+                    {
+                        // allowed, can be probably parsed
+                        continue;
+                    }
+
+                    result = default;
+                    return false;
+                }
+            }
+
+            // check if we can get by using plain parsing
+            if (BigInteger.TryParse(str, NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
+            {
+                return true;
+            }
+
+            if (str.Length > 2)
+            {
+                if (str.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
+                {
+                    // we get better precision if we don't hit floating point parsing that is performed by Esprima
+#if NETSTANDARD2_1_OR_GREATER
+                    var source = str.AsSpan(2);
+#else
+                    var source = str.Substring(2);
+#endif
+
+                    var c = source[0];
+                    if (c > 7 && Character.IsHexDigit(c))
+                    {
+                        // ensure we get positive number
+                        source = "0" + source.ToString();
+                    }
+
+                    if (BigInteger.TryParse(source, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out result))
+                    {
+                        return true;
+                    }
+                }
+                else if (str.StartsWith("0o", StringComparison.OrdinalIgnoreCase) && Character.IsOctalDigit(str[2]))
+                {
+                    // try parse large octal
+                    var bigInteger = new BigInteger();
+                    for (var i = 2; i < str.Length; i++)
+                    {
+                        var c = str[i];
+                        if (!Character.IsHexDigit(c))
+                        {
+                            return false;
+                        }
+                        bigInteger = bigInteger * 8 + c - '0';
+                    }
+
+                    result = bigInteger;
+                    return true;
+                }
+                else if (str.StartsWith("0b", StringComparison.OrdinalIgnoreCase) && Character.IsDecimalDigit(str[2]))
+                {
+                    // try parse large binary
+                    var bigInteger = new BigInteger();
+                    for (var i = 2; i < str.Length; i++)
+                    {
+                        var c = str[i];
+
+                        if (c != '0' && c != '1')
+                        {
+                            // not good
+                            return false;
+                        }
+
+                        bigInteger <<= 1;
+                        bigInteger += c == '1' ? 1 : 0;
+                    }
+
+                    result = bigInteger;
+                    return true;
+                }
+            }
+
+            return false;
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// https://tc39.es/ecma262/#sec-tobigint64
         /// https://tc39.es/ecma262/#sec-tobigint64
         /// </summary>
         /// </summary>
-        internal static double ToBigInt64(JsValue value)
+        internal static long ToBigInt64(BigInteger value)
         {
         {
-            ExceptionHelper.ThrowNotImplementedException("BigInt not implemented");
-            return 0;
+            var int64bit = BigIntegerModulo(value, BigInteger.Pow(2, 64));
+            if (int64bit >= BigInteger.Pow(2, 63))
+            {
+                return (long) (int64bit - BigInteger.Pow(2, 64));
+            }
+            return (long) int64bit;
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// https://tc39.es/ecma262/#sec-tobiguint64
         /// https://tc39.es/ecma262/#sec-tobiguint64
         /// </summary>
         /// </summary>
-        internal static double ToBigUint64(JsValue value)
+        internal static ulong ToBigUint64(BigInteger value)
         {
         {
-            ExceptionHelper.ThrowNotImplementedException("BigInt not implemented");
-            return 0;
+            return (ulong) BigIntegerModulo(value, BigInteger.Pow(2, 64));
         }
         }
 
 
+        /// <summary>
+        /// Implements the JS spec modulo operation as expected.
+        /// </summary>
+        internal static BigInteger BigIntegerModulo(BigInteger a, BigInteger n)
+        {
+            return (a %= n) < 0 && n > 0 || a > 0 && n < 0 ? a + n : a;
+        }
 
 
         /// <summary>
         /// <summary>
         /// https://tc39.es/ecma262/#sec-canonicalnumericindexstring
         /// https://tc39.es/ecma262/#sec-canonicalnumericindexstring
@@ -679,6 +851,12 @@ namespace Jint.Runtime
             return NumberPrototype.NumberToString(d, new DtoaBuilder(17), stringBuilder.Builder);
             return NumberPrototype.NumberToString(d, new DtoaBuilder(17), stringBuilder.Builder);
         }
         }
 
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static string ToString(BigInteger bigInteger)
+        {
+            return bigInteger.ToString();
+        }
+
         /// <summary>
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/6.0/#sec-topropertykey
         /// http://www.ecma-international.org/ecma-262/6.0/#sec-topropertykey
         /// </summary>
         /// </summary>
@@ -732,6 +910,8 @@ namespace Jint.Runtime
                     return ToString((int) ((JsNumber) o)._value);
                     return ToString((int) ((JsNumber) o)._value);
                 case InternalTypes.Number:
                 case InternalTypes.Number:
                     return ToString(((JsNumber) o)._value);
                     return ToString(((JsNumber) o)._value);
+                case InternalTypes.BigInt:
+                    return ToString(((JsBigInt) o)._value);
                 case InternalTypes.Symbol:
                 case InternalTypes.Symbol:
                     ExceptionHelper.ThrowTypeErrorNoEngine("Cannot convert a Symbol value to a string");
                     ExceptionHelper.ThrowTypeErrorNoEngine("Cannot convert a Symbol value to a string");
                     return null;
                     return null;
@@ -739,8 +919,6 @@ namespace Jint.Runtime
                     return Undefined.Text;
                     return Undefined.Text;
                 case InternalTypes.Null:
                 case InternalTypes.Null:
                     return Null.Text;
                     return Null.Text;
-                case InternalTypes.Object when o is IPrimitiveInstance p:
-                    return ToString(ToPrimitive(p.PrimitiveValue, Types.String));
                 case InternalTypes.Object when o is IObjectWrapper p:
                 case InternalTypes.Object when o is IObjectWrapper p:
                     return p.Target?.ToString();
                     return p.Target?.ToString();
                 default:
                 default:
@@ -759,6 +937,17 @@ namespace Jint.Runtime
             return ToObjectNonObject(realm, value);
             return ToObjectNonObject(realm, value);
         }
         }
 
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-isintegralnumber
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static bool IsIntegralNumber(double value)
+        {
+            return !double.IsNaN(value)
+                   && !double.IsInfinity(value)
+                   && Math.Floor(Math.Abs(value)) == Math.Abs(value);
+        }
+
         private static ObjectInstance ToObjectNonObject(Realm realm, JsValue value)
         private static ObjectInstance ToObjectNonObject(Realm realm, JsValue value)
         {
         {
             var type = value._type & ~InternalTypes.InternalFlags;
             var type = value._type & ~InternalTypes.InternalFlags;
@@ -769,6 +958,8 @@ namespace Jint.Runtime
                 case InternalTypes.Number:
                 case InternalTypes.Number:
                 case InternalTypes.Integer:
                 case InternalTypes.Integer:
                     return realm.Intrinsics.Number.Construct((JsNumber) value);
                     return realm.Intrinsics.Number.Construct((JsNumber) value);
+                case InternalTypes.BigInt:
+                    return realm.Intrinsics.BigInt.Construct((JsBigInt) value);
                 case InternalTypes.String:
                 case InternalTypes.String:
                     return realm.Intrinsics.String.Construct(value.ToString());
                     return realm.Intrinsics.String.Construct(value.ToString());
                 case InternalTypes.Symbol:
                 case InternalTypes.Symbol:

+ 1 - 1
README.md

@@ -70,7 +70,7 @@ The entire execution engine was rebuild with performance in mind, in many cases
 
 
 #### ECMAScript 2020
 #### ECMAScript 2020
 
 
--  BigInt
+-  BigInt
 - ✔ `globalThis` object
 - ✔ `globalThis` object
 - ✔ Nullish coalescing operator (`??`)
 - ✔ Nullish coalescing operator (`??`)