Title: [261199] trunk
Revision
261199
Author
ysuz...@apple.com
Date
2020-05-05 14:15:07 -0700 (Tue, 05 May 2020)

Log Message

[JSC] Implement BigInt.asIntN and BigInt.asUintN
https://bugs.webkit.org/show_bug.cgi?id=181144

Reviewed by Darin Adler.

JSTests:

* stress/bigint-asintn.js: Added.
(shouldBe):
(shouldThrow):
(BigInt.asIntN):
* stress/bigint-asuintn.js: Added.
(shouldBe):
(shouldThrow):
(BigInt.asUintN):
* test262/expectations.yaml:

Source/_javascript_Core:

This patch implements BigInt.asIntN[1] and BigInt.asUintN[2] features.
As the same to the other BigInt runtime C++ code, we port V8 code to JSC to implement both.

BigInt.asIntN is `static_cast<intN_t>(BigInt value)` and BigInt.asUintN is `static_cast<uintN_t>(BigInt value)`.
They are getting slice of N bits from two's complement representation of the given BigInt. The difference between
asIntN and asUintN is asIntN renders MSB as a sign.

This patch is once rolled out due to ARM64_32 build failure, which is caused by the existing bug[3]. Relanding it
since it is now fixed.

[1]: https://tc39.es/ecma262/#sec-bigint.asintn
[2]: https://tc39.es/ecma262/#sec-bigint.asuintn
[3]: https://trac.webkit.org/changeset/261174/webkit

* runtime/BigIntConstructor.cpp:
(JSC::toBigInt):
(JSC::bigIntConstructorFuncAsUintN):
(JSC::bigIntConstructorFuncAsIntN):
* runtime/JSBigInt.cpp:
(JSC::zeroImpl):
(JSC::JSBigInt::divideImpl):
(JSC::JSBigInt::unaryMinusImpl):
(JSC::JSBigInt::remainderImpl):
(JSC::JSBigInt::digitDiv):
(JSC::JSBigInt::absoluteSub):
(JSC::JSBigInt::asIntNImpl):
(JSC::JSBigInt::asUintNImpl):
(JSC::JSBigInt::truncateToNBits):
(JSC::JSBigInt::truncateAndSubFromPowerOfTwo):
(JSC::JSBigInt::asIntN):
(JSC::JSBigInt::asUintN):
* runtime/JSBigInt.h:

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (261198 => 261199)


--- trunk/JSTests/ChangeLog	2020-05-05 21:01:27 UTC (rev 261198)
+++ trunk/JSTests/ChangeLog	2020-05-05 21:15:07 UTC (rev 261199)
@@ -1,3 +1,20 @@
+2020-05-05  Yusuke Suzuki  <ysuz...@apple.com>
+
+        [JSC] Implement BigInt.asIntN and BigInt.asUintN
+        https://bugs.webkit.org/show_bug.cgi?id=181144
+
+        Reviewed by Darin Adler.
+
+        * stress/bigint-asintn.js: Added.
+        (shouldBe):
+        (shouldThrow):
+        (BigInt.asIntN):
+        * stress/bigint-asuintn.js: Added.
+        (shouldBe):
+        (shouldThrow):
+        (BigInt.asUintN):
+        * test262/expectations.yaml:
+
 2020-05-05  Ross Kirsling  <ross.kirsl...@sony.com>
 
         [Intl] Alphabetize extension keys and correctly mark const methods

Added: trunk/JSTests/stress/bigint-asintn.js (0 => 261199)


