diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-arraybuffer-prototype.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-arraybuffer-prototype.inc.h index c73c52795a..aa9d17ee50 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-arraybuffer-prototype.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-arraybuffer-prototype.inc.h @@ -33,6 +33,13 @@ ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_BYTE_LENGTH_UL, ecma_builtin_arraybuffer_prototype_bytelength_getter, ECMA_PROPERTY_FIXED) +#ifndef CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN +/* ECMA-262 v6, 24.1.4.4 */ +STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, + LIT_MAGIC_STRING_ARRAY_BUFFER_UL, + ECMA_PROPERTY_FLAG_CONFIGURABLE) +#endif /* !CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN */ + /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_SLICE, ecma_builtin_arraybuffer_prototype_object_slice, 2, 2) diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.c b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.c index 4d22424d81..64cd99479a 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.c @@ -21,7 +21,9 @@ #include "ecma-conversion.h" #include "ecma-function-object.h" #include "ecma-exceptions.h" +#include "ecma-gc.h" #include "ecma-helpers.h" +#include "jmem.h" #include "ecma-objects.h" #include "ecma-try-catch-macro.h" #include "lit-magic-strings.h" @@ -33,6 +35,69 @@ * @{ */ +#ifndef CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN +/** + * Helper function for Object.prototype.toString routine when + * the @@toStringTag property is present + * + * See also: + * ECMA-262 v6, 19.1.3.6 + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_builtin_helper_object_to_string_tag_helper (ecma_value_t tag_value) /**< string tag */ +{ + JERRY_ASSERT (ecma_is_value_string (tag_value)); + + ecma_string_t *tag_str_p = ecma_get_string_from_value (tag_value); + ecma_string_t *ret_string_p; + + /* Building string "[object #@@toStringTag#]" + The string size will be size("[object ") + size(#@@toStringTag#) + size ("]"). */ + const lit_utf8_size_t buffer_size = 9 + ecma_string_get_size (tag_str_p); + JMEM_DEFINE_LOCAL_ARRAY (str_buffer, buffer_size, lit_utf8_byte_t); + + lit_utf8_byte_t *buffer_ptr = str_buffer; + + const lit_magic_string_id_t magic_string_ids[] = + { + LIT_MAGIC_STRING_LEFT_SQUARE_CHAR, + LIT_MAGIC_STRING_OBJECT, + LIT_MAGIC_STRING_SPACE_CHAR, + }; + + /* Copy to buffer the "[object " string */ + for (uint32_t i = 0; i < sizeof (magic_string_ids) / sizeof (lit_magic_string_id_t); ++i) + { + buffer_ptr = lit_copy_magic_string_to_buffer (magic_string_ids[i], buffer_ptr, + (lit_utf8_size_t) ((str_buffer + buffer_size) - buffer_ptr)); + + JERRY_ASSERT (buffer_ptr <= str_buffer + buffer_size); + } + + /* Copy to buffer the #@@toStringTag# string */ + buffer_ptr += ecma_string_copy_to_utf8_buffer (tag_str_p, buffer_ptr, + (lit_utf8_size_t) ((str_buffer + buffer_size) - buffer_ptr)); + + JERRY_ASSERT (buffer_ptr <= str_buffer + buffer_size); + + /* Copy to buffer the "]" string */ + buffer_ptr = lit_copy_magic_string_to_buffer (LIT_MAGIC_STRING_RIGHT_SQUARE_CHAR, buffer_ptr, + (lit_utf8_size_t) ((str_buffer + buffer_size) - buffer_ptr)); + + JERRY_ASSERT (buffer_ptr <= str_buffer + buffer_size); + + ret_string_p = ecma_new_ecma_string_from_utf8 (str_buffer, (lit_utf8_size_t) (buffer_ptr - str_buffer)); + + JMEM_FINALIZE_LOCAL_ARRAY (str_buffer); + ecma_deref_ecma_string (tag_str_p); + + return ecma_make_string_value (ret_string_p); +} /* ecma_builtin_helper_object_to_string_tag_helper */ +#endif /* !CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN */ + /** * Common implementation of the Object.prototype.toString routine * @@ -75,7 +140,25 @@ ecma_builtin_helper_object_to_string (const ecma_value_t this_arg) /**< this arg type_string = ecma_object_get_class_name (obj_p); - ecma_free_value (obj_this); +#ifndef CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN + ecma_value_t tag_value = ecma_op_object_get_by_symbol_id (obj_p, LIT_MAGIC_STRING_TO_STRING_TAG); + + if (ECMA_IS_VALUE_ERROR (tag_value)) + { + ecma_deref_object (obj_p); + return tag_value; + } + + if (ecma_is_value_string (tag_value)) + { + ecma_deref_object (obj_p); + return ecma_builtin_helper_object_to_string_tag_helper (tag_value); + } + + ecma_free_value (tag_value); +#endif /* !CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN */ + + ecma_deref_object (obj_p); } ecma_string_t *ret_string_p; diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-json.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-json.inc.h index 8a3a8022f2..85a69337b1 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-json.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-json.inc.h @@ -21,6 +21,13 @@ #ifndef CONFIG_DISABLE_JSON_BUILTIN +#ifndef CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN +/* ECMA-262 v6, 24.3.3 */ +STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, + LIT_MAGIC_STRING_JSON_U, + ECMA_PROPERTY_FLAG_CONFIGURABLE) +#endif /* !CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN */ + /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_PARSE, ecma_builtin_json_parse, 2, 2) diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-map-prototype.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-map-prototype.inc.h index 4e99d050c1..a5a56915f8 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-map-prototype.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-map-prototype.inc.h @@ -29,6 +29,13 @@ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, ECMA_BUILTIN_ID_MAP, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) +#ifndef CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN +/* ECMA-262 v6, 23.1.3.13 */ +STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, + LIT_MAGIC_STRING_MAP_UL, + ECMA_PROPERTY_FLAG_CONFIGURABLE) +#endif /* !CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN */ + /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_CLEAR, ecma_builtin_map_prototype_object_clear, 0, 0) diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-math.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-math.inc.h index 63f7f64d71..1ef186fb25 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-math.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-math.inc.h @@ -64,6 +64,13 @@ NUMBER_VALUE (LIT_MAGIC_STRING_SQRT2_U, ECMA_BUILTIN_NUMBER_SQRT2, ECMA_PROPERTY_FIXED) +#ifndef CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN +/* ECMA-262 v6, 20.2.1.9 */ +STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, + LIT_MAGIC_STRING_MATH_UL, + ECMA_PROPERTY_FLAG_CONFIGURABLE) +#endif /* !CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN */ + /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_ABS, ECMA_MATH_OBJECT_ABS, 1, 1) diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-promise-prototype.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-promise-prototype.inc.h index 9e70297049..a03ff2450c 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-promise-prototype.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-promise-prototype.inc.h @@ -28,6 +28,13 @@ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 1, ECMA_PROPERTY_FLAG_WRITABLE) +#ifndef CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN +/* ECMA-262 v6, 25.4.5.4 */ +STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, + LIT_MAGIC_STRING_PROMISE_UL, + ECMA_PROPERTY_FLAG_CONFIGURABLE) +#endif /* !CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN */ + ROUTINE (LIT_MAGIC_STRING_THEN, ecma_builtin_promise_prototype_then, 2, 2) ROUTINE (LIT_MAGIC_STRING_CATCH, ecma_builtin_promise_prototype_catch, 1, 1) diff --git a/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-prototype.c b/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-prototype.c index 937366b3ad..036ae63715 100644 --- a/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-prototype.c +++ b/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-prototype.c @@ -140,6 +140,28 @@ ecma_builtin_typedarray_prototype_length_getter (ecma_value_t this_arg) /**< thi return ecma_raise_type_error (ECMA_ERR_MSG ("Argument 'this' is not a TypedArray.")); } /* ecma_builtin_typedarray_prototype_length_getter */ +#ifndef CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN +/** + * The %TypedArray%.prototype[Symbol.toStringTag] accessor + * + * See also: + * ES2015, 22.2.3.31 + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_builtin_typedarray_prototype_to_string_tag_getter (ecma_value_t this_arg) /**< this argument */ +{ + if (!ecma_is_typedarray (this_arg)) + { + return ECMA_VALUE_UNDEFINED; + } + + return ecma_make_magic_string_value (ecma_object_get_class_name (ecma_get_object_from_value (this_arg))); +} /* ecma_builtin_typedarray_prototype_to_string_tag_getter */ +#endif /* !CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN */ + /** * Type of routine. */ diff --git a/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-prototype.inc.h b/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-prototype.inc.h index 59047b80d7..5547eba246 100644 --- a/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-prototype.inc.h +++ b/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-prototype.inc.h @@ -45,6 +45,13 @@ ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_LENGTH, ecma_builtin_typedarray_prototype_length_getter, ECMA_PROPERTY_FIXED) +#ifndef CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN +/* ECMA-262 v6, 23.1.3.13 */ +ACCESSOR_READ_ONLY (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, + ecma_builtin_typedarray_prototype_to_string_tag_getter, + ECMA_PROPERTY_FLAG_CONFIGURABLE) +#endif /* !CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN */ + /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_TO_STRING_UL, ecma_builtin_typedarray_prototype_object_to_string, 0, 0) diff --git a/jerry-core/ecma/operations/ecma-objects.c b/jerry-core/ecma/operations/ecma-objects.c index b63bdef297..8310e7792a 100644 --- a/jerry-core/ecma/operations/ecma-objects.c +++ b/jerry-core/ecma/operations/ecma-objects.c @@ -734,6 +734,30 @@ ecma_op_object_get_by_magic_id (ecma_object_t *object_p, /**< the object */ return ecma_op_object_get (object_p, ecma_get_magic_string (property_id)); } /* ecma_op_object_get_by_magic_id */ +#ifndef CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN +/** + * [[Get]] operation of ecma object where the property is a well-known symbol + * + * @return ecma value + * Returned value must be freed with ecma_free_value + */ +ecma_value_t +ecma_op_object_get_by_symbol_id (ecma_object_t *object_p, /**< the object */ + lit_magic_string_id_t property_id) /**< property symbol id */ +{ + ecma_value_t symbol_value = ecma_op_object_get_by_magic_id (ecma_builtin_get (ECMA_BUILTIN_ID_SYMBOL), + property_id); + JERRY_ASSERT (ecma_is_value_symbol (symbol_value)); + + ecma_string_t *symbol_p = ecma_get_symbol_from_value (symbol_value); + ecma_value_t ret_value = ecma_op_object_get (object_p, symbol_p); + + ecma_deref_ecma_string (symbol_p); + + return ret_value; +} /* ecma_op_object_get_by_symbol_id */ +#endif /* !CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN */ + /** * [[Put]] ecma general object's operation * diff --git a/jerry-core/ecma/operations/ecma-objects.h b/jerry-core/ecma/operations/ecma-objects.h index 76ca231712..570d08cc8a 100644 --- a/jerry-core/ecma/operations/ecma-objects.h +++ b/jerry-core/ecma/operations/ecma-objects.h @@ -35,6 +35,9 @@ ecma_value_t ecma_op_object_find (ecma_object_t *object_p, ecma_string_t *proper ecma_value_t ecma_op_object_get_own_data_prop (ecma_object_t *object_p, ecma_string_t *property_name_p); ecma_value_t ecma_op_object_get (ecma_object_t *object_p, ecma_string_t *property_name_p); ecma_value_t ecma_op_object_get_by_magic_id (ecma_object_t *object_p, lit_magic_string_id_t property_id); +#ifndef CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN +ecma_value_t ecma_op_object_get_by_symbol_id (ecma_object_t *object_p, lit_magic_string_id_t property_id); +#endif /* CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN */ ecma_value_t ecma_op_object_put (ecma_object_t *object_p, ecma_string_t *property_name_p, ecma_value_t value, bool is_throw); ecma_value_t ecma_op_object_delete (ecma_object_t *obj_p, ecma_string_t *property_name_p, bool is_throw); diff --git a/tests/jerry/es2015/object-prototype-tostring.js b/tests/jerry/es2015/object-prototype-tostring.js new file mode 100644 index 0000000000..8347de579c --- /dev/null +++ b/tests/jerry/es2015/object-prototype-tostring.js @@ -0,0 +1,102 @@ +// 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. + +/* Symbol prototype @@toStringTag */ +assert (Symbol.prototype[Symbol.toStringTag] === "Symbol"); +assert (Object.prototype.toString.call (Symbol ()) === "[object Symbol]"); + +assert (delete Symbol.prototype[Symbol.toStringTag]); +assert (Symbol.prototype[Symbol.toStringTag] === undefined); +Symbol.prototype[Symbol.toStringTag] = "myStringTag1"; +assert (Object.prototype.toString.call (Symbol ()) === "[object myStringTag1]"); +Symbol.prototype[Symbol.toStringTag] = {}; +assert (Object.prototype.toString.call (Symbol ()) === "[object Symbol]"); + +/* Math @@toStringTag */ +assert (Math[Symbol.toStringTag] === "Math"); +assert (Object.prototype.toString.call (Math) === "[object Math]"); + +assert (delete Math[Symbol.toStringTag]); +assert (Math[Symbol.toStringTag] === undefined); +Math[Symbol.toStringTag] = "myStringTag2"; +assert (Object.prototype.toString.call (Math) === "[object myStringTag2]"); +Math[Symbol.toStringTag] = {}; +assert (Object.prototype.toString.call (Math) === "[object Math]"); + +/* ArrayBuffer.prototype @@toStringTag */ +assert (ArrayBuffer.prototype[Symbol.toStringTag] === "ArrayBuffer"); +assert (Object.prototype.toString.call (new ArrayBuffer ()) === "[object ArrayBuffer]"); + +assert (delete ArrayBuffer.prototype[Symbol.toStringTag]); +assert (ArrayBuffer.prototype[Symbol.toStringTag] === undefined); +ArrayBuffer.prototype[Symbol.toStringTag] = "myStringTag3"; +assert (Object.prototype.toString.call (new ArrayBuffer ()) === "[object myStringTag3]"); +ArrayBuffer.prototype[Symbol.toStringTag] = {}; +assert (ArrayBuffer.prototype.toString.call (new ArrayBuffer ()) === "[object ArrayBuffer]"); + +/* Promise.prototype @@toStringTag */ +assert (Promise.prototype[Symbol.toStringTag] === "Promise"); +assert (Object.prototype.toString.call (new Promise (function () {})) === "[object Promise]"); + +assert (delete Promise.prototype[Symbol.toStringTag]); +assert (Promise.prototype[Symbol.toStringTag] === undefined); +Promise.prototype[Symbol.toStringTag] = "myStringTag4"; +assert (Object.prototype.toString.call (new Promise (function () {})) === "[object myStringTag4]"); +Promise.prototype[Symbol.toStringTag] = {}; +assert (Promise.prototype.toString.call (new Promise (function () {})) === "[object Promise]"); + +/* Map.prototype @@toStringTag */ +assert (Map.prototype[Symbol.toStringTag] === "Map"); +assert (Object.prototype.toString.call (new Map ()) === "[object Map]"); +assert (Object.prototype.toString.call (Map) === "[object Function]"); + +assert (delete Map.prototype[Symbol.toStringTag]); +assert (Map.prototype[Symbol.toStringTag] === undefined); +Map.prototype[Symbol.toStringTag] = "myStringTag5"; +assert (Map.prototype.toString.call (new Map ()) === "[object myStringTag5]"); +assert (Object.prototype.toString.call (Map) === "[object Function]"); +Map.prototype[Symbol.toStringTag] = {}; +assert (Map.prototype.toString.call (new Map) === "[object Map]"); + +/* JSON @@toStringTag */ +assert (JSON[Symbol.toStringTag] === "JSON"); +assert (Object.prototype.toString.call (JSON) === "[object JSON]"); + +assert (delete JSON[Symbol.toStringTag]); +assert (JSON[Symbol.toStringTag] === undefined); +JSON[Symbol.toStringTag] = "myStringTag6"; +assert (Map.prototype.toString.call (JSON) === "[object myStringTag6]"); +JSON[Symbol.toStringTag] = {}; +assert (Object.prototype.toString.call (JSON) === "[object JSON]"); + +var typedArrayTypes = ["Int8Array", + "Uint8Array", + "Uint8ClampedArray", + "Int16Array", + "Uint16Array", + "Int32Array", + "Uint32Array", + "Float32Array", + "Float64Array"]; + +for (var i = 0; i < typedArrayTypes.length; i++) { + var typedArray = this[typedArrayTypes[i]]; + assert (typedArray.prototype[Symbol.toStringTag] === undefined); + assert (Object.prototype.toString.call (typedArray) === "[object Function]"); + assert (Object.prototype.toString.call (typedArray.prototype) === "[object Object]"); + + var newArray = new typedArray (); + assert (newArray[Symbol.toStringTag] === typedArrayTypes[i]); + assert (Object.prototype.toString.call (newArray) === "[object " + typedArrayTypes[i] + "]"); +}