From e7f485bfdb1942312c88546cd05d7f488ddc883a Mon Sep 17 00:00:00 2001 From: Robin Templeton Date: Sun, 8 Jul 2018 21:12:00 +0300 Subject: [PATCH] bug 1471134 - Part 1: Define methods for basic BigInt arithmetic. r=Waldo --- js/src/js.msg | 1 + js/src/vm/BigIntType.cpp | 110 +++++++++++++++++++++++++++++++++++++++ js/src/vm/BigIntType.h | 7 +++ 3 files changed, 118 insertions(+) diff --git a/js/src/js.msg b/js/src/js.msg index 98b370579997..181866a3d9df 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -652,6 +652,7 @@ MSG_DEF(JSMSG_RESPONSE_ALREADY_CONSUMED, 0, JSEXN_TYPEERR, "Res // BigInt MSG_DEF(JSMSG_BIGINT_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert BigInt to number") MSG_DEF(JSMSG_NUMBER_TO_BIGINT, 0, JSEXN_RANGEERR, "can't convert non-finite number to BigInt") +MSG_DEF(JSMSG_BIGINT_TOO_LARGE, 0, JSEXN_RANGEERR, "BigInt is too large to allocate") MSG_DEF(JSMSG_BIGINT_DIVISION_BY_ZERO, 0, JSEXN_RANGEERR, "BigInt division by zero") MSG_DEF(JSMSG_BIGINT_NEGATIVE_EXPONENT, 0, JSEXN_RANGEERR, "BigInt negative exponent") MSG_DEF(JSMSG_BIGINT_INVALID_SYNTAX, 0, JSEXN_SYNTAXERR, "invalid BigInt syntax") diff --git a/js/src/vm/BigIntType.cpp b/js/src/vm/BigIntType.cpp index 41e0619c3af3..45c281b74fe0 100644 --- a/js/src/vm/BigIntType.cpp +++ b/js/src/vm/BigIntType.cpp @@ -171,6 +171,116 @@ BigInt::copy(JSContext* cx, HandleBigInt x) return bi; } +// BigInt proposal section 1.1.7 +BigInt* +BigInt::add(JSContext* cx, HandleBigInt x, HandleBigInt y) +{ + BigInt* z = create(cx); + if (!z) + return nullptr; + mpz_add(z->num_, x->num_, y->num_); + return z; +} + +// BigInt proposal section 1.1.8 +BigInt* +BigInt::sub(JSContext* cx, HandleBigInt x, HandleBigInt y) +{ + BigInt* z = create(cx); + if (!z) + return nullptr; + mpz_sub(z->num_, x->num_, y->num_); + return z; +} + +// BigInt proposal section 1.1.4 +BigInt* +BigInt::mul(JSContext* cx, HandleBigInt x, HandleBigInt y) +{ + BigInt* z = create(cx); + if (!z) + return nullptr; + mpz_mul(z->num_, x->num_, y->num_); + return z; +} + +// BigInt proposal section 1.1.5 +BigInt* +BigInt::div(JSContext* cx, HandleBigInt x, HandleBigInt y) +{ + // Step 1. + if (mpz_size(y->num_) == 0) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_BIGINT_DIVISION_BY_ZERO); + return nullptr; + } + + // Steps 2-3. + BigInt* z = create(cx); + if (!z) + return nullptr; + mpz_tdiv_q(z->num_, x->num_, y->num_); + return z; +} + +// BigInt proposal section 1.1.6 +BigInt* +BigInt::mod(JSContext* cx, HandleBigInt x, HandleBigInt y) +{ + // Step 1. + if (mpz_size(y->num_) == 0) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_BIGINT_DIVISION_BY_ZERO); + return nullptr; + } + + // Steps 2-4. + BigInt* z = create(cx); + if (!z) + return nullptr; + mpz_tdiv_r(z->num_, x->num_, y->num_); + return z; +} + +// BigInt proposal section 1.1.3 +BigInt* +BigInt::pow(JSContext* cx, HandleBigInt x, HandleBigInt y) +{ + // Step 1. + if (mpz_sgn(y->num_) < 0) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_BIGINT_NEGATIVE_EXPONENT); + return nullptr; + } + + // Throw a RangeError if the exponent is too large. + if (!mpz_fits_uint_p(y->num_)) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_BIGINT_TOO_LARGE); + return nullptr; + } + unsigned long int power = mpz_get_ui(y->num_); + + // Steps 2-3. + BigInt* z = create(cx); + if (!z) + return nullptr; + + mpz_pow_ui(z->num_, x->num_, power); + return z; +} + +// BigInt proposal section 1.1.1 +BigInt* +BigInt::neg(JSContext* cx, HandleBigInt x) +{ + BigInt* res = create(cx); + if (!res) + return nullptr; + mpz_neg(res->num_, x->num_); + return res; +} + // BigInt proposal section 7.3 BigInt* js::ToBigInt(JSContext* cx, HandleValue val) diff --git a/js/src/vm/BigIntType.h b/js/src/vm/BigIntType.h index b3b5fcefcd7e..361de8e70e75 100644 --- a/js/src/vm/BigIntType.h +++ b/js/src/vm/BigIntType.h @@ -73,6 +73,13 @@ class BigInt final : public js::gc::TenuredCell static void init(); static BigInt* copy(JSContext* cx, Handle x); + static BigInt* add(JSContext* cx, Handle x, Handle y); + static BigInt* sub(JSContext* cx, Handle x, Handle y); + static BigInt* mul(JSContext* cx, Handle x, Handle y); + static BigInt* div(JSContext* cx, Handle x, Handle y); + static BigInt* mod(JSContext* cx, Handle x, Handle y); + static BigInt* pow(JSContext* cx, Handle x, Handle y); + static BigInt* neg(JSContext* cx, Handle x); static double numberValue(BigInt* x); static JSLinearString* toString(JSContext* cx, BigInt* x, uint8_t radix);