--- trunk/JSTests/stress/bigint-asintn.js	                        (rev 0)
+++ trunk/JSTests/stress/bigint-asintn.js	2020-05-05 21:15:07 UTC (rev 261199)
@@ -0,0 +1,107 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+shouldThrow(() => {
+    BigInt.asIntN(-1, 0n)
+}, `RangeError: number of bits cannot be negative`);
+
+{
+    let toIndex = false;
+    let toBigInt = false;
+    let index = {
+        [Symbol.toPrimitive]() {
+            shouldBe(toIndex, false);
+            shouldBe(toBigInt, false);
+            toIndex = true;
+            return 32;
+        }
+    };
+    let bigint = {
+        [Symbol.toPrimitive]() {
+            shouldBe(toIndex, true);
+            shouldBe(toBigInt, false);
+            toBigInt = true;
+            return 10n;
+        }
+    }
+    shouldBe(BigInt.asIntN(index, bigint), 10n);
+    shouldBe(toIndex, true);
+    shouldBe(toBigInt, true);
+}
+
+shouldBe(BigInt.asIntN(-0, 0n), 0n);
+
+shouldBe(BigInt.asIntN(0, 0n), 0n);
+shouldBe(BigInt.asIntN(1, 0n), 0n);
+shouldBe(BigInt.asIntN(2, 0n), 0n);
+shouldBe(BigInt.asIntN(3, 0n), 0n);
+
+shouldBe(BigInt.asIntN(0, 1n), 0n);
+shouldBe(BigInt.asIntN(1, 1n), -1n);
+shouldBe(BigInt.asIntN(2, 1n), 1n);
+shouldBe(BigInt.asIntN(3, 1n), 1n);
+
+shouldBe(BigInt.asIntN(30, 0x7fffffffn), -1n);
+shouldBe(BigInt.asIntN(31, 0x7fffffffn), -1n);
+shouldBe(BigInt.asIntN(32, 0x7fffffffn), 2147483647n);
+shouldBe(BigInt.asIntN(33, 0x7fffffffn), 2147483647n);
+shouldBe(BigInt.asIntN(34, 0x7fffffffn), 2147483647n);
+
+shouldBe(BigInt.asIntN(30, 0x80000000n), 0n);
+shouldBe(BigInt.asIntN(31, 0x80000000n), 0n);
+shouldBe(BigInt.asIntN(32, 0x80000000n), -2147483648n);
+shouldBe(BigInt.asIntN(33, 0x80000000n), 2147483648n);
+shouldBe(BigInt.asIntN(34, 0x80000000n), 2147483648n);
+
+shouldBe(BigInt.asIntN(30, -0x80000000n), 0n);
+shouldBe(BigInt.asIntN(31, -0x80000000n), 0n);
+shouldBe(BigInt.asIntN(32, -0x80000000n), -2147483648n);
+shouldBe(BigInt.asIntN(33, -0x80000000n), -2147483648n);
+shouldBe(BigInt.asIntN(34, -0x80000000n), -2147483648n);
+
+shouldBe(BigInt.asIntN(30, -0x80000001n), -1n);
+shouldBe(BigInt.asIntN(31, -0x80000001n), -1n);
+shouldBe(BigInt.asIntN(32, -0x80000001n), 2147483647n);
+shouldBe(BigInt.asIntN(33, -0x80000001n), -2147483649n);
+shouldBe(BigInt.asIntN(34, -0x80000001n), -2147483649n);
+
+shouldBe(BigInt.asIntN(62, 0x7fffffffffffffffn), -1n);
+shouldBe(BigInt.asIntN(63, 0x7fffffffffffffffn), -1n);
+shouldBe(BigInt.asIntN(64, 0x7fffffffffffffffn), 9223372036854775807n);
+shouldBe(BigInt.asIntN(65, 0x7fffffffffffffffn), 9223372036854775807n);
+shouldBe(BigInt.asIntN(66, 0x7fffffffffffffffn), 9223372036854775807n);
+
+shouldBe(BigInt.asIntN(62, 0x8000000000000000n), 0n);
+shouldBe(BigInt.asIntN(63, 0x8000000000000000n), 0n);
+shouldBe(BigInt.asIntN(64, 0x8000000000000000n), -9223372036854775808n);
+shouldBe(BigInt.asIntN(65, 0x8000000000000000n), 9223372036854775808n);
+shouldBe(BigInt.asIntN(66, 0x8000000000000000n), 9223372036854775808n);
+
+shouldBe(BigInt.asIntN(62, -0x800000000000000n), -576460752303423488n);
+shouldBe(BigInt.asIntN(63, -0x800000000000000n), -576460752303423488n);
+shouldBe(BigInt.asIntN(64, -0x800000000000000n), -576460752303423488n);
+shouldBe(BigInt.asIntN(65, -0x800000000000000n), -576460752303423488n);
+shouldBe(BigInt.asIntN(66, -0x800000000000000n), -576460752303423488n);
+
+shouldBe(BigInt.asIntN(62, -0x800000000000001n), -576460752303423489n);
+shouldBe(BigInt.asIntN(63, -0x800000000000001n), -576460752303423489n);
+shouldBe(BigInt.asIntN(64, -0x800000000000001n), -576460752303423489n);
+shouldBe(BigInt.asIntN(65, -0x800000000000001n), -576460752303423489n);
+shouldBe(BigInt.asIntN(66, -0x800000000000001n), -576460752303423489n);

Added: trunk/JSTests/stress/bigint-asuintn.js (0 => 261199)


