From 1fd0cac8c9e74d1ff32c81c71bd88db2fb491eba Mon Sep 17 00:00:00 2001 From: Robert Fancsik Date: Mon, 31 Aug 2020 10:03:05 +0200 Subject: [PATCH] Introduce new API function to obtain well-known symbols (#4163) - jerry_get_well_known_symbol JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu --- docs/02.API-REFERENCE.md | 79 ++++++++++++++++ jerry-core/api/jerry.c | 30 ++++++ jerry-core/include/jerryscript-core.h | 21 +++++ jerry-core/lit/lit-magic-strings.h | 4 +- tests/unit-core/test-symbol.c | 126 ++++++++++++++++++++++++++ 5 files changed, 259 insertions(+), 1 deletion(-) diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 219b2777d..a9af3bcc8 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -141,6 +141,25 @@ Container object types: *New in version 2.3*. +## jerry_well_known_symbol_t + +Well-known symbols: + + - JERRY_SYMBOL_HAS_INSTANCE - @@hasInstance well-known symbol + - JERRY_SYMBOL_IS_CONCAT_SPREADABLE - @@isConcatSpreadable well-known symbol + - JERRY_SYMBOL_ITERATOR - @@iterator well-known symbol + - JERRY_SYMBOL_ASYNC_ITERATOR - @@asyncIterator well-known symbol + - JERRY_SYMBOL_MATCH - @@match well-known symbol + - JERRY_SYMBOL_REPLACE - @@replace well-known symbol + - JERRY_SYMBOL_SEARCH - @@search well-known symbol + - JERRY_SYMBOL_SPECIES - @@species well-known symbol + - JERRY_SYMBOL_SPLIT - @@split well-known symbol + - JERRY_SYMBOL_TO_PRIMITIVE - @@toPrimitive well-known symbol + - JERRY_SYMBOL_TO_STRING_TAG - @@toStringTag well-known symbol + - JERRY_SYMBOL_UNSCOPABLES - @@unscopables well-known symbol + + *New in version [[NEXT_RELEASE]]*. + ## jerry_regexp_flags_t RegExp object optional flags: @@ -3917,6 +3936,66 @@ jerry_resolve_or_reject_promise (jerry_value_t promise, These APIs all depend on the es.next profile (or on build options). +## jerry_get_well_known_symbol + +**Summary** + +Get the well-known symbol corresponding to the given [well-known symbol id](#jerry_well_known_symbol_t). + +*Notes*: +- Returned value must be freed with [jerry_release_value](#jerry_release_value) when it + is no longer needed. +- This API depends on a build option (`JERRY_BUILTIN_SYMBOL`) and can be checked + in runtime with the `JERRY_FEATURE_SYMBOL` feature enum value, + see: [jerry_is_feature_enabled](#jerry_is_feature_enabled). +- The es.next profile enables this by default. +- If the symbol support is not enabled an undefined will be returned. + +**Prototype** + +```c +jerry_value_t +jerry_get_well_known_symbol (jerry_well_known_symbol_t symbol); +``` + +- `symbol` - [jerry_well_known_symbol_t](#jerry_well_known_symbol_t) enum value +- return value + - undefined value - if invalid well-known symbol was requested + - well-known symbol value, otherwise + +*New in version [[NEXT_RELEASE]]*. + +**Example** + +[doctest]: # () + +```c +#include "jerryscript.h" + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t array_value = jerry_create_array (5); + jerry_value_t symbol_iterator = jerry_get_well_known_symbol (JERRY_SYMBOL_ITERATOR); + jerry_value_t array_iterator = jerry_get_property (array_value, symbol_iterator); + + // usage of array_iterator + + jerry_release_value (array_iterator); + jerry_release_value (symbol_iterator); + jerry_release_value (array_value); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_well_known_symbol_t](#jerry_well_known_symbol_t) + ## jerry_get_symbol_descriptive_string **Summary** diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 268af6fda..3227e0c24 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -3766,6 +3766,36 @@ jerry_get_promise_state (const jerry_value_t promise) /**< promise object to get #endif /* ENABLED (JERRY_BUILTIN_PROMISE) */ } /* jerry_get_promise_state */ +/** + * Get the well-knwon symbol represented by the given `symbol` enum value. + * + * Note: + * returned value must be freed with jerry_release_value, when it is no longer needed. + * + * @return undefined value - if invalid well-known symbol was requested + * well-known symbol value - otherwise + */ +jerry_value_t +jerry_get_well_known_symbol (jerry_well_known_symbol_t symbol) /**< jerry_well_known_symbol_t enum value */ +{ + jerry_assert_api_available (); + +#if ENABLED (JERRY_ESNEXT) + lit_magic_string_id_t id = (lit_magic_string_id_t) (LIT_GLOBAL_SYMBOL__FISRT + symbol); + + if (!LIT_IS_GLOBAL_SYMBOL (id)) + { + return ECMA_VALUE_UNDEFINED; + } + + return ecma_make_symbol_value (ecma_op_get_global_symbol (id)); +#else /* !ENABLED (JERRY_ESNEXT) */ + JERRY_UNUSED (symbol); + + return ECMA_VALUE_UNDEFINED; +#endif /* ENABLED (JERRY_ESNEXT) */ +} /** jerry_get_well_known_symbol */ + /** * Call the SymbolDescriptiveString ecma builtin operation on the symbol value. * diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index d6b2180ac..00b93ec30 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -650,6 +650,27 @@ jerry_promise_state_t jerry_get_promise_state (const jerry_value_t promise); /** * Symbol functions. */ + +/** + * List of well-known symbols. + */ +typedef enum +{ + JERRY_SYMBOL_HAS_INSTANCE, /**< @@hasInstance well-known symbol */ + JERRY_SYMBOL_IS_CONCAT_SPREADABLE, /**< @@isConcatSpreadable well-known symbol */ + JERRY_SYMBOL_ITERATOR, /**< @@iterator well-known symbol */ + JERRY_SYMBOL_ASYNC_ITERATOR, /**< @@asyncIterator well-known symbol */ + JERRY_SYMBOL_MATCH, /**< @@match well-known symbol */ + JERRY_SYMBOL_REPLACE, /**< @@replace well-known symbol */ + JERRY_SYMBOL_SEARCH, /**< @@search well-known symbol */ + JERRY_SYMBOL_SPECIES, /**< @@species well-known symbol */ + JERRY_SYMBOL_SPLIT, /**< @@split well-known symbol */ + JERRY_SYMBOL_TO_PRIMITIVE, /**< @@toPrimitive well-known symbol */ + JERRY_SYMBOL_TO_STRING_TAG, /**< @@toStringTag well-known symbol */ + JERRY_SYMBOL_UNSCOPABLES, /**< @@unscopables well-known symbol */ +} jerry_well_known_symbol_t; + +jerry_value_t jerry_get_well_known_symbol (jerry_well_known_symbol_t symbol); jerry_value_t jerry_get_symbol_descriptive_string (const jerry_value_t symbol); /** diff --git a/jerry-core/lit/lit-magic-strings.h b/jerry-core/lit/lit-magic-strings.h index d41385509..8baab1cce 100644 --- a/jerry-core/lit/lit-magic-strings.h +++ b/jerry-core/lit/lit-magic-strings.h @@ -48,6 +48,7 @@ typedef enum LIT_INTERNAL_MAGIC_PROMISE_CAPABILITY, /**< PromiseCapability record */ /* List of well known symbols */ LIT_GLOBAL_SYMBOL_HAS_INSTANCE, /**< @@hasInstance well known symbol */ + LIT_GLOBAL_SYMBOL__FISRT = LIT_GLOBAL_SYMBOL_HAS_INSTANCE, /**< first global symbol */ LIT_GLOBAL_SYMBOL_IS_CONCAT_SPREADABLE, /**< @@isConcatSpreadable well known symbol */ LIT_GLOBAL_SYMBOL_ITERATOR, /**< @@iterator well known symbol */ LIT_GLOBAL_SYMBOL_ASYNC_ITERATOR, /**< @@asyncIterator well known symbol */ @@ -59,6 +60,7 @@ typedef enum LIT_GLOBAL_SYMBOL_TO_PRIMITIVE, /**< @@toPrimitive well known symbol */ LIT_GLOBAL_SYMBOL_TO_STRING_TAG, /**< @@toStringTag well known symbol */ LIT_GLOBAL_SYMBOL_UNSCOPABLES, /**< @@unscopables well known symbol */ + LIT_GLOBAL_SYMBOL__LAST = LIT_GLOBAL_SYMBOL_UNSCOPABLES, /**< last global symbol */ LIT_GC_MARK_REQUIRED_MAGIC_STRING__COUNT, /**< number of internal magic strings which will be used as * property names, and their values need to be marked during gc. */ LIT_INTERNAL_MAGIC_STRING_DELETED = LIT_GC_MARK_REQUIRED_MAGIC_STRING__COUNT, /**< special value for @@ -75,7 +77,7 @@ typedef enum /** * Checks whether the given id corresponds to a global symbol */ -#define LIT_IS_GLOBAL_SYMBOL(id) ((id) >= LIT_GLOBAL_SYMBOL_HAS_INSTANCE && (id) <= LIT_GLOBAL_SYMBOL_UNSCOPABLES) +#define LIT_IS_GLOBAL_SYMBOL(id) ((id) >= LIT_GLOBAL_SYMBOL__FISRT && (id) <= LIT_GLOBAL_SYMBOL__LAST) /** * Identifiers of implementation-defined external magic string constants diff --git a/tests/unit-core/test-symbol.c b/tests/unit-core/test-symbol.c index aee0dbc0f..1aed532fc 100644 --- a/tests/unit-core/test-symbol.c +++ b/tests/unit-core/test-symbol.c @@ -191,6 +191,132 @@ main (void) jerry_release_value (error_obj); jerry_release_value (null_value); + const jerry_char_t obj_src[] = "" + "({" + " [Symbol.hasInstance]: 1," + " [Symbol.isConcatSpreadable]: 2," + " [Symbol.iterator]: 3," + " [Symbol.asyncIterator]: 4," + " [Symbol.match]: 5," + " [Symbol.replace]: 6," + " [Symbol.search]: 7," + " [Symbol.species]: 8," + " [Symbol.split]: 9," + " [Symbol.toPrimitive]: 10," + " [Symbol.toStringTag]: 11," + " [Symbol.unscopables]: 12," + "})"; + + const char *symbols[] = + { + "hasInstance", + "isConcatSpreadable", + "iterator", + "asyncIterator", + "match", + "replace", + "search", + "species", + "split", + "toPrimitive", + "toStringTag", + "unscopables", + }; + + jerry_value_t obj = jerry_eval (obj_src, sizeof (obj_src) - 1, JERRY_PARSE_NO_OPTS); + TEST_ASSERT (jerry_value_is_object (obj)); + + jerry_value_t global_obj = jerry_get_global_object (); + jerry_value_t symbol_str = jerry_create_string ((const jerry_char_t *) "Symbol"); + jerry_value_t builtin_symbol = jerry_get_property (global_obj, symbol_str); + TEST_ASSERT (jerry_value_is_object (builtin_symbol)); + + double expected = 1.0; + uint32_t prop_index = 0; + + for (jerry_well_known_symbol_t id = JERRY_SYMBOL_HAS_INSTANCE; + id <= JERRY_SYMBOL_UNSCOPABLES; + id++, expected++, prop_index++) + { + jerry_value_t well_known_symbol = jerry_get_well_known_symbol (id); + + jerry_value_t prop_str = jerry_create_string ((const jerry_char_t *) symbols[prop_index]); + jerry_value_t current_global_symbol = jerry_get_property (builtin_symbol, prop_str); + jerry_release_value (prop_str); + + jerry_value_t relation = jerry_binary_operation (JERRY_BIN_OP_STRICT_EQUAL, + well_known_symbol, + current_global_symbol); + + TEST_ASSERT (jerry_value_is_boolean (relation) + && jerry_get_boolean_value (relation)); + + jerry_release_value (relation); + + jerry_value_t prop_result_wn = jerry_get_property (obj, well_known_symbol); + jerry_value_t prop_result_global = jerry_get_property (obj, current_global_symbol); + + TEST_ASSERT (jerry_value_is_number (prop_result_wn)); + double number_wn = jerry_get_number_value (prop_result_wn); + TEST_ASSERT (number_wn == expected); + + TEST_ASSERT (jerry_value_is_number (prop_result_global)); + double number_global = jerry_get_number_value (prop_result_global); + TEST_ASSERT (number_global == expected); + + jerry_release_value (prop_result_global); + jerry_release_value (prop_result_wn); + jerry_release_value (current_global_symbol); + jerry_release_value (well_known_symbol); + } + + jerry_release_value (builtin_symbol); + + /* Deletion of the 'Symbol' builtin makes the well-known symbols unaccessible from JS context + but the symbols still can be obtained via 'jerry_get_well_known_symbol' */ + const jerry_char_t deleter_src[] = "delete Symbol"; + + jerry_value_t deleter = jerry_eval (deleter_src, sizeof (deleter_src) - 1, JERRY_PARSE_NO_OPTS); + TEST_ASSERT (jerry_value_is_boolean (deleter) + && jerry_get_boolean_value (deleter)); + jerry_release_value (deleter); + + builtin_symbol = jerry_get_property (global_obj, symbol_str); + TEST_ASSERT (jerry_value_is_undefined (builtin_symbol)); + jerry_release_value (builtin_symbol); + + expected = 1.0; + prop_index = 0; + + for (jerry_well_known_symbol_t id = JERRY_SYMBOL_HAS_INSTANCE; + id <= JERRY_SYMBOL_UNSCOPABLES; + id++, expected++, prop_index++) + { + jerry_value_t well_known_symbol = jerry_get_well_known_symbol (id); + jerry_value_t prop_result_wn = jerry_get_property (obj, well_known_symbol); + + TEST_ASSERT (jerry_value_is_number (prop_result_wn)); + double number_wn = jerry_get_number_value (prop_result_wn); + TEST_ASSERT (number_wn == expected); + + jerry_release_value (prop_result_wn); + jerry_release_value (well_known_symbol); + } + + jerry_well_known_symbol_t invalid_symbol = (jerry_well_known_symbol_t) (JERRY_SYMBOL_UNSCOPABLES + 1); + jerry_value_t invalid_well_known_symbol = jerry_get_well_known_symbol (invalid_symbol); + TEST_ASSERT (jerry_value_is_undefined (invalid_well_known_symbol)); + jerry_release_value (invalid_well_known_symbol); + + invalid_symbol = (jerry_well_known_symbol_t) (JERRY_SYMBOL_HAS_INSTANCE - 1); + invalid_well_known_symbol = jerry_get_well_known_symbol (invalid_symbol); + TEST_ASSERT (jerry_value_is_undefined (invalid_well_known_symbol)); + jerry_release_value (invalid_well_known_symbol); + + jerry_release_value (symbol_str); + jerry_release_value (global_obj); + jerry_release_value (obj); + jerry_cleanup (); return 0;