Skip to content
Open
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
65 changes: 43 additions & 22 deletions jerry-core/ecma/base/ecma-helpers-value.c
Original file line number Diff line number Diff line change
Expand Up @@ -569,13 +569,21 @@ ecma_make_length_value (ecma_length_t number) /**< number to be encoded */
ecma_value_t
ecma_make_number_value (ecma_number_t ecma_number) /**< number to be encoded */
{
ecma_integer_value_t integer_value = (ecma_integer_value_t) ecma_number;

if ((ecma_number_t) integer_value == ecma_number
&& ((integer_value == 0) ? ecma_is_number_equal_to_positive_zero (ecma_number)
: ECMA_IS_INTEGER_NUMBER (integer_value)))
/* The value can only be stored as a simple integer when it is inside the
* ecma_integer_value_t range; converting a double outside of it (e.g. 1e308,
* or a number literal coming from a snapshot) is undefined behaviour and traps
* under -fsanitize=undefined. */
if (ecma_number >= (ecma_number_t) ECMA_INTEGER_NUMBER_MIN
&& ecma_number <= (ecma_number_t) ECMA_INTEGER_NUMBER_MAX)
{
return ecma_make_integer_value (integer_value);
ecma_integer_value_t integer_value = (ecma_integer_value_t) ecma_number;

if ((ecma_number_t) integer_value == ecma_number
&& ((integer_value == 0) ? ecma_is_number_equal_to_positive_zero (ecma_number)
: ECMA_IS_INTEGER_NUMBER (integer_value)))
{
return ecma_make_integer_value (integer_value);
}
}

return ecma_create_float_number (ecma_number);
Expand Down Expand Up @@ -1010,15 +1018,22 @@ ecma_update_float_number (ecma_value_t float_value, /**< original float value */
{
JERRY_ASSERT (ecma_is_value_float_number (float_value));

ecma_integer_value_t integer_number = (ecma_integer_value_t) new_number;
ecma_number_t *number_p = (ecma_number_t *) ecma_get_pointer_from_ecma_value (float_value);

if ((ecma_number_t) integer_number == new_number
&& ((integer_number == 0) ? ecma_is_number_equal_to_positive_zero (new_number)
: ECMA_IS_INTEGER_NUMBER (integer_number)))
/* See the note in ecma_make_number_value: the cast is only defined when the
* double is inside the ecma_integer_value_t range. */
if (new_number >= (ecma_number_t) ECMA_INTEGER_NUMBER_MIN
&& new_number <= (ecma_number_t) ECMA_INTEGER_NUMBER_MAX)
{
ecma_dealloc_number (number_p);
return ecma_make_integer_value (integer_number);
ecma_integer_value_t integer_number = (ecma_integer_value_t) new_number;

if ((ecma_number_t) integer_number == new_number
&& ((integer_number == 0) ? ecma_is_number_equal_to_positive_zero (new_number)
: ECMA_IS_INTEGER_NUMBER (integer_number)))
{
ecma_dealloc_number (number_p);
return ecma_make_integer_value (integer_number);
}
}

*number_p = new_number;
Expand Down Expand Up @@ -1062,19 +1077,25 @@ void
ecma_value_assign_number (ecma_value_t *value_p, /**< [in, out] ecma value */
ecma_number_t ecma_number) /**< number to assign */
{
ecma_integer_value_t integer_value = (ecma_integer_value_t) ecma_number;

if ((ecma_number_t) integer_value == ecma_number
&& ((integer_value == 0) ? ecma_is_number_equal_to_positive_zero (ecma_number)
: ECMA_IS_INTEGER_NUMBER (integer_value)))
/* See the note in ecma_make_number_value: the cast is only defined when the
* double is inside the ecma_integer_value_t range. */
if (ecma_number >= (ecma_number_t) ECMA_INTEGER_NUMBER_MIN
&& ecma_number <= (ecma_number_t) ECMA_INTEGER_NUMBER_MAX)
{
if (ecma_get_value_type_field (*value_p) != ECMA_TYPE_DIRECT
&& ecma_get_value_type_field (*value_p) != ECMA_TYPE_OBJECT)
ecma_integer_value_t integer_value = (ecma_integer_value_t) ecma_number;

if ((ecma_number_t) integer_value == ecma_number
&& ((integer_value == 0) ? ecma_is_number_equal_to_positive_zero (ecma_number)
: ECMA_IS_INTEGER_NUMBER (integer_value)))
{
ecma_free_value (*value_p);
if (ecma_get_value_type_field (*value_p) != ECMA_TYPE_DIRECT
&& ecma_get_value_type_field (*value_p) != ECMA_TYPE_OBJECT)
{
ecma_free_value (*value_p);
}
*value_p = ecma_make_integer_value (integer_value);
return;
}
*value_p = ecma_make_integer_value (integer_value);
return;
}

ecma_value_assign_float_number (value_p, ecma_number);
Expand Down
21 changes: 21 additions & 0 deletions tests/unit-core/test-api-value-type.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,27 @@ main (void)
jerry_set_realm (old_realm);
}

/* Numbers outside the ecma_integer_value_t range must round-trip as float
* values. Converting them to an integer to test for a small integer used to
* be undefined behaviour and trapped under -fsanitize=undefined. */
{
static const double out_of_range_numbers[] = { 1e308, -1e308, 1.5e300, -1.5e10, 1e18 };
for (size_t i = 0; i < sizeof (out_of_range_numbers) / sizeof (double); i++)
{
jerry_value_t value = jerry_number (out_of_range_numbers[i]);
TEST_ASSERT (!jerry_value_is_exception (value));
TEST_ASSERT (jerry_value_is_number (value));
TEST_ASSERT (jerry_value_as_number (value) == out_of_range_numbers[i]);
jerry_value_free (value);
}

/* Values inside the range must keep behaving as small integers. */
jerry_value_t small = jerry_number (42.0);
TEST_ASSERT (jerry_value_is_number (small));
TEST_ASSERT (jerry_value_as_number (small) == 42.0);
jerry_value_free (small);
}

jerry_cleanup ();

return 0;
Expand Down