--- trunk/JSTests/stress/bigint-asuintn.js	                        (rev 0)
+++ trunk/JSTests/stress/bigint-asuintn.js	2020-05-05 21:15:07 UTC (rev 261199)
@@ -0,0 +1,131 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+shouldThrow(() => {
+    BigInt.asUintN(-1, 0n)
+}, `RangeError: number of bits cannot be negative`);
+
+{
+    let toIndex = false;
+    let toBigInt = false;
+    let index = {
+        [Symbol.toPrimitive]() {
+            shouldBe(toIndex, false);
+            shouldBe(toBigInt, false);
+            toIndex = true;
+            return 32;
+        }
+    };
+    let bigint = {
+        [Symbol.toPrimitive]() {
+            shouldBe(toIndex, true);
+            shouldBe(toBigInt, false);
+            toBigInt = true;
+            return 10n;
+        }
+    }
+    shouldBe(BigInt.asUintN(index, bigint), 10n);
+    shouldBe(toIndex, true);
+    shouldBe(toBigInt, true);
+}
+
+shouldBe(BigInt.asUintN(-0, 0n), 0n);
+shouldBe(BigInt.asUintN(0, 0n), 0n);
+shouldBe(BigInt.asUintN(1, 0n), 0n);
+shouldBe(BigInt.asUintN(2, 0n), 0n);
+shouldBe(BigInt.asUintN(3, 0n), 0n);
+
+shouldBe(BigInt.asUintN(0, 1n), 0n);
+shouldBe(BigInt.asUintN(1, 1n), 1n);
+shouldBe(BigInt.asUintN(2, 1n), 1n);
+shouldBe(BigInt.asUintN(3, 1n), 1n);
+
+shouldBe(BigInt.asUintN(30, 0x7fffffffn), 1073741823n);
+shouldBe(BigInt.asUintN(31, 0x7fffffffn), 2147483647n);
+shouldBe(BigInt.asUintN(32, 0x7fffffffn), 2147483647n);
+shouldBe(BigInt.asUintN(33, 0x7fffffffn), 2147483647n);
+shouldBe(BigInt.asUintN(34, 0x7fffffffn), 2147483647n);
+
+shouldBe(BigInt.asUintN(30, 0x80000000n), 0n);
+shouldBe(BigInt.asUintN(31, 0x80000000n), 0n);
+shouldBe(BigInt.asUintN(32, 0x80000000n), 2147483648n);
+shouldBe(BigInt.asUintN(33, 0x80000000n), 2147483648n);
+shouldBe(BigInt.asUintN(34, 0x80000000n), 2147483648n);
+
+shouldBe(BigInt.asUintN(30, 0xffffffffn), 1073741823n);
+shouldBe(BigInt.asUintN(31, 0xffffffffn), 2147483647n);
+shouldBe(BigInt.asUintN(32, 0xffffffffn), 4294967295n);
+shouldBe(BigInt.asUintN(33, 0xffffffffn), 4294967295n);
+shouldBe(BigInt.asUintN(34, 0xffffffffn), 4294967295n);
+
+shouldBe(BigInt.asUintN(30, -0x80000000n), 0n);
+shouldBe(BigInt.asUintN(31, -0x80000000n), 0n);
+shouldBe(BigInt.asUintN(32, -0x80000000n), 2147483648n);
+shouldBe(BigInt.asUintN(33, -0x80000000n), 6442450944n);
+shouldBe(BigInt.asUintN(34, -0x80000000n), 15032385536n);
+
+shouldBe(BigInt.asUintN(30, -0x80000001n), 1073741823n);
+shouldBe(BigInt.asUintN(31, -0x80000001n), 2147483647n);
+shouldBe(BigInt.asUintN(32, -0x80000001n), 2147483647n);
+shouldBe(BigInt.asUintN(33, -0x80000001n), 6442450943n);
+shouldBe(BigInt.asUintN(34, -0x80000001n), 15032385535n);
+
+shouldBe(BigInt.asUintN(30, -0xffffffffn), 1n);
+shouldBe(BigInt.asUintN(31, -0xffffffffn), 1n);
+shouldBe(BigInt.asUintN(32, -0xffffffffn), 1n);
+shouldBe(BigInt.asUintN(33, -0xffffffffn), 4294967297n);
+shouldBe(BigInt.asUintN(34, -0xffffffffn), 12884901889n);
+
+shouldBe(BigInt.asUintN(62, 0x7fffffffffffffffn), 4611686018427387903n);
+shouldBe(BigInt.asUintN(63, 0x7fffffffffffffffn), 9223372036854775807n);
+shouldBe(BigInt.asUintN(64, 0x7fffffffffffffffn), 9223372036854775807n);
+shouldBe(BigInt.asUintN(65, 0x7fffffffffffffffn), 9223372036854775807n);
+shouldBe(BigInt.asUintN(66, 0x7fffffffffffffffn), 9223372036854775807n);
+
+shouldBe(BigInt.asUintN(62, 0x8000000000000000n), 0n);
+shouldBe(BigInt.asUintN(63, 0x8000000000000000n), 0n);
+shouldBe(BigInt.asUintN(64, 0x8000000000000000n), 9223372036854775808n);
+shouldBe(BigInt.asUintN(65, 0x8000000000000000n), 9223372036854775808n);
+shouldBe(BigInt.asUintN(66, 0x8000000000000000n), 9223372036854775808n);
+
+shouldBe(BigInt.asUintN(62, 0xffffffffffffffffn), 4611686018427387903n);
+shouldBe(BigInt.asUintN(63, 0xffffffffffffffffn), 9223372036854775807n);
+shouldBe(BigInt.asUintN(64, 0xffffffffffffffffn), 18446744073709551615n);
+shouldBe(BigInt.asUintN(65, 0xffffffffffffffffn), 18446744073709551615n);
+shouldBe(BigInt.asUintN(66, 0xffffffffffffffffn), 18446744073709551615n);
+
+shouldBe(BigInt.asUintN(62, -0x800000000000000n), 4035225266123964416n);
+shouldBe(BigInt.asUintN(63, -0x800000000000000n), 8646911284551352320n);
+shouldBe(BigInt.asUintN(64, -0x800000000000000n), 17870283321406128128n);
+shouldBe(BigInt.asUintN(65, -0x800000000000000n), 36317027395115679744n);
+shouldBe(BigInt.asUintN(66, -0x800000000000000n), 73210515542534782976n);
+
+shouldBe(BigInt.asUintN(62, -0x800000000000001n), 4035225266123964415n);
+shouldBe(BigInt.asUintN(63, -0x800000000000001n), 8646911284551352319n);
+shouldBe(BigInt.asUintN(64, -0x800000000000001n), 17870283321406128127n);
+shouldBe(BigInt.asUintN(65, -0x800000000000001n), 36317027395115679743n);
+shouldBe(BigInt.asUintN(66, -0x800000000000001n), 73210515542534782975n);
+
+shouldBe(BigInt.asUintN(62, -0xffffffffffffffffn), 1n);
+shouldBe(BigInt.asUintN(63, -0xffffffffffffffffn), 1n);
+shouldBe(BigInt.asUintN(64, -0xffffffffffffffffn), 1n);
+shouldBe(BigInt.asUintN(65, -0xffffffffffffffffn), 18446744073709551617n);
+shouldBe(BigInt.asUintN(66, -0xffffffffffffffffn), 55340232221128654849n);
+shouldBe(BigInt.asUintN(67, -0xffffffffffffffffn), 129127208515966861313n);

Modified: trunk/JSTests/test262/expectations.yaml (261198 => 261199)


--- trunk/JSTests/test262/expectations.yaml	2020-05-05 21:01:27 UTC (rev 261198)
+++ trunk/JSTests/test262/expectations.yaml	2020-05-05 21:15:07 UTC (rev 261199)
@@ -678,66 +678,6 @@
 test/built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-promise.js:
   default: 'Test262:AsyncTestFailure:Test262Error: Test262Error: AsyncGeneratorResolve(generator, resultValue, true) Expected SameValue(«[object Promise]», «unwrapped-value») to be true'
   strict mode: 'Test262:AsyncTestFailure:Test262Error: Test262Error: AsyncGeneratorResolve(generator, resultValue, true) Expected SameValue(«[object Promise]», «unwrapped-value») to be true'
