From a96705701dea00553c6be7420d6077fed46feca4 Mon Sep 17 00:00:00 2001 From: Ruben Ayrapetyan Date: Thu, 21 Aug 2014 14:43:43 +0400 Subject: [PATCH] Implementing prototype of ToString (Number) routine - ecma_number_to_zt_string; adding unit tests for the routine. --- src/libecmaobjects/ecma-helpers-conversion.c | 178 +++++++++++++++++++ src/libecmaobjects/ecma-helpers-number.c | 56 ++++-- src/libecmaobjects/ecma-helpers.h | 6 +- src/libecmaoperations/ecma-magic-strings.c | 37 ++-- src/libecmaoperations/ecma-magic-strings.h | 7 +- tests/unit/test_number_to_string.c | 73 ++++++++ 6 files changed, 329 insertions(+), 28 deletions(-) create mode 100644 tests/unit/test_number_to_string.c diff --git a/src/libecmaobjects/ecma-helpers-conversion.c b/src/libecmaobjects/ecma-helpers-conversion.c index 6c92ec57f..a90c5c5a9 100644 --- a/src/libecmaobjects/ecma-helpers-conversion.c +++ b/src/libecmaobjects/ecma-helpers-conversion.c @@ -22,6 +22,7 @@ #include "ecma-globals.h" #include "ecma-helpers.h" +#include "ecma-magic-strings.h" #include "jerry-libc.h" /** @@ -128,6 +129,183 @@ ecma_number_to_int32 (ecma_number_t value) /**< unsigned 32-bit integer value */ return (int32_t) value; } /* ecma_number_to_int32 */ +/** + * Convert ecma-number to zero-terminated string + * + * See also: + * ECMA-262 v5, 9.8.1 + */ +void +ecma_number_to_zt_string (ecma_number_t num, /**< ecma-number */ + ecma_char_t *buffer_p, /**< buffer for zt-string */ + size_t buffer_size) /**< size of buffer */ +{ + TODO (Support UTF-16); + + ecma_char_t digits_chars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + ecma_char_t plus_char = '+'; + ecma_char_t minus_char = '-'; + ecma_char_t dot_char = '.'; + ecma_char_t e_char = 'e'; + ecma_char_t null_char = '\0'; + + // 1. + if (ecma_number_is_nan (num)) + { + FIXME (/* Assert that buffer's size is enough */); + __strncpy ((char*) buffer_p, (char*) ecma_get_magic_string_zt (ECMA_MAGIC_STRING_NAN), buffer_size); + + return; + } + + ecma_char_t *dst_p = buffer_p; + + // 2. + if (ecma_number_is_zero (num)) + { + *dst_p++ = digits_chars[0]; + *dst_p++ = null_char; + + JERRY_ASSERT ((uint8_t*)dst_p - (uint8_t*)buffer_p <= (ssize_t) buffer_size); + + return; + } + + // 3. + if (ecma_number_is_negative (num)) + { + *dst_p++ = minus_char; + size_t new_buffer_size = (size_t) ((ssize_t) buffer_size - ((uint8_t*)dst_p - (uint8_t*)buffer_p)); + ecma_number_to_zt_string (ecma_number_negate (num), dst_p, new_buffer_size); + + return; + } + + // 4. + if (ecma_number_is_infinity (num)) + { + FIXME (/* Assert that buffer's size is enough */); + __strncpy ((char*) buffer_p, (char*) ecma_get_magic_string_zt (ECMA_MAGIC_STRING_INFINITY), buffer_size); + + return; + } + + // 5. + uint64_t fraction; + int32_t exponent; + int32_t dot_shift; + dot_shift = ecma_number_get_fraction_and_exponent (num, &fraction, &exponent); + + FIXME (Decimal representation); + + uint64_t s = fraction; + int32_t n = exponent + 1; + int32_t k = dot_shift; + + JERRY_ASSERT (s != 0); + while (!(s & 1)) + { + s >>= 1; + k--; + } + + // 6. + if (k <= n && n <= 21) + { + for (int32_t i = 0; i < k; i++) + { + uint64_t bit_mask = 1ul << (k - i - 1); + *dst_p++ = digits_chars [(s & bit_mask) ? 1 : 0]; + } + + for (int32_t i = 0; i < n - k; i++) + { + *dst_p++ = digits_chars[0]; + } + } + else if (0 < n && n <= 21) + { + // 7. + for (int32_t i = 0; i < n; i++) + { + uint64_t bit_mask = 1ul << (k - i - 1); + *dst_p++ = digits_chars [(s & bit_mask) ? 1 : 0]; + } + + *dst_p++ = dot_char; + + for (int32_t i = n; i < k; i++) + { + uint64_t bit_mask = 1ul << (k - i - 1); + *dst_p++ = digits_chars [(s & bit_mask) ? 1 : 0]; + } + } + else if (-6 <= n && n <= 0) + { + // 8. + *dst_p++ = digits_chars[0]; + *dst_p++ = '.'; + + for (int32_t i = 0; i < k; i++) + { + uint64_t bit_mask = 1ul << (k - i - 1); + *dst_p++ = digits_chars [(s & bit_mask) ? 1 : 0]; + } + } + else + { + if (k == 1) + { + // 9. + *dst_p++ = digits_chars [s ? 1 : 0]; + } + else + { + // 10. + uint64_t bit_mask = 1ul << (k - 1); + *dst_p++ = digits_chars [(s & bit_mask) ? 1 : 0]; + *dst_p++ = dot_char; + for (int32_t i = 1; i < k; i++) + { + bit_mask = 1ul << (k - i - 1); + *dst_p++ = digits_chars [(s & bit_mask) ? 1 : 0]; + } + } + + // 9., 10. + *dst_p++ = e_char; + *dst_p++ = (n >= 1) ? plus_char : minus_char; + int32_t t = (n >= 1) ? (n - 1) : -(n - 1); + + if (t == 0) + { + *dst_p++ = digits_chars[0]; + } + else + { + uint32_t t_bit = (1u << 31); + + while ((t & (int32_t) t_bit) == 0) + { + t_bit >>= 1; + + JERRY_ASSERT (t_bit != 0); + } + + while (t_bit != 0) + { + *dst_p++ = digits_chars [(t & (int32_t) t_bit) ? 1 : 0]; + + t_bit >>= 1; + } + } + } + + *dst_p++ = null_char; + + JERRY_ASSERT ((uint8_t*)dst_p - (uint8_t*)buffer_p <= (ssize_t) buffer_size); +} /* ecma_number_to_zt_string */ + /** * @} * @} diff --git a/src/libecmaobjects/ecma-helpers-number.c b/src/libecmaobjects/ecma-helpers-number.c index 2eb9400b7..c08dabdd1 100644 --- a/src/libecmaobjects/ecma-helpers-number.c +++ b/src/libecmaobjects/ecma-helpers-number.c @@ -58,14 +58,14 @@ JERRY_STATIC_ASSERT (sizeof (ecma_number_t) == sizeof (uint32_t)); */ typedef struct { - /** sign bit */ - unsigned int sign : ECMA_NUMBER_SIGN_WIDTH; + /** fraction field */ + unsigned int fraction : ECMA_NUMBER_FRACTION_WIDTH; /** biased exponent field */ unsigned int biased_exp : ECMA_NUMBER_BIASED_EXP_WIDTH; - /** fraction field */ - unsigned int fraction : ECMA_NUMBER_FRACTION_WIDTH; + /** sign bit */ + unsigned int sign : ECMA_NUMBER_SIGN_WIDTH; } ecma_number_fields_t; /** @@ -110,14 +110,14 @@ JERRY_STATIC_ASSERT (sizeof (ecma_number_t) == sizeof (uint64_t)); */ typedef struct { - /** sign bit */ - unsigned long int sign : ECMA_NUMBER_SIGN_WIDTH; + /** fraction field */ + unsigned long int fraction : ECMA_NUMBER_FRACTION_WIDTH; /** biased exponent field */ unsigned long int biased_exp : ECMA_NUMBER_BIASED_EXP_WIDTH; - /** fraction field */ - unsigned long int fraction : ECMA_NUMBER_FRACTION_WIDTH; + /** sign bit */ + unsigned long int sign : ECMA_NUMBER_SIGN_WIDTH; } ecma_number_fields_t; /** @@ -264,10 +264,11 @@ ecma_number_is_infinity (ecma_number_t num) /**< ecma-number */ /** * Get fraction and exponent of the number * - * @return normalized fraction field of the number + * @return shift of dot in the fraction */ -uint64_t +int32_t ecma_number_get_fraction_and_exponent (ecma_number_t num, /**< ecma-number */ + uint64_t *out_fraction_p, /**< out: fraction of the number */ int32_t *out_exponent_p) /**< out: exponent of the number */ { JERRY_ASSERT (!ecma_number_is_nan (num)); @@ -276,13 +277,14 @@ ecma_number_get_fraction_and_exponent (ecma_number_t num, /**< ecma-number */ uint32_t biased_exp = ecma_number_get_biased_exponent_field (num); uint64_t fraction = ecma_number_get_fraction_field (num); - int32_t exponent = (int32_t) biased_exp - ecma_number_exponent_bias; + int32_t exponent; if (unlikely (biased_exp == 0)) { /* IEEE-754 2008, 3.4, d */ + exponent = 1 - ecma_number_exponent_bias; - while (!(fraction & (1u << (ECMA_NUMBER_FRACTION_WIDTH - 1)))) + while (!(fraction & (1u << ECMA_NUMBER_FRACTION_WIDTH))) { JERRY_ASSERT (fraction != 0); @@ -293,14 +295,42 @@ ecma_number_get_fraction_and_exponent (ecma_number_t num, /**< ecma-number */ else { /* IEEE-754 2008, 3.4, c */ + exponent = (int32_t) biased_exp - ecma_number_exponent_bias; JERRY_ASSERT (biased_exp > 0 && biased_exp < (1u << ECMA_NUMBER_BIASED_EXP_WIDTH) - 1); + JERRY_ASSERT ((fraction & (1u << ECMA_NUMBER_FRACTION_WIDTH)) == 0); + fraction |= 1u << ECMA_NUMBER_FRACTION_WIDTH; } + *out_fraction_p = fraction; *out_exponent_p = exponent; - return fraction; + return ECMA_NUMBER_FRACTION_WIDTH + 1; } /* ecma_number_get_fraction_and_exponent */ +/** + * Negate ecma-number + * + * @return negated number + */ +ecma_number_t +ecma_number_negate (ecma_number_t num) /**< ecma-number */ +{ + JERRY_ASSERT (!ecma_number_is_nan (num)); + + union + { + ecma_number_fields_t fields; + ecma_number_t value; + } u; + + u.value = num; + + u.fields.sign = !u.fields.sign; + + return u.value; +} /* ecma_number_negate */ + + /** * @} * @} diff --git a/src/libecmaobjects/ecma-helpers.h b/src/libecmaobjects/ecma-helpers.h index 507eb2504..e2c21e413 100644 --- a/src/libecmaobjects/ecma-helpers.h +++ b/src/libecmaobjects/ecma-helpers.h @@ -105,7 +105,10 @@ extern bool ecma_number_is_nan (ecma_number_t num); extern bool ecma_number_is_negative (ecma_number_t num); extern bool ecma_number_is_zero (ecma_number_t num); extern bool ecma_number_is_infinity (ecma_number_t num); -extern uint64_t ecma_number_get_fraction_and_exponent (ecma_number_t num, int32_t *out_exponent_p); +extern int32_t ecma_number_get_fraction_and_exponent (ecma_number_t num, + uint64_t *out_fraction_p, + int32_t *out_exponent_p); +extern ecma_number_t ecma_number_negate (ecma_number_t num); /* ecma-helpers-values-collection.c */ @@ -187,6 +190,7 @@ extern void ecma_uint32_to_string (uint32_t value, ecma_char_t *out_buffer_p, si extern uint32_t ecma_number_to_uint32 (ecma_number_t value); extern int32_t ecma_number_to_int32 (ecma_number_t value); extern ecma_number_t ecma_uint32_to_number (uint32_t value); +extern void ecma_number_to_zt_string (ecma_number_t num, ecma_char_t *buffer_p, size_t buffer_size); #endif /* !JERRY_ECMA_HELPERS_H */ diff --git a/src/libecmaoperations/ecma-magic-strings.c b/src/libecmaoperations/ecma-magic-strings.c index 8738981b4..5514913be 100644 --- a/src/libecmaoperations/ecma-magic-strings.c +++ b/src/libecmaoperations/ecma-magic-strings.c @@ -25,28 +25,41 @@ */ /** - * Get specified magic string + * Get specified magic string as zero-terminated string * - * @return pointer to magic string contant + * @return pointer to zero-terminated magic string */ -ecma_string_t* -ecma_get_magic_string (ecma_magic_string_id_t id) /**< magic string id */ +const ecma_char_t* +ecma_get_magic_string_zt (ecma_magic_string_id_t id) /**< magic string id */ { TODO(Support UTF-16); switch (id) { - case ECMA_MAGIC_STRING_ARGUMENTS: return ecma_new_ecma_string ((ecma_char_t*) "arguments"); - case ECMA_MAGIC_STRING_EVAL: return ecma_new_ecma_string ((ecma_char_t*) "eval"); - case ECMA_MAGIC_STRING_PROTOTYPE: return ecma_new_ecma_string ((ecma_char_t*) "prototype"); - case ECMA_MAGIC_STRING_CONSTRUCTOR: return ecma_new_ecma_string ((ecma_char_t*) "constructor"); - case ECMA_MAGIC_STRING_CALLER: return ecma_new_ecma_string ((ecma_char_t*) "caller"); - case ECMA_MAGIC_STRING_CALLEE: return ecma_new_ecma_string ((ecma_char_t*) "callee"); - case ECMA_MAGIC_STRING_UNDEFINED: return ecma_new_ecma_string ((ecma_char_t*) "undefined"); - case ECMA_MAGIC_STRING_LENGTH: return ecma_new_ecma_string ((ecma_char_t*) "length"); + case ECMA_MAGIC_STRING_ARGUMENTS: return (ecma_char_t*) "arguments"; + case ECMA_MAGIC_STRING_EVAL: return (ecma_char_t*) "eval"; + case ECMA_MAGIC_STRING_PROTOTYPE: return (ecma_char_t*) "prototype"; + case ECMA_MAGIC_STRING_CONSTRUCTOR: return (ecma_char_t*) "constructor"; + case ECMA_MAGIC_STRING_CALLER: return (ecma_char_t*) "caller"; + case ECMA_MAGIC_STRING_CALLEE: return (ecma_char_t*) "callee"; + case ECMA_MAGIC_STRING_UNDEFINED: return (ecma_char_t*) "undefined"; + case ECMA_MAGIC_STRING_LENGTH: return (ecma_char_t*) "length"; + case ECMA_MAGIC_STRING_NAN: return (ecma_char_t*) "NaN"; + case ECMA_MAGIC_STRING_INFINITY: return (ecma_char_t*) "Infinity"; } JERRY_UNREACHABLE(); +} /* ecma_get_magic_string_zt */ + +/** + * Get specified magic string + * + * @return ecma-string containing specified magic string + */ +ecma_string_t* +ecma_get_magic_string (ecma_magic_string_id_t id) /**< magic string id */ +{ + return ecma_new_ecma_string (ecma_get_magic_string_zt (id)); } /* ecma_get_magic_string */ /** diff --git a/src/libecmaoperations/ecma-magic-strings.h b/src/libecmaoperations/ecma-magic-strings.h index bfc71770d..750a70551 100644 --- a/src/libecmaoperations/ecma-magic-strings.h +++ b/src/libecmaoperations/ecma-magic-strings.h @@ -36,10 +36,13 @@ typedef enum ECMA_MAGIC_STRING_CONSTRUCTOR, /**< "constructor" */ ECMA_MAGIC_STRING_CALLER, /**< "caller" */ ECMA_MAGIC_STRING_CALLEE, /**< "callee" */ - ECMA_MAGIC_STRING_UNDEFINED,/**< undefined */ - ECMA_MAGIC_STRING_LENGTH /**< length */ + ECMA_MAGIC_STRING_UNDEFINED,/**< "undefined" */ + ECMA_MAGIC_STRING_LENGTH, /**< "length" */ + ECMA_MAGIC_STRING_NAN, /**< "NaN" */ + ECMA_MAGIC_STRING_INFINITY /**< "Infinity" */ } ecma_magic_string_id_t; +extern const ecma_char_t* ecma_get_magic_string_zt (ecma_magic_string_id_t id); extern ecma_string_t* ecma_get_magic_string (ecma_magic_string_id_t id); /** diff --git a/tests/unit/test_number_to_string.c b/tests/unit/test_number_to_string.c new file mode 100644 index 000000000..233898472 --- /dev/null +++ b/tests/unit/test_number_to_string.c @@ -0,0 +1,73 @@ +/* Copyright 2014 Samsung Electronics Co., Ltd. + * + * 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-globals.h" +#include "ecma-helpers.h" +#include "globals.h" +#include "jerry-libc.h" + +#include + +/** + * Unit test's main function. + */ +int +main( int __unused argc, + char __unused **argv) +{ + const ecma_char_t* zt_strings[] = + { + (const ecma_char_t*) "1", + (const ecma_char_t*) "0.1", + (const ecma_char_t*) "11000000111001", + (const ecma_char_t*) "1e-10010101", + (const ecma_char_t*) "-1.01111000001010001e+1111111", + (const ecma_char_t*) "NaN", + (const ecma_char_t*) "Infinity", + (const ecma_char_t*) "-Infinity", + (const ecma_char_t*) "0", + (const ecma_char_t*) "0", + }; + + const ecma_number_t nums[] = + { + 1.0f, + 0.5f, + 12345.0f, + 1.0e-45f, + -2.5e+38f, + NAN, + INFINITY, + -INFINITY, + +0.0f, + -0.0f + }; + + for (uint32_t i = 0; + i < sizeof (nums) / sizeof (nums[0]); + i++) + { + ecma_char_t zt_str[64]; + + ecma_number_to_zt_string (nums[i], zt_str, sizeof (zt_str)); + + if (__strcmp ((char*)zt_str, (char*)zt_strings [i]) != 0) + { + return 1; + } + } + + return 0; +} /* main */