Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
358 changes: 358 additions & 0 deletions jerry-core/ecma/builtin-objects/ecma-builtin-bigint.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,31 @@
*/

#include "ecma-bigint.h"
#include "ecma-big-uint.h"
#include "ecma-builtins.h"
#include "ecma-exceptions.h"
#include <math.h>

#if JERRY_BUILTIN_BIGINT

#define ECMA_BUILTINS_INTERNAL
#include "ecma-builtins-internal.h"

/**
* This object has a custom dispatch function.
*/
#define BUILTIN_CUSTOM_DISPATCH

/**
* List of built-in routine identifiers.
*/
enum
{
ECMA_BUILTIN_BIGINT_START = 0,
ECMA_BUILTIN_BIGINT_AS_INT_N,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing comments.

ECMA_BUILTIN_BIGINT_AS_U_INT_N,
};

#define BUILTIN_INC_HEADER_NAME "ecma-builtin-bigint.inc.h"
#define BUILTIN_UNDERSCORED_ID bigint
#include "ecma-builtin-internal-routines-template.inc.h"
Expand All @@ -36,6 +53,311 @@
* @{
*/

/**
* The BigInt object's 'asIntN' and 'asUintN' routines
*
* See also:
* ECMA-262 v5, 11.0
*
* @return ecma value
* Returned value must be freed with ecma_free_value.
*/
static ecma_value_t
ecma_builtin_bigint_object_as_int_n (ecma_value_t bits, /**< number of bits */
ecma_value_t bigint, /**< bigint number */
bool is_signed) /**< The operation is signed */
{
ecma_number_t input_bits;
ecma_value_t bit_value = ecma_op_to_index (bits, &input_bits);

if (ECMA_IS_VALUE_ERROR (bit_value))
{
return bit_value;
}

ecma_value_t bigint_value = ecma_bigint_to_bigint (bigint, false);

if (ECMA_IS_VALUE_ERROR (bigint_value))
{
return bigint_value;
}

if (input_bits == 0 || bigint_value == ECMA_BIGINT_ZERO)
{
ecma_free_value (bigint_value);
return ECMA_BIGINT_ZERO;
}

const uint32_t size_of_divisor_in_bits = sizeof (uint32_t) * JERRY_BITSINBYTE;

uint32_t whole_part = (uint32_t) floor (input_bits / size_of_divisor_in_bits);
uint32_t remainder = (uint32_t) fmod (input_bits, size_of_divisor_in_bits);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The input_bits should be an integer number. Furthermore JerryScript has a maximum length for bigints, any number greater than that does not need any clamping (just increase its refcount and return with it).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The head of the code is coming from the standard. So the input_bits should be a number according to the ToIndex method.
(https://tc39.es/ecma262/#sec-bigint.asintn)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice but my comment stiill stands, if the number is greater than the maximum bits allowed, just return with the original value, otherwise you can use an integer div and mod. If uint conversion is needed, and the original value is negative, throw an allocation error.


uint32_t input_bit_length = 1;

if (whole_part == 0)
{
input_bit_length = (uint32_t) input_bits;
}
else
{
input_bit_length = (remainder > 0) ? (whole_part + 1) * size_of_divisor_in_bits : (uint32_t) input_bits;
}

uint32_t input_byte_size = (uint32_t) floor (input_bits / JERRY_BITSINBYTE);

if (fmod (input_bits, JERRY_BITSINBYTE) != 0)
{
input_byte_size += 1;
}

ecma_extended_primitive_t *input_bigint_p = ecma_get_extended_primitive_from_value (bigint_value);

uint32_t bigint_size = ECMA_BIGINT_GET_SIZE (input_bigint_p);
uint8_t input_bigint_sign = input_bigint_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN;

const uint32_t input_bits_in_byte = (uint32_t) input_bit_length / JERRY_BITSINBYTE;

uint32_t min_size = (input_bits_in_byte < bigint_size) ? input_bits_in_byte : bigint_size;

if (input_bigint_sign && (input_byte_size > bigint_size))
{
min_size = (input_bits_in_byte > bigint_size) ? input_bits_in_byte : bigint_size;
}

if (min_size < sizeof (uint32_t))
{
min_size = sizeof (uint32_t);
}

ecma_extended_primitive_t * result_p = ecma_bigint_create ((uint32_t) min_size);

if (JERRY_UNLIKELY (result_p == NULL))
{
ecma_deref_bigint (input_bigint_p);
return ECMA_VALUE_ERROR;
}

ecma_bigint_digit_t *last_digit_p = ECMA_BIGINT_GET_DIGITS (input_bigint_p, bigint_size);

/* Calculate the leading zeros of the input_bigint */

ecma_bigint_digit_t zeros = ecma_big_uint_count_leading_zero (last_digit_p[-1]);

uint32_t bits_of_bigint = (uint32_t) (bigint_size * JERRY_BITSINBYTE) - zeros;

uint32_t exact_size = 1;

if ((bits_of_bigint > (size_of_divisor_in_bits - 1)) || input_bigint_sign)
{
exact_size = (input_byte_size < bigint_size) ? (uint32_t) input_byte_size : bigint_size;

if (input_bigint_sign && (input_byte_size > bigint_size))
{
exact_size = (input_byte_size > bigint_size) ? (uint32_t) input_byte_size : bigint_size;
}
}
else
{
exact_size = bigint_size;
}

if (input_bigint_sign)
{
bits_of_bigint += 1;
}

if (bits_of_bigint > (input_bits - 1) || input_bigint_sign)
{
ecma_bigint_digit_t *digits_p = ECMA_BIGINT_GET_DIGITS (input_bigint_p, 0);
ecma_bigint_digit_t *digits_end_p = ECMA_BIGINT_GET_DIGITS (input_bigint_p, exact_size);
ecma_bigint_digit_t *result_number_p = ECMA_BIGINT_GET_DIGITS (result_p, 0);
int32_t first_cell = 0;
uint32_t surplus_bits = (whole_part > 0) ? (uint32_t) (whole_part * size_of_divisor_in_bits)
: (uint32_t) size_of_divisor_in_bits;
uint32_t mask_bit = (whole_part == 0) ? (uint32_t) input_bits : (uint32_t) input_bits - surplus_bits;

if (mask_bit == 0)
{
mask_bit = size_of_divisor_in_bits - 1;
}

uint32_t check_sign_mask = (uint32_t) 1 << (mask_bit - 1);
uint32_t mask = ((uint32_t) 1 << mask_bit) - 1;
uint32_t last_cell = (exact_size >= sizeof (uint32_t)) ? (uint32_t) (min_size / sizeof (uint32_t)) - 1 : 0;
bool is_positive = false;
bool is_representation_positive = false;

if (is_signed)
{
if (input_bigint_sign && ((~digits_p[last_cell] + 1) & check_sign_mask) == 0)
{
is_positive = true;
}

if ((digits_p[last_cell] & check_sign_mask) == 0)
{
is_representation_positive = true;
}
}

do
{
*result_number_p++ = (is_representation_positive
|| (!is_signed && !input_bigint_sign)) ? *digits_p++: ~(*digits_p++);
first_cell--;
}
while (digits_p < digits_end_p);

int16_t equal_bit_s = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bit_s?


if (remainder != 0)
{
equal_bit_s = -1;
}

int32_t last_cell_negative = (last_cell != 0) ? ((int32_t) last_cell * (-1)) : -1;
bool is_zero_values = false;

if (!is_signed)
{
if (input_bigint_sign)
{
is_zero_values = true;
}
}
else
{
if (((digits_p[-1] & check_sign_mask) > 0) || (result_number_p[-1] & check_sign_mask) > 0)
{
is_zero_values = true;
}
}
if (is_zero_values)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no else here so add a newline here

{
result_number_p[first_cell] += 1;

if (result_number_p[first_cell] == 0)
{
do
{
result_number_p[++first_cell] += 1;
}
while (first_cell != equal_bit_s);

first_cell = last_cell_negative;
}
}

result_number_p[-1] &= mask;
uint32_t surplus = (uint32_t) (min_size - exact_size) / sizeof (ecma_char_t);
uint32_t new_size = result_p->u.bigint_sign_and_size;

if ((min_size - exact_size) % (sizeof (ecma_char_t)) > 0 && surplus == 0)
{
surplus += (uint32_t) sizeof (ecma_char_t);
}
else
{
surplus = (uint32_t) (surplus * sizeof (ecma_char_t));
}

if (min_size / JERRY_BITSINBYTE < 1)
{
surplus = 0;
}

if (is_signed)
{
if (result_p->u.bigint_sign_and_size > exact_size
&& min_size > sizeof (uint32_t)
&& result_number_p[last_cell_negative] < 1)
{
new_size -= surplus;
}

new_size += 1;

if (is_positive || ((digits_p[-1] & check_sign_mask) == 0 && !input_bigint_sign))
{
new_size -= 1;
}
}

while (first_cell != 0)
{
if (result_number_p[first_cell] != 0)
{
break;
}

first_cell++;
}

if (first_cell == 0)
{
ecma_deref_bigint (result_p);
ecma_deref_bigint (input_bigint_p);

return ECMA_BIGINT_ZERO;
}

last_cell_negative = first_cell + (int32_t) last_cell;
int16_t zero_section_cnt = 0;

while (last_cell_negative > first_cell)
{
if (result_number_p[last_cell_negative] == 0)
{
zero_section_cnt++;
}

last_cell_negative--;
}

uint32_t size_limit = sizeof (uint32_t);

if (zero_section_cnt >= 1)
{
size_limit = new_size - (uint32_t) zero_section_cnt * size_limit;
new_size = (size_limit < sizeof (uint32_t)) ? (uint32_t) (JERRY_BITSINBYTE - size_limit) : size_limit;
}

if (new_size < result_p->u.bigint_sign_and_size)
{
result_p->refs_and_type = ECMA_EXTENDED_PRIMITIVE_REF_ONE | ECMA_TYPE_BIGINT;
uint32_t new_size_remainder = new_size % sizeof (uint32_t);
ecma_extended_primitive_t * new_result_p = ecma_bigint_create (new_size - new_size_remainder);

new_result_p->u.bigint_sign_and_size += new_size_remainder;
memcpy (new_result_p + 1, result_p + 1, new_size - new_size_remainder);

ecma_deref_bigint (result_p);
ecma_deref_bigint (input_bigint_p);

return ecma_make_extended_primitive_value (new_result_p, ECMA_TYPE_BIGINT);
}

result_p->u.bigint_sign_and_size = new_size;
result_p->refs_and_type = ECMA_EXTENDED_PRIMITIVE_REF_ONE | ECMA_TYPE_BIGINT;

ecma_deref_bigint (input_bigint_p);
return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT);
}

memcpy (result_p + 1, input_bigint_p + 1, exact_size);
result_p->refs_and_type = ECMA_EXTENDED_PRIMITIVE_REF_ONE | ECMA_TYPE_BIGINT;

if (input_bigint_p->u.bigint_sign_and_size & ECMA_BIGINT_SIGN)
{
ecma_deref_bigint (input_bigint_p);
return ecma_bigint_negate (result_p);
}

ecma_deref_bigint (input_bigint_p);
return ecma_make_extended_primitive_value (result_p, ECMA_TYPE_BIGINT);
} /* ecma_builtin_bigint_object_as_int_n */

/**
* Handle calling [[Call]] of built-in BigInt object
*
Expand Down Expand Up @@ -71,6 +393,42 @@ ecma_builtin_bigint_dispatch_construct (const ecma_value_t *arguments_list_p, /*
return ecma_raise_type_error (ECMA_ERR_MSG ("BigInt function is not a constructor"));
} /* ecma_builtin_bigint_dispatch_construct */

/**
* Dispatcher of the built-in's routines
*
* @return ecma value
* Returned value must be freed with ecma_free_value.
*/
ecma_value_t
ecma_builtin_bigint_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */
ecma_value_t this_arg, /**< 'this' argument value */
const ecma_value_t arguments_list_p[], /**< list of arguments
* passed to routine */
uint32_t arguments_number) /**< length of arguments' list */
{
JERRY_UNUSED_2 (this_arg, arguments_number);

switch (builtin_routine_id)
{
case ECMA_BUILTIN_BIGINT_AS_INT_N:
{
return ecma_builtin_bigint_object_as_int_n (arguments_list_p[0],
arguments_list_p[1],
true);
}
case ECMA_BUILTIN_BIGINT_AS_U_INT_N:
{
return ecma_builtin_bigint_object_as_int_n (arguments_list_p[0],
arguments_list_p[1],
false);
}
default:
{
JERRY_UNREACHABLE ();
}
}
} /* ecma_builtin_bigint_dispatch_routine */

/**
* @}
* @}
Expand Down
5 changes: 5 additions & 0 deletions jerry-core/ecma/builtin-objects/ecma-builtin-bigint.inc.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE,
ECMA_BUILTIN_ID_BIGINT_PROTOTYPE,
ECMA_PROPERTY_FIXED)

/* Routine properties:
* (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */

ROUTINE (LIT_MAGIC_STRING_AS_INT_N, ECMA_BUILTIN_BIGINT_AS_INT_N, 2, 2)
ROUTINE (LIT_MAGIC_STRING_AS_U_INT_N, ECMA_BUILTIN_BIGINT_AS_U_INT_N, 2, 2)
#endif /* JERRY_BUILTIN_BIGINT */

#include "ecma-builtin-helpers-macro-undefs.inc.h"
Loading