-test/built-ins/BigInt/asIntN/arithmetic.js:
-  default: 'Test262Error: Expected SameValue(«undefined», «0») to be true'
-  strict mode: 'Test262Error: Expected SameValue(«undefined», «0») to be true'
-test/built-ins/BigInt/asIntN/bigint-tobigint-errors.js:
-  default: 'Test262Error: ToBigInt: no argument => undefined => TypeError Expected a TypeError to be thrown but no exception was thrown at all'
-  strict mode: 'Test262Error: ToBigInt: no argument => undefined => TypeError Expected a TypeError to be thrown but no exception was thrown at all'
-test/built-ins/BigInt/asIntN/bigint-tobigint-toprimitive.js:
-  default: 'Test262Error: ToPrimitive: @@toPrimitive takes precedence Expected SameValue(«undefined», «1») to be true'
-  strict mode: 'Test262Error: ToPrimitive: @@toPrimitive takes precedence Expected SameValue(«undefined», «1») to be true'
-test/built-ins/BigInt/asIntN/bigint-tobigint-wrapped-values.js:
-  default: 'Test262Error: ToPrimitive: unbox object with internal slot Expected SameValue(«undefined», «0») to be true'
-  strict mode: 'Test262Error: ToPrimitive: unbox object with internal slot Expected SameValue(«undefined», «0») to be true'
-test/built-ins/BigInt/asIntN/bigint-tobigint.js:
-  default: 'Test262Error: Expected SameValue(«undefined», «0») to be true'
-  strict mode: 'Test262Error: Expected SameValue(«undefined», «0») to be true'
-test/built-ins/BigInt/asIntN/bits-toindex-errors.js:
-  default: 'Test262Error: ToIndex: throw when integerIndex < 0 Expected a RangeError to be thrown but no exception was thrown at all'
-  strict mode: 'Test262Error: ToIndex: throw when integerIndex < 0 Expected a RangeError to be thrown but no exception was thrown at all'
-test/built-ins/BigInt/asIntN/bits-toindex-toprimitive.js:
-  default: 'Test262Error: ToPrimitive: @@toPrimitive takes precedence Expected SameValue(«undefined», «-1») to be true'
-  strict mode: 'Test262Error: ToPrimitive: @@toPrimitive takes precedence Expected SameValue(«undefined», «-1») to be true'
-test/built-ins/BigInt/asIntN/bits-toindex-wrapped-values.js:
-  default: 'Test262Error: ToPrimitive: unbox object with internal slot Expected SameValue(«undefined», «0») to be true'
-  strict mode: 'Test262Error: ToPrimitive: unbox object with internal slot Expected SameValue(«undefined», «0») to be true'
-test/built-ins/BigInt/asIntN/bits-toindex.js:
-  default: 'Test262Error: Expected SameValue(«undefined», «0») to be true'
-  strict mode: 'Test262Error: Expected SameValue(«undefined», «0») to be true'
-test/built-ins/BigInt/asIntN/order-of-steps.js:
-  default: 'Test262Error: Expected SameValue(«0», «2») to be true'
-  strict mode: 'Test262Error: Expected SameValue(«0», «2») to be true'
-test/built-ins/BigInt/asUintN/arithmetic.js:
-  default: 'Test262Error: Expected SameValue(«undefined», «0») to be true'
-  strict mode: 'Test262Error: Expected SameValue(«undefined», «0») to be true'
-test/built-ins/BigInt/asUintN/bigint-tobigint-errors.js:
-  default: 'Test262Error: ToBigInt: no argument => undefined => TypeError Expected a TypeError to be thrown but no exception was thrown at all'
-  strict mode: 'Test262Error: ToBigInt: no argument => undefined => TypeError Expected a TypeError to be thrown but no exception was thrown at all'
-test/built-ins/BigInt/asUintN/bigint-tobigint-toprimitive.js:
-  default: 'Test262Error: ToPrimitive: @@toPrimitive takes precedence Expected SameValue(«undefined», «1») to be true'
-  strict mode: 'Test262Error: ToPrimitive: @@toPrimitive takes precedence Expected SameValue(«undefined», «1») to be true'
-test/built-ins/BigInt/asUintN/bigint-tobigint-wrapped-values.js:
-  default: 'Test262Error: ToPrimitive: unbox object with internal slot Expected SameValue(«undefined», «0») to be true'
-  strict mode: 'Test262Error: ToPrimitive: unbox object with internal slot Expected SameValue(«undefined», «0») to be true'
-test/built-ins/BigInt/asUintN/bigint-tobigint.js:
-  default: 'Test262Error: Expected SameValue(«undefined», «0») to be true'
-  strict mode: 'Test262Error: Expected SameValue(«undefined», «0») to be true'
-test/built-ins/BigInt/asUintN/bits-toindex-errors.js:
-  default: 'Test262Error: ToIndex: throw when integerIndex < 0 Expected a RangeError to be thrown but no exception was thrown at all'
-  strict mode: 'Test262Error: ToIndex: throw when integerIndex < 0 Expected a RangeError to be thrown but no exception was thrown at all'
-test/built-ins/BigInt/asUintN/bits-toindex-toprimitive.js:
-  default: 'Test262Error: ToPrimitive: @@toPrimitive takes precedence Expected SameValue(«undefined», «1») to be true'
-  strict mode: 'Test262Error: ToPrimitive: @@toPrimitive takes precedence Expected SameValue(«undefined», «1») to be true'
-test/built-ins/BigInt/asUintN/bits-toindex-wrapped-values.js:
-  default: 'Test262Error: ToPrimitive: unbox object with internal slot Expected SameValue(«undefined», «0») to be true'
-  strict mode: 'Test262Error: ToPrimitive: unbox object with internal slot Expected SameValue(«undefined», «0») to be true'
-test/built-ins/BigInt/asUintN/bits-toindex.js:
-  default: 'Test262Error: Expected SameValue(«undefined», «0») to be true'
-  strict mode: 'Test262Error: Expected SameValue(«undefined», «0») to be true'
-test/built-ins/BigInt/asUintN/order-of-steps.js:
-  default: 'Test262Error: Expected SameValue(«0», «2») to be true'
-  strict mode: 'Test262Error: Expected SameValue(«0», «2») to be true'
 test/built-ins/DataView/custom-proto-access-detaches-buffer.js:
   default: 'Test262Error: Expected a TypeError to be thrown but no exception was thrown at all'
   strict mode: 'Test262Error: Expected a TypeError to be thrown but no exception was thrown at all'

