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
21 changed files with 266 additions and 54 deletions
+134
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