diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-string.c b/jerry-core/ecma/builtin-objects/ecma-builtin-string.c index 1c4faf42c..79bd05b0a 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-string.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-string.c @@ -13,6 +13,7 @@ * limitations under the License. */ +#include "lit-strings.h" #include "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" @@ -109,6 +110,75 @@ ecma_builtin_string_object_from_char_code (ecma_value_t this_arg, /**< 'this' ar return ret_value; } /* ecma_builtin_string_object_from_char_code */ +#if ENABLED (JERRY_ES2015_BUILTIN) +/** + * The String object's 'fromCodePoint' routine + * + * See also: + * ECMA-262 v6, 21.1.2.2 + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_builtin_string_object_from_code_point (ecma_value_t this_arg, /**< 'this' argument */ + const ecma_value_t args[], /**< arguments list */ + ecma_length_t args_number) /**< number of arguments */ +{ + JERRY_UNUSED (this_arg); + + if (args_number == 0) + { + return ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); + } + + ecma_stringbuilder_t builder = ecma_stringbuilder_create (); + + for (ecma_length_t index = 0; index < args_number; index++) + { + ecma_value_t to_number_value = ecma_op_to_number (args[index]); + + if (ECMA_IS_VALUE_ERROR (to_number_value)) + { + ecma_stringbuilder_destroy (&builder); + return to_number_value; + } + + ecma_number_t to_number_num = ecma_get_number_from_value (to_number_value); + ecma_free_value (to_number_value); + + ecma_number_t to_int_num; + ecma_value_t to_int_value = ecma_op_to_integer (to_number_value, &to_int_num); + + if (ECMA_IS_VALUE_ERROR (to_int_value)) + { + ecma_stringbuilder_destroy (&builder); + return to_int_value; + } + + if (to_number_num != to_int_num || to_int_num < 0 || to_int_num > LIT_UNICODE_CODE_POINT_MAX) + { + ecma_stringbuilder_destroy (&builder); + return ecma_raise_range_error (ECMA_ERR_MSG ("Error: Invalid code point")); + } + + lit_code_point_t code_point = (uint32_t) to_int_num; + + ecma_char_t converted_cp[2]; + uint8_t encoded_size = lit_utf16_encode_code_point (code_point, converted_cp); + + for (uint8_t i = 0; i < encoded_size; i++) + { + ecma_stringbuilder_append_char (&builder, converted_cp[i]); + } + } + + ecma_string_t *ret_str_p = ecma_stringbuilder_finalize (&builder); + + return ecma_make_string_value (ret_str_p); +} /* ecma_builtin_string_object_from_code_point */ +#endif /* ENABLED (JERRY_ES2015_BUILTIN) */ + /** * Handle calling [[Call]] of built-in String object * diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-string.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-string.inc.h index a8435a90b..b6cd3bef0 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-string.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-string.inc.h @@ -41,6 +41,10 @@ OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_FROM_CHAR_CODE_UL, ecma_builtin_string_object_from_char_code, NON_FIXED, 1) +#if ENABLED (JERRY_ES2015_BUILTIN) +ROUTINE (LIT_MAGIC_STRING_FROM_CODE_POINT_UL, ecma_builtin_string_object_from_code_point, NON_FIXED, 1) +#endif /* ENABLED (JERRY_ES2015_BUILTIN) */ + #endif /* ENABLED (JERRY_BUILTIN_STRING) */ #include "ecma-builtin-helpers-macro-undefs.inc.h" diff --git a/jerry-core/lit/lit-magic-strings.inc.h b/jerry-core/lit/lit-magic-strings.inc.h index 9862d9fe0..40acfe228 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -684,6 +684,11 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_EXTENSIBLE, "isExtensible") #if ENABLED (JERRY_BUILTIN_DATE) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_DATE_STRING_UL, "toDateString") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_TIME_STRING_UL, "toTimeString") +#endif +#if ENABLED (JERRY_BUILTIN_STRING) && ENABLED (JERRY_ES2015_BUILTIN) +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FROM_CODE_POINT_UL, "fromCodePoint") +#endif +#if ENABLED (JERRY_BUILTIN_DATE) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_UTC_MINUTES_UL, "getUTCMinutes") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_UTC_SECONDS_UL, "getUTCSeconds") #endif @@ -848,7 +853,9 @@ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (12, LIT_MAGIC_STRING_SET_ITERATOR_UL) #else LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (12, LIT_MAGIC_STRING_CONFIGURABLE) #endif -#if ENABLED (JERRY_BUILTIN_DATE) +#if ENABLED (JERRY_BUILTIN_STRING) && ENABLED (JERRY_ES2015_BUILTIN) +LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (13, LIT_MAGIC_STRING_FROM_CODE_POINT_UL) +#elif ENABLED (JERRY_BUILTIN_DATE) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (13, LIT_MAGIC_STRING_GET_UTC_MINUTES_UL) #else LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (13, LIT_MAGIC_STRING_IS_PROTOTYPE_OF_UL) diff --git a/jerry-core/lit/lit-magic-strings.ini b/jerry-core/lit/lit-magic-strings.ini index 072d2ca59..200a64e8d 100644 --- a/jerry-core/lit/lit-magic-strings.ini +++ b/jerry-core/lit/lit-magic-strings.ini @@ -286,6 +286,7 @@ LIT_MAGIC_STRING_SET_ITERATOR_UL = "Set Iterator" LIT_MAGIC_STRING_MAP_ITERATOR_UL = "Map Iterator" LIT_MAGIC_STRING_CONFIGURABLE = "configurable" LIT_MAGIC_STRING_FROM_CHAR_CODE_UL = "fromCharCode" +LIT_MAGIC_STRING_FROM_CODE_POINT_UL = "fromCodePoint" LIT_MAGIC_STRING_IS_EXTENSIBLE = "isExtensible" LIT_MAGIC_STRING_TO_DATE_STRING_UL = "toDateString" LIT_MAGIC_STRING_TO_TIME_STRING_UL = "toTimeString" diff --git a/jerry-core/lit/lit-strings.c b/jerry-core/lit/lit-strings.c index c5dbf2adf..5b0027252 100644 --- a/jerry-core/lit/lit-strings.c +++ b/jerry-core/lit/lit-strings.c @@ -243,6 +243,29 @@ convert_code_point_to_high_surrogate (lit_code_point_t code_point) /**< code poi return (LIT_UTF16_HIGH_SURROGATE_MARKER | code_unit_bits); } /* convert_code_point_to_high_surrogate */ +/** + * UTF16 Encoding method for a code point + * + * See also: + * ECMA-262 v6, 10.1.1 + * + * @return uint8_t, the number of returning code points + */ +uint8_t +lit_utf16_encode_code_point (lit_code_point_t cp, /**< the code point we encode */ + ecma_char_t *cu_p) /**< result of the encoding */ +{ + if (cp <= LIT_UTF16_CODE_UNIT_MAX) + { + cu_p[0] = (ecma_char_t) cp; + return 1; + } + + cu_p[0] = convert_code_point_to_high_surrogate (cp); + cu_p[1] = convert_code_point_to_low_surrogate (cp); + return 2; +} /* lit_utf16_encode_code_point */ + /** * Calculate size of a zero-terminated utf-8 string * diff --git a/jerry-core/lit/lit-strings.h b/jerry-core/lit/lit-strings.h index 208108bde..304fe7257 100644 --- a/jerry-core/lit/lit-strings.h +++ b/jerry-core/lit/lit-strings.h @@ -123,6 +123,8 @@ lit_code_point_t lit_convert_surrogate_pair_to_code_point (ecma_char_t high_surr bool lit_compare_utf8_strings_relational (const lit_utf8_byte_t *string1_p, lit_utf8_size_t string1_size, const lit_utf8_byte_t *string2_p, lit_utf8_size_t string2_size); +uint8_t lit_utf16_encode_code_point (lit_code_point_t cp, ecma_char_t *cu_p); + /* read code point from buffer */ lit_utf8_size_t lit_read_code_point_from_utf8 (const lit_utf8_byte_t *buf_p, lit_utf8_size_t buf_size, lit_code_point_t *code_point); diff --git a/tests/jerry/es2015/string-fromcodepoint.js b/tests/jerry/es2015/string-fromcodepoint.js new file mode 100644 index 000000000..b4e10626c --- /dev/null +++ b/tests/jerry/es2015/string-fromcodepoint.js @@ -0,0 +1,64 @@ +// 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. + +// Tests with valid inputs +assert(String.fromCodePoint(42) === "*") +assert(String.fromCodePoint(65, 90) === "AZ"); +assert(String.fromCodePoint(0x404) === "Є"); +assert(String.fromCodePoint(194564) === "你"); +assert(String.fromCodePoint(0x1D306, 0x61, 0x1D307) === "𝌆a𝌇"); +assert(String.fromCodePoint(0x1F303) === "🌃"); + +// Tests with invalid inputs +try { + assert(String.fromCodePoint('_')); + assert(false); +} catch (e) { + assert(e instanceof RangeError); +} + +try { + assert(String.fromCodePoint(Infinity)); + assert(false); +} catch (e) { + assert(e instanceof RangeError); +} + +try { + assert(String.fromCodePoint(-1)); + assert(false); +} catch (e) { + assert(e instanceof RangeError); +} + +try { + assert(String.fromCodePoint(3.14)); + assert(false); +} catch (e) { + assert(e instanceof RangeError); +} + +try { + assert(String.fromCodePoint(3e-2)); + assert(false); +} catch (e) { + assert(e instanceof RangeError); +} + +try { + assert(String.fromCodePoint(NaN)); + assert(false); +} catch (e) { + assert(e instanceof RangeError); +}