Modified: trunk/Source/_javascript_Core/ChangeLog (261198 => 261199)


--- trunk/Source/_javascript_Core/ChangeLog	2020-05-05 21:01:27 UTC (rev 261198)
+++ trunk/Source/_javascript_Core/ChangeLog	2020-05-05 21:15:07 UTC (rev 261199)
@@ -1,3 +1,43 @@
+2020-05-05  Yusuke Suzuki  <ysuz...@apple.com>
+
+        [JSC] Implement BigInt.asIntN and BigInt.asUintN
+        https://bugs.webkit.org/show_bug.cgi?id=181144
+
+        Reviewed by Darin Adler.
+
+        This patch implements BigInt.asIntN[1] and BigInt.asUintN[2] features.
+        As the same to the other BigInt runtime C++ code, we port V8 code to JSC to implement both.
+
+        BigInt.asIntN is `static_cast<intN_t>(BigInt value)` and BigInt.asUintN is `static_cast<uintN_t>(BigInt value)`.
+        They are getting slice of N bits from two's complement representation of the given BigInt. The difference between
+        asIntN and asUintN is asIntN renders MSB as a sign.
+
+        This patch is once rolled out due to ARM64_32 build failure, which is caused by the existing bug[3]. Relanding it
+        since it is now fixed.
+
+        [1]: https://tc39.es/ecma262/#sec-bigint.asintn
+        [2]: https://tc39.es/ecma262/#sec-bigint.asuintn
+        [3]: https://trac.webkit.org/changeset/261174/webkit
+
+        * runtime/BigIntConstructor.cpp:
+        (JSC::toBigInt):
+        (JSC::bigIntConstructorFuncAsUintN):
+        (JSC::bigIntConstructorFuncAsIntN):
+        * runtime/JSBigInt.cpp:
+        (JSC::zeroImpl):
+        (JSC::JSBigInt::divideImpl):
+        (JSC::JSBigInt::unaryMinusImpl):
+        (JSC::JSBigInt::remainderImpl):
+        (JSC::JSBigInt::digitDiv):
+        (JSC::JSBigInt::absoluteSub):
+        (JSC::JSBigInt::asIntNImpl):
+        (JSC::JSBigInt::asUintNImpl):
+        (JSC::JSBigInt::truncateToNBits):
+        (JSC::JSBigInt::truncateAndSubFromPowerOfTwo):
+        (JSC::JSBigInt::asIntN):
+        (JSC::JSBigInt::asUintN):
+        * runtime/JSBigInt.h:
+
 2020-05-05  Ross Kirsling  <ross.kirsl...@sony.com>
 
         [Intl] Alphabetize extension keys and correctly mark const methods

Modified: trunk/Source/_javascript_Core/runtime/BigIntConstructor.cpp (261198 => 261199)


--- trunk/Source/_javascript_Core/runtime/BigIntConstructor.cpp	2020-05-05 21:01:27 UTC (rev 261198)
+++ trunk/Source/_javascript_Core/runtime/BigIntConstructor.cpp	2020-05-05 21:15:07 UTC (rev 261199)
@@ -77,29 +77,31 @@
 
 JSValue toBigInt(JSGlobalObject* globalObject, JSValue argument)
 {
-    ASSERT(argument.isPrimitive());
     VM& vm = globalObject->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSValue primitive = argument.toPrimitive(globalObject);
+    RETURN_IF_EXCEPTION(scope, { });
     
-    if (argument.isBigInt())
-        return argument;
+    if (primitive.isBigInt())
+        return primitive;
 
-    if (argument.isBoolean()) {
+    if (primitive.isBoolean()) {
 #if USE(BIGINT32)
-        return jsBigInt32(argument.asBoolean());
+        return jsBigInt32(primitive.asBoolean());
 #else
-        return JSBigInt::createFrom(vm, argument.asBoolean());
+        return JSBigInt::createFrom(vm, primitive.asBoolean());
 #endif
     }
 
-    if (argument.isString()) {
+    if (primitive.isString()) {
         scope.release();
-        return toStringView(globalObject, argument, [&] (StringView view) {
+        return toStringView(globalObject, primitive, [&] (StringView view) {
             return JSBigInt::parseInt(globalObject, view);
         });
     }
 
-    ASSERT(argument.isUndefinedOrNull() || argument.isNumber() || argument.isSymbol());
+    ASSERT(primitive.isUndefinedOrNull() || primitive.isNumber() || primitive.isSymbol());
     throwTypeError(globalObject, scope, "Invalid argument type in ToBigInt operation"_s);
     return jsUndefined();
 }
@@ -131,18 +133,44 @@
     RELEASE_AND_RETURN(scope, JSValue::encode(toBigInt(globalObject, primitive)));
 }
 
-EncodedJSValue JSC_HOST_CALL bigIntConstructorFuncAsUintN(JSGlobalObject*, CallFrame*)
+EncodedJSValue JSC_HOST_CALL bigIntConstructorFuncAsUintN(JSGlobalObject* globalObject, CallFrame* callFrame)
 {
-    // FIXME: [ESNext][BigInt] Implement BigInt.asIntN and BigInt.asUintN
-    // https://bugs.webkit.org/show_bug.cgi?id=181144
-    return encodedJSUndefined();
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto numberOfBits = callFrame->argument(0).toIndex(globalObject, "number of bits");
+    RETURN_IF_EXCEPTION(scope, { });
+
+    JSValue bigInt = toBigInt(globalObject, callFrame->argument(1));
+    RETURN_IF_EXCEPTION(scope, { });
+
+#if USE(BIGINT32)
+    if (bigInt.isBigInt32())
+        RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::asUintN(globalObject, numberOfBits, bigInt.bigInt32AsInt32())));
+#endif
+
+    ASSERT(bigInt.isHeapBigInt());
+    RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::asUintN(globalObject, numberOfBits, bigInt.asHeapBigInt())));
 }
 
