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);