diff --git a/jerry-core/ecma/base/ecma-helpers-conversion.c b/jerry-core/ecma/base/ecma-helpers-conversion.c index 756973d30..aa037133d 100644 --- a/jerry-core/ecma/base/ecma-helpers-conversion.c +++ b/jerry-core/ecma/base/ecma-helpers-conversion.c @@ -280,13 +280,87 @@ static const uint8_t ecma_uint4_clz[] = { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, */ #define EPSILON 0.0000001 +/** + * ECMA-defined conversion from string to number for different radixes (2, 8, 16). + * + * See also: + * ECMA-262 v5 9.3.1 + * ECMA-262 v6 7.1.3.1 + * + * @return NaN - if the conversion fails + * converted number - otherwise + */ +static ecma_number_t +ecma_utf8_string_to_number_by_radix (const lit_utf8_byte_t *str_p, /**< utf-8 string */ + const lit_utf8_byte_t *end_p, /**< end of utf-8 string */ + uint32_t radix) /**< radix */ +{ + JERRY_ASSERT (radix == 2 || radix == 8 || radix == 16); + ecma_number_t num = ECMA_NUMBER_ZERO; + +#if ENABLED (JERRY_ES2015) + if (radix <= 8) + { + lit_code_point_t upper_limit = LIT_CHAR_0 + radix; + + for (const lit_utf8_byte_t * iter_p = str_p; iter_p <= end_p; iter_p++) + { + int32_t digit_value; + + if (*iter_p >= LIT_CHAR_0 && *iter_p < upper_limit) + { + digit_value = (*iter_p - LIT_CHAR_0); + } + else + { + return ecma_number_make_nan (); + } + + num = num * radix + (ecma_number_t) digit_value; + } + + return num; + } +#endif /* ENABLED (JERRY_ES2015) */ + + for (const lit_utf8_byte_t * iter_p = str_p; iter_p <= end_p; iter_p++) + { + int32_t digit_value; + + if (*iter_p >= LIT_CHAR_0 + && *iter_p <= LIT_CHAR_9) + { + digit_value = (*iter_p - LIT_CHAR_0); + } + else if (*iter_p >= LIT_CHAR_LOWERCASE_A + && *iter_p <= LIT_CHAR_LOWERCASE_F) + { + digit_value = 10 + (*iter_p - LIT_CHAR_LOWERCASE_A); + } + else if (*iter_p >= LIT_CHAR_UPPERCASE_A + && *iter_p <= LIT_CHAR_UPPERCASE_F) + { + digit_value = 10 + (*iter_p - LIT_CHAR_UPPERCASE_A); + } + else + { + return ecma_number_make_nan (); + } + + num = num * radix + (ecma_number_t) digit_value; + } + + return num; +} /* ecma_utf8_string_to_number_by_radix */ + /** * ECMA-defined conversion of string to Number. * * See also: * ECMA-262 v5, 9.3.1 * - * @return ecma-number + * @return NaN - if the conversion fails + * converted number - otherwise */ ecma_number_t ecma_utf8_string_to_number (const lit_utf8_byte_t *str_p, /**< utf-8 string */ @@ -307,46 +381,28 @@ ecma_utf8_string_to_number (const lit_utf8_byte_t *str_p, /**< utf-8 string */ return ECMA_NUMBER_ZERO; } - if ((end_p >= str_p + 2) - && str_p[0] == LIT_CHAR_0 - && (str_p[1] == LIT_CHAR_LOWERCASE_X - || str_p[1] == LIT_CHAR_UPPERCASE_X)) + if (end_p >= str_p + 2 + && str_p[0] == LIT_CHAR_0) { - /* Hex literal handling */ - str_p += 2; - - ecma_number_t num = ECMA_NUMBER_ZERO; - - for (const lit_utf8_byte_t * iter_p = str_p; - iter_p <= end_p; - iter_p++) + switch (LEXER_TO_ASCII_LOWERCASE (str_p[1])) { - int32_t digit_value; - - if (*iter_p >= LIT_CHAR_0 - && *iter_p <= LIT_CHAR_9) + case LIT_CHAR_LOWERCASE_X : { - digit_value = (*iter_p - LIT_CHAR_0); + return ecma_utf8_string_to_number_by_radix (str_p + 2, end_p, 16); } - else if (*iter_p >= LIT_CHAR_LOWERCASE_A - && *iter_p <= LIT_CHAR_LOWERCASE_F) + case LIT_CHAR_LOWERCASE_O : { - digit_value = 10 + (*iter_p - LIT_CHAR_LOWERCASE_A); + return ecma_utf8_string_to_number_by_radix (str_p + 2, end_p, 8); } - else if (*iter_p >= LIT_CHAR_UPPERCASE_A - && *iter_p <= LIT_CHAR_UPPERCASE_F) + case LIT_CHAR_LOWERCASE_B : { - digit_value = 10 + (*iter_p - LIT_CHAR_UPPERCASE_A); + return ecma_utf8_string_to_number_by_radix (str_p + 2, end_p, 2); } - else + default: { - return ecma_number_make_nan (); + break; } - - num = num * 16 + (ecma_number_t) digit_value; } - - return num; } bool sign = false; /* positive */ diff --git a/tests/jerry/es2015/to-number-string.js b/tests/jerry/es2015/to-number-string.js new file mode 100644 index 000000000..8bf56fb34 --- /dev/null +++ b/tests/jerry/es2015/to-number-string.js @@ -0,0 +1,49 @@ +/* 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. + */ + +assert(+"0b1101001" === 105); +assert(+"0B1101001" === 105); +assert(+"0o767" === 503); +assert(+"0O767" === 503); + +function assertNaN(str) { + assert(isNaN(+str)); +} + +assertNaN("0b"); +assertNaN("0b12"); +assertNaN("0b10101100103"); +assertNaN("0b101foo"); +assertNaN("0bfoo101"); +assertNaN("0b12345"); + +assertNaN("0B"); +assertNaN("0B12"); +assertNaN("0B10101100103"); +assertNaN("0B101foo"); +assertNaN("0Bfoo101"); +assertNaN("0B12345"); + +assertNaN("0o"); +assertNaN("0o1289"); +assertNaN("0o88888"); +assertNaN("0o12345foo"); +assertNaN("0ofoo12345"); + +assertNaN("0O"); +assertNaN("0O1289"); +assertNaN("0O88888"); +assertNaN("0O12345foo"); +assertNaN("0Ofoo12345");