-EncodedJSValue JSC_HOST_CALL bigIntConstructorFuncAsIntN(JSGlobalObject*, CallFrame*)
+EncodedJSValue JSC_HOST_CALL bigIntConstructorFuncAsIntN(JSGlobalObject* globalObject, CallFrame* callFrame)
 {
-    // FIXME: [ESNext][BigInt] Implement BigInt.asIntN and BigInt.asUintN
-    // https://bugs.webkit.org/show_bug.cgi?id=181144
-    return encodedJSUndefined();
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto numberOfBits = callFrame->argument(0).toIndex(globalObject, "number of bits");
+    RETURN_IF_EXCEPTION(scope, { });
+
+    JSValue bigInt = toBigInt(globalObject, callFrame->argument(1));
+    RETURN_IF_EXCEPTION(scope, { });
+
+#if USE(BIGINT32)
+    if (bigInt.isBigInt32())
+        RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::asIntN(globalObject, numberOfBits, bigInt.bigInt32AsInt32())));
+#endif
+
+    ASSERT(bigInt.isHeapBigInt());
+    RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::asIntN(globalObject, numberOfBits, bigInt.asHeapBigInt())));
 }
 
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/JSBigInt.cpp (261198 => 261199)


--- trunk/Source/_javascript_Core/runtime/JSBigInt.cpp	2020-05-05 21:01:27 UTC (rev 261198)
+++ trunk/Source/_javascript_Core/runtime/JSBigInt.cpp	2020-05-05 21:15:07 UTC (rev 261199)
@@ -55,8 +55,6 @@
 #include <algorithm>
 #include <wtf/MathExtras.h>
 
-#define STATIC_ASSERT(cond) static_assert(cond, "JSBigInt assumes " #cond)
-
 namespace JSC {
 
 const ClassInfo JSBigInt::s_info = { "BigInt", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(JSBigInt) };
@@ -395,6 +393,16 @@
     return tryConvertToBigInt32(implResult.payload.asHeapBigInt());
 }
 
