diff options
author | Zoltan Herczeg <zherczeg.u-szeged@partner.samsung.com> | 2020-07-30 12:08:34 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-30 12:08:34 +0200 |
commit | df2f7782f7d26537c138acde2ee2e3fb124a17e7 (patch) | |
tree | 84edbda91c7f0995828d81d8c9c74d66575e0e09 | |
parent | 3eb69075f78b7712fbef15200648e8424f37cfb6 (diff) |
Implement BigInt primitve type and some of its operations (#4062)
Supported operations:
- parse BigInt (decimal, hexadecimal, binary)
- toString with any radix between 2 and 36
- arithmetic operations: negate, add, subtract, multiply, divide, modulo
- left and right shift
JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
29 files changed, 3221 insertions, 176 deletions
diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 3b1b5754..6a4423a7 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -652,9 +652,9 @@ jerry_value_is_abort (const jerry_value_t value) /**< api value */ return false; } - ecma_error_reference_t *error_ref_p = ecma_get_error_reference_from_value (value); + ecma_extended_primitive_t *error_ref_p = ecma_get_extended_primitive_from_value (value); - return (error_ref_p->refs_and_flags & ECMA_ERROR_REF_ABORT) != 0; + return ECMA_EXTENDED_PRIMITIVE_GET_TYPE (error_ref_p) == ECMA_EXTENDED_PRIMITIVE_ABORT; } /* jerry_value_is_abort */ /** @@ -998,6 +998,9 @@ jerry_is_feature_enabled (const jerry_feature_t feature) /**< feature to check * #if ENABLED (JERRY_BUILTIN_WEAKSET) || feature == JERRY_FEATURE_WEAKSET #endif /* ENABLED (JERRY_BUILTIN_WEAKSET) */ +#if ENABLED (JERRY_BUILTIN_BIGINT) + || feature == JERRY_FEATURE_BIGINT +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ ); } /* jerry_is_feature_enabled */ @@ -1166,7 +1169,7 @@ jerry_get_value_from_error (jerry_value_t value, /**< api value */ return release ? value : ecma_copy_value (value); } - jerry_value_t ret_val = jerry_acquire_value (ecma_get_error_reference_from_value (value)->value); + jerry_value_t ret_val = jerry_acquire_value (ecma_get_extended_primitive_from_value (value)->u.value); if (release) { @@ -1186,7 +1189,7 @@ jerry_get_error_type (jerry_value_t value) /**< api value */ { if (JERRY_UNLIKELY (ecma_is_value_error_reference (value))) { - value = ecma_get_error_reference_from_value (value)->value; + value = ecma_get_extended_primitive_from_value (value)->u.value; } if (!ecma_is_value_object (value)) @@ -1360,7 +1363,7 @@ jerry_acquire_value (jerry_value_t value) /**< API value */ if (JERRY_UNLIKELY (ecma_is_value_error_reference (value))) { - ecma_ref_error_reference (ecma_get_error_reference_from_value (value)); + ecma_ref_extended_primitive (ecma_get_extended_primitive_from_value (value)); return value; } @@ -1377,7 +1380,7 @@ jerry_release_value (jerry_value_t value) /**< API value */ if (JERRY_UNLIKELY (ecma_is_value_error_reference (value))) { - ecma_deref_error_reference (ecma_get_error_reference_from_value (value)); + ecma_deref_error_reference (ecma_get_extended_primitive_from_value (value)); return; } diff --git a/jerry-core/config.h b/jerry-core/config.h index 3d848f79..c8c1be01 100644 --- a/jerry-core/config.h +++ b/jerry-core/config.h @@ -115,6 +115,10 @@ # define JERRY_BUILTIN_TYPEDARRAY JERRY_ESNEXT #endif /* !defined (JERRY_BUILTIN_TYPEDARRAY) */ +#ifndef JERRY_BUILTIN_BIGINT +# define JERRY_BUILTIN_BIGINT JERRY_ESNEXT +#endif /* !defined (JERRY_BUILTIN_BIGINT) */ + #ifndef JERRY_MODULE_SYSTEM # define JERRY_MODULE_SYSTEM JERRY_ESNEXT #endif /* !defined (JERRY_MODULE_SYSTEM) */ @@ -555,6 +559,10 @@ || ((JERRY_BUILTIN_TYPEDARRAY != 0) && (JERRY_BUILTIN_TYPEDARRAY != 1)) # error "Invalid value for JERRY_BUILTIN_TYPEDARRAY macro." #endif +#if !defined (JERRY_BUILTIN_BIGINT) \ +|| ((JERRY_BUILTIN_BIGINT != 0) && (JERRY_BUILTIN_BIGINT != 1)) +# error "Invalid value for JERRY_BUILTIN_BIGINT macro." +#endif #if !defined (JERRY_MODULE_SYSTEM) \ || ((JERRY_MODULE_SYSTEM != 0) && (JERRY_MODULE_SYSTEM != 1)) # error "Invalid value for JERRY_MODULE_SYSTEM macro." diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index b0d46bb0..60d0386f 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -1202,11 +1202,14 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */ switch (ext_object_p->u.class_prop.class_id) { + case LIT_MAGIC_STRING_STRING_UL: + case LIT_MAGIC_STRING_NUMBER_UL: #if ENABLED (JERRY_ESNEXT) case LIT_MAGIC_STRING_SYMBOL_UL: #endif /* ENABLED (JERRY_ESNEXT) */ - case LIT_MAGIC_STRING_STRING_UL: - case LIT_MAGIC_STRING_NUMBER_UL: +#if ENABLED (JERRY_BUILTIN_BIGINT) + case LIT_MAGIC_STRING_BIGINT_UL: +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ { ecma_free_value (ext_object_p->u.class_prop.u.value); break; diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index c1e5f903..06af59c7 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -85,6 +85,7 @@ typedef enum ECMA_TYPE_OBJECT = 3, /**< pointer to description of an object */ ECMA_TYPE_SYMBOL = 4, /**< pointer to description of a symbol */ ECMA_TYPE_DIRECT_STRING = 5, /**< directly encoded string values */ + ECMA_TYPE_BIGINT = 6, /**< pointer to a bigint primitive */ ECMA_TYPE_ERROR = 7, /**< pointer to description of an error reference (only supported by C API) */ ECMA_TYPE_SNAPSHOT_OFFSET = ECMA_TYPE_ERROR, /**< offset to a snapshot number/string */ ECMA_TYPE___MAX = ECMA_TYPE_ERROR /** highest value for ecma types */ @@ -1710,28 +1711,44 @@ typedef struct } ecma_stringbuilder_t; /** - * Abort flag for error reference. + * Types for extended primitive values. */ -#define ECMA_ERROR_REF_ABORT 0x1 +typedef enum +{ +#ifndef JERRY_BUILTIN_BIGINT + ECMA_EXTENDED_PRIMITIVE_BIGINT, /**< BigInt value */ +#endif /* !defined (JERRY_BUILTIN_BIGINT) */ + ECMA_EXTENDED_PRIMITIVE_ERROR, /**< external API error reference */ + ECMA_EXTENDED_PRIMITIVE_ABORT, /**< external API abort reference */ +} ecma_extended_primitive_type_t; /** - * Value for increasing or decreasing the reference counter. + * Representation of a thrown value on API level. */ -#define ECMA_ERROR_REF_ONE (1u << 1) +typedef struct +{ + uint32_t refs_and_type; /**< reference counter and type */ + union + { + ecma_value_t value; /**< referenced value */ + uint32_t bigint_sign_and_size; /**< BigInt properties */ + } u; +} ecma_extended_primitive_t; /** - * Maximum value of the reference counter. + * Get the type of an extended primitve value. */ -#define ECMA_ERROR_MAX_REF (UINT32_MAX - 1u) +#define ECMA_EXTENDED_PRIMITIVE_GET_TYPE(primitve_p) ((primitve_p)->refs_and_type & 0x7) /** - * Representation of a thrown value on API level. + * Value for increasing or decreasing the reference counter. */ -typedef struct -{ - uint32_t refs_and_flags; /**< reference counter */ - ecma_value_t value; /**< referenced value */ -} ecma_error_reference_t; +#define ECMA_EXTENDED_PRIMITIVE_REF_ONE (1u << 3) + +/** + * Maximum value of the reference counter. + */ +#define ECMA_EXTENDED_PRIMITIVE_MAX_REF (UINT32_MAX - (ECMA_EXTENDED_PRIMITIVE_REF_ONE - 1)) #if ENABLED (JERRY_PROPRETY_HASHMAP) @@ -2009,7 +2026,7 @@ typedef struct ecma_object_t *buffer_p; /**< [[ViewedArrayBuffer]] internal slot */ uint32_t byte_offset; /**< [[ByteOffset]] internal slot */ } ecma_dataview_object_t; -#endif /* ENABLED (JERRY_BUILTIN_DATAVIEW */ +#endif /* ENABLED (JERRY_BUILTIN_DATAVIEW) */ /** * Flag for indicating whether the symbol is a well known symbol @@ -2091,6 +2108,28 @@ typedef uint64_t ecma_length_t; typedef uint32_t ecma_length_t; #endif /* ENABLED (JERRY_ESNEXT) */ +#if ENABLED (JERRY_BUILTIN_BIGINT) + +/** + * BigUInt data is a sequence of uint32_t numbers. + */ +typedef uint32_t ecma_bigint_digit_t; + +/** + * Return the size of a BigInt value in ecma_bigint_data_t units. + */ +#define ECMA_BIGINT_GET_SIZE(value_p) \ + ((value_p)->u.bigint_sign_and_size & ~(uint32_t) (sizeof (ecma_bigint_digit_t) - 1)) + +/** + * Size of memory needs to be allocated for the digits of a BigInt. + * The value is rounded up for two digits. + */ +#define ECMA_BIGINT_GET_BYTE_SIZE(size) \ + (size_t) (((size) + sizeof (ecma_bigint_digit_t)) & ~(2 * sizeof (ecma_bigint_digit_t) - 1)) + +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + /** * Struct for counting the different types properties in objects */ diff --git a/jerry-core/ecma/base/ecma-helpers-value.c b/jerry-core/ecma/base/ecma-helpers-value.c index 428d3cff..8cd97d42 100644 --- a/jerry-core/ecma/base/ecma-helpers-value.c +++ b/jerry-core/ecma/base/ecma-helpers-value.c @@ -330,6 +330,22 @@ ecma_is_value_symbol (ecma_value_t value) /**< ecma value */ } /* ecma_is_value_symbol */ #endif /* ENABLED (JERRY_ESNEXT) */ +#if ENABLED (JERRY_BUILTIN_BIGINT) + +/** + * Check if the value is bigint. + * + * @return true - if the value contains bigint value, + * false - otherwise + */ +inline bool JERRY_ATTR_CONST JERRY_ATTR_ALWAYS_INLINE +ecma_is_value_bigint (ecma_value_t value) /**< ecma value */ +{ + return (ecma_get_value_type_field (value) == ECMA_TYPE_BIGINT); +} /* ecma_is_value_bigint */ + +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + /** * Check if the value can be property name. * @@ -401,13 +417,29 @@ ecma_is_value_error_reference (ecma_value_t value) /**< ecma value */ void ecma_check_value_type_is_spec_defined (ecma_value_t value) /**< ecma value */ { +#if ENABLED (JERRY_ESNEXT) +#define ECMA_CHECK_IS_VALUE_SYMBOL(value) ecma_is_value_symbol(value) +#else /* !ENABLED (JERRY_ESNEXT) */ +#define ECMA_CHECK_IS_VALUE_SYMBOL(value) false +#endif /* ENABLED (JERRY_ESNEXT) */ + +#if ENABLED (JERRY_BUILTIN_BIGINT) +#define ECMA_CHECK_IS_VALUE_BIGINT(value) ecma_is_value_bigint(value) +#else /* !ENABLED (JERRY_BUILTIN_BIGINT) */ +#define ECMA_CHECK_IS_VALUE_BIGINT(value) false +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + JERRY_ASSERT (ecma_is_value_undefined (value) || ecma_is_value_null (value) || ecma_is_value_boolean (value) || ecma_is_value_number (value) || ecma_is_value_string (value) - || ECMA_ASSERT_VALUE_IS_SYMBOL (value) + || ECMA_CHECK_IS_VALUE_SYMBOL (value) + || ECMA_CHECK_IS_VALUE_BIGINT (value) || ecma_is_value_object (value)); + +#undef ECMA_CHECK_IS_VALUE_SYMBOL +#undef ECMA_CHECK_IS_VALUE_BIGINT } /* ecma_check_value_type_is_spec_defined */ /** @@ -686,12 +718,14 @@ ecma_make_object_value (const ecma_object_t *object_p) /**< object to reference * @return ecma-value representation of the Error reference */ inline ecma_value_t JERRY_ATTR_PURE JERRY_ATTR_ALWAYS_INLINE -ecma_make_error_reference_value (const ecma_error_reference_t *error_ref_p) /**< error reference */ +ecma_make_extended_primitive_value (const ecma_extended_primitive_t *primitve_p, /**< extended primitve value */ + uint32_t type) /**< ecma type of extended primitve value */ { - JERRY_ASSERT (error_ref_p != NULL); + JERRY_ASSERT (primitve_p != NULL); + JERRY_ASSERT (type == ECMA_TYPE_BIGINT || type == ECMA_TYPE_ERROR); - return ecma_pointer_to_ecma_value (error_ref_p) | ECMA_TYPE_ERROR; -} /* ecma_make_error_reference_value */ + return ecma_pointer_to_ecma_value (primitve_p) | type; +} /* ecma_make_extended_primitive_value */ /** * Get integer value from an integer ecma value @@ -817,13 +851,14 @@ ecma_get_object_from_value (ecma_value_t value) /**< ecma value */ * * @return the pointer */ -inline ecma_error_reference_t *JERRY_ATTR_PURE JERRY_ATTR_ALWAYS_INLINE -ecma_get_error_reference_from_value (ecma_value_t value) /**< ecma value */ +inline ecma_extended_primitive_t *JERRY_ATTR_PURE JERRY_ATTR_ALWAYS_INLINE +ecma_get_extended_primitive_from_value (ecma_value_t value) /**< ecma value */ { - JERRY_ASSERT (ecma_get_value_type_field (value) == ECMA_TYPE_ERROR); + JERRY_ASSERT (ecma_get_value_type_field (value) == ECMA_TYPE_BIGINT + || ecma_get_value_type_field (value) == ECMA_TYPE_ERROR); - return (ecma_error_reference_t *) ecma_get_pointer_from_ecma_value (value); -} /* ecma_get_error_reference_from_value */ + return (ecma_extended_primitive_t *) ecma_get_pointer_from_ecma_value (value); +} /* ecma_get_extended_primitive_from_value */ /** * Invert a boolean value @@ -866,6 +901,13 @@ ecma_copy_value (ecma_value_t value) /**< value description */ return value; } #endif /* ENABLED (JERRY_ESNEXT) */ +#if ENABLED (JERRY_BUILTIN_BIGINT) + case ECMA_TYPE_BIGINT: + { + ecma_ref_extended_primitive (ecma_get_extended_primitive_from_value (value)); + return value; + } +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ case ECMA_TYPE_OBJECT: { ecma_ref_object (ecma_get_object_from_value (value)); @@ -1093,6 +1135,14 @@ ecma_free_value (ecma_value_t value) /**< value description */ break; } +#if ENABLED (JERRY_BUILTIN_BIGINT) + case ECMA_TYPE_BIGINT: + { + ecma_deref_bigint (ecma_get_extended_primitive_from_value (value)); + break; + } +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + default: { JERRY_ASSERT (ecma_get_value_type_field (value) == ECMA_TYPE_DIRECT @@ -1192,6 +1242,12 @@ ecma_get_typeof_lit_id (ecma_value_t value) /**< input ecma value */ ret_value = LIT_MAGIC_STRING_SYMBOL; } #endif /* ENABLED (JERRY_ESNEXT) */ +#if ENABLED (JERRY_BUILTIN_BIGINT) + else if (ecma_is_value_bigint (value)) + { + ret_value = LIT_MAGIC_STRING_BIGINT; + } +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ else { JERRY_ASSERT (ecma_is_value_object (value)); diff --git a/jerry-core/ecma/base/ecma-helpers.c b/jerry-core/ecma/base/ecma-helpers.c index 1f7ee990..059edb41 100644 --- a/jerry-core/ecma/base/ecma-helpers.c +++ b/jerry-core/ecma/base/ecma-helpers.c @@ -1216,10 +1216,74 @@ ecma_free_property_descriptor (ecma_property_descriptor_t *prop_desc_p) /**< pro /** * The size of error reference must be 8 bytes to use jmem_pools_alloc(). */ -JERRY_STATIC_ASSERT (sizeof (ecma_error_reference_t) == 8, +JERRY_STATIC_ASSERT (sizeof (ecma_extended_primitive_t) == 8, ecma_error_reference_size_must_be_8_bytes); /** + * Increase ref count of an extended primitve value. + */ +void +ecma_ref_extended_primitive (ecma_extended_primitive_t *primitve_p) /**< extended primitve value */ +{ + if (JERRY_LIKELY (primitve_p->refs_and_type < ECMA_EXTENDED_PRIMITIVE_MAX_REF)) + { + primitve_p->refs_and_type += ECMA_EXTENDED_PRIMITIVE_REF_ONE; + } + else + { + jerry_fatal (ERR_REF_COUNT_LIMIT); + } +} /* ecma_ref_extended_primitive */ + +/** + * Decrease ref count of an error reference. + */ +void +ecma_deref_error_reference (ecma_extended_primitive_t *error_ref_p) /**< error reference */ +{ + JERRY_ASSERT (error_ref_p->refs_and_type >= ECMA_EXTENDED_PRIMITIVE_REF_ONE); + + error_ref_p->refs_and_type -= ECMA_EXTENDED_PRIMITIVE_REF_ONE; + + if (error_ref_p->refs_and_type < ECMA_EXTENDED_PRIMITIVE_REF_ONE) + { + ecma_free_value (error_ref_p->u.value); + jmem_pools_free (error_ref_p, sizeof (ecma_extended_primitive_t)); + } +} /* ecma_deref_error_reference */ + +#if ENABLED (JERRY_BUILTIN_BIGINT) + +/** + * Decrease ref count of a bigint value. + */ +void +ecma_deref_bigint (ecma_extended_primitive_t *bigint_p) /**< bigint value */ +{ + JERRY_ASSERT (bigint_p->refs_and_type >= ECMA_EXTENDED_PRIMITIVE_REF_ONE); + + bigint_p->refs_and_type -= ECMA_EXTENDED_PRIMITIVE_REF_ONE; + + if (bigint_p->refs_and_type >= ECMA_EXTENDED_PRIMITIVE_REF_ONE) + { + return; + } + + uint32_t size = ECMA_BIGINT_GET_SIZE (bigint_p); + + if (size == 0) + { + jmem_pools_free (bigint_p, sizeof (ecma_extended_primitive_t)); + return; + } + + size_t mem_size = ECMA_BIGINT_GET_BYTE_SIZE (size) + sizeof (ecma_extended_primitive_t); + jmem_heap_free_block (bigint_p, mem_size); +} /* ecma_deref_bigint */ + +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + +/** * Create an error reference from a given value. * * Note: @@ -1231,11 +1295,13 @@ ecma_value_t ecma_create_error_reference (ecma_value_t value, /**< referenced value */ bool is_exception) /**< error reference is an exception */ { - ecma_error_reference_t *error_ref_p = (ecma_error_reference_t *) jmem_pools_alloc (sizeof (ecma_error_reference_t)); + ecma_extended_primitive_t *error_ref_p; + error_ref_p = (ecma_extended_primitive_t *) jmem_pools_alloc (sizeof (ecma_extended_primitive_t)); - error_ref_p->refs_and_flags = ECMA_ERROR_REF_ONE | (is_exception ? 0 : ECMA_ERROR_REF_ABORT); - error_ref_p->value = value; - return ecma_make_error_reference_value (error_ref_p); + error_ref_p->refs_and_type = (ECMA_EXTENDED_PRIMITIVE_REF_ONE + | (is_exception ? ECMA_EXTENDED_PRIMITIVE_ERROR : ECMA_EXTENDED_PRIMITIVE_ABORT)); + error_ref_p->u.value = value; + return ecma_make_extended_primitive_value (error_ref_p, ECMA_TYPE_ERROR); } /* ecma_create_error_reference */ /** @@ -1270,39 +1336,6 @@ ecma_create_error_object_reference (ecma_object_t *object_p) /**< referenced obj } /* ecma_create_error_object_reference */ /** - * Increase ref count of an error reference. - */ -void -ecma_ref_error_reference (ecma_error_reference_t *error_ref_p) /**< error reference */ -{ - if (JERRY_LIKELY (error_ref_p->refs_and_flags < ECMA_ERROR_MAX_REF)) - { - error_ref_p->refs_and_flags += ECMA_ERROR_REF_ONE; - } - else - { - jerry_fatal (ERR_REF_COUNT_LIMIT); - } -} /* ecma_ref_error_reference */ - -/** - * Decrease ref count of an error reference. - */ -void -ecma_deref_error_reference (ecma_error_reference_t *error_ref_p) /**< error reference */ -{ - JERRY_ASSERT (error_ref_p->refs_and_flags >= ECMA_ERROR_REF_ONE); - - error_ref_p->refs_and_flags -= ECMA_ERROR_REF_ONE; - - if (error_ref_p->refs_and_flags < ECMA_ERROR_REF_ONE) - { - ecma_free_value (error_ref_p->value); - jmem_pools_free (error_ref_p, sizeof (ecma_error_reference_t)); - } -} /* ecma_deref_error_reference */ - -/** * Raise error from the given error reference. * * Note: the error reference's ref count is also decreased @@ -1311,23 +1344,23 @@ void ecma_raise_error_from_error_reference (ecma_value_t value) /**< error reference */ { JERRY_ASSERT (!jcontext_has_pending_exception () && !jcontext_has_pending_abort ()); - ecma_error_reference_t *error_ref_p = ecma_get_error_reference_from_value (value); + ecma_extended_primitive_t *error_ref_p = ecma_get_extended_primitive_from_value (value); - JERRY_ASSERT (error_ref_p->refs_and_flags >= ECMA_ERROR_REF_ONE); + JERRY_ASSERT (error_ref_p->refs_and_type >= ECMA_EXTENDED_PRIMITIVE_REF_ONE); - ecma_value_t referenced_value = error_ref_p->value; + ecma_value_t referenced_value = error_ref_p->u.value; jcontext_set_exception_flag (true); - jcontext_set_abort_flag (error_ref_p->refs_and_flags & ECMA_ERROR_REF_ABORT); + jcontext_set_abort_flag (ECMA_EXTENDED_PRIMITIVE_GET_TYPE (error_ref_p) == ECMA_EXTENDED_PRIMITIVE_ABORT); - if (error_ref_p->refs_and_flags >= 2 * ECMA_ERROR_REF_ONE) + if (error_ref_p->refs_and_type >= 2 * ECMA_EXTENDED_PRIMITIVE_REF_ONE) { - error_ref_p->refs_and_flags -= ECMA_ERROR_REF_ONE; + error_ref_p->refs_and_type -= ECMA_EXTENDED_PRIMITIVE_REF_ONE; referenced_value = ecma_copy_value (referenced_value); } else { - jmem_pools_free (error_ref_p, sizeof (ecma_error_reference_t)); + jmem_pools_free (error_ref_p, sizeof (ecma_extended_primitive_t)); } JERRY_CONTEXT (error_value) = referenced_value; diff --git a/jerry-core/ecma/base/ecma-helpers.h b/jerry-core/ecma/base/ecma-helpers.h index 1e012917..728f7f29 100644 --- a/jerry-core/ecma/base/ecma-helpers.h +++ b/jerry-core/ecma/base/ecma-helpers.h @@ -243,6 +243,9 @@ bool JERRY_ATTR_CONST ecma_is_value_string (ecma_value_t value); #if ENABLED (JERRY_ESNEXT) bool JERRY_ATTR_CONST ecma_is_value_symbol (ecma_value_t value); #endif /* ENABLED (JERRY_ESNEXT) */ +#if ENABLED (JERRY_BUILTIN_BIGINT) +bool JERRY_ATTR_CONST ecma_is_value_bigint (ecma_value_t value); +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ bool JERRY_ATTR_CONST ecma_is_value_prop_name (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_direct_string (ecma_value_t value); bool JERRY_ATTR_CONST ecma_is_value_non_direct_string (ecma_value_t value); @@ -267,7 +270,8 @@ ecma_value_t JERRY_ATTR_PURE ecma_make_symbol_value (const ecma_string_t *ecma_s ecma_value_t JERRY_ATTR_PURE ecma_make_prop_name_value (const ecma_string_t *ecma_prop_name_p); ecma_value_t JERRY_ATTR_PURE ecma_make_magic_string_value (lit_magic_string_id_t id); ecma_value_t JERRY_ATTR_PURE ecma_make_object_value (const ecma_object_t *object_p); -ecma_value_t JERRY_ATTR_PURE ecma_make_error_reference_value (const ecma_error_reference_t *error_ref_p); +ecma_value_t JERRY_ATTR_PURE ecma_make_extended_primitive_value (const ecma_extended_primitive_t *primitve_p, + uint32_t type); ecma_integer_value_t JERRY_ATTR_CONST ecma_get_integer_from_value (ecma_value_t value); ecma_number_t JERRY_ATTR_PURE ecma_get_float_from_value (ecma_value_t value); ecma_number_t * ecma_get_pointer_from_float_value (ecma_value_t value); @@ -278,7 +282,7 @@ ecma_string_t JERRY_ATTR_PURE *ecma_get_symbol_from_value (ecma_value_t value); #endif /* ENABLED (JERRY_ESNEXT) */ ecma_string_t JERRY_ATTR_PURE *ecma_get_prop_name_from_value (ecma_value_t value); ecma_object_t JERRY_ATTR_PURE *ecma_get_object_from_value (ecma_value_t value); -ecma_error_reference_t JERRY_ATTR_PURE *ecma_get_error_reference_from_value (ecma_value_t value); +ecma_extended_primitive_t JERRY_ATTR_PURE *ecma_get_extended_primitive_from_value (ecma_value_t value); ecma_value_t JERRY_ATTR_CONST ecma_invert_boolean_value (ecma_value_t value); ecma_value_t ecma_copy_value (ecma_value_t value); ecma_value_t ecma_fast_copy_value (ecma_value_t value); @@ -498,11 +502,15 @@ void ecma_set_property_lcached (ecma_property_t *property_p, bool is_lcached); ecma_property_descriptor_t ecma_make_empty_property_descriptor (void); void ecma_free_property_descriptor (ecma_property_descriptor_t *prop_desc_p); +void ecma_ref_extended_primitive (ecma_extended_primitive_t *primitve_p); +void ecma_deref_error_reference (ecma_extended_primitive_t *error_ref_p); +#if ENABLED (JERRY_BUILTIN_BIGINT) +void ecma_deref_bigint (ecma_extended_primitive_t *bigint_p); +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + ecma_value_t ecma_create_error_reference (ecma_value_t value, bool is_exception); ecma_value_t ecma_create_error_reference_from_context (void); ecma_value_t ecma_create_error_object_reference (ecma_object_t *object_p); -void ecma_ref_error_reference (ecma_error_reference_t *error_ref_p); -void ecma_deref_error_reference (ecma_error_reference_t *error_ref_p); void ecma_raise_error_from_error_reference (ecma_value_t value); void ecma_bytecode_ref (ecma_compiled_code_t *bytecode_p); diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-bigint-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-bigint-prototype.c new file mode 100644 index 00000000..99d98e86 --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-bigint-prototype.c @@ -0,0 +1,134 @@ +/* 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 "ecma-bigint.h" +#include "ecma-exceptions.h" + +#if ENABLED (JERRY_BUILTIN_BIGINT) + +#define ECMA_BUILTINS_INTERNAL +#include "ecma-builtins-internal.h" + +/** + * This object has a custom dispatch function. + */ +#define BUILTIN_INC_HEADER_NAME "ecma-builtin-bigint-prototype.inc.h" +#define BUILTIN_UNDERSCORED_ID bigint_prototype +#include "ecma-builtin-internal-routines-template.inc.h" + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmabuiltins + * @{ + * + * \addtogroup bigint ECMA BigInt object built-in + * @{ + */ + +/** + * The BigInt.prototype object's 'valueOf' routine + * + * See also: + * ECMA-262 v11, 20.2.3.4 + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_builtin_bigint_prototype_object_value_of (ecma_value_t this_arg) /**< this argument */ +{ + if (ecma_is_value_bigint (this_arg)) + { + return ecma_copy_value (this_arg); + } + + if (ecma_is_value_object (this_arg)) + { + ecma_object_t *object_p = ecma_get_object_from_value (this_arg); + + if (ecma_object_class_is (object_p, LIT_MAGIC_STRING_BIGINT_UL)) + { + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; + + JERRY_ASSERT (ecma_is_value_bigint (ext_object_p->u.class_prop.u.value)); + + return ecma_copy_value (ext_object_p->u.class_prop.u.value); + } + } + + return ecma_raise_type_error (ECMA_ERR_MSG ("BigInt value expected.")); +} /* ecma_builtin_bigint_prototype_object_value_of */ + +/** + * The BigInt.prototype object's 'toString' routine + * + * See also: + * ECMA-262 v11, 20.2.3.3 + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_builtin_bigint_prototype_object_to_string (ecma_value_t this_arg, /**< this argument */ + const ecma_value_t *arguments_list_p, /**< arguments list */ + uint32_t arguments_list_len) /**< number of arguments */ +{ + ecma_value_t bigint = ecma_builtin_bigint_prototype_object_value_of (this_arg); + + if (ECMA_IS_VALUE_ERROR (bigint)) + { + return bigint; + } + + uint32_t radix = 10; + + if (arguments_list_len > 0 && !ecma_is_value_undefined (arguments_list_p[0])) + { + ecma_number_t arg_num; + + if (ECMA_IS_VALUE_ERROR (ecma_op_to_integer (arguments_list_p[0], &arg_num))) + { + ecma_free_value (bigint); + return ECMA_VALUE_ERROR; + } + + if (arg_num < 2 || arg_num > 36) + { + ecma_free_value (bigint); + return ecma_raise_range_error (ECMA_ERR_MSG ("Radix must be between 2 and 36.")); + } + + radix = (uint32_t) arg_num; + } + + ecma_string_t *string_p = ecma_bigint_to_string (bigint, radix); + ecma_free_value (bigint); + + if (string_p == NULL) + { + return ECMA_VALUE_ERROR; + } + + return ecma_make_string_value (string_p); +} /* ecma_builtin_bigint_prototype_object_to_string */ + +/** + * @} + * @} + * @} + */ + +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-bigint-prototype.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-bigint-prototype.inc.h new file mode 100644 index 00000000..8496198a --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-bigint-prototype.inc.h @@ -0,0 +1,44 @@ +/* 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. + */ + +/* + * BigInt.prototype built-in description + */ + +#include "ecma-builtin-helpers-macro-defines.inc.h" + +#if ENABLED (JERRY_BUILTIN_BIGINT) + +/* Object properties: + * (property name, object pointer getter) */ + +/* ECMA-262 v11, 20.2.3.1 */ +OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, + ECMA_BUILTIN_ID_BIGINT, + ECMA_PROPERTY_CONFIGURABLE_WRITABLE) + +/* ECMA-262 v11, 20.2.3.5 */ +STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, + LIT_MAGIC_STRING_BIGINT_UL, + ECMA_PROPERTY_FLAG_CONFIGURABLE) + +/* Routine properties: + * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ +ROUTINE (LIT_MAGIC_STRING_VALUE_OF_UL, ecma_builtin_bigint_prototype_object_value_of, 0, 0) +ROUTINE (LIT_MAGIC_STRING_TO_STRING_UL, ecma_builtin_bigint_prototype_object_to_string, NON_FIXED, 1) + +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + +#include "ecma-builtin-helpers-macro-undefs.inc.h" diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.c b/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.c new file mode 100644 index 00000000..343805fa --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.c @@ -0,0 +1,93 @@ +/* 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 "ecma-bigint.h" +#include "ecma-builtins.h" +#include "ecma-exceptions.h" + +#if ENABLED (JERRY_BUILTIN_BIGINT) + +#define ECMA_BUILTINS_INTERNAL +#include "ecma-builtins-internal.h" + +#define BUILTIN_INC_HEADER_NAME "ecma-builtin-bigint.inc.h" +#define BUILTIN_UNDERSCORED_ID bigint +#include "ecma-builtin-internal-routines-template.inc.h" + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmabuiltins + * @{ + * + * \addtogroup bigint ECMA BigInt object built-in + * @{ + */ + +/** + * Handle calling [[Call]] of built-in BigInt object + * + * See also: + * ECMA-262 v11, 20.2.1.1 + * + * @return ecma value + */ +ecma_value_t +ecma_builtin_bigint_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ + uint32_t arguments_list_len) /**< number of arguments */ +{ + 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]; + + if (!ecma_is_value_string (value)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("TODO: Only strings are supported now")); + } + + ecma_string_t *string_p = ecma_get_string_from_value (value); + + ECMA_STRING_TO_UTF8_STRING (string_p, string_buffer_p, string_buffer_size); + + ecma_value_t result = ecma_bigint_parse_string (string_buffer_p, string_buffer_size); + + ECMA_FINALIZE_UTF8_STRING (string_buffer_p, string_buffer_size); + return result; +} /* ecma_builtin_bigint_dispatch_call */ + +/** + * Handle calling [[Construct]] of built-in BigInt object + * + * See also: + * ECMA-262 v11, 20.2.1 + * + * @return ecma value + */ +ecma_value_t +ecma_builtin_bigint_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ + uint32_t arguments_list_len) /**< number of arguments */ +{ + JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); + + return ecma_raise_type_error (ECMA_ERR_MSG ("BigInt function is not a constructor.")); +} /* ecma_builtin_bigint_dispatch_construct */ + +/** + * @} + * @} + * @} + */ + +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.inc.h new file mode 100644 index 00000000..c851c5cc --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-bigint.inc.h @@ -0,0 +1,47 @@ +/* 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. + */ + +/* + * BigInt built-in description + */ + +#include "ecma-builtin-helpers-macro-defines.inc.h" + +#if ENABLED (JERRY_BUILTIN_BIGINT) + +/* Number properties: + * (property name, number value, writable, enumerable, configurable) */ + +/* ECMA-262 v11, 20.2.1 */ +NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, + 1, + ECMA_PROPERTY_FLAG_CONFIGURABLE) + +/* ECMA-262 v11, 20.2.1 */ +STRING_VALUE (LIT_MAGIC_STRING_NAME, + LIT_MAGIC_STRING_BIGINT_UL, + ECMA_PROPERTY_FLAG_CONFIGURABLE) + +/* Object properties: + * (property name, object pointer getter) */ + +/* ECMA-262 v11, 20.2.2.3 */ +OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, + ECMA_BUILTIN_ID_BIGINT_PROTOTYPE, + ECMA_PROPERTY_FIXED) + +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + +#include "ecma-builtin-helpers-macro-undefs.inc.h" diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-global.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-global.inc.h index 9dba8647..99c085bf 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-global.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-global.inc.h @@ -253,6 +253,13 @@ OBJECT_VALUE (LIT_MAGIC_STRING_PROXY_UL, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) #endif /* ENABLED (JERRY_BUILTIN_PROXY) */ +#if ENABLED (JERRY_BUILTIN_BIGINT) +/* ECMA-262 v11, 20.2.1 */ +OBJECT_VALUE (LIT_MAGIC_STRING_BIGINT_UL, + ECMA_BUILTIN_ID_BIGINT, + ECMA_PROPERTY_CONFIGURABLE_WRITABLE) +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h index a3b4a005..8cf86362 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h @@ -677,6 +677,23 @@ BUILTIN (ECMA_BUILTIN_ID_MAP_ITERATOR_PROTOTYPE, true, map_iterator_prototype) #endif /* ENABLED (JERRY_BUILTIN_SET) */ + +#if ENABLED (JERRY_BUILTIN_BIGINT) +/* The %BigInt.prototype% object */ +BUILTIN (ECMA_BUILTIN_ID_BIGINT_PROTOTYPE, + ECMA_OBJECT_TYPE_GENERAL, + ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, + true, + bigint_prototype) + +/* The %BigInt% object */ +BUILTIN_ROUTINE (ECMA_BUILTIN_ID_BIGINT, + ECMA_OBJECT_TYPE_FUNCTION, + ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, + true, + bigint) +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + #endif /* ENABLED (JERRY_ESNEXT) */ #if ENABLED (JERRY_BUILTIN_DATAVIEW) diff --git a/jerry-core/ecma/operations/ecma-big-uint.c b/jerry-core/ecma/operations/ecma-big-uint.c new file mode 100644 index 00000000..81158f83 --- /dev/null +++ b/jerry-core/ecma/operations/ecma-big-uint.c @@ -0,0 +1,1252 @@ +/* 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 "ecma-big-uint.h" +#include "ecma-helpers.h" +#include "jmem.h" +#include "lit-char-helpers.h" + +#if ENABLED (JERRY_BUILTIN_BIGINT) + +JERRY_STATIC_ASSERT (sizeof (ecma_bigint_two_digits_t) == 2 * sizeof (ecma_bigint_digit_t), + ecma_big_int_two_digits_must_be_twice_as_long_as_ecma_big_int_digit); + +JERRY_STATIC_ASSERT ((1 << ECMA_BIGINT_DIGIT_SHIFT) == (8 * sizeof (ecma_bigint_digit_t)), + ecma_bigint_digit_shift_is_incorrect); + +/** + * Create a new BigInt value + * + * @return new BigInt value, NULL on error + */ +ecma_extended_primitive_t * +ecma_bigint_create (uint32_t size) /**< size of the new BigInt value */ +{ + JERRY_ASSERT ((size % sizeof (ecma_bigint_digit_t)) == 0); + + if (JERRY_UNLIKELY (size > ECMA_BIGINT_MAX_SIZE)) + { + return NULL; + } + + ecma_extended_primitive_t *value_p; + + if (size == 0) + { + value_p = (ecma_extended_primitive_t *) jmem_pools_alloc (sizeof (ecma_extended_primitive_t)); + } + else + { + size_t mem_size = ECMA_BIGINT_GET_BYTE_SIZE (size) + sizeof (ecma_extended_primitive_t); + value_p = (ecma_extended_primitive_t *) jmem_heap_alloc_block_null_on_error (mem_size); + } + + if (JERRY_UNLIKELY (value_p == NULL)) + { + return NULL; + } + + value_p->refs_and_type = ECMA_EXTENDED_PRIMITIVE_REF_ONE | ECMA_TYPE_BIGINT; + value_p->u.bigint_sign_and_size = size; + return value_p; +} /* ecma_bigint_create */ + +/** + * Extend a BigInt value with a new data prefix value + * + * @return new BigInt value, NULL on error + */ +ecma_extended_primitive_t * +ecma_big_uint_extend (ecma_extended_primitive_t *value_p, /**< BigInt value */ + ecma_bigint_digit_t digit) /**< new digit */ +{ + uint32_t old_size = ECMA_BIGINT_GET_SIZE (value_p); + + if (ECMA_BIGINT_SIZE_IS_ODD (old_size)) + { + value_p->u.bigint_sign_and_size += (uint32_t) sizeof (ecma_bigint_digit_t); + *ECMA_BIGINT_GET_DIGITS (value_p, old_size) = digit; + return value_p; + } + + ecma_extended_primitive_t *result_p = ecma_bigint_create (old_size + (uint32_t) sizeof (ecma_bigint_digit_t)); + + if (JERRY_UNLIKELY (result_p == NULL)) + { + ecma_deref_bigint (value_p); + return NULL; + } + + memcpy (result_p + 1, value_p + 1, old_size); + ecma_deref_bigint (value_p); + + *ECMA_BIGINT_GET_DIGITS (result_p, old_size) = digit; + return result_p; +} /* ecma_big_uint_extend */ + +/** + * Compare two BigUInt numbers + * + * return -1, if value1 < value2, 0 if they are equal, and 1 otherwise + */ +int +ecma_big_uint_compare (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */ + ecma_extended_primitive_t *right_value_p) /**< right BigUInt value */ +{ + uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p); + uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_value_p); + + JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0); + JERRY_ASSERT (right_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (right_value_p, right_size) != 0); + + if (left_size > right_size) + { + return 1; + } + + if (left_size < right_size) + { + return -1; + } + + ecma_bigint_digit_t *start_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0); + ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, left_size); + ecma_bigint_digit_t *right_p = ECMA_BIGINT_GET_DIGITS (right_value_p, left_size); + + do + { + ecma_bigint_digit_t left_value = *(--left_p); + ecma_bigint_digit_t right_value = *(--right_p); + + if (left_value < right_value) + { + return -1; + } + + if (left_value > right_value) + { + return 1; + } + } + while (left_p > start_p); + + return 0; +} /* ecma_big_uint_compare */ + +/** + * In-place multiply and addition operation with digit + * + * return updated value on success, NULL if no memory is available + */ +ecma_extended_primitive_t * +ecma_big_uint_mul_digit (ecma_extended_primitive_t *value_p, /**< BigUInt value */ + ecma_bigint_digit_t mul, /**< multiply value */ + ecma_bigint_digit_t add) /**< addition value */ +{ + JERRY_ASSERT (mul > 1); + JERRY_ASSERT (add < mul); + + if (JERRY_UNLIKELY (value_p == NULL)) + { + JERRY_ASSERT (add > 0); + + value_p = ecma_bigint_create (sizeof (ecma_bigint_digit_t)); + + if (JERRY_UNLIKELY (value_p == NULL)) + { + return NULL; + } + + *ECMA_BIGINT_GET_DIGITS (value_p, 0) = add; + return value_p; + } + + uint32_t size = ECMA_BIGINT_GET_SIZE (value_p); + + JERRY_ASSERT (size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (value_p, size) != 0); + + ecma_bigint_digit_t *current_p = ECMA_BIGINT_GET_DIGITS (value_p, 0); + ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (value_p, size); + ecma_bigint_digit_t carry = add; + + do + { + ecma_bigint_two_digits_t multiply_result = ((ecma_bigint_two_digits_t) *current_p) * mul; + ecma_bigint_digit_t multiply_result_low, new_carry; + + multiply_result_low = (ecma_bigint_digit_t) multiply_result; + new_carry = (ecma_bigint_digit_t) (multiply_result >> (8 * sizeof (ecma_bigint_digit_t))); + + multiply_result_low += carry; + if (multiply_result_low < carry) + { + new_carry++; + } + + *current_p++ = multiply_result_low; + carry = new_carry; + } + while (current_p < end_p); + + if (carry == 0) + { + return value_p; + } + + return ecma_big_uint_extend (value_p, carry); +} /* ecma_big_uint_mul_digit */ + +/** + * Convert a BigUInt to a human readable number + * + * return char sequence on success, NULL otherwise + */ +lit_utf8_byte_t * +ecma_big_uint_to_string (ecma_extended_primitive_t *value_p, /**< BigUInt value */ + uint32_t radix, /**< radix number between 2 and 36 */ + uint32_t *char_start_p, /**< [out] start offset of numbers */ + uint32_t *char_size_p) /**< [out] size of the output buffer */ +{ + uint32_t size = ECMA_BIGINT_GET_SIZE (value_p); + + JERRY_ASSERT (radix >= 2 && radix <= 36); + JERRY_ASSERT (size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (value_p, size) != 0); + + uint32_t max_size = size * 8; + + if (radix < 16) + { + if (radix >= 8) + { + /* Most frequent case. */ + max_size = (max_size + 2) / 3; + } + else if (radix >= 4) + { + max_size = (max_size + 1) >> 1; + } + } + else if (radix < 32) + { + max_size = (max_size + 3) >> 2; + } + else + { + max_size = (max_size + 4) / 5; + } + + /* This space can be used to store a sign. */ + max_size += (uint32_t) (2 * sizeof (ecma_bigint_digit_t) - 1); + max_size &= ~(uint32_t) (sizeof (ecma_bigint_digit_t) - 1); + *char_size_p = max_size; + + lit_utf8_byte_t *result_p = (lit_utf8_byte_t *) jmem_heap_alloc_block_null_on_error (max_size); + + if (JERRY_UNLIKELY (result_p == NULL)) + { + return NULL; + } + + memcpy (result_p, value_p + 1, size); + + ecma_bigint_digit_t *start_p = (ecma_bigint_digit_t *) (result_p + size); + ecma_bigint_digit_t *end_p = (ecma_bigint_digit_t *) result_p; + lit_utf8_byte_t *string_p = result_p + max_size; + + do + { + ecma_bigint_digit_t *current_p = (ecma_bigint_digit_t *) start_p; + ecma_bigint_digit_t remainder = 0; + + if (sizeof (uintptr_t) == sizeof (ecma_bigint_two_digits_t)) + { + do + { + ecma_bigint_two_digits_t result = *(--current_p) | ECMA_BIGINT_HIGH_DIGIT (remainder); + + *current_p = (ecma_bigint_digit_t) (result / radix); + remainder = (ecma_bigint_digit_t) (result % radix); + } + while (current_p > end_p); + } + else + { + if (ECMA_BIGINT_SIZE_IS_ODD ((uintptr_t) current_p - (uintptr_t) end_p)) + { + ecma_bigint_digit_t result = *(--current_p); + *current_p = result / radix; + remainder = result % radix; + } + + while (current_p > end_p) + { + /* The following algorithm splits the 64 bit input into three numbers, extend + * them with remainder, divide them by radix, and updates the three bit ranges + * corresponding to the three numbers. */ + + const uint32_t extract_bits_low = 10; + const uint32_t extract_bits_low_mask = (uint32_t) ((1 << extract_bits_low) - 1); + const uint32_t extract_bits_high = (uint32_t) ((sizeof (ecma_bigint_digit_t) * 8) - extract_bits_low); + const uint32_t extract_bits_high_mask = (uint32_t) ((1 << extract_bits_high) - 1); + + ecma_bigint_digit_t result_high = current_p[-1]; + ecma_bigint_digit_t result_mid = (result_high & extract_bits_low_mask) << extract_bits_low; + + result_high = (result_high >> extract_bits_low) | (remainder << extract_bits_high); + result_mid |= (result_high % radix) << (extract_bits_low * 2); + result_high = (result_high / radix) << extract_bits_low; + + ecma_bigint_digit_t result_low = current_p[-2]; + result_mid |= result_low >> extract_bits_high; + result_low = (result_low & extract_bits_high_mask) | ((result_mid % radix) << extract_bits_high); + + result_mid = result_mid / radix; + + current_p[-1] = result_high | (result_mid >> extract_bits_low); + current_p[-2] = (result_low / radix) | (result_mid << extract_bits_high); + + remainder = result_low % radix; + current_p -= 2; + } + } + + *(--string_p) = (lit_utf8_byte_t) ((remainder < 10) ? (remainder + LIT_CHAR_0) + : (remainder + (LIT_CHAR_LOWERCASE_A - 10))); + JERRY_ASSERT (string_p >= (lit_utf8_byte_t *) start_p); + + if (start_p[-1] == 0) + { + start_p--; + } + } + while (start_p > end_p); + + *char_start_p = (uint32_t) (string_p - result_p); + return result_p; +} /* ecma_big_uint_to_string */ + +/** + * Add right BigUInt value to the left BigUInt value + * + * return new BigInt value, NULL on error + */ +ecma_extended_primitive_t * +ecma_big_uint_add (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */ + ecma_extended_primitive_t *right_value_p) /**< right BigUInt value */ +{ + uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p); + uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_value_p); + + JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0); + JERRY_ASSERT (right_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (right_value_p, right_size) != 0); + + if (left_size < right_size) + { + /* Swap values. */ + ecma_extended_primitive_t *tmp_value_p = left_value_p; + left_value_p = right_value_p; + right_value_p = tmp_value_p; + + uint32_t tmp_size = left_size; + left_size = right_size; + right_size = tmp_size; + } + + ecma_extended_primitive_t *result_p = ecma_bigint_create (left_size); + + if (JERRY_UNLIKELY (result_p == NULL)) + { + return NULL; + } + + ecma_bigint_digit_t *current_p = ECMA_BIGINT_GET_DIGITS (result_p, 0); + ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (result_p, right_size); + ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0); + ecma_bigint_digit_t *right_p = ECMA_BIGINT_GET_DIGITS (right_value_p, 0); + ecma_bigint_digit_t carry = 0; + + left_size -= right_size; + + do + { + ecma_bigint_digit_t left = *left_p++; + + if (carry == 0 || left != ~(ecma_bigint_digit_t) 0) + { + left += carry; + carry = 0; + } + else + { + left = 0; + carry = 1; + } + + ecma_bigint_digit_t right = *right_p++; + left += right; + + if (left < right) + { + JERRY_ASSERT (carry == 0); + carry = 1; + } + + *current_p++ = left; + } + while (current_p < end_p); + + end_p = (ecma_bigint_digit_t *) (((uint8_t *) end_p) + left_size); + + if (carry != 0) + { + while (true) + { + if (JERRY_UNLIKELY (current_p == end_p)) + { + return ecma_big_uint_extend (result_p, 1); + } + + ecma_bigint_digit_t value = *left_p++; + + if (value != ~(ecma_bigint_digit_t) 0) + { + *current_p++ = value + 1; + break; + } + + *current_p++ = 0; + } + } + + if (current_p < end_p) + { + memcpy (current_p, left_p, (size_t) ((uint8_t *) end_p - (uint8_t *) current_p)); + } + + return result_p; +} /* ecma_big_uint_add */ + +/** + * Substract right BigUInt value from the left BigUInt value + * + * return new BigInt value, NULL on error + */ +ecma_extended_primitive_t * +ecma_big_uint_sub (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */ + ecma_extended_primitive_t *right_value_p) /**< right BigUInt value */ +{ + uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p); + uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_value_p); + + JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0); + JERRY_ASSERT (right_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (right_value_p, right_size) != 0); + JERRY_ASSERT (left_size >= right_size); + + ecma_extended_primitive_t *result_p = ecma_bigint_create (left_size); + + if (JERRY_UNLIKELY (result_p == NULL)) + { + return NULL; + } + + ecma_bigint_digit_t *current_p = ECMA_BIGINT_GET_DIGITS (result_p, 0); + ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (result_p, right_size); + ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0); + ecma_bigint_digit_t *right_p = ECMA_BIGINT_GET_DIGITS (right_value_p, 0); + ecma_bigint_digit_t carry = 0; + + left_size -= right_size; + + do + { + ecma_bigint_digit_t left = *left_p++; + ecma_bigint_digit_t right = *right_p++; + + if (carry == 0 || left != 0) + { + left -= carry; + carry = left < right; + } + else + { + left = ~(ecma_bigint_digit_t) 0; + carry = 1; + } + + *current_p++ = left - right; + } + while (current_p < end_p); + + end_p = (ecma_bigint_digit_t *) (((uint8_t *) end_p) + left_size); + + if (carry != 0) + { + while (true) + { + JERRY_ASSERT (current_p < end_p); + + ecma_bigint_digit_t value = *left_p++; + + if (value != 0) + { + *current_p++ = value - 1; + break; + } + + *current_p++ = ~(ecma_bigint_digit_t) 0; + } + } + + if (current_p < end_p) + { + memcpy (current_p, left_p, (size_t) ((uint8_t *) end_p - (uint8_t *) current_p)); + return result_p; + } + + ecma_bigint_digit_t *new_end_p = current_p; + + if (new_end_p[-1] != 0) + { + return result_p; + } + + do + { + new_end_p--; + + JERRY_ASSERT (new_end_p > ECMA_BIGINT_GET_DIGITS (result_p, 0)); + } + while (new_end_p[-1] == 0); + + ecma_bigint_digit_t *result_data_p = ECMA_BIGINT_GET_DIGITS (result_p, 0); + uint32_t old_size = ECMA_BIGINT_GET_SIZE (result_p); + uint32_t new_size = (uint32_t) ((uint8_t *) new_end_p - (uint8_t *) result_data_p); + + if (ECMA_BIGINT_SIZE_IS_ODD (new_size) && ((new_size + sizeof (ecma_bigint_digit_t)) == old_size)) + { + result_p->u.bigint_sign_and_size -= (uint32_t) sizeof (ecma_bigint_digit_t); + return result_p; + } + + ecma_extended_primitive_t *new_result_p = ecma_bigint_create (new_size); + + if (JERRY_UNLIKELY (new_result_p == NULL)) + { + ecma_deref_bigint (result_p); + return NULL; + } + + memcpy (ECMA_BIGINT_GET_DIGITS (new_result_p, 0), result_data_p, new_size); + ecma_deref_bigint (result_p); + + return new_result_p; +} /* ecma_big_uint_sub */ + +/** + * Multiply two BigUInt values + * + * return new BigInt value, NULL on error + */ +ecma_extended_primitive_t * +ecma_big_uint_mul (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */ + ecma_extended_primitive_t *right_value_p) /**< right BigUInt value */ +{ + uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p); + uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_value_p); + + JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0); + JERRY_ASSERT (right_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (right_value_p, right_size) != 0); + + if (left_size < right_size) + { + /* Swap values. */ + ecma_extended_primitive_t *tmp_value_p = left_value_p; + left_value_p = right_value_p; + right_value_p = tmp_value_p; + + uint32_t tmp_size = left_size; + left_size = right_size; + right_size = tmp_size; + } + + uint32_t result_size = left_size + right_size - (uint32_t) sizeof (ecma_bigint_digit_t); + + ecma_extended_primitive_t *result_p = ecma_bigint_create (result_size); + + if (JERRY_UNLIKELY (result_p == NULL)) + { + return NULL; + } + + memset (ECMA_BIGINT_GET_DIGITS (result_p, 0), 0, result_size); + + /* Lower amount of space is allocated by default. This value provides extra space if needed. */ + ecma_bigint_digit_t extra_space[1] = { 0 }; + + ecma_bigint_digit_t *right_p = ECMA_BIGINT_GET_DIGITS (right_value_p, 0); + ecma_bigint_digit_t *right_end_p = ECMA_BIGINT_GET_DIGITS (right_value_p, right_size); + ecma_bigint_digit_t *left_start_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0); + ecma_bigint_digit_t *left_end_p = ECMA_BIGINT_GET_DIGITS (left_value_p, left_size); + + ecma_bigint_digit_t *result_start_p = ECMA_BIGINT_GET_DIGITS (result_p, 0); + ecma_bigint_digit_t *result_end_p = ECMA_BIGINT_GET_DIGITS (result_p, result_size); + + do + { + ecma_bigint_two_digits_t right = *right_p++; + + if (right == 0) + { + result_start_p++; + continue; + } + + ecma_bigint_digit_t *left_p = left_start_p; + ecma_bigint_digit_t *destination_p = result_start_p; + ecma_bigint_digit_t carry = 0; + + do + { + JERRY_ASSERT (destination_p != (ecma_bigint_digit_t *) (extra_space + 1)); + + ecma_bigint_two_digits_t multiply_result; + ecma_bigint_digit_t multiply_result_low, new_carry; + ecma_bigint_digit_t value = *destination_p; + + multiply_result = ((ecma_bigint_two_digits_t) (*left_p++)) * ((ecma_bigint_two_digits_t) right); + multiply_result_low = (ecma_bigint_digit_t) multiply_result; + value += multiply_result_low; + new_carry = (ecma_bigint_digit_t) (multiply_result >> (8 * sizeof (ecma_bigint_digit_t))); + + /* The new_carry can never overflow because: + * a) If left or right is less than 0xff..ff, new_carry will be less than or equal to + * 0xff...fd, and increasing it by maximum of two (carries) cannot overflow. + * b) If left and right are both equal to 0xff..ff, multiply_result_low will be 1, + * and computing value + carry + 1 can only increase new_carry at most once. */ + + if (value < multiply_result_low) + { + JERRY_ASSERT (new_carry < ~(ecma_bigint_digit_t) 0); + new_carry++; + } + + value += carry; + + if (value < carry) + { + JERRY_ASSERT (new_carry < ~(ecma_bigint_digit_t) 0); + new_carry++; + } + + carry = new_carry; + *destination_p++ = value; + + if (destination_p == result_end_p) + { + destination_p = (ecma_bigint_digit_t *) extra_space; + } + } + while (left_p < left_end_p); + + while (carry > 0) + { + JERRY_ASSERT (destination_p != (ecma_bigint_digit_t *) (extra_space + 1)); + + ecma_bigint_digit_t value = *destination_p; + + value += carry; + carry = (value < carry); + + *destination_p++ = value; + + if (destination_p == result_end_p) + { + destination_p = (ecma_bigint_digit_t *) extra_space; + } + } + + result_start_p++; + } + while (right_p < right_end_p); + + if (extra_space[0] == 0) + { + return result_p; + } + + 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 BigInt 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 + * + * return new BigInt value, NULL on error + */ +static ecma_extended_primitive_t * +ecma_big_uint_div_digit (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */ + ecma_bigint_digit_t divisor_digit, /**< divisor value */ + bool is_mod) /**< true if return with remainder */ +{ + uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p); + + JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0); + JERRY_ASSERT (divisor_digit > 0); + + ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, left_size - sizeof (ecma_bigint_digit_t)); + ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0); + + ecma_bigint_digit_t last_digit = *left_p; + ecma_bigint_digit_t remainder = last_digit % divisor_digit; + + last_digit = last_digit / divisor_digit; + + ecma_bigint_digit_t result_size = 0; + ecma_extended_primitive_t *result_p = NULL; + ecma_bigint_digit_t *current_p = NULL; + + if (!is_mod) + { + result_size = left_size; + + if (last_digit == 0) + { + result_size -= (uint32_t) sizeof (ecma_bigint_digit_t); + } + + result_p = ecma_bigint_create (result_size); + + if (JERRY_UNLIKELY (result_p == NULL)) + { + return NULL; + } + + current_p = ECMA_BIGINT_GET_DIGITS (result_p, result_size); + + if (last_digit != 0) + { + *(--current_p) = last_digit; + } + } + + while (left_p > end_p) + { + const uint32_t shift = 1 << ECMA_BIGINT_DIGIT_SHIFT; + + ecma_bigint_two_digits_t result = *(--left_p) | (((ecma_bigint_two_digits_t) remainder) << shift); + + if (!is_mod) + { + *(--current_p) = (ecma_bigint_digit_t) (result / divisor_digit); + } + + remainder = (ecma_bigint_digit_t) (result % divisor_digit); + } + + if (!is_mod) + { + JERRY_ASSERT (current_p == ECMA_BIGINT_GET_DIGITS (result_p, 0)); + return result_p; + } + + if (remainder == 0) + { + return ecma_bigint_create (0); + } + + result_p = ecma_bigint_create (sizeof (ecma_bigint_digit_t)); + + if (JERRY_UNLIKELY (result_p == NULL)) + { + return NULL; + } + + *ECMA_BIGINT_GET_DIGITS (result_p, 0) = remainder; + return result_p; +} /* ecma_big_uint_div_digit */ + +/** + * Shift left BigInt digits + * + * return newly allocated buffer, NULL on error + */ +static ecma_bigint_digit_t * +ecma_big_uint_div_shift_left (ecma_extended_primitive_t *value_p, /**< BigUInt value */ + ecma_bigint_digit_t shift_left, /**< left shift */ + bool extend) /**< extend the result with an extra digit */ +{ + uint32_t size = ECMA_BIGINT_GET_SIZE (value_p); + + JERRY_ASSERT (size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (value_p, size) != 0); + + ecma_bigint_digit_t *source_p = ECMA_BIGINT_GET_DIGITS (value_p, 0); + ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (value_p, size); + + if (extend) + { + size += (uint32_t) sizeof (ecma_bigint_digit_t); + } + + ecma_bigint_digit_t *result_p = (ecma_bigint_digit_t *) jmem_heap_alloc_block_null_on_error (size); + + if (JERRY_UNLIKELY (result_p == NULL)) + { + return result_p; + } + + if (shift_left == 0) + { + JERRY_ASSERT (extend); + + size -= (uint32_t) sizeof (ecma_bigint_digit_t); + *(ecma_bigint_digit_t *) (((uint8_t *) result_p) + size) = 0; + + memcpy (result_p, source_p, size); + return result_p; + } + + ecma_bigint_digit_t *destination_p = result_p; + ecma_bigint_digit_t carry = 0; + uint32_t shift_right = (1 << ECMA_BIGINT_DIGIT_SHIFT) - shift_left; + + do + { + ecma_bigint_digit_t value = *source_p++; + + *destination_p++ = (value << shift_left) | carry; + carry = value >> shift_right; + } + while (source_p < end_p); + + if (extend) + { + *destination_p++ = carry; + } + + return result_p; +} /* ecma_big_uint_div_shift_left */ + +/** + * Divide left BigUInt value with right BigUInt value + * + * return new BigInt value, NULL on error + */ +ecma_extended_primitive_t * +ecma_big_uint_div_mod (ecma_extended_primitive_t *dividend_value_p, /**< divider BigUInt value */ + ecma_extended_primitive_t *divisor_value_p, /**< divisor BigUInt value */ + bool is_mod) /**< true if return with remainder instead of quotient */ +{ + /* This algorithm is based on Donald Knuth’s "Algorithm D" */ + uint32_t divisor_size = ECMA_BIGINT_GET_SIZE (divisor_value_p); + + JERRY_ASSERT (divisor_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (divisor_value_p, divisor_size) != 0); + + /* The divisor must have at least two digits, so the single digit case is handled separately. */ + if (divisor_size == sizeof (ecma_bigint_digit_t)) + { + return ecma_big_uint_div_digit (dividend_value_p, *ECMA_BIGINT_GET_DIGITS (divisor_value_p, 0), is_mod); + } + + /* D1. [Normalize] */ + ecma_bigint_digit_t divisor_high = ECMA_BIGINT_GET_LAST_DIGIT (divisor_value_p, divisor_size); + ecma_bigint_digit_t shift_left = ecma_big_uint_count_leading_zero (divisor_high); + ecma_bigint_digit_t *buffer_p = ecma_big_uint_div_shift_left (dividend_value_p, shift_left, true); + + if (JERRY_UNLIKELY (buffer_p == NULL)) + { + return NULL; + } + + uint32_t dividend_size = ECMA_BIGINT_GET_SIZE (dividend_value_p); + ecma_extended_primitive_t *result_p = NULL; + ecma_bigint_digit_t *divisor_p; + + JERRY_ASSERT (dividend_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (dividend_value_p, dividend_size) != 0); + JERRY_ASSERT (dividend_size >= divisor_size); + + if (shift_left > 0) + { + divisor_p = ecma_big_uint_div_shift_left (divisor_value_p, shift_left, false); + + if (JERRY_UNLIKELY (divisor_p == NULL)) + { + goto error; + } + } + else + { + divisor_p = ECMA_BIGINT_GET_DIGITS (divisor_value_p, 0); + } + + ecma_bigint_digit_t *dividend_end_p = (ecma_bigint_digit_t *) (((uint8_t *) buffer_p) + dividend_size); + ecma_bigint_digit_t *dividend_p = (ecma_bigint_digit_t *) (((uint8_t *) dividend_end_p) - divisor_size); + ecma_bigint_digit_t *divisor_end_p = (ecma_bigint_digit_t *) (((uint8_t *) divisor_p) + divisor_size); + ecma_bigint_digit_t divisor_low = divisor_end_p[-2]; + + divisor_high = divisor_end_p[-1]; + JERRY_ASSERT ((divisor_high & (((ecma_bigint_digit_t) 1) << (8 * sizeof (ecma_bigint_digit_t) - 1))) != 0); + + do + { + /* D3. [Calculate Q′] */ + ecma_bigint_digit_t result_div; + + /* This do-while(false) statement allows local declarations and early exit. */ + do + { + ecma_bigint_digit_t result_mod; + + if (dividend_end_p[0] < divisor_high) + { + ecma_bigint_two_digits_t dividend = dividend_end_p[-1] | ECMA_BIGINT_HIGH_DIGIT (dividend_end_p[0]); + result_div = (ecma_bigint_digit_t) (dividend / divisor_high); + result_mod = (ecma_bigint_digit_t) (dividend % divisor_high); + } + else + { + JERRY_ASSERT (dividend_end_p[0] == divisor_high && dividend_end_p[-1] < divisor_high); + + result_div = ~((ecma_bigint_digit_t) 0); + result_mod = dividend_end_p[-1] + divisor_high; + + if (result_mod < divisor_high) + { + break; + } + } + + ecma_bigint_two_digits_t low_digits = ((ecma_bigint_two_digits_t) result_div) * divisor_low; + + while (low_digits > (ECMA_BIGINT_HIGH_DIGIT (result_mod) | divisor_low)) + { + result_div--; + result_mod += divisor_high; + + /* If result_mod becomes a two digit long number, the condition of the loop must be true, + * so the loop can be aborted. This loop stops after maximum of two iterations, since + * the highest bit of divisor_high is set. */ + if (result_mod < divisor_high) + { + break; + } + + /* Subtraction is faster than recomputing result_div * divisor_low. */ + low_digits -= divisor_low; + } + } + while (false); + + /* D4. [Multiply and subtract] */ + ecma_bigint_digit_t *destination_p = dividend_p; + ecma_bigint_digit_t *source_p = divisor_p; + ecma_bigint_digit_t carry = 0; + + do + { + ecma_bigint_two_digits_t multiply_result = ((ecma_bigint_two_digits_t) (*source_p++)) * result_div; + ecma_bigint_digit_t multiply_result_low, new_carry; + ecma_bigint_digit_t value = *destination_p; + + /* The new carry never overflows. See the comment in ecma_big_uint_mul. */ + new_carry = (ecma_bigint_digit_t) (multiply_result >> (8 * sizeof (ecma_bigint_digit_t))); + multiply_result_low = (ecma_bigint_digit_t) multiply_result; + + if (value < multiply_result_low) + { + new_carry++; + } + + value -= multiply_result_low; + + if (value < carry) + { + new_carry++; + } + + *destination_p++ = value - carry; + carry = new_carry; + } + while (source_p < divisor_end_p); + + bool negative_result = *destination_p < carry; + *destination_p -= carry; + + if (negative_result) + { + /* D6. [Add back] */ + result_div--; + + destination_p = dividend_p; + source_p = divisor_p; + carry = 0; + + do + { + ecma_bigint_digit_t left = *destination_p; + + if (carry == 0 || left != ~(ecma_bigint_digit_t) 0) + { + left += carry; + carry = 0; + } + else + { + left = 0; + carry = 1; + } + + ecma_bigint_digit_t right = *source_p++; + left += right; + + if (left < right) + { + JERRY_ASSERT (carry == 0); + carry = 1; + } + + *destination_p++ = left; + } + while (source_p < divisor_end_p); + } + + *dividend_end_p = result_div; + + dividend_p--; + dividend_end_p--; + } + while (dividend_p >= buffer_p); + + ecma_bigint_digit_t *source_p; + ecma_bigint_digit_t *source_end_p; + + if (is_mod) + { + source_p = buffer_p; + source_end_p = dividend_end_p; + + while (source_end_p > source_p && *source_end_p == 0) + { + source_end_p--; + } + + if ((*source_end_p >> shift_left) != 0) + { + source_end_p++; + /* This is required to reset carry below. */ + *source_end_p = 0; + } + } + else + { + source_p = dividend_end_p + 1; + source_end_p = (ecma_bigint_digit_t *) (((uint8_t *) buffer_p) + dividend_size); + + if (*source_end_p != 0) + { + source_end_p++; + } + } + + result_p = ecma_bigint_create ((uint32_t) ((uint8_t *) source_end_p - (uint8_t *) source_p)); + + if (result_p != NULL && source_p != source_end_p) + { + ecma_bigint_digit_t *destination_p = ECMA_BIGINT_GET_DIGITS (result_p, 0); + + if (is_mod && shift_left > 0) + { + ecma_bigint_digit_t shift_right = shift_left; + + shift_left = (ecma_bigint_digit_t) (8 * (sizeof (ecma_bigint_digit_t)) - shift_left); + destination_p += source_end_p - source_p; + + ecma_bigint_digit_t carry = *source_end_p << shift_left; + + do + { + ecma_bigint_digit_t value = *(--source_end_p); + + *(--destination_p) = (value >> shift_right) | carry; + carry = value << shift_left; + } + while (source_end_p > source_p); + } + else + { + memcpy (destination_p, source_p, (size_t) ((uint8_t *) source_end_p - (uint8_t *) source_p)); + } + } + +error: + jmem_heap_free_block (buffer_p, dividend_size + sizeof (ecma_bigint_digit_t)); + + if (shift_left > 0 && divisor_p != NULL) + { + jmem_heap_free_block (divisor_p, divisor_size); + } + + return result_p; +} /* ecma_big_uint_div_mod */ + +/** + * Shift left BigInt values by an uint32 value + * + * return new BigInt value, NULL on error + */ +ecma_extended_primitive_t * +ecma_big_uint_shift_left (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */ + uint32_t right_value) /**< shift value */ +{ + JERRY_ASSERT (right_value > 0); + + uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p); + JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0); + + uint32_t zero_size = (right_value >> ECMA_BIGINT_DIGIT_SHIFT) * (uint32_t) sizeof (ecma_bigint_digit_t); + uint32_t result_size = left_size + zero_size; + + uint32_t shift_left = right_value & ((1 << ECMA_BIGINT_DIGIT_SHIFT) - 1); + uint32_t shift_right = (1 << ECMA_BIGINT_DIGIT_SHIFT) - shift_left; + + if (shift_left > 0 && (ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) >> shift_right) != 0) + { + result_size += (uint32_t) sizeof (ecma_bigint_digit_t); + } + + if (result_size > ECMA_BIGINT_MAX_SIZE) + { + return NULL; + } + + ecma_extended_primitive_t *result_value_p = ecma_bigint_create (result_size); + ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, 0); + ecma_bigint_digit_t *result_p = ECMA_BIGINT_GET_DIGITS (result_value_p, 0); + + if (zero_size > 0) + { + memset (result_p, 0, zero_size); + result_p = (ecma_bigint_digit_t *) (((uint8_t *) result_p) + zero_size); + } + + if (shift_left == 0) + { + /* Shift by full digits. */ + memcpy (result_p, left_p, left_size); + return result_value_p; + } + + ecma_bigint_digit_t *left_end_p = ECMA_BIGINT_GET_DIGITS (left_value_p, left_size); + ecma_bigint_digit_t carry = 0; + + do + { + ecma_bigint_digit_t value = *left_p++; + + *result_p++ = (value << shift_left) | carry; + carry = value >> shift_right; + } + while (left_p < left_end_p); + + if (carry > 0) + { + *result_p = carry; + } + + return result_value_p; +} /* ecma_big_uint_shift_left */ + +/** + * Shift right BigInt values by an uint32 value + * + * return new BigInt value, NULL on error + */ +ecma_extended_primitive_t * +ecma_big_uint_shift_right (ecma_extended_primitive_t *left_value_p, /**< left BigUInt value */ + uint32_t right_value) /**< shift value */ +{ + JERRY_ASSERT (right_value > 0); + + uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_value_p); + JERRY_ASSERT (left_size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) != 0); + + uint32_t crop_size = (right_value >> ECMA_BIGINT_DIGIT_SHIFT) * (uint32_t) sizeof (ecma_bigint_digit_t); + + uint32_t shift_right = right_value & ((1 << ECMA_BIGINT_DIGIT_SHIFT) - 1); + uint32_t shift_left = (1 << ECMA_BIGINT_DIGIT_SHIFT) - shift_right; + ecma_bigint_digit_t carry = 0; + + if (shift_right > 0 + && (ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) >> shift_right) == 0) + { + carry = ECMA_BIGINT_GET_LAST_DIGIT (left_value_p, left_size) << shift_left; + left_size -= (uint32_t) sizeof (ecma_bigint_digit_t); + } + + if (left_size <= crop_size) + { + return ecma_bigint_create (0); + } + + uint32_t size = left_size - crop_size; + ecma_extended_primitive_t *result_value_p = ecma_bigint_create (size); + + if (shift_right == 0) + { + memcpy (ECMA_BIGINT_GET_DIGITS (result_value_p, 0), ECMA_BIGINT_GET_DIGITS (left_value_p, crop_size), size); + return result_value_p; + } + + ecma_bigint_digit_t *left_p = ECMA_BIGINT_GET_DIGITS (left_value_p, left_size); + ecma_bigint_digit_t *result_p = ECMA_BIGINT_GET_DIGITS (result_value_p, size); + ecma_bigint_digit_t *end_p = ECMA_BIGINT_GET_DIGITS (result_value_p, 0); + + do + { + ecma_bigint_digit_t value = *(--left_p); + + *(--result_p) = (value >> shift_right) | carry; + carry = value << shift_left; + } + while (result_p > end_p); + + return result_value_p; +} /* ecma_big_uint_shift_right */ + +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ diff --git a/jerry-core/ecma/operations/ecma-big-uint.h b/jerry-core/ecma/operations/ecma-big-uint.h new file mode 100644 index 00000000..a8e16af8 --- /dev/null +++ b/jerry-core/ecma/operations/ecma-big-uint.h @@ -0,0 +1,88 @@ +/* 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. + */ + +#ifndef ECMA_BIG_UINT_H +#define ECMA_BIG_UINT_H + +#include "ecma-globals.h" + +#if ENABLED (JERRY_BUILTIN_BIGINT) + +/** + * Limit of BigUInt memory allocation in JerryScript. + */ +#define ECMA_BIGINT_MAX_SIZE 0x10000 + +/** + * Unsigned type which can hold two digits. + */ +typedef uint64_t ecma_bigint_two_digits_t; + +/** + * Shift used by left/right shifting of a value. + */ +#define ECMA_BIGINT_DIGIT_SHIFT 5 + +/** + * Return with the digits of a BigInt value. + */ +#define ECMA_BIGINT_GET_DIGITS(value_p, offset) \ + ((ecma_bigint_digit_t *) (((uint8_t *) (value_p)) + sizeof (ecma_extended_primitive_t) + (offset))) + +/** + * Return with the digits of a BigInt value. + */ +#define ECMA_BIGINT_GET_LAST_DIGIT(value_p, size) \ + *ECMA_BIGINT_GET_DIGITS (value_p, size - sizeof (ecma_bigint_digit_t)) + +/** + * Returns true if size is an odd number. + */ +#define ECMA_BIGINT_SIZE_IS_ODD(size) \ + (((size) & sizeof (ecma_bigint_digit_t)) != 0) + +/** + * Returns a two digit value where the high digit is set to the passed digit. + */ +#define ECMA_BIGINT_HIGH_DIGIT(digit) \ + (((ecma_bigint_two_digits_t) digit) << (8 * sizeof (ecma_bigint_digit_t))) + +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); + +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, + ecma_bigint_digit_t mul, ecma_bigint_digit_t add); + +uint8_t *ecma_big_uint_to_string (ecma_extended_primitive_t *value_p, uint32_t radix, + uint32_t *char_start_p, uint32_t *char_size_p); + +ecma_extended_primitive_t *ecma_big_uint_add (ecma_extended_primitive_t *left_value_p, + ecma_extended_primitive_t *right_value_p); +ecma_extended_primitive_t *ecma_big_uint_sub (ecma_extended_primitive_t *left_value_p, + ecma_extended_primitive_t *right_value_p); +ecma_extended_primitive_t *ecma_big_uint_mul (ecma_extended_primitive_t *left_value_p, + ecma_extended_primitive_t *right_value_p); +ecma_extended_primitive_t *ecma_big_uint_div_mod (ecma_extended_primitive_t *dividend_value_p, + ecma_extended_primitive_t *divisor_value_p, + bool is_mod); + +ecma_extended_primitive_t *ecma_big_uint_shift_left (ecma_extended_primitive_t *left_value_p, uint32_t right_value); +ecma_extended_primitive_t *ecma_big_uint_shift_right (ecma_extended_primitive_t *left_value_p, uint32_t right_value); + +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + +#endif /* ECMA_BIG_UINT_H */ diff --git a/jerry-core/ecma/operations/ecma-bigint-object.c b/jerry-core/ecma/operations/ecma-bigint-object.c new file mode 100644 index 00000000..fbce1938 --- /dev/null +++ b/jerry-core/ecma/operations/ecma-bigint-object.c @@ -0,0 +1,66 @@ +/* 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 "ecma-alloc.h" +#include "ecma-bigint-object.h" +#include "ecma-builtins.h" +#include "ecma-exceptions.h" +#include "ecma-gc.h" +#include "ecma-globals.h" +#include "ecma-helpers.h" +#include "ecma-objects.h" +#include "ecma-objects-general.h" + +#if ENABLED (JERRY_BUILTIN_BIGINT) + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmabigintobject ECMA BigInt object related routines + * @{ + */ + +/** + * BigInt object creation operation. + * + * See also: ECMA-262 v11, 7.1.18 + * + * @return ecma value + * Returned value must be freed with ecma_free_value + */ +ecma_value_t +ecma_op_create_bigint_object (ecma_value_t arg) /**< argument passed to the toObject operation */ +{ + JERRY_ASSERT (ecma_is_value_bigint (arg)); + + ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_BIGINT_PROTOTYPE); + + ecma_object_t *object_p = ecma_create_object (prototype_obj_p, + sizeof (ecma_extended_object_t), + ECMA_OBJECT_TYPE_CLASS); + + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; + ext_object_p->u.class_prop.class_id = LIT_MAGIC_STRING_BIGINT_UL; + ext_object_p->u.class_prop.u.value = ecma_copy_value (arg); + + return ecma_make_object_value (object_p); +} /* ecma_op_create_bigint_object */ + +/** + * @} + * @} + */ + +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ diff --git a/jerry-core/ecma/operations/ecma-bigint-object.h b/jerry-core/ecma/operations/ecma-bigint-object.h new file mode 100644 index 00000000..254e7598 --- /dev/null +++ b/jerry-core/ecma/operations/ecma-bigint-object.h @@ -0,0 +1,39 @@ +/* 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. + */ + +#ifndef ECMA_BIGINT_OBJECT_H +#define ECMA_BIGINT_OBJECT_H + +#include "ecma-globals.h" + +#if ENABLED (JERRY_BUILTIN_BIGINT) + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmabigintobject ECMA BigInt object related routines + * @{ + */ + +ecma_value_t ecma_op_create_bigint_object (ecma_value_t arg); + +/** + * @} + * @} + */ + +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + +#endif /* !ECMA_BIGINT_OBJECT_H */ diff --git a/jerry-core/ecma/operations/ecma-bigint.c b/jerry-core/ecma/operations/ecma-bigint.c new file mode 100644 index 00000000..207f6fda --- /dev/null +++ b/jerry-core/ecma/operations/ecma-bigint.c @@ -0,0 +1,508 @@ +/* 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 "ecma-bigint.h" +#include "ecma-big-uint.h" +#include "ecma-exceptions.h" +#include "ecma-helpers.h" +#include "lit-char-helpers.h" + +#if ENABLED (JERRY_BUILTIN_BIGINT) + +/** + * Raise a not enough memory error + * + * @return ECMA_VALUE_ERROR + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_bigint_raise_memory_error (void) +{ + return ecma_raise_range_error (ECMA_ERR_MSG ("Cannot allocate memory for a BigInt value")); +} /* ecma_bigint_raise_memory_error */ + +/** + * Parse a string and create a BigInt value + * + * @return ecma BigInt value or ECMA_VALUE_ERROR + * Returned value must be freed with ecma_free_value. + */ +ecma_value_t +ecma_bigint_parse_string (const lit_utf8_byte_t *string_p, /**< string represenation of the BigInt */ + lit_utf8_size_t size) /**< string size */ +{ + ecma_bigint_digit_t radix = 10; + uint32_t sign = 0; + + if (size >= 3 && string_p[0] == LIT_CHAR_0) + { + if (string_p[1] == LIT_CHAR_LOWERCASE_X || string_p[1] == LIT_CHAR_UPPERCASE_X) + { + radix = 16; + string_p += 2; + size -= 2; + } + else if (string_p[1] == LIT_CHAR_LOWERCASE_B || string_p[1] == LIT_CHAR_UPPERCASE_B) + { + radix = 2; + string_p += 2; + size -= 2; + } + } + else if (size >= 2) + { + if (string_p[0] == LIT_CHAR_PLUS) + { + size--; + string_p++; + } + else if (string_p[0] == LIT_CHAR_MINUS) + { + sign = ECMA_BIGINT_SIGN; + size--; + string_p++; + } + } + else if (size == 0) + { + return ecma_raise_syntax_error (ECMA_ERR_MSG ("BigInt cannot be constructed from empty string")); + } + + const lit_utf8_byte_t *string_end_p = string_p + size; + + while (string_p < string_end_p && *string_p == LIT_CHAR_0) + { + string_p++; + } + + ecma_extended_primitive_t *result_p = NULL; + + if (string_p == string_end_p) + { + result_p = ecma_bigint_create (0); + } + else + { + do + { + ecma_bigint_digit_t digit = radix; + + if (*string_p >= LIT_CHAR_0 && *string_p <= LIT_CHAR_9) + { + digit = (ecma_bigint_digit_t) (*string_p - LIT_CHAR_0); + } + else + { + lit_utf8_byte_t character = (lit_utf8_byte_t) LEXER_TO_ASCII_LOWERCASE (*string_p); + + if (character >= LIT_CHAR_LOWERCASE_A && character <= LIT_CHAR_LOWERCASE_F) + { + digit = (ecma_bigint_digit_t) (character - (LIT_CHAR_LOWERCASE_A - 10)); + } + } + + if (digit >= radix) + { + if (result_p != NULL) + { + ecma_deref_bigint (result_p); + } + return ecma_raise_syntax_error (ECMA_ERR_MSG ("String cannot be converted to BigInt value")); + } + + result_p = ecma_big_uint_mul_digit (result_p, radix, digit); + + if (JERRY_UNLIKELY (result_p == NULL)) + { + break; + } + } + while (++string_p < string_end_p); + } + + if (JERRY_UNLIKELY (result_p == NULL)) + { + return ecma_bigint_raise_memory_error (); + } + + result_p->u.bigint_sign_and_size |= sign; + return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); +} /* ecma_bigint_parse_string */ + +/** + * Create a string representation for a BigInt value + * + * @return ecma string or ECMA_VALUE_ERROR + * Returned value must be freed with ecma_free_value. + */ +ecma_string_t * +ecma_bigint_to_string (ecma_value_t value, /**< BigInt value */ + ecma_bigint_digit_t radix) /**< conversion radix */ +{ + JERRY_ASSERT (ecma_is_value_bigint (value)); + + ecma_extended_primitive_t *bigint_p = ecma_get_extended_primitive_from_value (value); + + if (ECMA_BIGINT_GET_SIZE (bigint_p) == 0) + { + return ecma_new_ecma_string_from_code_unit (LIT_CHAR_0); + } + + uint32_t char_start_p, char_size_p; + lit_utf8_byte_t *string_buffer_p = ecma_big_uint_to_string (bigint_p, radix, &char_start_p, &char_size_p); + + if (JERRY_UNLIKELY (string_buffer_p == NULL)) + { + ecma_raise_range_error (ECMA_ERR_MSG ("Cannot allocate memory for a string representation of a BigInt value")); + return NULL; + } + + JERRY_ASSERT (char_start_p > 0); + + if (bigint_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) + { + string_buffer_p[--char_start_p] = LIT_CHAR_MINUS; + } + + ecma_string_t *string_p; + string_p = ecma_new_ecma_string_from_utf8 (string_buffer_p + char_start_p, char_size_p - char_start_p); + + jmem_heap_free_block (string_buffer_p, char_size_p); + return string_p; +} /* ecma_bigint_to_string */ + +/** + * Negate a non-zero BigInt value + * + * @return ecma BigInt value or ECMA_VALUE_ERROR + * Returned value must be freed with ecma_free_value. + */ +ecma_value_t +ecma_bigint_negate (ecma_extended_primitive_t *value_p) /**< BigInt value */ +{ + uint32_t size = ECMA_BIGINT_GET_SIZE (value_p); + + JERRY_ASSERT (size > 0 && ECMA_BIGINT_GET_LAST_DIGIT (value_p, size) != 0); + + ecma_extended_primitive_t *result_p = ecma_bigint_create (size); + + if (JERRY_UNLIKELY (result_p == NULL)) + { + return ecma_bigint_raise_memory_error (); + } + + memcpy (result_p + 1, value_p + 1, size); + result_p->refs_and_type = ECMA_EXTENDED_PRIMITIVE_REF_ONE | ECMA_TYPE_BIGINT; + result_p->u.bigint_sign_and_size = value_p->u.bigint_sign_and_size ^ ECMA_BIGINT_SIGN; + + return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); +} /* ecma_bigint_negate */ + +/** + * Add/subtract right BigInt value to/from left BigInt value + * + * @return ecma BigInt value or ECMA_VALUE_ERROR + * Returned value must be freed with ecma_free_value. + */ +ecma_value_t +ecma_bigint_add_sub (ecma_value_t left_value, /**< left BigInt value */ + ecma_value_t right_value, /**< right BigInt value */ + bool is_add) /**< true if add operation should be performed */ +{ + JERRY_ASSERT (ecma_is_value_bigint (left_value) && ecma_is_value_bigint (right_value)); + + ecma_extended_primitive_t *left_p = ecma_get_extended_primitive_from_value (left_value); + ecma_extended_primitive_t *right_p = ecma_get_extended_primitive_from_value (right_value); + uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_p); + uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_p); + + if (right_size == 0) + { + ecma_ref_extended_primitive (left_p); + return left_value; + } + + if (left_size == 0) + { + if (!is_add) + { + return ecma_bigint_negate (right_p); + } + + ecma_ref_extended_primitive (right_p); + return right_value; + } + + uint32_t sign = is_add ? 0 : ECMA_BIGINT_SIGN; + + if (((left_p->u.bigint_sign_and_size ^ right_p->u.bigint_sign_and_size) & ECMA_BIGINT_SIGN) == sign) + { + ecma_extended_primitive_t *result_p = ecma_big_uint_add (left_p, right_p); + + if (JERRY_UNLIKELY (result_p == NULL)) + { + return ecma_bigint_raise_memory_error (); + } + + result_p->u.bigint_sign_and_size |= left_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN; + return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); + } + + int compare_result = ecma_big_uint_compare (left_p, right_p); + ecma_extended_primitive_t *result_p; + + if (compare_result == 0) + { + sign = 0; + result_p = ecma_bigint_create (0); + } + else if (compare_result > 0) + { + sign = left_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN; + result_p = ecma_big_uint_sub (left_p, right_p); + } + else + { + sign = right_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN; + + if (!is_add) + { + sign ^= ECMA_BIGINT_SIGN; + } + + result_p = ecma_big_uint_sub (right_p, left_p); + } + + if (JERRY_UNLIKELY (result_p == NULL)) + { + return ecma_bigint_raise_memory_error (); + } + + result_p->u.bigint_sign_and_size |= sign; + return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); +} /* ecma_bigint_add_sub */ + +/** + * Multiply two BigInt values + * + * @return ecma BigInt value or ECMA_VALUE_ERROR + * Returned value must be freed with ecma_free_value. + */ +ecma_value_t +ecma_bigint_mul (ecma_value_t left_value, /**< left BigInt value */ + ecma_value_t right_value) /**< right BigInt value */ +{ + JERRY_ASSERT (ecma_is_value_bigint (left_value) && ecma_is_value_bigint (right_value)); + + ecma_extended_primitive_t *left_p = ecma_get_extended_primitive_from_value (left_value); + ecma_extended_primitive_t *right_p = ecma_get_extended_primitive_from_value (right_value); + uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_p); + uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_p); + + if (left_size == 0) + { + ecma_ref_extended_primitive (left_p); + return left_value; + } + + if (right_size == 0) + { + ecma_ref_extended_primitive (right_p); + return right_value; + } + + if (left_size == sizeof (ecma_bigint_digit_t) + && ECMA_BIGINT_GET_LAST_DIGIT (left_p, sizeof (ecma_bigint_digit_t)) == 1) + { + if (left_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) + { + return ecma_bigint_negate (right_p); + } + + ecma_ref_extended_primitive (right_p); + return right_value; + } + + if (right_size == sizeof (ecma_bigint_digit_t) + && ECMA_BIGINT_GET_LAST_DIGIT (right_p, sizeof (ecma_bigint_digit_t)) == 1) + { + if (right_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) + { + return ecma_bigint_negate (left_p); + } + + ecma_ref_extended_primitive (left_p); + return left_value; + } + + ecma_extended_primitive_t *result_p = ecma_big_uint_mul (left_p, right_p); + + if (JERRY_UNLIKELY (result_p == NULL)) + { + return ecma_bigint_raise_memory_error (); + } + + uint32_t sign = (left_p->u.bigint_sign_and_size ^ right_p->u.bigint_sign_and_size) & ECMA_BIGINT_SIGN; + result_p->u.bigint_sign_and_size |= sign; + return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); +} /* ecma_bigint_mul */ + +/** + * Divide two BigInt values + * + * @return ecma BigInt value or ECMA_VALUE_ERROR + * Returned value must be freed with ecma_free_value. + */ +ecma_value_t +ecma_bigint_div_mod (ecma_value_t left_value, /**< left BigInt value */ + ecma_value_t right_value, /**< right BigInt value */ + bool is_mod) /**< true if return with remainder */ +{ + JERRY_ASSERT (ecma_is_value_bigint (left_value) && ecma_is_value_bigint (right_value)); + + ecma_extended_primitive_t *left_p = ecma_get_extended_primitive_from_value (left_value); + ecma_extended_primitive_t *right_p = ecma_get_extended_primitive_from_value (right_value); + uint32_t left_size = ECMA_BIGINT_GET_SIZE (left_p); + uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_p); + + if (right_size == 0) + { + return ecma_raise_range_error (ECMA_ERR_MSG ("BigInt division by zero")); + } + + if (left_size == 0) + { + ecma_ref_extended_primitive (left_p); + return left_value; + } + + int compare_result = ecma_big_uint_compare (left_p, right_p); + ecma_extended_primitive_t *result_p; + + if (compare_result < 0) + { + if (is_mod) + { + ecma_ref_extended_primitive (left_p); + return left_value; + } + else + { + result_p = ecma_bigint_create (0); + } + } + else if (compare_result == 0) + { + if (is_mod) + { + result_p = ecma_bigint_create (0); + } + else + { + result_p = ecma_bigint_create (sizeof (ecma_bigint_digit_t)); + + if (result_p != NULL) + { + *ECMA_BIGINT_GET_DIGITS (result_p, 0) = 1; + } + } + } + else + { + result_p = ecma_big_uint_div_mod (left_p, right_p, is_mod); + } + + if (JERRY_UNLIKELY (result_p == NULL)) + { + return ecma_bigint_raise_memory_error (); + } + + if (ECMA_BIGINT_GET_SIZE (result_p) == 0) + { + return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); + } + + if (is_mod) + { + result_p->u.bigint_sign_and_size |= left_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN; + } + else + { + uint32_t sign = (left_p->u.bigint_sign_and_size ^ right_p->u.bigint_sign_and_size) & ECMA_BIGINT_SIGN; + result_p->u.bigint_sign_and_size |= sign; + } + + return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); +} /* ecma_bigint_div_mod */ + +/** + * Shift left BigInt value to left or right + * + * @return ecma BigInt value or ECMA_VALUE_ERROR + * Returned value must be freed with ecma_free_value. + */ +ecma_value_t +ecma_bigint_shift (ecma_value_t left_value, /**< left BigInt value */ + ecma_value_t right_value, /**< right BigInt value */ + bool is_left) /**< true if left shift operation should be performed */ +{ + JERRY_ASSERT (ecma_is_value_bigint (left_value) && ecma_is_value_bigint (right_value)); + + ecma_extended_primitive_t *left_p = ecma_get_extended_primitive_from_value (left_value); + ecma_extended_primitive_t *right_p = ecma_get_extended_primitive_from_value (right_value); + uint32_t right_size = ECMA_BIGINT_GET_SIZE (right_p); + + if (right_size == 0 || ECMA_BIGINT_GET_SIZE (left_p) == 0) + { + ecma_ref_extended_primitive (left_p); + return left_value; + } + + if (right_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN) + { + is_left = !is_left; + } + + if (right_size > sizeof (ecma_bigint_digit_t)) + { + if (is_left) + { + return ecma_bigint_raise_memory_error (); + } + + return ecma_make_extended_primitive_value (ecma_bigint_create (0), ECMA_TYPE_BIGINT); + } + + ecma_extended_primitive_t *result_p; + ecma_bigint_digit_t shift = ECMA_BIGINT_GET_LAST_DIGIT (right_p, sizeof (ecma_bigint_digit_t)); + + if (is_left) + { + result_p = ecma_big_uint_shift_left (left_p, shift); + } + else + { + result_p = ecma_big_uint_shift_right (left_p, shift); + } + + if (JERRY_UNLIKELY (result_p == NULL)) + { + return ecma_bigint_raise_memory_error (); + } + + result_p->u.bigint_sign_and_size |= left_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN; + return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT); +} /* ecma_bigint_shift */ +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ diff --git a/jerry-core/ecma/operations/ecma-bigint.h b/jerry-core/ecma/operations/ecma-bigint.h new file mode 100644 index 00000000..e88be173 --- /dev/null +++ b/jerry-core/ecma/operations/ecma-bigint.h @@ -0,0 +1,39 @@ +/* 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. + */ + +#ifndef ECMA_BIG_INT_H +#define ECMA_BIG_INT_H + +#include "ecma-globals.h" + +#if ENABLED (JERRY_BUILTIN_BIGINT) + +/** + * Sign bit of a BigInt value. The number is negative, if this bit is set. + */ +#define ECMA_BIGINT_SIGN 0x1 + +ecma_value_t ecma_bigint_parse_string (const lit_utf8_byte_t *string_p, lit_utf8_size_t size); +ecma_string_t *ecma_bigint_to_string (ecma_value_t value, ecma_bigint_digit_t radix); + +ecma_value_t ecma_bigint_negate (ecma_extended_primitive_t *value_p); +ecma_value_t ecma_bigint_add_sub (ecma_value_t left_value, ecma_value_t right_value, bool is_add); +ecma_value_t ecma_bigint_mul (ecma_value_t left_value, ecma_value_t right_value); +ecma_value_t ecma_bigint_div_mod (ecma_value_t left_value, ecma_value_t right_value, bool is_mod); +ecma_value_t ecma_bigint_shift (ecma_value_t left_value, ecma_value_t right_value, bool is_left); + +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + +#endif /* ECMA_BIG_INT_H */ diff --git a/jerry-core/ecma/operations/ecma-conversion.c b/jerry-core/ecma/operations/ecma-conversion.c index c3a3ee2f..f1aeb781 100644 --- a/jerry-core/ecma/operations/ecma-conversion.c +++ b/jerry-core/ecma/operations/ecma-conversion.c @@ -20,6 +20,8 @@ #include <math.h> #include "ecma-alloc.h" +#include "ecma-bigint.h" +#include "ecma-bigint-object.h" #include "ecma-boolean-object.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" @@ -279,12 +281,6 @@ ecma_op_to_number (ecma_value_t value) /**< ecma value */ ecma_string_t *str_p = ecma_get_string_from_value (value); return ecma_make_number_value (ecma_string_to_number (str_p)); } -#if ENABLED (JERRY_ESNEXT) - if (ecma_is_value_symbol (value)) - { - return ecma_raise_type_error (ECMA_ERR_MSG ("Cannot convert a Symbol value to a number.")); - } -#endif /* ENABLED (JERRY_ESNEXT) */ if (ecma_is_value_undefined (value)) { @@ -301,6 +297,20 @@ ecma_op_to_number (ecma_value_t value) /**< ecma value */ return ecma_make_integer_value (ecma_is_value_true (value) ? 1 : 0); } +#if ENABLED (JERRY_ESNEXT) + if (ecma_is_value_symbol (value)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Cannot convert a Symbol value to a number")); + } +#endif /* ENABLED (JERRY_ESNEXT) */ + +#if ENABLED (JERRY_BUILTIN_BIGINT) + if (ecma_is_value_bigint (value)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Cannot convert a BigInt value to a number")); + } +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + JERRY_ASSERT (ecma_is_value_object (value)); ecma_object_t *obj_p = ecma_get_object_from_value (value); @@ -364,13 +374,6 @@ ecma_get_number (ecma_value_t value, /**< ecma value*/ return ECMA_VALUE_EMPTY; } -#if ENABLED (JERRY_ESNEXT) - if (ecma_is_value_symbol (value)) - { - return ecma_raise_type_error (ECMA_ERR_MSG ("Cannot convert a Symbol value to a number.")); - } -#endif /* ENABLED (JERRY_ESNEXT) */ - if (ecma_is_value_true (value)) { *number_p = 1; @@ -383,6 +386,20 @@ ecma_get_number (ecma_value_t value, /**< ecma value*/ return ECMA_VALUE_EMPTY; } +#if ENABLED (JERRY_ESNEXT) + if (ecma_is_value_symbol (value)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Cannot convert a Symbol value to a number.")); + } +#endif /* ENABLED (JERRY_ESNEXT) */ + +#if ENABLED (JERRY_BUILTIN_BIGINT) + if (ecma_is_value_bigint (value)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Cannot convert a BigInt value to a number")); + } +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + JERRY_ASSERT (ecma_is_value_object (value)); ecma_object_t *obj_p = ecma_get_object_from_value (value); @@ -452,6 +469,16 @@ ecma_op_to_string (ecma_value_t value) /**< ecma value */ return ecma_get_magic_string (LIT_MAGIC_STRING_NULL); } + if (ecma_is_value_true (value)) + { + return ecma_get_magic_string (LIT_MAGIC_STRING_TRUE); + } + + if (ecma_is_value_false (value)) + { + return ecma_get_magic_string (LIT_MAGIC_STRING_FALSE); + } + #if ENABLED (JERRY_ESNEXT) if (ecma_is_value_symbol (value)) { @@ -460,15 +487,12 @@ ecma_op_to_string (ecma_value_t value) /**< ecma value */ } #endif /* ENABLED (JERRY_ESNEXT) */ - if (ecma_is_value_true (value)) - { - return ecma_get_magic_string (LIT_MAGIC_STRING_TRUE); - } - - if (ecma_is_value_false (value)) +#if ENABLED (JERRY_BUILTIN_BIGINT) + if (ecma_is_value_bigint (value)) { - return ecma_get_magic_string (LIT_MAGIC_STRING_FALSE); + return ecma_bigint_to_string (value, 10); } +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ JERRY_ASSERT (ecma_is_value_object (value)); @@ -567,6 +591,12 @@ ecma_op_to_object (ecma_value_t value) /**< ecma value */ return ecma_op_create_symbol_object (value); } #endif /* ENABLED (JERRY_ESNEXT) */ +#if ENABLED (JERRY_BUILTIN_BIGINT) + else if (ecma_is_value_bigint (value)) + { + return ecma_op_create_bigint_object (value); + } +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ else { if (ecma_is_value_undefined (value) diff --git a/jerry-core/ecma/operations/ecma-get-put-value.c b/jerry-core/ecma/operations/ecma-get-put-value.c index 36f1a580..1667184a 100644 --- a/jerry-core/ecma/operations/ecma-get-put-value.c +++ b/jerry-core/ecma/operations/ecma-get-put-value.c @@ -174,6 +174,12 @@ ecma_op_get_value_object_base (ecma_value_t base_value, /**< base value */ id = ECMA_BUILTIN_ID_SYMBOL_PROTOTYPE; } #endif /* ENABLED (JERRY_ESNEXT) */ +#if ENABLED (JERRY_BUILTIN_BIGINT) + else if (ecma_is_value_bigint (base_value)) + { + id = ECMA_BUILTIN_ID_BIGINT_PROTOTYPE; + } +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ else { JERRY_ASSERT (ecma_is_value_boolean (base_value)); diff --git a/jerry-core/ecma/operations/ecma-objects.c b/jerry-core/ecma/operations/ecma-objects.c index c270cb31..a8bac03c 100644 --- a/jerry-core/ecma/operations/ecma-objects.c +++ b/jerry-core/ecma/operations/ecma-objects.c @@ -2648,6 +2648,12 @@ ecma_object_get_class_name (ecma_object_t *obj_p) /**< object */ return LIT_MAGIC_STRING_FUNCTION_UL; } #endif /* ENABLED (JERRY_BUILTIN_PROXY) */ +#if ENABLED (JERRY_BUILTIN_BIGINT) + case ECMA_BUILTIN_ID_BIGINT: + { + return LIT_MAGIC_STRING_FUNCTION_UL; + } +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ default: { JERRY_ASSERT (ecma_object_check_class_name_is_object (obj_p)); diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index 99075458..f88b0100 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -104,6 +104,7 @@ typedef enum JERRY_FEATURE_SET, /**< Set support */ JERRY_FEATURE_WEAKMAP, /**< WeakMap support */ JERRY_FEATURE_WEAKSET, /**< WeakSet support */ + JERRY_FEATURE_BIGINT, /**< BigInt support */ JERRY_FEATURE__COUNT /**< number of features. NOTE: must be at the end of the list */ } jerry_feature_t; diff --git a/jerry-core/lit/lit-magic-strings.inc.h b/jerry-core/lit/lit-magic-strings.inc.h index 40bdab6f..e9893ec8 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -307,6 +307,9 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_VALUE, "value") #if ENABLED (JERRY_PARSER) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_RESOURCE_EVAL, "<eval>") #endif +#if ENABLED (JERRY_BUILTIN_BIGINT) +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BIGINT_UL, "BigInt") +#endif #if ENABLED (JERRY_BUILTIN_MATH) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LOG10E_U, "LOG10E") #endif @@ -318,6 +321,9 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_STRING_UL, "String") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SYMBOL_UL, "Symbol") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ASSIGN, "assign") #endif +#if ENABLED (JERRY_BUILTIN_BIGINT) +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BIGINT, "bigint") +#endif #if ENABLED (JERRY_BUILTIN_DATAVIEW) \ || ENABLED (JERRY_BUILTIN_TYPEDARRAY) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BUFFER, "buffer") @@ -981,6 +987,8 @@ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (4, LIT_MAGIC_STRING_DATE_UL) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (5, LIT_MAGIC_STRING_ARRAY_UL) #if ENABLED (JERRY_PARSER) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (6, LIT_MAGIC_STRING_RESOURCE_EVAL) +#elif ENABLED (JERRY_BUILTIN_BIGINT) +LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (6, LIT_MAGIC_STRING_BIGINT_UL) #elif ENABLED (JERRY_BUILTIN_MATH) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (6, LIT_MAGIC_STRING_LOG10E_U) #else diff --git a/jerry-core/lit/lit-magic-strings.ini b/jerry-core/lit/lit-magic-strings.ini index 6778a886..06964823 100644 --- a/jerry-core/lit/lit-magic-strings.ini +++ b/jerry-core/lit/lit-magic-strings.ini @@ -132,6 +132,7 @@ LIT_MAGIC_STRING_THROW = "throw" LIT_MAGIC_STRING_TRUNC = "trunc" LIT_MAGIC_STRING_VALUE = "value" LIT_MAGIC_STRING_RESOURCE_EVAL = "<eval>" +LIT_MAGIC_STRING_BIGINT_UL = "BigInt" LIT_MAGIC_STRING_LOG10E_U = "LOG10E" LIT_MAGIC_STRING_NUMBER_UL = "Number" LIT_MAGIC_STRING_OBJECT_UL = "Object" @@ -139,6 +140,7 @@ LIT_MAGIC_STRING_REGEXP_UL = "RegExp" LIT_MAGIC_STRING_STRING_UL = "String" LIT_MAGIC_STRING_SYMBOL_UL = "Symbol" LIT_MAGIC_STRING_ASSIGN = "assign" +LIT_MAGIC_STRING_BIGINT = "bigint" LIT_MAGIC_STRING_BUFFER = "buffer" LIT_MAGIC_STRING_CALLEE = "callee" LIT_MAGIC_STRING_CALLER = "caller" diff --git a/jerry-core/vm/opcodes-ecma-arithmetics.c b/jerry-core/vm/opcodes-ecma-arithmetics.c index 89482a36..cb0ea169 100644 --- a/jerry-core/vm/opcodes-ecma-arithmetics.c +++ b/jerry-core/vm/opcodes-ecma-arithmetics.c @@ -14,7 +14,9 @@ */ #include "ecma-alloc.h" +#include "ecma-bigint.h" #include "ecma-conversion.h" +#include "ecma-exceptions.h" #include "ecma-helpers.h" #include "ecma-number-arithmetic.h" #include "ecma-objects.h" @@ -45,48 +47,128 @@ do_number_arithmetic (number_arithmetic_op op, /**< number arithmetic operation ecma_value_t left_value, /**< left value */ ecma_value_t right_value) /**< right value */ { - ecma_value_t ret_value = ECMA_VALUE_EMPTY; - - ECMA_OP_TO_NUMBER_TRY_CATCH (num_left, left_value, ret_value); - ECMA_OP_TO_NUMBER_TRY_CATCH (num_right, right_value, ret_value); - - ecma_number_t result = ECMA_NUMBER_ZERO; + bool free_left_value = false; + bool free_right_value = false; - switch (op) + if (ecma_is_value_object (left_value)) { - case NUMBER_ARITHMETIC_SUBTRACTION: - { - result = num_left - num_right; - break; - } - case NUMBER_ARITHMETIC_MULTIPLICATION: + ecma_object_t *obj_p = ecma_get_object_from_value (left_value); + left_value = ecma_op_object_default_value (obj_p, ECMA_PREFERRED_TYPE_NUMBER); + free_left_value = true; + + if (ECMA_IS_VALUE_ERROR (left_value)) { - result = num_left * num_right; - break; + return left_value; } - case NUMBER_ARITHMETIC_DIVISION: + } + + if (ecma_is_value_object (right_value)) + { + ecma_object_t *obj_p = ecma_get_object_from_value (right_value); + right_value = ecma_op_object_default_value (obj_p, ECMA_PREFERRED_TYPE_NUMBER); + free_right_value = true; + + if (ECMA_IS_VALUE_ERROR (right_value)) { - result = num_left / num_right; - break; + if (free_left_value) + { + ecma_free_value (left_value); + } + return right_value; } - case NUMBER_ARITHMETIC_REMAINDER: + } + + ecma_value_t ret_value = ECMA_VALUE_EMPTY; + +#if ENABLED (JERRY_BUILTIN_BIGINT) + if (JERRY_LIKELY (!ecma_is_value_bigint (left_value)) + || JERRY_LIKELY (!ecma_is_value_bigint (right_value))) + { +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + ECMA_OP_TO_NUMBER_TRY_CATCH (num_left, left_value, ret_value); + ECMA_OP_TO_NUMBER_TRY_CATCH (num_right, right_value, ret_value); + + ecma_number_t result = ECMA_NUMBER_ZERO; + + switch (op) { - result = ecma_op_number_remainder (num_left, num_right); - break; - } + case NUMBER_ARITHMETIC_SUBTRACTION: + { + result = num_left - num_right; + break; + } + case NUMBER_ARITHMETIC_MULTIPLICATION: + { + result = num_left * num_right; + break; + } + case NUMBER_ARITHMETIC_DIVISION: + { + result = num_left / num_right; + break; + } + case NUMBER_ARITHMETIC_REMAINDER: + { + result = ecma_op_number_remainder (num_left, num_right); + break; + } #if ENABLED (JERRY_ESNEXT) - case NUMBER_ARITHMETIC_EXPONENTIATION: + case NUMBER_ARITHMETIC_EXPONENTIATION: + { + result = ecma_number_pow (num_left, num_right); + break; + } +#endif /* ENABLED (JERRY_ESNEXT) */ + } + + ret_value = ecma_make_number_value (result); + + ECMA_OP_TO_NUMBER_FINALIZE (num_right); + ECMA_OP_TO_NUMBER_FINALIZE (num_left); +#if ENABLED (JERRY_BUILTIN_BIGINT) + } + else + { + switch (op) { - result = ecma_number_pow (num_left, num_right); - break; + case NUMBER_ARITHMETIC_SUBTRACTION: + { + ret_value = ecma_bigint_add_sub (left_value, right_value, false); + break; + } + case NUMBER_ARITHMETIC_MULTIPLICATION: + { + ret_value = ecma_bigint_mul (left_value, right_value); + break; + } + case NUMBER_ARITHMETIC_DIVISION: + { + ret_value = ecma_bigint_div_mod (left_value, right_value, false); + break; + } + case NUMBER_ARITHMETIC_REMAINDER: + { + ret_value = ecma_bigint_div_mod (left_value, right_value, true); + break; + } + default: + { + ret_value = ecma_raise_common_error (ECMA_ERR_MSG ("Not supported BigInt operation")); + break; + } } -#endif /* ENABLED (JERRY_ESNEXT) */ } +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ - ret_value = ecma_make_number_value (result); + if (free_left_value) + { + ecma_free_value (left_value); + } - ECMA_OP_TO_NUMBER_FINALIZE (num_right); - ECMA_OP_TO_NUMBER_FINALIZE (num_left); + if (free_right_value) + { + ecma_free_value (right_value); + } return ret_value; } /* do_number_arithmetic */ @@ -175,6 +257,13 @@ opfunc_addition (ecma_value_t left_value, /**< left value */ ecma_deref_ecma_string (string2_p); } +#if ENABLED (JERRY_BUILTIN_BIGINT) + else if (JERRY_UNLIKELY (ecma_is_value_bigint (left_value)) + && JERRY_UNLIKELY (ecma_is_value_bigint (right_value))) + { + ret_value = ecma_bigint_add_sub (left_value, right_value, true); + } +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ else { ECMA_OP_TO_NUMBER_TRY_CATCH (num_left, left_value, ret_value); @@ -211,15 +300,63 @@ ecma_value_t opfunc_unary_operation (ecma_value_t left_value, /**< left value */ bool is_plus) /**< unary plus flag */ { + bool free_left_value = false; + + if (ecma_is_value_object (left_value)) + { + ecma_object_t *obj_p = ecma_get_object_from_value (left_value); + left_value = ecma_op_object_default_value (obj_p, ECMA_PREFERRED_TYPE_NUMBER); + free_left_value = true; + + if (ECMA_IS_VALUE_ERROR (left_value)) + { + return left_value; + } + } + ecma_value_t ret_value = ECMA_VALUE_EMPTY; - ECMA_OP_TO_NUMBER_TRY_CATCH (num_var_value, - left_value, - ret_value); +#if ENABLED (JERRY_BUILTIN_BIGINT) + if (JERRY_LIKELY (!ecma_is_value_bigint (left_value))) + { +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + ECMA_OP_TO_NUMBER_TRY_CATCH (num_var_value, + left_value, + ret_value); + + ret_value = ecma_make_number_value (is_plus ? num_var_value : -num_var_value); - ret_value = ecma_make_number_value (is_plus ? num_var_value : -num_var_value); + ECMA_OP_TO_NUMBER_FINALIZE (num_var_value); - ECMA_OP_TO_NUMBER_FINALIZE (num_var_value); +#if ENABLED (JERRY_BUILTIN_BIGINT) + } + else + { + if (is_plus) + { + ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("Unary operation plus is not allowed for BigInt numbers")); + } + else + { + ecma_extended_primitive_t *left_p = ecma_get_extended_primitive_from_value (left_value); + + if (ECMA_BIGINT_GET_SIZE (left_p) == 0) + { + ecma_ref_extended_primitive (left_p); + ret_value = left_value; + } + else + { + ret_value = ecma_bigint_negate (left_p); + } + } + } +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + + if (free_left_value) + { + ecma_free_value (left_value); + } return ret_value; } /* opfunc_unary_operation */ diff --git a/jerry-core/vm/opcodes-ecma-bitwise.c b/jerry-core/vm/opcodes-ecma-bitwise.c index ac328167..233f306c 100644 --- a/jerry-core/vm/opcodes-ecma-bitwise.c +++ b/jerry-core/vm/opcodes-ecma-bitwise.c @@ -14,8 +14,11 @@ */ #include "ecma-alloc.h" +#include "ecma-bigint.h" #include "ecma-conversion.h" +#include "ecma-exceptions.h" #include "ecma-helpers.h" +#include "ecma-objects.h" #include "ecma-try-catch-macro.h" #include "opcodes.h" @@ -45,61 +48,131 @@ do_number_bitwise_logic (number_bitwise_logic_op op, /**< number bitwise logic o JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (left_value) && !ECMA_IS_VALUE_ERROR (right_value)); - ecma_value_t ret_value = ECMA_VALUE_EMPTY; - - ECMA_OP_TO_NUMBER_TRY_CATCH (num_left, left_value, ret_value); - ECMA_OP_TO_NUMBER_TRY_CATCH (num_right, right_value, ret_value); + bool free_left_value = false; + bool free_right_value = false; - ecma_number_t result = ECMA_NUMBER_ZERO; - uint32_t right_uint32 = ecma_number_to_uint32 (num_right); - - switch (op) + if (ecma_is_value_object (left_value)) { - case NUMBER_BITWISE_LOGIC_AND: - { - uint32_t left_uint32 = ecma_number_to_uint32 (num_left); - result = (ecma_number_t) ((int32_t) (left_uint32 & right_uint32)); - break; - } - case NUMBER_BITWISE_LOGIC_OR: - { - uint32_t left_uint32 = ecma_number_to_uint32 (num_left); - result = (ecma_number_t) ((int32_t) (left_uint32 | right_uint32)); - break; - } - case NUMBER_BITWISE_LOGIC_XOR: - { - uint32_t left_uint32 = ecma_number_to_uint32 (num_left); - result = (ecma_number_t) ((int32_t) (left_uint32 ^ right_uint32)); - break; - } - case NUMBER_BITWISE_SHIFT_LEFT: + ecma_object_t *obj_p = ecma_get_object_from_value (left_value); + left_value = ecma_op_object_default_value (obj_p, ECMA_PREFERRED_TYPE_NUMBER); + free_left_value = true; + + if (ECMA_IS_VALUE_ERROR (left_value)) { - result = (ecma_number_t) (ecma_number_to_int32 (num_left) << (right_uint32 & 0x1F)); - break; + return left_value; } - case NUMBER_BITWISE_SHIFT_RIGHT: + } + + if (ecma_is_value_object (right_value)) + { + ecma_object_t *obj_p = ecma_get_object_from_value (right_value); + right_value = ecma_op_object_default_value (obj_p, ECMA_PREFERRED_TYPE_NUMBER); + free_right_value = true; + + if (ECMA_IS_VALUE_ERROR (right_value)) { - result = (ecma_number_t) (ecma_number_to_int32 (num_left) >> (right_uint32 & 0x1F)); - break; + if (free_left_value) + { + ecma_free_value (left_value); + } + return right_value; } - case NUMBER_BITWISE_SHIFT_URIGHT: + } + + ecma_value_t ret_value = ECMA_VALUE_EMPTY; + +#if ENABLED (JERRY_BUILTIN_BIGINT) + if (JERRY_LIKELY (!ecma_is_value_bigint (left_value)) + || JERRY_LIKELY (!ecma_is_value_bigint (right_value))) + { +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ + ECMA_OP_TO_NUMBER_TRY_CATCH (num_left, left_value, ret_value); + ECMA_OP_TO_NUMBER_TRY_CATCH (num_right, right_value, ret_value); + + ecma_number_t result = ECMA_NUMBER_ZERO; + uint32_t right_uint32 = ecma_number_to_uint32 (num_right); + + switch (op) { - uint32_t left_uint32 = ecma_number_to_uint32 (num_left); - result = (ecma_number_t) (left_uint32 >> (right_uint32 & 0x1F)); - break; + case NUMBER_BITWISE_LOGIC_AND: + { + uint32_t left_uint32 = ecma_number_to_uint32 (num_left); + result = (ecma_number_t) ((int32_t) (left_uint32 & right_uint32)); + break; + } + case NUMBER_BITWISE_LOGIC_OR: + { + uint32_t left_uint32 = ecma_number_to_uint32 (num_left); + result = (ecma_number_t) ((int32_t) (left_uint32 | right_uint32)); + break; + } + case NUMBER_BITWISE_LOGIC_XOR: + { + uint32_t left_uint32 = ecma_number_to_uint32 (num_left); + result = (ecma_number_t) ((int32_t) (left_uint32 ^ right_uint32)); + break; + } + case NUMBER_BITWISE_SHIFT_LEFT: + { + result = (ecma_number_t) (ecma_number_to_int32 (num_left) << (right_uint32 & 0x1F)); + break; + } + case NUMBER_BITWISE_SHIFT_RIGHT: + { + result = (ecma_number_t) (ecma_number_to_int32 (num_left) >> (right_uint32 & 0x1F)); + break; + } + case NUMBER_BITWISE_SHIFT_URIGHT: + { + uint32_t left_uint32 = ecma_number_to_uint32 (num_left); + result = (ecma_number_t) (left_uint32 >> (right_uint32 & 0x1F)); + break; + } + case NUMBER_BITWISE_NOT: + { + result = (ecma_number_t) ((int32_t) ~right_uint32); + break; + } } - case NUMBER_BITWISE_NOT: + + ret_value = ecma_make_number_value (result); + + ECMA_OP_TO_NUMBER_FINALIZE (num_right); + ECMA_OP_TO_NUMBER_FINALIZE (num_left); +#if ENABLED (JERRY_BUILTIN_BIGINT) + } + else + { + switch (op) { - result = (ecma_number_t) ((int32_t) ~right_uint32); - break; + case NUMBER_BITWISE_SHIFT_LEFT: + { + ret_value = ecma_bigint_shift (left_value, right_value, true); + break; + } + case NUMBER_BITWISE_SHIFT_RIGHT: + { + ret_value = ecma_bigint_shift (left_value, right_value, false); + break; + } + default: + { + ret_value = ecma_raise_common_error (ECMA_ERR_MSG ("Not supported BigInt operation")); + break; + } } } +#endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ - ret_value = ecma_make_number_value (result); + if (free_left_value) + { + ecma_free_value (left_value); + } - ECMA_OP_TO_NUMBER_FINALIZE (num_right); - ECMA_OP_TO_NUMBER_FINALIZE (num_left); + if (free_right_value) + { + ecma_free_value (right_value); + } return ret_value; } /* do_number_bitwise_logic */ diff --git a/tests/jerry/es.next/bigint1.js b/tests/jerry/es.next/bigint1.js new file mode 100644 index 00000000..47f24cad --- /dev/null +++ b/tests/jerry/es.next/bigint1.js @@ -0,0 +1,237 @@ +/* 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. + */ + +function check_result(bigint, expected) +{ + assert(bigint.toString() === expected) +} + +function check_result16(bigint, expected) +{ + assert(bigint.toString(16) === expected) +} + +function check_syntax_error(code) +{ + try { + eval(code) + assert(false) + } catch (e) { + assert(e instanceof SyntaxError) + } +} + +assert(typeof BigInt("0") == "bigint") + +// Test BigInt string parsing and toString + +check_syntax_error("BigInt('-0x5')"); +check_syntax_error("BigInt('-')"); +check_syntax_error("BigInt('00x5')"); +check_syntax_error("BigInt('11a')"); +check_syntax_error("BigInt('0b2')"); +check_syntax_error("BigInt('1n')"); + +check_result(BigInt("0"), "0") +check_result(BigInt("-0"), "0") +check_result(BigInt("100000000000000000000000000000000000000"), "100000000000000000000000000000000000000") +check_result(BigInt("-1234567890123456789012345678901234567890"), "-1234567890123456789012345678901234567890") +check_result(BigInt("+1"), "1") +check_result(BigInt("+000000000000000000001"), "1") +check_result(BigInt("-000000000000000000000"), "0") +check_result(BigInt("0x00abcdefABCDEF0123456789000000000000000"), "239460437713606077082343926293727858623774720") +check_result(BigInt("0b00100000000000010000000000010000000000010"), "274911469570") + +assert(BigInt("100000000000000000000000000000000000000").toString(22) === "2ci67fiek1bkhec5fig7aiii9hf8c") +check_result16(BigInt("239460437713606077082343926293727858623774720"), "abcdefabcdef0123456789000000000000000") + +// Test negate + +check_result(-BigInt("0"), "0") +check_result(-BigInt("100"), "-100") +check_result(-BigInt("-100"), "100") +check_result(-BigInt("100000000000000000000000000000000000000000000"), "-100000000000000000000000000000000000000000000") +check_result(-BigInt("-100000000000000000000000000000000000000000000"), "100000000000000000000000000000000000000000000") + +// Test addition + +check_result(BigInt("0") + BigInt("0"), "0") +check_result(BigInt("1") + BigInt("1"), "2") +check_result(BigInt("0") + BigInt("100"), "100") +check_result(BigInt("0") + BigInt("-100"), "-100") +check_result(BigInt("100") + BigInt("0"), "100") +check_result(BigInt("-100") + BigInt("0"), "-100") + +check_result(BigInt("100000000000000000000000000000000000000") + BigInt("100000000000000000000000000000000000000"), + "200000000000000000000000000000000000000"); +check_result(BigInt("-100000000000000000000000000000000000000") + BigInt("-100000000000000000000000000000000000000"), + "-200000000000000000000000000000000000000"); +check_result(BigInt("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3") + BigInt("0xd"), + "115792089237316195423570985008687907853269984665640564039457584007913129639936"); +check_result(BigInt("0xd") + BigInt("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3"), + "115792089237316195423570985008687907853269984665640564039457584007913129639936"); + +check_result(BigInt("100000000000000000000000000000000000000") + BigInt("-100000000000000000000000000000000000000"), "0") +check_result(BigInt("100000000000000000000000000000000000001") + BigInt("-100000000000000000000000000000000000000"), "1") +check_result(BigInt("100000000000000000000000000000000000000") + BigInt("-100000000000000000000000000000000000001"), "-1") +check_result(BigInt("-100000000000000000000000000000000000000") + BigInt("100000000000000000000000000000000000000"), "0") +check_result(BigInt("-100000000000000000000000000000000000001") + BigInt("100000000000000000000000000000000000000"), "-1") +check_result(BigInt("-100000000000000000000000000000000000000") + BigInt("100000000000000000000000000000000000001"), "1") + +// Test substraction + +check_result(BigInt("0") - BigInt("0"), "0") +check_result(BigInt("2") - BigInt("1"), "1") +check_result(BigInt("0") - BigInt("100"), "-100") +check_result(BigInt("0") - BigInt("-100"), "100") +check_result(BigInt("100") - BigInt("0"), "100") +check_result(BigInt("-100") - BigInt("0"), "-100") + +check_result(BigInt("100000000000000000000000000000000000000") - BigInt("-100000000000000000000000000000000000000"), + "200000000000000000000000000000000000000"); +check_result(BigInt("-100000000000000000000000000000000000000") - BigInt("100000000000000000000000000000000000000"), + "-200000000000000000000000000000000000000"); +check_result(BigInt("100000000000000000000000000000000000000") - BigInt("-1"), + "100000000000000000000000000000000000001"); +check_result(BigInt("-100000000000000000000000000000000000000") - BigInt("1"), + "-100000000000000000000000000000000000001"); +check_result(BigInt("1") - BigInt("-100000000000000000000000000000000000000"), + "100000000000000000000000000000000000001"); +check_result(BigInt("-1") - BigInt("100000000000000000000000000000000000000"), + "-100000000000000000000000000000000000001"); + +check_result(BigInt("100000000000000000000000000000000000000") - BigInt("100000000000000000000000000000000000000"), "0") +check_result(BigInt("100000000000000000000000000000000000001") - BigInt("100000000000000000000000000000000000000"), "1") +check_result(BigInt("100000000000000000000000000000000000000") - BigInt("100000000000000000000000000000000000001"), "-1") +check_result(BigInt("-100000000000000000000000000000000000000") - BigInt("-100000000000000000000000000000000000000"), "0") +check_result(BigInt("-100000000000000000000000000000000000001") - BigInt("-100000000000000000000000000000000000000"), "-1") +check_result(BigInt("-100000000000000000000000000000000000000") - BigInt("-100000000000000000000000000000000000001"), "1") + +// Test multiplication + +check_result(BigInt("0") * BigInt("0"), "0") +check_result(BigInt("1000") * BigInt("0"), "0") +check_result(BigInt("0") * BigInt("1000"), "0") +check_result(BigInt("1") * BigInt("100000000000000000000000000000000000000"), "100000000000000000000000000000000000000") +check_result(BigInt("1") * BigInt("-100000000000000000000000000000000000000"), "-100000000000000000000000000000000000000") +check_result(BigInt("-1") * BigInt("100000000000000000000000000000000000000"), "-100000000000000000000000000000000000000") +check_result(BigInt("-1") * BigInt("-100000000000000000000000000000000000000"), "100000000000000000000000000000000000000") +check_result(BigInt("100000000000000000000000000000000000000") * BigInt("1"), "100000000000000000000000000000000000000") +check_result(BigInt("-100000000000000000000000000000000000000") * BigInt("1"), "-100000000000000000000000000000000000000") +check_result(BigInt("100000000000000000000000000000000000000") * BigInt("-1"), "-100000000000000000000000000000000000000") +check_result(BigInt("-100000000000000000000000000000000000000") * BigInt("-1"), "100000000000000000000000000000000000000") + +check_result(BigInt("100000000000000000000000000000000000000") * BigInt("100000000000000000000000000000000000000"), + "10000000000000000000000000000000000000000000000000000000000000000000000000000") +check_result(BigInt("100000000000000000000000000000000000000") * BigInt("-100000000000000000000000000000000000000"), + "-10000000000000000000000000000000000000000000000000000000000000000000000000000") +check_result(BigInt("-100000000000000000000000000000000000000") * BigInt("100000000000000000000000000000000000000"), + "-10000000000000000000000000000000000000000000000000000000000000000000000000000") +check_result(BigInt("-100000000000000000000000000000000000000") * BigInt("-100000000000000000000000000000000000000"), + "10000000000000000000000000000000000000000000000000000000000000000000000000000") + +// Test divide + +try { + BigInt("32") / BigInt("0") + assert(false) +} catch (e) { + assert(e instanceof RangeError) +} + +try { + BigInt("32") % BigInt("0") + assert(false) +} catch (e) { + assert(e instanceof RangeError) +} + +check_result(BigInt("0") / BigInt("1234"), "0") +check_result(BigInt("0") % BigInt("1234"), "0") + +check_result(BigInt("100") / BigInt("70"), "1") +check_result(BigInt("100") % BigInt("70"), "30") +check_result(BigInt("-100") / BigInt("70"), "-1") +check_result(BigInt("-100") % BigInt("70"), "-30") +check_result(BigInt("100") / BigInt("-70"), "-1") +check_result(BigInt("100") % BigInt("-70"), "30") +check_result(BigInt("-100") / BigInt("-70"), "1") +check_result(BigInt("-100") % BigInt("-70"), "-30") + +check_result(BigInt("100") / BigInt("100"), "1") +check_result(BigInt("100") % BigInt("100"), "0") +check_result(BigInt("-100") / BigInt("100"), "-1") +check_result(BigInt("-100") % BigInt("100"), "0") +check_result(BigInt("100") / BigInt("-100"), "-1") +check_result(BigInt("100") % BigInt("-100"), "0") +check_result(BigInt("-100") / BigInt("-100"), "1") +check_result(BigInt("-100") % BigInt("-100"), "0") + +/* Division by small value. */ +check_result(BigInt("100000000000000000000") / BigInt("1000000"), "100000000000000") +check_result(BigInt("100000000000000000000") % BigInt("1000000"), "0") +check_result(BigInt("12345678901234567890") / BigInt("1000000"), "12345678901234") +check_result(BigInt("12345678901234567890") % BigInt("1000000"), "567890") + +/* Division by large value. */ +check_result(BigInt("100000000000000000000") / BigInt("100000000000000000"), "1000") +check_result(BigInt("100000000000000000000") % BigInt("100000000000000000"), "0") +check_result(BigInt("12345678901234567890123456789012345678901234567890123456789012345678901234567890") / BigInt("10000000000000000000000000000000000000"), + "1234567890123456789012345678901234567890123") +check_result(BigInt("12345678901234567890123456789012345678901234567890123456789012345678901234567890") % BigInt("10000000000000000000000000000000000000"), + "4567890123456789012345678901234567890") +check_result16(BigInt("0xffffffffffffffffffffffff") / BigInt("0x100000000"), "ffffffffffffffff") +check_result16(BigInt("0xffffffffffffffffffffffff") % BigInt("0x100000000"), "ffffffff") + +/* Triggers a corner case. */ +check_result(BigInt("170141183420855150493001878992821682176") / BigInt("39614081266355540842216685573"), "4294967293") +check_result(BigInt("170141183420855150493001878992821682176") % BigInt("39614081266355540842216685573"), "39614081266355540837921718287") + +// Test shift + +check_result(BigInt("0") << BigInt("10000000"), "0") +check_result(BigInt("0") >> BigInt("10000000"), "0") +check_result(BigInt("10000000") << BigInt("0"), "10000000") +check_result(BigInt("10000000") >> BigInt("0"), "10000000") + +check_result(BigInt("4096") << BigInt("2"), "16384") +check_result(BigInt("4096") << BigInt("-2"), "1024") +check_result(BigInt("4096") >> BigInt("2"), "1024") +check_result(BigInt("4096") >> BigInt("-2"), "16384") + +check_result16(BigInt("0x8fef5fcfffef5fcfffef5fcfffef5fcff") << BigInt("1"), "11fdebf9fffdebf9fffdebf9fffdebf9fe") +check_result16(BigInt("0x8fef5fcfffef5fcfffef5fcfffef5fcff") << BigInt("19"), "47f7afe7fff7afe7fff7afe7fff7afe7f80000") +check_result16(BigInt("0x8fef5fcfffef5fcfffef5fcfffef5fcff") << BigInt("31"), "47f7afe7fff7afe7fff7afe7fff7afe7f80000000") +check_result16(BigInt("0x8fef5fcfffef5fcfffef5fcfffef5fcff") << BigInt("32"), "8fef5fcfffef5fcfffef5fcfffef5fcff00000000") +check_result16(BigInt("0x8fef5fcfffef5fcfffef5fcfffef5fcff") << BigInt("51"), "47f7afe7fff7afe7fff7afe7fff7afe7f8000000000000") +check_result16(BigInt("0x8fef5fcfffef5fcfffef5fcfffef5fcff") << BigInt("63"), "47f7afe7fff7afe7fff7afe7fff7afe7f8000000000000000") +check_result16(BigInt("0x8fef5fcfffef5fcfffef5fcfffef5fcff") << BigInt("64"), "8fef5fcfffef5fcfffef5fcfffef5fcff0000000000000000") + +check_result16(BigInt("0x8fef5fcfffef5fcfffef5fcfffef5fcff") >> BigInt("1"), "47f7afe7fff7afe7fff7afe7fff7afe7f") +check_result16(BigInt("0x8fef5fcfffef5fcfffef5fcfffef5fcff") >> BigInt("19"), "11fdebf9fffdebf9fffdebf9fffde") +check_result16(BigInt("0x8fef5fcfffef5fcfffef5fcfffef5fcff") >> BigInt("31"), "11fdebf9fffdebf9fffdebf9ff") +check_result16(BigInt("0x8fef5fcfffef5fcfffef5fcfffef5fcff") >> BigInt("32"), "8fef5fcfffef5fcfffef5fcff") +check_result16(BigInt("0x8fef5fcfffef5fcfffef5fcfffef5fcff") >> BigInt("51"), "11fdebf9fffdebf9fffde") +check_result16(BigInt("0x8fef5fcfffef5fcfffef5fcfffef5fcff") >> BigInt("63"), "11fdebf9fffdebf9ff") +check_result16(BigInt("0x8fef5fcfffef5fcfffef5fcfffef5fcff") >> BigInt("64"), "8fef5fcfffef5fcff") + +check_result16(BigInt("0x8fef5fcfffef5fcfffef5fcfffef5fcff") >> BigInt("10000000000000000000000000"), "0") + +try { + BigInt("0x8fef5fcfffef5fcfffef5fcfffef5fcff") << BigInt("10000000000000000000000000"); + assert(false) +} catch (e) { + assert(e instanceof RangeError) +} diff --git a/tests/jerry/es.next/bigint2.js b/tests/jerry/es.next/bigint2.js new file mode 100644 index 00000000..9b4e9cf1 --- /dev/null +++ b/tests/jerry/es.next/bigint2.js @@ -0,0 +1,61 @@ +/* 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. + */ + +try { + new BigInt("1") + assert(false) +} catch (e) { + assert(e instanceof TypeError) +} + +function check_type_error (code) +{ + try { + eval(code) + assert(false) + } catch (e) { + assert(e instanceof TypeError) + } +} + +check_type_error("+BigInt('0')") + +check_type_error("BigInt('1') + 1") +check_type_error("BigInt('2') - 2") +check_type_error("BigInt('3') * 3") +check_type_error("BigInt('4') / 4") +check_type_error("BigInt('5') % 5") +check_type_error("BigInt('6') ** 6") + +check_type_error("1 + BigInt('1')") +check_type_error("2 - BigInt('2')") +check_type_error("3 * BigInt('3')") +check_type_error("4 / BigInt('4')") +check_type_error("5 % BigInt('5')") +check_type_error("6 ** BigInt('6')") + +check_type_error("BigInt('1') & 1") +check_type_error("BigInt('2') | 2") +check_type_error("BigInt('3') ^ 3") +check_type_error("BigInt('4') << 4") +check_type_error("BigInt('5') >> 5") +check_type_error("BigInt('6') >>> 6") + +check_type_error("1 & BigInt('1')") +check_type_error("2 | BigInt('2')") +check_type_error("3 ^ BigInt('3')") +check_type_error("4 << BigInt('4')") +check_type_error("5 >> BigInt('5')") +check_type_error("6 >>> BigInt('6')") |