-
Notifications
You must be signed in to change notification settings - Fork 686
Implement BigInt asIntN and asUintN methods #4736
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,14 +14,31 @@ | |
| */ | ||
|
|
||
| #include "ecma-bigint.h" | ||
| #include "ecma-big-uint.h" | ||
| #include "ecma-builtins.h" | ||
| #include "ecma-exceptions.h" | ||
| #include <math.h> | ||
|
|
||
| #if JERRY_BUILTIN_BIGINT | ||
|
|
||
| #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, | ||
| ECMA_BUILTIN_BIGINT_AS_INT_N, | ||
| ECMA_BUILTIN_BIGINT_AS_U_INT_N, | ||
| }; | ||
|
|
||
| #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 +53,311 @@ | |
| * @{ | ||
| */ | ||
|
|
||
| /** | ||
| * 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; | ||
| } | ||
|
|
||
| const uint32_t size_of_divisor_in_bits = sizeof (uint32_t) * JERRY_BITSINBYTE; | ||
|
|
||
| uint32_t whole_part = (uint32_t) floor (input_bits / size_of_divisor_in_bits); | ||
| uint32_t remainder = (uint32_t) fmod (input_bits, size_of_divisor_in_bits); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The input_bits should be an integer number. Furthermore JerryScript has a maximum length for bigints, any number greater than that does not need any clamping (just increase its refcount and return with it).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The head of the code is coming from the standard. So the input_bits should be a number according to the ToIndex method.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice but my comment stiill stands, if the number is greater than the maximum bits allowed, just return with the original value, otherwise you can use an integer div and mod. If uint conversion is needed, and the original value is negative, throw an allocation error. |
||
|
|
||
| uint32_t input_bit_length = 1; | ||
batizdaniel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| 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) floor (input_bits / JERRY_BITSINBYTE); | ||
|
|
||
| if (fmod (input_bits, JERRY_BITSINBYTE) != 0) | ||
| { | ||
| input_byte_size += 1; | ||
| } | ||
|
|
||
| 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); | ||
| uint8_t input_bigint_sign = input_bigint_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN; | ||
|
|
||
| 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_bit_s = 0; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. bit_s? |
||
|
|
||
| if (remainder != 0) | ||
| { | ||
| equal_bit_s = -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) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there is no |
||
| { | ||
| result_number_p[first_cell] += 1; | ||
|
|
||
| if (result_number_p[first_cell] == 0) | ||
| { | ||
| do | ||
| { | ||
| result_number_p[++first_cell] += 1; | ||
| } | ||
| while (first_cell != equal_bit_s); | ||
|
|
||
| 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 +393,42 @@ ecma_builtin_bigint_dispatch_construct (const ecma_value_t *arguments_list_p, /* | |
| return ecma_raise_type_error (ECMA_ERR_MSG ("BigInt function is not a 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 */ | ||
|
|
||
| /** | ||
| * @} | ||
| * @} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing comments.