+static ALWAYS_INLINE JSBigInt::ImplResult zeroImpl(VM& vm)
+{
+#if USE(BIGINT32)
+    UNUSED_PARAM(vm);
+    return jsBigInt32(0);
+#else
+    return JSBigInt::createZero(vm);
+#endif
+}
+
 // Multiplies {this} with {factor} and adds {summand} to the result.
 void JSBigInt::inplaceMultiplyAdd(Digit factor, Digit summand)
 {
@@ -566,13 +574,8 @@
     // 2. Let quotient be the mathematical value of x divided by y.
     // 3. Return a BigInt representing quotient rounded towards 0 to the next
     //    integral value.
-    if (absoluteCompare(x, y) == ComparisonResult::LessThan) {
-#if USE(BIGINT32)
-        return jsBigInt32(0);
-#else
-        return JSBigInt::createZero(vm);
-#endif
-    }
+    if (absoluteCompare(x, y) == ComparisonResult::LessThan)
+        return zeroImpl(vm);
 
     JSBigInt* quotient = nullptr;
     bool resultSign = x.sign() != y.sign();
@@ -622,13 +625,8 @@
 template <typename BigIntImpl>
 JSBigInt::ImplResult JSBigInt::unaryMinusImpl(VM& vm, BigIntImpl x)
 {
-    if (x.isZero()) {
-#if USE(BIGINT32)
-        return jsBigInt32(0);
-#else
-        return JSBigInt::createZero(vm);
-#endif
-    }
+    if (x.isZero())
+        return zeroImpl(vm);
 
     JSBigInt* result = copy(vm, x);
     result->setSign(!x.sign());
@@ -660,23 +658,13 @@
     JSBigInt* remainder;
     if (y.length() == 1) {
         Digit divisor = y.digit(0);
-        if (divisor == 1) {
-#if USE(BIGINT32)
-        return jsBigInt32(0);
-#else
-        return JSBigInt::createZero(vm);
-#endif
-        }
+        if (divisor == 1)
+            return zeroImpl(vm);
 
         Digit remainderDigit;
         absoluteDivWithDigitDivisor(vm, x, divisor, nullptr, remainderDigit);
-        if (!remainderDigit) {
-#if USE(BIGINT32)
-            return jsBigInt32(0);
-#else
-            return JSBigInt::createZero(vm);
-#endif
-        }
+        if (!remainderDigit)
+            return zeroImpl(vm);
 
         remainder = createWithLengthUnchecked(vm, 1);
         remainder->setDigit(0, remainderDigit);
@@ -1185,7 +1173,7 @@
     // left operand". We mask the right operand of the shift by {shiftMask} (`digitBits - 1`), which makes `digitBits - 0` zero.
     // This shifting produces a value which covers 0 < {s} <= (digitBits - 1) cases. {s} == digitBits never happen as we asserted.
     // Since {sZeroMask} clears the value in the case of {s} == 0, {s} == 0 case is also covered.
-    STATIC_ASSERT(sizeof(CPURegister) == sizeof(Digit));
+    static_assert(sizeof(CPURegister) == sizeof(Digit));
     Digit sZeroMask = static_cast<Digit>((-static_cast<CPURegister>(s)) >> (digitBits - 1));
     static constexpr unsigned shiftMask = digitBits - 1;
     Digit un32 = (high << s) | ((low >> ((digitBits - s) & shiftMask)) & sZeroMask);
@@ -1425,7 +1413,7 @@
         return resultSign == x.sign() ? ImplResult { x } : JSBigInt::unaryMinusImpl(vm, x);
 
     if (comparisonResult == ComparisonResult::Equal)
-        return JSBigInt::createZero(vm);
+        return zeroImpl(vm);
 
     JSBigInt* result = JSBigInt::createWithLengthUnchecked(vm, x.length());
 
@@ -2731,4 +2719,205 @@
     return jsNumber(bitwise_cast<double>(doubleBits));
 }
 
+template <typename BigIntImpl>
+JSBigInt::ImplResult JSBigInt::asIntNImpl(JSGlobalObject* globalObject, uint64_t n, BigIntImpl bigInt)
+{
+    VM& vm = globalObject->vm();
+
+    if (bigInt.isZero())
+        return bigInt;
+    if (n == 0)
+        return zeroImpl(vm);
+
+    uint64_t neededLength = (n + digitBits - 1) / digitBits;
+    uint64_t length = static_cast<uint64_t>(bigInt.length());
+    // If bigInt has less than n bits, return it directly.
+    if (length < neededLength)
+        return bigInt;
+    ASSERT(neededLength <= INT32_MAX);
+    Digit topDigit = bigInt.digit(static_cast<int32_t>(neededLength) - 1);
+    Digit compareDigit = static_cast<Digit>(1) << ((n - 1) % digitBits);
+    if (length == neededLength && topDigit < compareDigit)
+        return bigInt;
+
+    // Otherwise we have to truncate (which is a no-op in the special case
+    // of bigInt == -2^(n-1)), and determine the right sign. We also might have
+    // to subtract from 2^n to simulate having two's complement representation.
+    // In most cases, the result's sign is bigInt.sign() xor "(n-1)th bit present".
+    // The only exception is when bigInt is negative, has the (n-1)th bit, and all
+    // its bits below (n-1) are zero. In that case, the result is the minimum
+    // n-bit integer (example: asIntN(3, -12n) => -4n).
+    bool hasBit = (topDigit & compareDigit) == compareDigit;
+    ASSERT(n <= INT32_MAX);
+    int32_t N = static_cast<int32_t>(n);
+    if (!hasBit)
+        return truncateToNBits(vm, N, bigInt);
+    if (!bigInt.sign())
+        return truncateAndSubFromPowerOfTwo(vm, N, bigInt, true);
+
+    // Negative numbers must subtract from 2^n, except for the special case
+    // described above.
+    if ((topDigit & (compareDigit - 1)) == 0) {
+        for (int32_t i = static_cast<int32_t>(neededLength) - 2; i >= 0; i--) {
+            if (bigInt.digit(i) != 0)
+                return truncateAndSubFromPowerOfTwo(vm, N, bigInt, false);
+        }
+        // Truncation is no-op if bigInt == -2^(n-1).
+        if (length == neededLength && topDigit == compareDigit)
+            return bigInt;
+        return truncateToNBits(vm, N, bigInt);
+    }
+    return truncateAndSubFromPowerOfTwo(vm, N, bigInt, false);
+}
+
+template <typename BigIntImpl>
+JSBigInt::ImplResult JSBigInt::asUintNImpl(JSGlobalObject* globalObject, uint64_t n, BigIntImpl bigInt)
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    if (bigInt.isZero())
+        return bigInt;
+    if (n == 0)
+        return zeroImpl(vm);
+
+    // If bigInt is negative, simulate two's complement representation.
+    if (bigInt.sign()) {
+        if (n > maxLengthBits) {
+            throwOutOfMemoryError(globalObject, scope, "BigInt generated from this operation is too big"_s);
+            return nullptr;
+        }
+        return truncateAndSubFromPowerOfTwo(vm, static_cast<int32_t>(n), bigInt, false);
+    }
+
+    // If bigInt is positive and has up to n bits, return it directly.
+    if (n >= maxLengthBits)
+        return bigInt;
+    static_assert(maxLengthBits < INT32_MAX - digitBits);
+    int32_t neededLength = static_cast<int32_t>((n + digitBits - 1) / digitBits);
+    if (static_cast<int32_t>(bigInt.length()) < neededLength)
+        return bigInt;
+
+    int32_t bitsInTopDigit = n % digitBits;
+    if (static_cast<int32_t>(bigInt.length()) == neededLength) {
+        if (bitsInTopDigit == 0)
+            return bigInt;
+        Digit topDigit = bigInt.digit(neededLength - 1);
+        if ((topDigit >> bitsInTopDigit) == 0)
+            return bigInt;
+    }
+
+    // Otherwise, truncate.
+    ASSERT(n <= INT32_MAX);
+    return truncateToNBits(vm, static_cast<int32_t>(n), bigInt);
+}
+
+template <typename BigIntImpl>
+JSBigInt::ImplResult JSBigInt::truncateToNBits(VM& vm, int32_t n, BigIntImpl bigInt)
+{
+    ASSERT(n != 0);
+    ASSERT(bigInt.length() > n / digitBits);
+
+    int32_t neededDigits = (n + (digitBits - 1)) / digitBits;
+    ASSERT(neededDigits <= static_cast<int32_t>(bigInt.length()));
+    JSBigInt* result = createWithLengthUnchecked(vm, neededDigits);
+    ASSERT(result);
+
+    // Copy all digits except the MSD.
+    int32_t last = neededDigits - 1;
+    for (int32_t i = 0; i < last; i++)
+        result->setDigit(i, bigInt.digit(i));
+
+    // The MSD might contain extra bits that we don't want.
+    Digit msd = bigInt.digit(last);
+    if (n % digitBits != 0) {
+        int32_t drop = digitBits - (n % digitBits);
+        msd = (msd << drop) >> drop;
+    }
+    result->setDigit(last, msd);
+    result->setSign(bigInt.sign());
+    return result->rightTrim(vm);
+}
+
+// Subtracts the least significant n bits of abs(bigInt) from 2^n.
+template <typename BigIntImpl>
+JSBigInt::ImplResult JSBigInt::truncateAndSubFromPowerOfTwo(VM& vm, int32_t n, BigIntImpl bigInt, bool resultSign)
+{
+    ASSERT(n != 0);
+    ASSERT(n <= static_cast<int32_t>(maxLengthBits));
+
+    int32_t neededDigits = (n + (digitBits - 1)) / digitBits;
+    ASSERT(neededDigits <= static_cast<int32_t>(maxLength)); // Follows from n <= maxLengthBits.
+    JSBigInt* result = createWithLengthUnchecked(vm, neededDigits);
+    ASSERT(result);
+
+    // Process all digits except the MSD.
+    int32_t i = 0;
+    int32_t last = neededDigits - 1;
+    int32_t length = bigInt.length();
+    Digit borrow = 0;
+    // Take digits from bigInt unless its length is exhausted.
+    int32_t limit = std::min(last, length);
+    for (; i < limit; i++) {
+        Digit newBorrow = 0;
+        Digit difference = digitSub(0, bigInt.digit(i), newBorrow);
+        difference = digitSub(difference, borrow, newBorrow);
+        result->setDigit(i, difference);
+        borrow = newBorrow;
+    }
+    // Then simulate leading zeroes in {bigInt} as needed.
+    for (; i < last; i++) {
+        Digit newBorrow = 0;
+        Digit difference = digitSub(0, borrow, newBorrow);
+        result->setDigit(i, difference);
+        borrow = newBorrow;
+    }
+
+    // The MSD might contain extra bits that we don't want.
+    Digit msd = last < length ? bigInt.digit(last) : 0;
+    int32_t msdBitsConsumed = n % digitBits;
+    Digit resultMSD;
+    if (msdBitsConsumed == 0) {
+        Digit newBorrow = 0;
+        resultMSD = digitSub(0, msd, newBorrow);
+        resultMSD = digitSub(resultMSD, borrow, newBorrow);
+    } else {
+        int32_t drop = digitBits - msdBitsConsumed;
+        msd = (msd << drop) >> drop;
+        Digit minuendMSD = static_cast<Digit>(1) << (digitBits - drop);
+        Digit newBorrow = 0;
+        resultMSD = digitSub(minuendMSD, msd, newBorrow);
+        resultMSD = digitSub(resultMSD, borrow, newBorrow);
+        ASSERT(newBorrow == 0); // result < 2^n.
+        // If all subtracted bits were zero, we have to get rid of the
+        // materialized minuendMSD again.
+        resultMSD &= (minuendMSD - 1);
+    }
+    result->setDigit(last, resultMSD);
+    result->setSign(resultSign);
+    return result->rightTrim(vm);
+}
+
+JSValue JSBigInt::asIntN(JSGlobalObject* globalObject, uint64_t n, JSBigInt* bigInt)
+{
+    return tryConvertToBigInt32(asIntNImpl(globalObject, n, HeapBigIntImpl { bigInt }));
+}
+
+JSValue JSBigInt::asUintN(JSGlobalObject* globalObject, uint64_t n, JSBigInt* bigInt)
+{
+    return tryConvertToBigInt32(asUintNImpl(globalObject, n, HeapBigIntImpl { bigInt }));
+}
+
+#if USE(BIGINT32)
+JSValue JSBigInt::asIntN(JSGlobalObject* globalObject, uint64_t n, int32_t bigInt)
+{
+    return tryConvertToBigInt32(asIntNImpl(globalObject, n, Int32BigIntImpl { bigInt }));
+}
+
+JSValue JSBigInt::asUintN(JSGlobalObject* globalObject, uint64_t n, int32_t bigInt)
+{
+    return tryConvertToBigInt32(asUintNImpl(globalObject, n, Int32BigIntImpl { bigInt }));
+}
+#endif
+
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/JSBigInt.h (261198 => 261199)


--- trunk/Source/_javascript_Core/runtime/JSBigInt.h	2020-05-05 21:01:27 UTC (rev 261198)
+++ trunk/Source/_javascript_Core/runtime/JSBigInt.h	2020-05-05 21:15:07 UTC (rev 261199)
@@ -412,6 +412,14 @@
         return toNumberHeap(jsCast<JSBigInt*>(bigInt));
     }
 
+
+    static JSValue asIntN(JSGlobalObject*, uint64_t numberOfBits, JSBigInt*);
+    static JSValue asUintN(JSGlobalObject*, uint64_t numberOfBits, JSBigInt*);
+#if USE(BIGINT32)
+    static JSValue asIntN(JSGlobalObject*, uint64_t numberOfBits, int32_t bigIntAsInt32);
+    static JSValue asUintN(JSGlobalObject*, uint64_t numberOfBits, int32_t bigIntAsInt32);
+#endif
+
     Digit digit(unsigned);
     void setDigit(unsigned, Digit); // Use only when initializing.
     JS_EXPORT_PRIVATE JSBigInt* rightTrim(VM&);
@@ -542,6 +550,15 @@
     template <typename BigIntImpl>
     static Optional<Digit> toShiftAmount(BigIntImpl x);
 
+    template <typename BigIntImpl>
+    static ImplResult asIntNImpl(JSGlobalObject*, uint64_t, BigIntImpl);
+    template <typename BigIntImpl>
+    static ImplResult asUintNImpl(JSGlobalObject*, uint64_t, BigIntImpl);
+    template <typename BigIntImpl>
+    static ImplResult truncateToNBits(VM&, int32_t, BigIntImpl);
+    template <typename BigIntImpl>
+    static ImplResult truncateAndSubFromPowerOfTwo(VM&, int32_t, BigIntImpl, bool resultSign);
+
     inline static size_t offsetOfData()
     {
         return OBJECT_OFFSETOF(JSBigInt, m_data);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to