diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.c b/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.c index b349db16cf..501deee017 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.c @@ -13,6 +13,7 @@ * limitations under the License. */ +#include "ecma-big-uint.h" #include "ecma-bigint.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" @@ -22,6 +23,21 @@ #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" +/** + * This object has a custom dispatch function. + */ +#define BUILTIN_CUSTOM_DISPATCH + +/** + * List of built-in routine identifiers. + */ +enum +{ + ECMA_BUILTIN_BIGINT_START = 0, /**< Special value, should be ignored */ + ECMA_BUILTIN_BIGINT_AS_INT_N, /**< The 'asIntN' routine of the BigInt object */ + ECMA_BUILTIN_BIGINT_AS_U_INT_N, /**< The 'asUintN' routine of the BigInt object */ +}; + #define BUILTIN_INC_HEADER_NAME "ecma-builtin-bigint.inc.h" #define BUILTIN_UNDERSCORED_ID bigint #include "ecma-builtin-internal-routines-template.inc.h" @@ -36,6 +52,308 @@ * @{ */ +/** + * The BigInt object's 'asIntN' and 'asUintN' routines + * + * See also: + * ECMA-262 v5, 11.0 + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_builtin_bigint_object_as_int_n (ecma_value_t bits, /**< number of bits */ + ecma_value_t bigint, /**< bigint number */ + bool is_signed) /**< The operation is signed */ +{ + ecma_number_t input_bits; + ecma_value_t bit_value = ecma_op_to_index (bits, &input_bits); + + if (ECMA_IS_VALUE_ERROR (bit_value)) + { + return bit_value; + } + + ecma_value_t bigint_value = ecma_bigint_to_bigint (bigint, false); + + if (ECMA_IS_VALUE_ERROR (bigint_value)) + { + return bigint_value; + } + + if (input_bits == 0 || bigint_value == ECMA_BIGINT_ZERO) + { + ecma_free_value (bigint_value); + return ECMA_BIGINT_ZERO; + } + + ecma_extended_primitive_t *input_bigint_p = ecma_get_extended_primitive_from_value (bigint_value); + uint32_t bigint_size = ECMA_BIGINT_GET_SIZE (input_bigint_p); + + if (input_bits >= UINT32_MAX) + { + return bigint_value; + } + + uint8_t input_bigint_sign = input_bigint_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN; + const uint32_t size_of_divisor_in_bits = sizeof (uint32_t) * JERRY_BITSINBYTE; + uint32_t whole_part = (uint32_t) input_bits / size_of_divisor_in_bits; + uint32_t remainder = (uint32_t) input_bits % size_of_divisor_in_bits; + uint32_t input_bit_length = 1; + + if (whole_part == 0) + { + input_bit_length = (uint32_t) input_bits; + } + else + { + input_bit_length = (remainder > 0) ? (whole_part + 1) * size_of_divisor_in_bits : (uint32_t) input_bits; + } + + uint32_t input_byte_size = (uint32_t) input_bits / JERRY_BITSINBYTE; + + if ((uint32_t) input_bits % JERRY_BITSINBYTE != 0) + { + input_byte_size += 1; + } + + const uint32_t input_bits_in_byte = (uint32_t) input_bit_length / JERRY_BITSINBYTE; + uint32_t min_size = (input_bits_in_byte < bigint_size) ? input_bits_in_byte : bigint_size; + + if (input_bigint_sign && (input_byte_size > bigint_size)) + { + min_size = (input_bits_in_byte > bigint_size) ? input_bits_in_byte : bigint_size; + } + + if (min_size < sizeof (uint32_t)) + { + min_size = sizeof (uint32_t); + } + + ecma_extended_primitive_t *result_p = ecma_bigint_create ((uint32_t) min_size); + + if (JERRY_UNLIKELY (result_p == NULL)) + { + ecma_deref_bigint (input_bigint_p); + return ECMA_VALUE_ERROR; + } + + ecma_bigint_digit_t *last_digit_p = ECMA_BIGINT_GET_DIGITS (input_bigint_p, bigint_size); + + /* Calculate the leading zeros of the input_bigint */ + + ecma_bigint_digit_t zeros = ecma_big_uint_count_leading_zero (last_digit_p[-1]); + uint32_t bits_of_bigint = (uint32_t) (bigint_size * JERRY_BITSINBYTE) - zeros; + uint32_t exact_size = 1; + + if ((bits_of_bigint > (size_of_divisor_in_bits - 1)) || input_bigint_sign) + { + exact_size = (input_byte_size < bigint_size) ? (uint32_t) input_byte_size : bigint_size; + + if (input_bigint_sign && (input_byte_size > bigint_size)) + { + exact_size = (input_byte_size > bigint_size) ? (uint32_t) input_byte_size : bigint_size; + } + } + else + { + exact_size = bigint_size; + } + + if (input_bigint_sign) + { + bits_of_bigint += 1; + } + + if (bits_of_bigint > (input_bits - 1) || input_bigint_sign) + { + ecma_bigint_digit_t *digits_p = ECMA_BIGINT_GET_DIGITS (input_bigint_p, 0); + ecma_bigint_digit_t *digits_end_p = ECMA_BIGINT_GET_DIGITS (input_bigint_p, exact_size); + ecma_bigint_digit_t *result_number_p = ECMA_BIGINT_GET_DIGITS (result_p, 0); + int32_t first_cell = 0; + uint32_t surplus_bits = + (whole_part > 0) ? (uint32_t) (whole_part * size_of_divisor_in_bits) : (uint32_t) size_of_divisor_in_bits; + uint32_t mask_bit = (whole_part == 0) ? (uint32_t) input_bits : (uint32_t) input_bits - surplus_bits; + + if (mask_bit == 0) + { + mask_bit = size_of_divisor_in_bits - 1; + } + + uint32_t check_sign_mask = (uint32_t) 1 << (mask_bit - 1); + uint32_t mask = ((uint32_t) 1 << mask_bit) - 1; + uint32_t last_cell = (exact_size >= sizeof (uint32_t)) ? (uint32_t) (min_size / sizeof (uint32_t)) - 1 : 0; + bool is_positive = false; + bool is_representation_positive = false; + + if (is_signed) + { + if (input_bigint_sign && ((~digits_p[last_cell] + 1) & check_sign_mask) == 0) + { + is_positive = true; + } + + if ((digits_p[last_cell] & check_sign_mask) == 0) + { + is_representation_positive = true; + } + } + + do + { + *result_number_p++ = + (is_representation_positive || (!is_signed && !input_bigint_sign)) ? *digits_p++ : ~(*digits_p++); + first_cell--; + } while (digits_p < digits_end_p); + + int16_t equal_bits = 0; + + if (remainder != 0) + { + equal_bits = -1; + } + + int32_t last_cell_negative = (last_cell != 0) ? ((int32_t) last_cell * (-1)) : -1; + bool is_zero_values = false; + + if (!is_signed) + { + if (input_bigint_sign) + { + is_zero_values = true; + } + } + else + { + if (((digits_p[-1] & check_sign_mask) > 0) || (result_number_p[-1] & check_sign_mask) > 0) + { + is_zero_values = true; + } + } + + if (is_zero_values) + { + result_number_p[first_cell] += 1; + + if (result_number_p[first_cell] == 0) + { + do + { + result_number_p[++first_cell] += 1; + } while (first_cell != equal_bits); + + first_cell = last_cell_negative; + } + } + + result_number_p[-1] &= mask; + uint32_t surplus = (uint32_t) (min_size - exact_size) / sizeof (ecma_char_t); + uint32_t new_size = result_p->u.bigint_sign_and_size; + + if ((min_size - exact_size) % (sizeof (ecma_char_t)) > 0 && surplus == 0) + { + surplus += (uint32_t) sizeof (ecma_char_t); + } + else + { + surplus = (uint32_t) (surplus * sizeof (ecma_char_t)); + } + + if (min_size / JERRY_BITSINBYTE < 1) + { + surplus = 0; + } + + if (is_signed) + { + if (result_p->u.bigint_sign_and_size > exact_size && min_size > sizeof (uint32_t) + && result_number_p[last_cell_negative] < 1) + { + new_size -= surplus; + } + + new_size += 1; + + if (is_positive || ((digits_p[-1] & check_sign_mask) == 0 && !input_bigint_sign)) + { + new_size -= 1; + } + } + + while (first_cell != 0) + { + if (result_number_p[first_cell] != 0) + { + break; + } + + first_cell++; + } + + if (first_cell == 0) + { + ecma_deref_bigint (result_p); + ecma_deref_bigint (input_bigint_p); + + return ECMA_BIGINT_ZERO; + } + + last_cell_negative = first_cell + (int32_t) last_cell; + int16_t zero_section_cnt = 0; + + while (last_cell_negative > first_cell) + { + if (result_number_p[last_cell_negative] == 0) + { + zero_section_cnt++; + } + + last_cell_negative--; + } + + uint32_t size_limit = sizeof (uint32_t); + + if (zero_section_cnt >= 1) + { + size_limit = new_size - (uint32_t) zero_section_cnt * size_limit; + new_size = (size_limit < sizeof (uint32_t)) ? (uint32_t) (JERRY_BITSINBYTE - size_limit) : size_limit; + } + + if (new_size < result_p->u.bigint_sign_and_size) + { + result_p->refs_and_type = ECMA_EXTENDED_PRIMITIVE_REF_ONE | ECMA_TYPE_BIGINT; + uint32_t new_size_remainder = new_size % sizeof (uint32_t); + ecma_extended_primitive_t *new_result_p = ecma_bigint_create (new_size - new_size_remainder); + + new_result_p->u.bigint_sign_and_size += new_size_remainder; + memcpy (new_result_p + 1, result_p + 1, new_size - new_size_remainder); + + ecma_deref_bigint (result_p); + ecma_deref_bigint (input_bigint_p); + + return ecma_make_extended_primitive_value (new_result_p, ECMA_TYPE_BIGINT); + } + + result_p->u.bigint_sign_and_size = new_size; + result_p->refs_and_type = ECMA_EXTENDED_PRIMITIVE_REF_ONE | ECMA_TYPE_BIGINT; + + ecma_deref_bigint (input_bigint_p); + return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); + } + + memcpy (result_p + 1, input_bigint_p + 1, exact_size); + result_p->refs_and_type = ECMA_EXTENDED_PRIMITIVE_REF_ONE | ECMA_TYPE_BIGINT; + + if (input_bigint_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) + { + ecma_deref_bigint (input_bigint_p); + return ecma_bigint_negate (result_p); + } + + ecma_deref_bigint (input_bigint_p); + return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); +} /* ecma_builtin_bigint_object_as_int_n */ + /** * Handle calling [[Call]] of built-in BigInt object * @@ -71,6 +389,38 @@ ecma_builtin_bigint_dispatch_construct (const ecma_value_t *arguments_list_p, /* return ecma_raise_type_error (ECMA_ERR_BIGINT_FUNCTION_NOT_CONSTRUCTOR); } /* ecma_builtin_bigint_dispatch_construct */ +/** + * Dispatcher of the built-in's routines + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +ecma_value_t +ecma_builtin_bigint_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ + ecma_value_t this_arg, /**< 'this' argument value */ + const ecma_value_t arguments_list_p[], /**< list of arguments + * passed to routine */ + uint32_t arguments_number) /**< length of arguments' list */ +{ + JERRY_UNUSED_2 (this_arg, arguments_number); + + switch (builtin_routine_id) + { + case ECMA_BUILTIN_BIGINT_AS_INT_N: + { + return ecma_builtin_bigint_object_as_int_n (arguments_list_p[0], arguments_list_p[1], true); + } + case ECMA_BUILTIN_BIGINT_AS_U_INT_N: + { + return ecma_builtin_bigint_object_as_int_n (arguments_list_p[0], arguments_list_p[1], false); + } + default: + { + JERRY_UNREACHABLE (); + } + } +} /* ecma_builtin_bigint_dispatch_routine */ + /** * @} * @} diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.inc.h index d8b216ab7d..0063daef78 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.inc.h @@ -36,6 +36,11 @@ STRING_VALUE (LIT_MAGIC_STRING_NAME, LIT_MAGIC_STRING_BIGINT_UL, ECMA_PROPERTY_F /* ECMA-262 v11, 20.2.2.3 */ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, ECMA_BUILTIN_ID_BIGINT_PROTOTYPE, ECMA_PROPERTY_FIXED) +/* Routine properties: + * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ + +ROUTINE (LIT_MAGIC_STRING_AS_INT_N, ECMA_BUILTIN_BIGINT_AS_INT_N, 2, 2) +ROUTINE (LIT_MAGIC_STRING_AS_U_INT_N, ECMA_BUILTIN_BIGINT_AS_U_INT_N, 2, 2) #endif /* JERRY_BUILTIN_BIGINT */ #include "ecma-builtin-helpers-macro-undefs.inc.h" diff --git a/jerry-core/lit/lit-magic-strings.inc.h b/jerry-core/lit/lit-magic-strings.inc.h index 30849a6db8..78820ded58 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -294,6 +294,9 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SCRIPT_UL, "Script") #endif /* JERRY_PARSER */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_STRING_UL, "String") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SYMBOL_UL, "Symbol") +#if JERRY_BUILTIN_BIGINT +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_AS_INT_N, "asIntN") +#endif /* JERRY_BUILTIN_BIGINT */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ASSIGN, "assign") #if JERRY_BUILTIN_BIGINT LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BIGINT, "bigint") @@ -411,6 +414,9 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_WEAKREF_UL, "WeakRef") #if JERRY_BUILTIN_CONTAINER LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_WEAKSET_UL, "WeakSet") #endif /* JERRY_BUILTIN_CONTAINER */ +#if JERRY_BUILTIN_BIGINT +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_AS_U_INT_N, "asUintN") +#endif /* JERRY_BUILTIN_BIGINT */ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BOOLEAN, "boolean") #if JERRY_BUILTIN_ANNEXB && JERRY_BUILTIN_REGEXP LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_COMPILE, "compile") diff --git a/jerry-core/lit/lit-magic-strings.ini b/jerry-core/lit/lit-magic-strings.ini index d2235f46e6..52b675819d 100644 --- a/jerry-core/lit/lit-magic-strings.ini +++ b/jerry-core/lit/lit-magic-strings.ini @@ -144,6 +144,8 @@ LIT_MAGIC_STRING_THROW = "throw" LIT_MAGIC_STRING_TRUNC = "trunc" LIT_MAGIC_STRING_VALUE = "value" LIT_MAGIC_STRING_SOURCE_NAME_EVAL = "" +LIT_MAGIC_STRING_AS_INT_N = "asIntN" +LIT_MAGIC_STRING_AS_U_INT_N = "asUintN" LIT_MAGIC_STRING_BIGINT_UL = "BigInt" LIT_MAGIC_STRING_ERRORS_UL = "errors" LIT_MAGIC_STRING_HAS_OWN_UL = "hasOwn" diff --git a/tests/jerry/es.next/bigint-as-int-n.js b/tests/jerry/es.next/bigint-as-int-n.js new file mode 100644 index 0000000000..053e1d17b8 --- /dev/null +++ b/tests/jerry/es.next/bigint-as-int-n.js @@ -0,0 +1,107 @@ +// Copyright JS Foundation and other contributors, http://js.foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +assert(BigInt.asIntN('1', 1n) === -1n) +assert(BigInt.asIntN(1.1, 1n) === -1n) +assert(BigInt.asIntN(2, 2n**2n) === 0n) +assert(BigInt.asIntN(undefined, 1n) === 0n) + +var array = [1, 1n, -1n] + +assert(BigInt.asIntN(array[0], 1n) == -1n) +assert(BigInt.asIntN(array[0], array[1]) == -1n) +assert(BigInt.asIntN(array[0], array[2]) == -1n) + +function f(param1, param2) { + assert(BigInt.asIntN(param1, param2) === 0n) +} +f(0,10n) +f(10,0n) + +try { + assert(f(1, "1n") === 1n) + assert(false) +} catch (e) { + assert(e instanceof SyntaxError) +} + +try { + assert(BigInt.asIntN(Number.POSITIVE_INFINITY, 1n) === 1n) + assert(false) +} catch (e) { + assert(e instanceof RangeError) +} + +try { + assert(BigInt.asIntN(Number.NEGATIVE_INFINITY, 1n) === 1n) + assert(false) +} catch (e) { + assert(e instanceof RangeError) +} + +try { + assert(BigInt.asIntN(1, Number.POSITIVE_INFINITY) === 1n) + assert(false) +} catch (e) { + assert(e instanceof TypeError) +} + +try { + assert(BigInt.asIntN(1, Number.NEGATIVE_INFINITY) === 1n) + assert(false) +} catch (e) { + assert(e instanceof TypeError) +} + +try { + assert(BigInt.asIntN(1, 1 + 1n) === 1n) + assert(false) +} catch (e) { + assert(e instanceof TypeError) +} + +try { + assert(BigInt.asIntN(2n, 2n**2n) === 0n) + assert(false) +} catch (e) { + assert(e instanceof TypeError) +} + +try { + BigInt.asIntN().call(undefined,1n) + assert (false); +} catch(e) { + assert(e instanceof TypeError); +} + +try { + BigInt.asIntN().call(1,undefined) + assert (false); +} catch(e) { + assert(e instanceof TypeError); +} + +try { + BigInt.asIntN().call(undefined,undefined) + assert (false); +} catch(e) { + assert(e instanceof TypeError); +} + +try { + assert(BigInt.asIntN(Infinity, 2n) === RangeError) + assert(false) +} catch (e) { + assert(e instanceof RangeError) +} diff --git a/tests/jerry/es.next/bigint-as-uint-n.js b/tests/jerry/es.next/bigint-as-uint-n.js new file mode 100644 index 0000000000..b90b3c725b --- /dev/null +++ b/tests/jerry/es.next/bigint-as-uint-n.js @@ -0,0 +1,107 @@ +// Copyright JS Foundation and other contributors, http://js.foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +assert(BigInt.asUintN('1', 1n) === 1n) +assert(BigInt.asUintN(1.1, 1n) === 1n) +assert(BigInt.asUintN(2, 2n**2n) === 0n) +assert(BigInt.asUintN(undefined, 1n) === 0n) + +var array = [1, 1n, -1n] + +assert(BigInt.asUintN(array[0], 1n) == 1n) +assert(BigInt.asUintN(array[0], array[1]) == 1n) +assert(BigInt.asUintN(array[0], array[2]) == 1n) + +function f(param1, param2) { +assert(BigInt.asUintN(param1, param2) === 0n) +} +f(0,10n) +f(10,0n) + +try { + assert(f(1, "1n") === 1n) + assert(false) +} catch (e) { + assert(e instanceof SyntaxError) +} + +try { + assert(BigInt.asUintN(Number.POSITIVE_INFINITY, 1n) === 1n) + assert(false) +} catch (e) { + assert(e instanceof RangeError) +} + +try { + assert(BigInt.asUintN(Number.NEGATIVE_INFINITY, 1n) === 1n) + assert(false) +} catch (e) { + assert(e instanceof RangeError) +} + +try { + assert(BigInt.asUintN(1, Number.POSITIVE_INFINITY) === 1n) + assert(false) +} catch (e) { + assert(e instanceof TypeError) +} + +try { + assert(BigInt.asUintN(1, Number.NEGATIVE_INFINITY) === 1n) + assert(false) +} catch (e) { + assert(e instanceof TypeError) +} + +try { + assert(BigInt.asUintN(1, 1 + 1n) === 1n) + assert(false) +} catch (e) { + assert(e instanceof TypeError) +} + +try { + assert(BigInt.asUintN(2n, 2n**2n) === 0n) + assert(false) +} catch (e) { + assert(e instanceof TypeError) +} + +try { + BigInt.asUintN().call(undefined,1n) + assert (false); +} catch(e) { + assert(e instanceof TypeError); +} + +try { + BigInt.asUintN().call(1,undefined) + assert (false); +} catch(e) { + assert(e instanceof TypeError); +} + +try { + BigInt.asUintN().call(undefined,undefined) + assert (false); +} catch(e) { + assert(e instanceof TypeError); +} + +try { + assert(BigInt.asUintN(Infinity, 2n) === RangeError) + assert(false) +} catch (e) { + assert(e instanceof RangeError) +} diff --git a/tests/snapshot-tests-skiplist.txt b/tests/snapshot-tests-skiplist.txt index 2952f12358..47207ab720 100644 --- a/tests/snapshot-tests-skiplist.txt +++ b/tests/snapshot-tests-skiplist.txt @@ -4,6 +4,8 @@ tests/jerry/bigint6.js tests/jerry/bigint7.js tests/jerry/bigint8.js tests/jerry/bigint9.js +tests/jerry/es.next/bigint-as-int-n.js +tests/jerry/es.next/bigint-as-uint-n.js tests/jerry/logical-assignment.js tests/jerry/module-circular-01.mjs tests/jerry/module-circular-02.mjs diff --git a/tests/test262-excludelist.xml b/tests/test262-excludelist.xml index 742cb67d7c..f52974e7e7 100644 --- a/tests/test262-excludelist.xml +++ b/tests/test262-excludelist.xml @@ -2,32 +2,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - -