Support BigInt to number conversion using Number constructor (#4121)

JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
Zoltan Herczeg 2020-08-12 16:33:31 +02:00 committed by GitHub
parent 0c154306a8
commit 6adf0c1a87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 266 additions and 54 deletions

View File

@ -1301,7 +1301,7 @@ jerry_value_to_number (const jerry_value_t value) /**< input value */
return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (error_value_msg_p)));
}
return jerry_return (ecma_op_to_number (value));
return jerry_return (ecma_op_to_number (value, ECMA_TO_NUMERIC_NO_OPTS));
} /* jerry_value_to_number */
/**

View File

@ -48,7 +48,7 @@ JERRY_STATIC_ASSERT (sizeof (ecma_number_t) == sizeof (uint32_t),
*
* @return ecma-number with specified sign, biased_exponent and fraction
*/
static ecma_number_t
ecma_number_t
ecma_number_pack (bool sign, /**< sign */
uint32_t biased_exp, /**< biased exponent */
uint64_t fraction) /**< fraction */
@ -111,7 +111,7 @@ JERRY_STATIC_ASSERT (sizeof (ecma_number_t) == sizeof (uint64_t),
*
* @return ecma-number with specified sign, biased_exponent and fraction
*/
static ecma_number_t
ecma_number_t
ecma_number_pack (bool sign, /**< sign */
uint32_t biased_exp, /**< biased exponent */
uint64_t fraction) /**< fraction */

View File

@ -426,6 +426,7 @@ ecma_string_t *ecma_stringbuilder_finalize (ecma_stringbuilder_t *builder_p);
void ecma_stringbuilder_destroy (ecma_stringbuilder_t *builder_p);
/* ecma-helpers-number.c */
ecma_number_t ecma_number_pack (bool sign, uint32_t biased_exp, uint64_t fraction);
void ecma_number_unpack (ecma_number_t num, bool *sign_p, uint32_t *biased_exp_p, uint64_t *fraction_p);
ecma_number_t ecma_number_make_nan (void);
ecma_number_t ecma_number_make_infinity (bool sign);

View File

@ -396,7 +396,7 @@ ecma_builtin_date_prototype_dispatch_set (uint16_t builtin_routine_id, /**< buil
for (uint32_t i = 0; i < conversions; i++)
{
ecma_value_t value = ecma_op_to_number (arguments_list[i]);
ecma_value_t value = ecma_op_to_number (arguments_list[i], ECMA_TO_NUMERIC_NO_OPTS);
if (ECMA_IS_VALUE_ERROR (value))
{

View File

@ -776,7 +776,7 @@ ecma_builtin_date_dispatch_construct (const ecma_value_t *arguments_list_p, /**<
}
else
{
ecma_value_t prim_value = ecma_op_to_number (argument);
ecma_value_t prim_value = ecma_op_to_number (argument, ECMA_TO_NUMERIC_NO_OPTS);
if (ECMA_IS_VALUE_ERROR (prim_value))
{

View File

@ -1269,7 +1269,7 @@ ecma_builtin_json_serialize_property (ecma_json_stringify_context_t *context_p,
/* 5.a */
if (class_name == LIT_MAGIC_STRING_NUMBER_UL)
{
value = ecma_op_to_number (value);
value = ecma_op_to_number (value, ECMA_TO_NUMERIC_NO_OPTS);
ecma_deref_object (obj_p);
if (ECMA_IS_VALUE_ERROR (value))
@ -1598,7 +1598,7 @@ ecma_builtin_json_stringify (ecma_value_t this_arg, /**< 'this' argument */
/* 5.a */
if (class_name == LIT_MAGIC_STRING_NUMBER_UL)
{
ecma_value_t value = ecma_op_to_number (arg3);
ecma_value_t value = ecma_op_to_number (arg3, ECMA_TO_NUMERIC_NO_OPTS);
if (ECMA_IS_VALUE_ERROR (value))
{

View File

@ -135,7 +135,7 @@ ecma_builtin_math_object_max_min (bool is_max, /**< 'max' or 'min' operation */
}
else
{
ecma_value_t value = ecma_op_to_number (*arg);
ecma_value_t value = ecma_op_to_number (*arg, ECMA_TO_NUMERIC_NO_OPTS);
if (ECMA_IS_VALUE_ERROR (value))
{
@ -215,7 +215,7 @@ ecma_builtin_math_object_hypot (const ecma_value_t *arg, /**< arguments list */
}
else
{
ecma_value_t value = ecma_op_to_number (*arg);
ecma_value_t value = ecma_op_to_number (*arg, ECMA_TO_NUMERIC_NO_OPTS);
if (ECMA_IS_VALUE_ERROR (value))
{
return value;
@ -357,7 +357,7 @@ ecma_builtin_math_dispatch_routine (uint16_t builtin_routine_id, /**< built-in w
}
else
{
ecma_value_t value = ecma_op_to_number (arguments_list[0]);
ecma_value_t value = ecma_op_to_number (arguments_list[0], ECMA_TO_NUMERIC_NO_OPTS);
if (ECMA_IS_VALUE_ERROR (value))
{
@ -379,7 +379,7 @@ ecma_builtin_math_dispatch_routine (uint16_t builtin_routine_id, /**< built-in w
}
else
{
ecma_value_t value = ecma_op_to_number (arguments_list[1]);
ecma_value_t value = ecma_op_to_number (arguments_list[1], ECMA_TO_NUMERIC_NO_OPTS);
if (ECMA_IS_VALUE_ERROR (value))
{

View File

@ -16,6 +16,7 @@
#include <math.h>
#include "ecma-alloc.h"
#include "ecma-bigint.h"
#include "ecma-builtins.h"
#include "ecma-conversion.h"
#include "ecma-exceptions.h"
@ -84,7 +85,16 @@ ecma_builtin_number_dispatch_call (const ecma_value_t *arguments_list_p, /**< ar
}
else
{
ret_value = ecma_op_to_number (arguments_list_p[0]);
ret_value = ecma_op_to_number (arguments_list_p[0], ECMA_TO_NUMERIC_ALLOW_BIGINT);
#if ENABLED (JERRY_BUILTIN_BIGINT)
if (ecma_is_value_bigint (ret_value))
{
ecma_value_t bigint = ret_value;
ret_value = ecma_bigint_to_number (bigint);
ecma_free_value (bigint);
}
#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */
}
return ret_value;
@ -103,13 +113,30 @@ ecma_builtin_number_dispatch_construct (const ecma_value_t *arguments_list_p, /*
if (arguments_list_len == 0)
{
ecma_value_t completion = ecma_op_create_number_object (ecma_make_integer_value (0));
return completion;
return ecma_op_create_number_object (ecma_make_integer_value (0));
}
else
#if ENABLED (JERRY_BUILTIN_BIGINT)
ecma_value_t value = ecma_op_to_number (arguments_list_p[0], ECMA_TO_NUMERIC_ALLOW_BIGINT);
if (ecma_is_value_bigint (value))
{
return ecma_op_create_number_object (arguments_list_p[0]);
ecma_value_t bigint = value;
value = ecma_bigint_to_number (bigint);
ecma_free_value (bigint);
}
if (ECMA_IS_VALUE_ERROR (value))
{
return value;
}
ecma_value_t result = ecma_op_create_number_object (value);
ecma_free_value (value);
return result;
#else /* !ENABLED (JERRY_BUILTIN_BIGINT) */
return ecma_op_create_number_object (arguments_list_p[0]);
#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */
} /* ecma_builtin_number_dispatch_construct */
#if ENABLED (JERRY_ESNEXT)

View File

@ -291,7 +291,7 @@ ecma_builtin_string_object_from_code_point (ecma_value_t this_arg, /**< 'this' a
for (uint32_t index = 0; index < args_number; index++)
{
ecma_value_t to_number_value = ecma_op_to_number (args[index]);
ecma_value_t to_number_value = ecma_op_to_number (args[index], ECMA_TO_NUMERIC_NO_OPTS);
if (ECMA_IS_VALUE_ERROR (to_number_value))
{

View File

@ -925,7 +925,7 @@ ecma_op_array_object_set_length (ecma_object_t *object_p, /**< the array object
{
bool is_throw = (flags & ECMA_ARRAY_OBJECT_SET_LENGTH_FLAG_IS_THROW);
ecma_value_t completion = ecma_op_to_number (new_value);
ecma_value_t completion = ecma_op_to_number (new_value, ECMA_TO_NUMERIC_NO_OPTS);
if (ECMA_IS_VALUE_ERROR (completion))
{
@ -941,7 +941,7 @@ ecma_op_array_object_set_length (ecma_object_t *object_p, /**< the array object
if (ecma_is_value_object (new_value))
{
ecma_value_t compared_num_val = ecma_op_to_number (new_value);
ecma_value_t compared_num_val = ecma_op_to_number (new_value, ECMA_TO_NUMERIC_NO_OPTS);
if (ECMA_IS_VALUE_ERROR (compared_num_val))
{

View File

@ -119,7 +119,7 @@ ecma_op_create_arraybuffer_object (const ecma_value_t *arguments_list_p, /**< li
}
else
{
ecma_value_t to_number_value = ecma_op_to_number (arguments_list_p[0]);
ecma_value_t to_number_value = ecma_op_to_number (arguments_list_p[0], ECMA_TO_NUMERIC_NO_OPTS);
if (ECMA_IS_VALUE_ERROR (to_number_value))
{

View File

@ -93,6 +93,32 @@ ecma_big_uint_extend (ecma_extended_primitive_t *value_p, /**< BigUInt value */
return result_p;
} /* ecma_big_uint_extend */
/**
* Count the number of leading zero bits of a digit
*
* return number of leading zero bits
*/
ecma_bigint_digit_t
ecma_big_uint_count_leading_zero (ecma_bigint_digit_t digit) /**< digit value */
{
ecma_bigint_digit_t shift = 4 * sizeof (ecma_bigint_digit_t);
ecma_bigint_digit_t result = 8 * sizeof (ecma_bigint_digit_t);
do
{
ecma_bigint_digit_t value = digit >> shift;
if (value > 0)
{
digit = value;
result -= shift;
}
shift >>= 1;
}
while (shift > 0);
return result - digit;
} /* ecma_big_uint_count_leading_zero */
/**
* Helper function which discards the leading zero digits of a BigUInt value
*
@ -876,32 +902,6 @@ ecma_big_uint_mul (ecma_extended_primitive_t *left_value_p, /**< left BigUInt va
return ecma_big_uint_extend (result_p, extra_space[0]);
} /* ecma_big_uint_mul */
/**
* Count the number of leading zero bits of a digit
*
* return new BigUInt value, NULL on error
*/
static ecma_bigint_digit_t
ecma_big_uint_count_leading_zero (ecma_bigint_digit_t digit) /**< digit value */
{
ecma_bigint_digit_t shift = 4 * sizeof (ecma_bigint_digit_t);
ecma_bigint_digit_t result = 8 * sizeof (ecma_bigint_digit_t);
do
{
ecma_bigint_digit_t value = digit >> shift;
if (value > 0)
{
digit = value;
result -= shift;
}
shift >>= 1;
}
while (shift > 0);
return result - digit;
} /* ecma_big_uint_count_leading_zero */
/**
* Divide left BigUInt value with right digit value
*

View File

@ -100,6 +100,8 @@ typedef enum
ecma_extended_primitive_t *ecma_bigint_create (uint32_t size);
ecma_extended_primitive_t *ecma_big_uint_extend (ecma_extended_primitive_t *value_p, ecma_bigint_digit_t digit);
ecma_bigint_digit_t ecma_big_uint_count_leading_zero (ecma_bigint_digit_t digit);
int ecma_big_uint_compare (ecma_extended_primitive_t *left_value_p, ecma_extended_primitive_t *right_value_p);
ecma_extended_primitive_t *ecma_big_uint_mul_digit (ecma_extended_primitive_t *value_p,

View File

@ -482,6 +482,140 @@ ecma_bigint_to_bigint (ecma_value_t value, /**< any value */
return result;
} /* ecma_bigint_to_bigint */
/**
* Convert a BigInt value to number value
*
* @return ecma number value or ECMA_VALUE_ERROR
* Returned value must be freed with ecma_free_value.
*/
ecma_value_t
ecma_bigint_to_number (ecma_value_t value) /**< BigInt value */
{
JERRY_ASSERT (ecma_is_value_bigint (value));
if (value == ECMA_BIGINT_ZERO)
{
return ecma_make_integer_value (0);
}
ecma_extended_primitive_t *value_p = ecma_get_extended_primitive_from_value (value);
uint32_t size = ECMA_BIGINT_GET_SIZE (value_p);
ecma_bigint_digit_t *digits_p = ECMA_BIGINT_GET_DIGITS (value_p, size);
if (size == sizeof (ecma_bigint_digit_t))
{
if (!(value_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN))
{
if (digits_p[-1] <= ECMA_INTEGER_NUMBER_MAX)
{
return ecma_make_integer_value ((ecma_integer_value_t) digits_p[-1]);
}
}
else if (digits_p[-1] <= -ECMA_INTEGER_NUMBER_MIN)
{
return ecma_make_integer_value (-(ecma_integer_value_t) digits_p[-1]);
}
}
uint64_t fraction = 0;
ecma_bigint_digit_t shift_left;
if (digits_p[-1] == 1)
{
JERRY_ASSERT (size > sizeof (ecma_bigint_digit_t));
fraction = ((uint64_t) digits_p[-2]) << (8 * sizeof (ecma_bigint_digit_t));
shift_left = (uint32_t) (8 * sizeof (ecma_bigint_digit_t));
if (size >= 3 * sizeof (ecma_bigint_digit_t))
{
fraction |= (uint64_t) digits_p[-3];
}
}
else
{
shift_left = ecma_big_uint_count_leading_zero (digits_p[-1]) + 1;
fraction = ((uint64_t) digits_p[-1]) << (8 * sizeof (ecma_bigint_digit_t) + shift_left);
if (size >= 2 * sizeof (ecma_bigint_digit_t))
{
fraction |= ((uint64_t) digits_p[-2]) << shift_left;
}
if (size >= 3 * sizeof (ecma_bigint_digit_t))
{
fraction |= ((uint64_t) digits_p[-3]) >> (8 * sizeof (ecma_bigint_digit_t) - shift_left);
}
}
uint32_t biased_exp = (uint32_t) (((1 << (ECMA_NUMBER_BIASED_EXP_WIDTH - 1)) - 1) + (size * 8 - shift_left));
/* Rounding result. */
const uint64_t rounding_bit = (((uint64_t) 1) << (8 * sizeof (uint64_t) - ECMA_NUMBER_FRACTION_WIDTH - 1));
bool round_up = false;
if (fraction & rounding_bit)
{
round_up = true;
/* IEEE_754 roundTiesToEven mode: when rounding_bit is set, and all the remaining bits
* are zero, the number needs to be rounded down the bit before rounding_bit is zero. */
if ((fraction & ((rounding_bit << 2) - 1)) == rounding_bit)
{
round_up = false;
if ((size >= (3 * sizeof (ecma_bigint_digit_t)))
&& (shift_left == (8 * sizeof (ecma_bigint_digit_t))
|| (digits_p[-3] & ((((ecma_bigint_digit_t) 1) << shift_left) - 1)) == 0))
{
ecma_bigint_digit_t *digits_start_p = ECMA_BIGINT_GET_DIGITS (value_p, 0);
digits_p -= 3;
while (digits_p > digits_start_p)
{
if (digits_p[-1] != 0)
{
round_up = true;
break;
}
digits_p--;
}
}
}
}
if (round_up)
{
fraction += rounding_bit;
fraction >>= (8 * sizeof (uint64_t) - ECMA_NUMBER_FRACTION_WIDTH);
if (fraction == 0)
{
biased_exp++;
}
}
else
{
fraction >>= (8 * sizeof (uint64_t) - ECMA_NUMBER_FRACTION_WIDTH);
}
bool sign = (value_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN);
ecma_number_t result;
if (biased_exp < (1u << ECMA_NUMBER_BIASED_EXP_WIDTH) - 1)
{
result = ecma_number_pack (sign, biased_exp, fraction);
}
else
{
result = ecma_number_make_infinity (sign);
}
return ecma_make_number_value (result);
} /* ecma_bigint_to_number */
/**
* Returns with a BigInt if the value is BigInt,
* or the value is object, and its default value is BigInt

View File

@ -53,6 +53,7 @@ ecma_value_t ecma_bigint_parse_string (const lit_utf8_byte_t *string_p, lit_utf8
ecma_value_t ecma_bigint_parse_string_value (ecma_value_t string, uint32_t options);
ecma_string_t *ecma_bigint_to_string (ecma_value_t value, ecma_bigint_digit_t radix);
ecma_value_t ecma_bigint_to_bigint (ecma_value_t value, bool allow_numbers);
ecma_value_t ecma_bigint_to_number (ecma_value_t value);
ecma_value_t ecma_bigint_get_bigint (ecma_value_t value, bool *free_result_p);
ecma_value_t ecma_bigint_create_from_digits (const uint64_t *digits_p, uint32_t size, bool sign);
uint32_t ecma_bigint_get_size_in_digits (ecma_value_t value);

View File

@ -113,7 +113,7 @@ ecma_op_abstract_equality_compare (ecma_value_t x, /**< first operand */
if (ecma_is_value_number (y))
{
/* 4. */
ecma_value_t x_num_value = ecma_op_to_number (x);
ecma_value_t x_num_value = ecma_op_to_number (x, ECMA_TO_NUMERIC_NO_OPTS);
if (ECMA_IS_VALUE_ERROR (x_num_value))
{

View File

@ -269,8 +269,11 @@ ecma_op_to_boolean (ecma_value_t value) /**< ecma value */
* Returned value must be freed with ecma_free_value
*/
ecma_value_t
ecma_op_to_number (ecma_value_t value) /**< ecma value */
ecma_op_to_number (ecma_value_t value, /**< ecma value */
ecma_to_numeric_options_t options) /**< option bits */
{
JERRY_UNUSED (options);
ecma_check_value_type_is_spec_defined (value);
if (ecma_is_value_integer_number (value))
@ -314,6 +317,10 @@ ecma_op_to_number (ecma_value_t value) /**< ecma value */
#if ENABLED (JERRY_BUILTIN_BIGINT)
if (ecma_is_value_bigint (value))
{
if (options & ECMA_TO_NUMERIC_ALLOW_BIGINT)
{
return ecma_copy_value (value);
}
return ecma_raise_type_error (ECMA_ERR_MSG ("Cannot convert a BigInt value to a number"));
}
#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */
@ -329,7 +336,7 @@ ecma_op_to_number (ecma_value_t value) /**< ecma value */
return def_value;
}
ecma_value_t ret_value = ecma_op_to_number (def_value);
ecma_value_t ret_value = ecma_op_to_number (def_value, options);
ecma_fast_free_value (def_value);

View File

@ -43,7 +43,7 @@ typedef enum
typedef enum
{
ECMA_TO_NUMERIC_NO_OPTS = 0, /**< no options (same as toNumber operation) */
ECMA_TO_NUMERIC_ALLOW_BIGINT = (1 << 0), /**< allow BigInt values */
ECMA_TO_NUMERIC_ALLOW_BIGINT = (1 << 0), /**< allow BigInt values (ignored if BigInts are disabled) */
} ecma_to_numeric_options_t;
ecma_value_t ecma_op_check_object_coercible (ecma_value_t value);
@ -53,7 +53,7 @@ bool ecma_op_same_value_zero (ecma_value_t x, ecma_value_t y);
#endif /* ENABLED (JERRY_BUILTIN_MAP) */
ecma_value_t ecma_op_to_primitive (ecma_value_t value, ecma_preferred_type_hint_t preferred_type);
bool ecma_op_to_boolean (ecma_value_t value);
ecma_value_t ecma_op_to_number (ecma_value_t value);
ecma_value_t ecma_op_to_number (ecma_value_t value, ecma_to_numeric_options_t options);
ecma_value_t ecma_op_to_numeric (ecma_value_t value, ecma_number_t *number_p, ecma_to_numeric_options_t options);
ecma_string_t *ecma_op_to_string (ecma_value_t value);
ecma_string_t *ecma_op_to_property_key (ecma_value_t value);

View File

@ -41,7 +41,7 @@
ecma_value_t
ecma_op_create_number_object (ecma_value_t arg) /**< argument passed to the Number constructor */
{
ecma_value_t conv_to_num_completion = ecma_op_to_number (arg);
ecma_value_t conv_to_num_completion = ecma_op_to_number (arg, ECMA_TO_NUMERIC_NO_OPTS);
if (ECMA_IS_VALUE_ERROR (conv_to_num_completion))
{

View File

@ -0,0 +1,42 @@
/* 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(Number(0n) === 0)
assert(Number(1n) === 1)
assert(Number(2n) === 2)
assert(Number(0x100n) === 256)
assert(Number(-1n) === -1)
assert(Number(-2n) === -2)
assert(Number(-0x100n) === -256)
assert(Number(0x1fffffffffffffn) === 0x1fffffffffffff)
assert(Number(-0x3fffffffffffffn) === -0x40000000000000)
assert(Number(1000n ** 1000n) === Number.POSITIVE_INFINITY)
assert(Number((-1000n) ** 1001n) === Number.NEGATIVE_INFINITY)
// Rounding to even
assert(Number(0x80000000000004n) === 0x80000000000000)
assert(Number(0x80000000000008n) === 0x80000000000008)
assert(Number(0x8000000000000cn) === 0x80000000000010)
assert(Number(0x80000000000004n) === 0x80000000000000)
assert(Number(0x80000000000006n) === 0x80000000000008)
assert(Number(0x800000000000f400000000000000000000000000000000n) === 0x800000000000f000000000000000000000000000000000)
assert(Number(0x800000000000f400000000000000000000000000000001n) === 0x800000000000f800000000000000000000000000000000)
// Construct
assert((new Number(0n)).valueOf() === 0)
assert((new Number(-3256n)).valueOf() == -3256)

View File

@ -658,7 +658,6 @@
<test id="built-ins/BigInt/asUintN/order-of-steps.js"><reason></reason></test>
<test id="built-ins/BigInt/constructor-empty-string.js"><reason></reason></test>
<test id="built-ins/BigInt/constructor-trailing-leading-spaces.js"><reason></reason></test>
<test id="built-ins/BigInt/prototype/toString/a-z.js"><reason></reason></test>
<test id="built-ins/BigInt/prototype/toString/length.js"><reason></reason></test>
<test id="built-ins/BigInt/prototype/valueOf/cross-realm.js"><reason></reason></test>
<test id="built-ins/Boolean/proto-from-ctor-realm.js"><reason></reason></test>
@ -1062,7 +1061,6 @@
<test id="built-ins/NativeErrors/SyntaxError/proto-from-ctor-realm.js"><reason></reason></test>
<test id="built-ins/NativeErrors/TypeError/proto-from-ctor-realm.js"><reason></reason></test>
<test id="built-ins/NativeErrors/URIError/proto-from-ctor-realm.js"><reason></reason></test>
<test id="built-ins/Number/bigint-conversion.js"><reason></reason></test>
<test id="built-ins/Number/proto-from-ctor-realm.js"><reason></reason></test>
<test id="built-ins/Number/prototype/toExponential/return-values.js"><reason></reason></test>
<test id="built-ins/Number/prototype/toFixed/exactness.js"><reason></reason></test>