From cb1468298323f4373a02f651234aa6a1d28149c1 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Fri, 7 Aug 2020 13:43:29 +0200 Subject: [PATCH] Create API for handling BigInts (#4111) JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- docs/02.API-REFERENCE.md | 293 ++++++++++++++++++ jerry-core/api/jerry.c | 135 +++++++- .../builtin-objects/ecma-builtin-bigint.c | 32 +- jerry-core/ecma/operations/ecma-bigint.c | 223 ++++++++++++- jerry-core/ecma/operations/ecma-bigint.h | 6 +- jerry-core/include/jerryscript-core.h | 9 + tests/test262-esnext-excludelist.xml | 3 - tests/unit-core/test-bigint.c | 186 +++++++++++ 8 files changed, 833 insertions(+), 54 deletions(-) create mode 100644 tests/unit-core/test-bigint.c diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 278b46e6c..d8d7af98f 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -2031,6 +2031,69 @@ main (void) - [jerry_release_value](#jerry_release_value) - [jerry_create_symbol](#jerry_create_symbol) + +## jerry_value_is_bigint + +**Summary** + +Returns whether the given `jerry_value_t` is a bigint value. + +*Notes*: +- This API depends on a build option (`JERRY_BUILTIN_BIGINT`) and can be checked + in runtime with the `JERRY_FEATURE_BIGINT` feature enum value, + see: [jerry_is_feature_enabled](#jerry_is_feature_enabled). +- The es.next profile enables this by default. + +**Prototype** + +```c +bool +jerry_value_is_bigint (const jerry_value_t value) +``` + +- `value` - API value +- return value + - true, if the given `jerry_value_t` is a BigInt + - false, otherwise + +*New in version [[NEXT_RELEASE]]* + +**Example** + +[doctest]: # () + +```c +#include "jerryscript.h" + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t string_value = jerry_create_string ((const jerry_char_t *) "12345678"); + jerry_value_t bigint_value = jerry_value_to_bigint (string_value); + + jerry_release_value (string_value); + + if (jerry_value_is_bigint (bigint_value)) + { + // usage of bigint_value + } + + jerry_release_value (bigint_value); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_release_value](#jerry_release_value) +- [jerry_create_bigint](#jerry_create_bigint) +- [jerry_value_to_bigint](#jerry_value_to_bigint) + + ## jerry_value_is_typedarray **Summary** @@ -3374,6 +3437,50 @@ jerry_value_to_string (const jerry_value_t value); - [jerry_value_to_primitive](#jerry_value_to_primitive) +## jerry_value_to_bigint + +**Summary** + +Call the BigInt constructor ecma builtin operation on the api value. + +*Note*: Returned value must be freed with [jerry_release_value](#jerry_release_value) when it +is no longer needed. + +**Prototype** + +```c +jerry_value_t +jerry_value_to_bigint (const jerry_value_t value); +``` + +- `value` - api value +- return value + - converted BigInt value, if success + - thrown error, otherwise + +*New in version [[NEXT_RELEASE]]* + +**Example** + +```c +{ + jerry_value_t value; + ... // create or acquire value + + jerry_value_t bigint_value = jerry_value_to_bigint (value); + + jerry_release_value (bigint_value); + jerry_release_value (value); +} +``` + +**See also** + +- [jerry_release_value](#jerry_release_value) +- [jerry_value_is_bigint](#jerry_value_is_bigint) +- [jerry_get_bigint_digits](#jerry_get_bigint_digits) + + # Functions for promise objects These APIs all depend on the es.next profile (or on some build options). @@ -3643,6 +3750,133 @@ main (void) } ``` +# Functions for BigInts + +These APIs all depend on build option (`JERRY_BUILTIN_BIGINT`). + +## jerry_get_bigint_size_in_digits + +**Summary** + +Returns the size of uint64 digits of a BigInt value. This value is the +minimum size of the buffer which can hold all digits of a BigInt value when +the digits are retreived by `[jerry_get_bigint_digits](#jerry_get_bigint_digits)`. + +*Notes*: +- This API depends on a build option (`JERRY_BUILTIN_BIGINT`) and can be checked + in runtime with the `JERRY_FEATURE_BIGINT` feature enum value, + see: [jerry_is_feature_enabled](#jerry_is_feature_enabled). +- Returned value must be freed with [jerry_release_value](#jerry_release_value) when it + is no longer needed. + +**Prototype** + +```c +uint32_t +jerry_get_bigint_size_in_digits (jerry_value_t value) +``` + +- `value` - BigInt value +- return value + - number of digits (can be zero for BigInt zero) + - if value is not a BigInt value, it returns with zero + +*New in version [[NEXT_RELEASE]]* + +**Example** + +[doctest]: # () + +```c +#include "jerryscript.h" + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + uint64_t digits[4] = { 0x1, 0x1, 0x0, 0x0 }; + jerry_value_t bigint_value = jerry_create_bigint (digits, 4, true); + + /* Prints two, because the leading zeroes in digits buffer are discarded. */ + printf("size: %d\n", (int) jerry_get_bigint_size_in_digits (bigint_value)); + + jerry_release_value (bigint_value); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_value_is_bigint](#jerry_value_is_bigint) +- [jerry_get_bigint_digits](#jerry_get_bigint_digits) + + +## jerry_get_bigint_digits + +**Summary** + +Copies the uint64 digits of a BigInt value into a buffer. This function supports any +buffer sizes. If the buffer size is smaller than the size returned by +`[jerry_get_bigint_size_in_digits](#jerry_get_bigint_size_in_digits)`, only the +least significant digits are copied into the buffer. If the buffer size is greater, +the unused digits are filled with zero. + +*Notes*: +- This API depends on a build option (`JERRY_BUILTIN_BIGINT`) and can be checked + in runtime with the `JERRY_FEATURE_BIGINT` feature enum value, + see: [jerry_is_feature_enabled](#jerry_is_feature_enabled). +- Returned value must be freed with [jerry_release_value](#jerry_release_value) when it + is no longer needed. + +**Prototype** + +```c +void +jerry_get_bigint_digits (jerry_value_t value, uint64_t *digits_p, uint32_t size, bool *sign_p) +``` + +- `value` - BigInt value +- `digits_p` - output buffer for digits +- `size` - size of the output buffer +- `sign_p` - the boolean passed to the function is set to false when the BigInt + is positive, and set to true otherwise (this parameter can be NULL) + +*New in version [[NEXT_RELEASE]]* + +**Example** + +[doctest]: # () + +```c +#include "jerryscript.h" + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + uint64_t digits[4] = { 0x1, 0x1, 0x0, 0x0 }; + jerry_value_t bigint_value = jerry_create_bigint (digits, 4, true); + + uint64_t get_digits[4]; + bool sign; + jerry_get_bigint_digits (bigint_value, get_digits, 2, &sign); + + jerry_release_value (bigint_value); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_value_is_bigint](#jerry_value_is_bigint) +- [jerry_get_bigint_size_in_digits](#jerry_get_bigint_size_in_digits) + # Acquire and release API values @@ -4716,6 +4950,65 @@ main (void) - [jerry_release_value](#jerry_release_value) +## jerry_create_bigint + +**Summary** + +Create BigInt value from uint64 digits + +*Note*: +- This API depends on a build option (`JERRY_BUILTIN_BIGINT`) and can be checked + in runtime with the `JERRY_FEATURE_BIGINT` feature enum value, + see: [jerry_is_feature_enabled](#jerry_is_feature_enabled). +- Returned value must be freed with [jerry_release_value](#jerry_release_value) + when it is no longer needed. + +**Prototype** + +```c +jerry_value_t +jerry_create_bigint (const uint64_t *digits_p, uint32_t size, bool sign) +``` + +- `digits_p` - array of uint64 digits, least significant digit first +- `size` - size of the `digits_p` array +- `sign` - false if the created value should be positive, and true if the created value should be negative +- return value + - value of the created bigint, if success + - thrown error, otherwise + +*New in version [[NEXT_RELEASE]]* + +**Example** + +[doctest]: # () + +```c +#include "jerryscript.h" + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + uint64_t digits[2] = { 0x1, 0x1 }; + jerry_value_t bigint_value = jerry_create_bigint (digits, 2, true); + + // usage of bigint_value + + jerry_release_value (bigint_value); + + jerry_cleanup (); +} +``` + +**See also** + +- [jerry_release_value](#jerry_release_value) +- [jerry_value_is_bigint](#jerry_value_is_bigint) +- [jerry_get_bigint_digits](#jerry_get_bigint_digits) + + ## jerry_create_regexp **Summary** diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 6a4423a7e..e870a7747 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -19,6 +19,7 @@ #include "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-arraybuffer-object.h" +#include "ecma-bigint.h" #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-comparison.h" @@ -108,6 +109,15 @@ static const char * const error_value_msg_p = "argument cannot have an error fla */ static const char * const wrong_args_msg_p = "wrong type of argument"; +#if !ENABLED (JERRY_BUILTIN_BIGINT) + +/** + * Error message, if BigInt support is disabled + */ +static const char * const error_bigint_not_supported_p = "BigInt support is disabled"; + +#endif /* !ENABLED (JERRY_BUILTIN_BIGINT) */ + #endif /* ENABLED (JERRY_ERROR_MESSAGES) */ /** \addtogroup jerry Jerry engine interface @@ -841,6 +851,25 @@ jerry_value_is_symbol (const jerry_value_t value) /**< api value */ #endif /* ENABLED (JERRY_ESNEXT) */ } /* jerry_value_is_symbol */ +/** + * Check if the specified value is BigInt. + * + * @return true - if the specified value is BigInt, + * false - otherwise + */ +bool +jerry_value_is_bigint (const jerry_value_t value) /**< api value */ +{ + jerry_assert_api_available (); + +#if ENABLED (JERRY_BUILTIN_BIGINT) + return ecma_is_value_bigint (value); +#else /* !ENABLED (JERRY_BUILTIN_BIGINT) */ + JERRY_UNUSED (value); + return false; +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ +} /* jerry_value_is_bigint */ + /** * Check if the specified value is undefined. * @@ -1345,9 +1374,36 @@ jerry_value_to_string (const jerry_value_t value) /**< input value */ return ecma_create_error_reference_from_context (); } - return jerry_return (ecma_make_string_value (str_p)); + return ecma_make_string_value (str_p); } /* jerry_value_to_string */ +/** + * Call the BigInt constructor ecma builtin operation on the api value. + * + * Note: + * returned value must be freed with jerry_release_value, when it is no longer needed. + * + * @return BigInt value - if success + * thrown error - otherwise + */ +jerry_value_t +jerry_value_to_bigint (const jerry_value_t value) /**< input value */ +{ + jerry_assert_api_available (); + +#if ENABLED (JERRY_BUILTIN_BIGINT) + if (ecma_is_value_error_reference (value)) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (error_value_msg_p))); + } + + return jerry_return (ecma_bigint_to_bigint (value, true)); +#else /* !ENABLED (JERRY_BUILTIN_BIGINT) */ + JERRY_UNUSED (value); + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (error_bigint_not_supported_p))); +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ +} /* jerry_value_to_bigint */ + /** * Acquire specified Jerry API value. * @@ -1776,6 +1832,29 @@ jerry_create_symbol (const jerry_value_t value) /**< api value */ #endif /* ENABLED (JERRY_ESNEXT) */ } /* jerry_create_symbol */ +/** + * Create BigInt from a sequence of uint64 digits + * + * @return value of the created bigint, if success + * thrown error, otherwise + */ +jerry_value_t +jerry_create_bigint (const uint64_t *digits_p, /**< BigInt digits (lowest digit first) */ + uint32_t size, /**< number of BigInt digits */ + bool sign) /**< sign bit, true if the result should be negative */ +{ + jerry_assert_api_available (); + +#if ENABLED (JERRY_BUILTIN_BIGINT) + return jerry_return (ecma_bigint_create_from_digits (digits_p, size, sign)); +#else /* !ENABLED (JERRY_BUILTIN_BIGINT) */ + JERRY_UNUSED (digits_p); + JERRY_UNUSED (size); + JERRY_UNUSED (sign); + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (error_bigint_not_supported_p))); +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ +} /* jerry_create_bigint */ + /** * Calculates the size of the given pattern and creates a RegExp object. * @@ -3405,6 +3484,60 @@ jerry_get_symbol_descriptive_string (const jerry_value_t symbol) /**< symbol val #endif /* ENABLED (JERRY_ESNEXT) */ } /** jerry_get_symbol_descriptive_string */ +/** + * Get the number of uint64 digits of a BigInt value + * + * @return number of uint64 digits + */ +uint32_t +jerry_get_bigint_size_in_digits (jerry_value_t value) /**< BigInt value */ +{ + jerry_assert_api_available (); + +#if ENABLED (JERRY_BUILTIN_BIGINT) + if (!ecma_is_value_bigint (value)) + { + return 0; + } + + return ecma_bigint_get_size_in_digits (value); +#else /* !ENABLED (JERRY_BUILTIN_BIGINT) */ + JERRY_UNUSED (value); + return 0; +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ +} /* jerry_get_bigint_size_in_digits */ + +/** + * Get the uint64 digits of a BigInt value (lowest digit first) + */ +void +jerry_get_bigint_digits (jerry_value_t value, /**< BigInt value */ + uint64_t *digits_p, /**< [out] buffer for digits */ + uint32_t size, /**< buffer size in digits */ + bool *sign_p) /**< [out] sign of BigInt */ +{ +#if ENABLED (JERRY_BUILTIN_BIGINT) + if (!ecma_is_value_bigint (value)) + { + if (sign_p != NULL) + { + *sign_p = false; + } + memset (digits_p, 0, size * sizeof (uint64_t)); + } + + ecma_bigint_get_digits_and_sign (value, digits_p, size, sign_p); +#else /* !ENABLED (JERRY_BUILTIN_BIGINT) */ + JERRY_UNUSED (value); + + if (sign_p != NULL) + { + *sign_p = false; + } + memset (digits_p, 0, size * sizeof (uint64_t)); +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ +} /* jerry_get_bigint_digits */ + /** * Validate UTF-8 string * diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.c b/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.c index fcc06bfe4..d298873a1 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.c @@ -51,37 +51,7 @@ ecma_builtin_bigint_dispatch_call (const ecma_value_t *arguments_list_p, /**< ar JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); ecma_value_t value = (arguments_list_len == 0) ? ECMA_VALUE_UNDEFINED : arguments_list_p[0]; - bool free_value = false; - - if (ecma_is_value_object (value)) - { - ecma_object_t *obj_p = ecma_get_object_from_value (value); - value = ecma_op_object_default_value (obj_p, ECMA_PREFERRED_TYPE_NUMBER); - free_value = true; - - if (ECMA_IS_VALUE_ERROR (value)) - { - return value; - } - } - - ecma_value_t ret_value = ECMA_VALUE_EMPTY; - - if (ecma_is_value_number (value)) - { - ret_value = ecma_bigint_number_to_bigint (ecma_get_number_from_value (value)); - } - else - { - ret_value = ecma_bigint_to_bigint (value); - } - - if (free_value) - { - ecma_free_value (value); - } - - return ret_value; + return ecma_bigint_to_bigint (value, true); } /* ecma_builtin_bigint_dispatch_call */ /** diff --git a/jerry-core/ecma/operations/ecma-bigint.c b/jerry-core/ecma/operations/ecma-bigint.c index b6d2d6d31..8cbc84479 100644 --- a/jerry-core/ecma/operations/ecma-bigint.c +++ b/jerry-core/ecma/operations/ecma-bigint.c @@ -17,6 +17,7 @@ #include "ecma-big-uint.h" #include "ecma-exceptions.h" #include "ecma-helpers.h" +#include "ecma-objects.h" #include "lit-char-helpers.h" #if ENABLED (JERRY_BUILTIN_BIGINT) @@ -340,7 +341,7 @@ ecma_bigint_number_to_digits (ecma_number_t number, /**< ecma number */ * @return ecma BigInt value or ECMA_VALUE_ERROR * Returned value must be freed with ecma_free_value. */ -ecma_value_t +static ecma_value_t ecma_bigint_number_to_bigint (ecma_number_t number) /**< ecma number */ { if (ecma_number_is_nan (number) || ecma_number_is_infinity (number)) @@ -397,34 +398,222 @@ ecma_bigint_number_to_bigint (ecma_number_t number) /**< ecma number */ * Returned value must be freed with ecma_free_value. */ ecma_value_t -ecma_bigint_to_bigint (ecma_value_t value) /**< any value */ +ecma_bigint_to_bigint (ecma_value_t value, /**< any value */ + bool allow_numbers) /**< converting ecma numbers is allowed */ { - if (ecma_is_value_boolean (value)) - { - if (ecma_is_value_false (value)) - { - return ECMA_BIGINT_ZERO; - } + bool free_value = false; + if (ecma_is_value_object (value)) + { + value = ecma_op_object_default_value (ecma_get_object_from_value (value), ECMA_PREFERRED_TYPE_NUMBER); + free_value = true; + + if (ECMA_IS_VALUE_ERROR (value)) + { + return value; + } + } + + ecma_value_t result; + + if (ecma_is_value_string (value)) + { + result = ecma_bigint_parse_string_value (value, ECMA_BIGINT_PARSE_NO_OPTIONS); + } + else if (ecma_is_value_bigint (value)) + { + result = value; + + if (!free_value && value != ECMA_BIGINT_ZERO) + { + ecma_ref_extended_primitive (ecma_get_extended_primitive_from_value (value)); + } + else + { + free_value = false; + } + } + else if (allow_numbers && ecma_is_value_number (value)) + { + result = ecma_bigint_number_to_bigint (ecma_get_number_from_value (value)); + } + else if (ecma_is_value_false (value)) + { + result = ECMA_BIGINT_ZERO; + } + else if (ecma_is_value_true (value)) + { ecma_extended_primitive_t *result_p = ecma_bigint_create (sizeof (ecma_bigint_digit_t)); - if (JERRY_UNLIKELY (result_p == NULL)) + if (result_p != NULL) { - return ecma_bigint_raise_memory_error (); + *ECMA_BIGINT_GET_DIGITS (result_p, 0) = 1; + result = ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); + } + else + { + result = ecma_bigint_raise_memory_error (); } - - *ECMA_BIGINT_GET_DIGITS (result_p, 0) = 1; - return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); } - - if (!ecma_is_value_string (value)) + else { - return ecma_raise_type_error (ECMA_ERR_MSG ("Value cannot be converted to BigInt")); + result = ecma_raise_type_error (ECMA_ERR_MSG ("Value cannot be converted to BigInt")); } - return ecma_bigint_parse_string_value (value, ECMA_BIGINT_PARSE_NO_OPTIONS); + if (free_value) + { + ecma_free_value (value); + } + + return result; } /* ecma_bigint_to_bigint */ +/** + * Create BigInt value from uint64 digits + * + * @return ecma BigInt value or ECMA_VALUE_ERROR + * Returned value must be freed with ecma_free_value. + */ +ecma_value_t +ecma_bigint_create_from_digits (const uint64_t *digits_p, /**< BigInt digits */ + uint32_t size, /**< number of BigInt digits */ + bool sign) /**< sign bit, true if the result should be negative */ +{ + const uint64_t *digits_end_p = digits_p + size; + + while (digits_end_p > digits_p && digits_end_p[-1] == 0) + { + digits_end_p--; + } + + if (digits_p == digits_end_p) + { + return ECMA_BIGINT_ZERO; + } + + size = (uint32_t) (digits_end_p - digits_p); + + if (size < ECMA_BIGINT_MAX_SIZE) + { + size *= (uint32_t) sizeof (uint64_t); + } + + if ((digits_end_p[-1] >> (8 * sizeof (ecma_bigint_digit_t))) == 0) + { + size -= (uint32_t) sizeof (ecma_bigint_digit_t); + } + + ecma_extended_primitive_t *result_value_p = ecma_bigint_create (size); + + if (JERRY_UNLIKELY (result_value_p == NULL)) + { + return ecma_bigint_raise_memory_error (); + } + + if (sign) + { + result_value_p->u.bigint_sign_and_size |= ECMA_BIGINT_SIGN; + } + + ecma_bigint_digit_t *result_p = ECMA_BIGINT_GET_DIGITS (result_value_p, 0); + + while (digits_p < digits_end_p) + { + uint64_t digit = *digits_p++; + + result_p[0] = (ecma_bigint_digit_t) digit; + result_p[1] = (ecma_bigint_digit_t) (digit >> (8 * sizeof (ecma_bigint_digit_t))); + result_p+= 2; + } + + return ecma_make_extended_primitive_value (result_value_p, ECMA_TYPE_BIGINT); +} /* ecma_bigint_create_from_digits */ + +/** + * Get the number of uint64 digits of a BigInt value + * + * @return number of uint64 digits + */ +uint32_t +ecma_bigint_get_size_in_digits (ecma_value_t value) /**< BigInt value */ +{ + JERRY_ASSERT (ecma_is_value_bigint (value)); + + if (value == ECMA_BIGINT_ZERO) + { + return 0; + } + + ecma_extended_primitive_t *value_p = ecma_get_extended_primitive_from_value (value); + uint32_t size = ECMA_BIGINT_GET_SIZE (value_p); + + return (size + (uint32_t) sizeof (ecma_bigint_digit_t)) / sizeof (uint64_t); +} /* ecma_bigint_get_size_in_digits */ + +/** + * Get the uint64 digits of a BigInt value + */ +void +ecma_bigint_get_digits_and_sign (ecma_value_t value, /**< BigInt value */ + uint64_t *digits_p, /**< [out] buffer for digits */ + uint32_t size, /**< buffer size in digits */ + bool *sign_p) /**< [out] sign of BigInt */ +{ + JERRY_ASSERT (ecma_is_value_bigint (value)); + + if (value == ECMA_BIGINT_ZERO) + { + if (sign_p != NULL) + { + *sign_p = false; + } + memset (digits_p, 0, size * sizeof (uint64_t)); + return; + } + + ecma_extended_primitive_t *value_p = ecma_get_extended_primitive_from_value (value); + + if (sign_p != NULL) + { + *sign_p = (value_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) != 0; + } + + uint32_t bigint_size = ECMA_BIGINT_GET_SIZE (value_p); + uint32_t copy_size = bigint_size / sizeof (uint64_t); + + if (copy_size > size) + { + copy_size = size; + } + + const uint64_t *digits_end_p = digits_p + copy_size; + ecma_bigint_digit_t *source_p = ECMA_BIGINT_GET_DIGITS (value_p, 0); + + while (digits_p < digits_end_p) + { + *digits_p++ = source_p[0] | (((uint64_t) source_p[1]) << (8 * sizeof (ecma_bigint_digit_t))); + source_p += 2; + } + + size -= copy_size; + + if (size == 0) + { + return; + } + + if (ECMA_BIGINT_SIZE_IS_ODD (bigint_size)) + { + *digits_p++ = source_p[0]; + size--; + } + + if (size > 0) + { + memset (digits_p, 0, size * sizeof (uint64_t)); + } +} /* ecma_bigint_get_digits_and_sign */ + /** * Compare two BigInt values * diff --git a/jerry-core/ecma/operations/ecma-bigint.h b/jerry-core/ecma/operations/ecma-bigint.h index d617f16a3..014189f43 100644 --- a/jerry-core/ecma/operations/ecma-bigint.h +++ b/jerry-core/ecma/operations/ecma-bigint.h @@ -42,8 +42,10 @@ ecma_value_t ecma_bigint_parse_string (const lit_utf8_byte_t *string_p, lit_utf8 uint32_t options); 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_number_to_bigint (ecma_number_t number); -ecma_value_t ecma_bigint_to_bigint (ecma_value_t value); +ecma_value_t ecma_bigint_to_bigint (ecma_value_t value, bool allow_numbers); +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); +void ecma_bigint_get_digits_and_sign (ecma_value_t value, uint64_t *digits_p, uint32_t size, bool *sign_p); bool ecma_bigint_is_equal_to_bigint (ecma_value_t left_value, ecma_value_t right_value); bool ecma_bigint_is_equal_to_number (ecma_value_t left_value, ecma_number_t right_value); diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index f88b01002..1db8611be 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -388,6 +388,7 @@ bool jerry_value_is_promise (const jerry_value_t value); bool jerry_value_is_proxy (const jerry_value_t value); bool jerry_value_is_string (const jerry_value_t value); bool jerry_value_is_symbol (const jerry_value_t value); +bool jerry_value_is_bigint (const jerry_value_t value); bool jerry_value_is_undefined (const jerry_value_t value); /** @@ -474,6 +475,7 @@ jerry_value_t jerry_value_to_number (const jerry_value_t value); jerry_value_t jerry_value_to_object (const jerry_value_t value); jerry_value_t jerry_value_to_primitive (const jerry_value_t value); jerry_value_t jerry_value_to_string (const jerry_value_t value); +jerry_value_t jerry_value_to_bigint (const jerry_value_t value); /** * Acquire types with reference counter (increase the references). @@ -512,6 +514,7 @@ jerry_value_t jerry_create_external_string (const jerry_char_t *str_p, jerry_value_t jerry_create_external_string_sz (const jerry_char_t *str_p, jerry_size_t str_size, jerry_object_native_free_callback_t free_cb); jerry_value_t jerry_create_symbol (const jerry_value_t value); +jerry_value_t jerry_create_bigint (const uint64_t *digits_p, uint32_t size, bool sign); jerry_value_t jerry_create_undefined (void); /** @@ -595,6 +598,12 @@ jerry_promise_state_t jerry_get_promise_state (const jerry_value_t promise); */ jerry_value_t jerry_get_symbol_descriptive_string (const jerry_value_t symbol); +/** + * BigInt functions. + */ +uint32_t jerry_get_bigint_size_in_digits (jerry_value_t value); +void jerry_get_bigint_digits (jerry_value_t value, uint64_t *digits_p, uint32_t size, bool *sign_p); + /** * Input validator functions. */ diff --git a/tests/test262-esnext-excludelist.xml b/tests/test262-esnext-excludelist.xml index 234a6a712..9ada3426e 100644 --- a/tests/test262-esnext-excludelist.xml +++ b/tests/test262-esnext-excludelist.xml @@ -662,7 +662,6 @@ - @@ -1097,7 +1096,6 @@ - @@ -6833,7 +6831,6 @@ - diff --git a/tests/unit-core/test-bigint.c b/tests/unit-core/test-bigint.c new file mode 100644 index 000000000..3fb654d5b --- /dev/null +++ b/tests/unit-core/test-bigint.c @@ -0,0 +1,186 @@ +/* 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. + */ + +#include "jerryscript.h" +#include "test-common.h" + +int +main (void) +{ + if (!jerry_is_feature_enabled (JERRY_FEATURE_BIGINT)) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Bigint support is disabled!\n"); + return 0; + } + + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t string = jerry_create_string ((const jerry_char_t *) "0xfffffff1fffffff2fffffff3"); + TEST_ASSERT (!jerry_value_is_error (string)); + + jerry_value_t bigint = jerry_value_to_bigint (string); + jerry_release_value (string); + + TEST_ASSERT (!jerry_value_is_error (bigint)); + TEST_ASSERT (jerry_value_is_bigint (bigint)); + + string = jerry_value_to_string (bigint); + TEST_ASSERT (!jerry_value_is_error (string)); + + static jerry_char_t str_buffer[64]; + const char *expected_string_p = "79228162256009920505775652851"; + + jerry_size_t size = jerry_string_to_char_buffer (string, str_buffer, sizeof (str_buffer)); + TEST_ASSERT (size == strlen (expected_string_p)); + TEST_ASSERT (memcmp (str_buffer, expected_string_p, size) == 0); + jerry_release_value (string); + + TEST_ASSERT (jerry_get_bigint_size_in_digits (bigint) == 2); + + uint64_t digits_buffer[4]; + bool sign; + + memset (digits_buffer, 0xff, sizeof (digits_buffer)); + sign = true; + jerry_get_bigint_digits (bigint, digits_buffer, 0, &sign); + TEST_ASSERT (sign == false); + TEST_ASSERT (digits_buffer[0] == ~((uint64_t) 0)); + TEST_ASSERT (digits_buffer[1] == ~((uint64_t) 0)); + TEST_ASSERT (digits_buffer[2] == ~((uint64_t) 0)); + TEST_ASSERT (digits_buffer[3] == ~((uint64_t) 0)); + + memset (digits_buffer, 0xff, sizeof (digits_buffer)); + sign = true; + jerry_get_bigint_digits (bigint, digits_buffer, 1, &sign); + TEST_ASSERT (sign == false); + TEST_ASSERT (digits_buffer[0] == 0xfffffff2fffffff3ull); + TEST_ASSERT (digits_buffer[1] == ~((uint64_t) 0)); + TEST_ASSERT (digits_buffer[2] == ~((uint64_t) 0)); + TEST_ASSERT (digits_buffer[3] == ~((uint64_t) 0)); + + memset (digits_buffer, 0xff, sizeof (digits_buffer)); + sign = true; + jerry_get_bigint_digits (bigint, digits_buffer, 2, &sign); + TEST_ASSERT (sign == false); + TEST_ASSERT (digits_buffer[0] == 0xfffffff2fffffff3ull); + TEST_ASSERT (digits_buffer[1] == 0xfffffff1ull); + TEST_ASSERT (digits_buffer[2] == ~((uint64_t) 0)); + TEST_ASSERT (digits_buffer[3] == ~((uint64_t) 0)); + + memset (digits_buffer, 0xff, sizeof (digits_buffer)); + sign = true; + jerry_get_bigint_digits (bigint, digits_buffer, 3, &sign); + TEST_ASSERT (sign == false); + TEST_ASSERT (digits_buffer[0] == 0xfffffff2fffffff3ull); + TEST_ASSERT (digits_buffer[1] == 0xfffffff1ull); + TEST_ASSERT (digits_buffer[2] == 0); + TEST_ASSERT (digits_buffer[3] == ~((uint64_t) 0)); + + memset (digits_buffer, 0xff, sizeof (digits_buffer)); + jerry_get_bigint_digits (bigint, digits_buffer, 4, NULL); + TEST_ASSERT (digits_buffer[0] == 0xfffffff2fffffff3ull); + TEST_ASSERT (digits_buffer[1] == 0xfffffff1ull); + TEST_ASSERT (digits_buffer[2] == 0); + TEST_ASSERT (digits_buffer[3] == 0); + + jerry_release_value (bigint); + + digits_buffer[0] = 0; + digits_buffer[1] = 0; + digits_buffer[2] = 0; + /* Sign of zero value is always positive, even if we set negative. */ + bigint = jerry_create_bigint (digits_buffer, 3, true); + TEST_ASSERT (jerry_value_is_bigint (bigint)); + TEST_ASSERT (jerry_get_bigint_size_in_digits (bigint) == 0); + + memset (digits_buffer, 0xff, sizeof (digits_buffer)); + sign = true; + jerry_get_bigint_digits (bigint, digits_buffer, 2, &sign); + TEST_ASSERT (sign == false); + TEST_ASSERT (digits_buffer[0] == 0); + TEST_ASSERT (digits_buffer[1] == 0); + TEST_ASSERT (digits_buffer[2] == ~((uint64_t) 0)); + TEST_ASSERT (digits_buffer[3] == ~((uint64_t) 0)); + + jerry_release_value (bigint); + + digits_buffer[0] = 1; + digits_buffer[1] = 0; + digits_buffer[2] = 0; + digits_buffer[3] = 0; + bigint = jerry_create_bigint (digits_buffer, 4, true); + TEST_ASSERT (jerry_value_is_bigint (bigint)); + TEST_ASSERT (jerry_get_bigint_size_in_digits (bigint) == 1); + + memset (digits_buffer, 0xff, sizeof (digits_buffer)); + sign = false; + jerry_get_bigint_digits (bigint, digits_buffer, 1, &sign); + TEST_ASSERT (sign == true); + TEST_ASSERT (digits_buffer[0] == 1); + TEST_ASSERT (digits_buffer[1] == ~((uint64_t) 0)); + TEST_ASSERT (digits_buffer[2] == ~((uint64_t) 0)); + TEST_ASSERT (digits_buffer[3] == ~((uint64_t) 0)); + + memset (digits_buffer, 0xff, sizeof (digits_buffer)); + sign = false; + jerry_get_bigint_digits (bigint, digits_buffer, 2, &sign); + TEST_ASSERT (sign == true); + TEST_ASSERT (digits_buffer[0] == 1); + TEST_ASSERT (digits_buffer[1] == 0); + TEST_ASSERT (digits_buffer[2] == ~((uint64_t) 0)); + TEST_ASSERT (digits_buffer[3] == ~((uint64_t) 0)); + + jerry_release_value (bigint); + + digits_buffer[0] = 0; + digits_buffer[1] = 1; + digits_buffer[2] = 0; + digits_buffer[3] = 0; + bigint = jerry_create_bigint (digits_buffer, 4, true); + TEST_ASSERT (jerry_value_is_bigint (bigint)); + TEST_ASSERT (jerry_get_bigint_size_in_digits (bigint) == 2); + + memset (digits_buffer, 0xff, sizeof (digits_buffer)); + sign = false; + jerry_get_bigint_digits (bigint, digits_buffer, 1, &sign); + TEST_ASSERT (sign == true); + TEST_ASSERT (digits_buffer[0] == 0); + TEST_ASSERT (digits_buffer[1] == ~((uint64_t) 0)); + TEST_ASSERT (digits_buffer[2] == ~((uint64_t) 0)); + TEST_ASSERT (digits_buffer[3] == ~((uint64_t) 0)); + + memset (digits_buffer, 0xff, sizeof (digits_buffer)); + sign = false; + jerry_get_bigint_digits (bigint, digits_buffer, 2, &sign); + TEST_ASSERT (sign == true); + TEST_ASSERT (digits_buffer[0] == 0); + TEST_ASSERT (digits_buffer[1] == 1); + TEST_ASSERT (digits_buffer[2] == ~((uint64_t) 0)); + TEST_ASSERT (digits_buffer[3] == ~((uint64_t) 0)); + + memset (digits_buffer, 0xff, sizeof (digits_buffer)); + sign = false; + jerry_get_bigint_digits (bigint, digits_buffer, 3, &sign); + TEST_ASSERT (sign == true); + TEST_ASSERT (digits_buffer[0] == 0); + TEST_ASSERT (digits_buffer[1] == 1); + TEST_ASSERT (digits_buffer[2] == 0); + TEST_ASSERT (digits_buffer[3] == ~((uint64_t) 0)); + + jerry_release_value (bigint); + + jerry_cleanup (); + return 0; +} /* main */