From 9509c3da853089fac41e4ab067b913c5be016907 Mon Sep 17 00:00:00 2001 From: Daniel Balla Date: Tue, 10 Mar 2020 18:10:56 +0100 Subject: [PATCH] Add Map, Set, WeakMap, WeakSet basic functionality to the API (#3502) JerryScript-DCO-1.0-Signed-off-by: Daniel Balla dballa@inf.u-szeged.hu --- docs/02.API-REFERENCE.md | 139 ++++++++++++++++++++++++ jerry-core/api/jerry.c | 148 ++++++++++++++++++++++++++ jerry-core/include/jerryscript-core.h | 20 ++++ tests/unit-core/test-container.c | 86 +++++++++++++++ 4 files changed, 393 insertions(+) create mode 100644 tests/unit-core/test-container.c diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 925bb5ee8..31bb296cb 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -71,8 +71,25 @@ Possible compile time enabled feature types: - JERRY_FEATURE_SYMBOL - symbol support - JERRY_FEATURE_DATAVIEW - DataView support - JERRY_FEATURE_PROXY - Proxy support + - JERRY_FEATURE_MAP - Map support + - JERRY_FEATURE_SET - Set support + - JERRY_FEATURE_WEAKMAP - WeakMap support + - JERRY_FEATURE_WEAKSET - WeakSet support *New in version 2.0*. +*Changed in version 2.3* : Added `JERRY_FEATURE_WEAKMAP`, `JERRY_FEATURE_WEAKSET` values. + +## jerry_container_type_t + +Container object types: + + - JERRY_CONTAINER_TYPE_INVALID - Invalid container + - JERRY_CONTAINER_TYPE_MAP - Map type + - JERRY_CONTAINER_TYPE_SET - Set type + - JERRY_CONTAINER_TYPE_WEAKMAP - WeakMap type + - JERRY_CONTAINER_TYPE_WEAKSET - WeakSet type + + *New in version 2.3*. ## jerry_regexp_flags_t @@ -2072,6 +2089,64 @@ main (void) - [jerry_create_typedarray](#jerry_create_typedarray) +## jerry_get_container_type + +**Summary** + +Checks whether the given `jerry_value_t` is the given `jerry_container_type_t` type container object. + +*Notes* +- This API function depends on a build option (`JERRY_ES2015_BUILTIN_CONTAINER`) and can be checked + runtime with the `JERRY_FEATURE_MAP, JERRY_FEATURE_SET, JERRY_FEATURE_WEAKMAP, JERRY_FEATURE_WEAKSET` + feature enum values. + see: [jerry_is_feature_enabled](#jerry_is_feature_enabled). +- The ES2015-subset profile enables this by default. + +*New in version 2.3*. + +**Prototype** + +```c +jerry_container_type_t +jerry_get_container_type (const jerry_value_t value) +``` + +- `value` - Container object +- return value + - The corresponding enum value of `jerry_container_type_t`, or `JERRY_CONTAINER_TYPE_INVALID` if the container + was not a valid container object. +**Example** + +[doctest]: # () + +```c +#include "jerryscript.h" +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t value = jerry_create_container (JERRY_CONTAINER_TYPE_MAP, NULL, 0); + + if (jerry_get_container_type (value) == JERRY_CONTAINER_TYPE_MAP) + { + /* "value" is a map. */ + } + + jerry_release_value (value); + + jerry_cleanup (); + + return 0; +} +``` + +**See also** + +- [jerry_create_container](#jerry_create_container) +- [jerry_container_type_t](#jerry_container_type_t) + + ## jerry_value_is_undefined **Summary** @@ -4802,6 +4877,70 @@ jerry_create_typedarray_for_arraybuffer_sz (jerry_typedarray_type_t type_name, - [jerry_release_value](#jerry_release_value) +## jerry_create_container + +**Summary** + +Create a jerry_value_t representing a given type container object. + +*Notes*: +- This method is expected to work the same way as the JavaScript Map constructor. +- 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_ES2015_BUILTIN_CONTAINER`) and can be checked + in runtime with the `JERRY_FEATURE_MAP, JERRY_FEATURE_SET, JERRY_FEATURE_WEAKMAP, JERRY_FEATURE_WEAKSET` + feature enum values. + see: [jerry_is_feature_enabled](#jerry_is_feature_enabled). +- The ES2015-subset profile enables this by default. + +*New in version 2.3*. + +**Prototype** + +```c +jerry_value_t +jerry_create_container (jerry_container_type_t container_type, + const jerry_value_t *arguments_list_p, + jerry_length_t arguments_list_len); +``` + +- `container_type` - Type of the container to be created, see `jerry_container_type_t`. +- `arguments_list_p` - The arguments passed to the container constructor to be inserted to the container. +- `arguments_list_len` - The length of the above arguments. +- return value - the new container object as a `jerry_value_t` + +**Example** + +[doctest]: # () + +```c +#include "jerryscript.h" + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + jerry_char_t src[] = "[1,2,3,4].entries()"; + jerry_value_t iterable = jerry_eval (src, sizeof (src) - 1, JERRY_PARSE_NO_OPTS); + + jerry_value_t map = jerry_create_container (JERRY_CONTAINER_TYPE_MAP, &iterable, 1); + jerry_release_value (iterable); + + // use the Map + + jerry_release_value (map); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_container_type_t](#jerry_container_type_t) +- [jerry_get_container_type](#jerry_get_container_type) + + ## jerry_create_undefined **Summary** diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 8e8694e0f..7dd8da276 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -22,6 +22,7 @@ #include "ecma-builtin-helpers.h" #include "ecma-builtins.h" #include "ecma-comparison.h" +#include "ecma-container-object.h" #include "ecma-dataview-object.h" #include "ecma-exceptions.h" #include "ecma-eval.h" @@ -1001,6 +1002,18 @@ jerry_is_feature_enabled (const jerry_feature_t feature) /**< feature to check * #if ENABLED (JERRY_LOGGING) || feature == JERRY_FEATURE_LOGGING #endif /* ENABLED (JERRY_LOGGING) */ +#if ENABLED (JERRY_ES2015_BUILTIN_MAP) + || feature == JERRY_FEATURE_MAP +#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */ +#if ENABLED (JERRY_ES2015_BUILTIN_SET) + || feature == JERRY_FEATURE_SET +#endif /* ENABLED (JERRY_ES2015_BUILTIN_SET) */ +#if ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) + || feature == JERRY_FEATURE_WEAKMAP +#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) */ +#if ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) + || feature == JERRY_FEATURE_WEAKSET +#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) */ ); } /* jerry_is_feature_enabled */ @@ -4376,6 +4389,141 @@ jerry_json_stringify (const jerry_value_t object_to_stringify) /**< a jerry_obje #endif /* ENABLED (JERRY_BUILTIN_JSON) */ } /* jerry_json_stringify */ +/** + * Create a container type specified in jerry_container_type_t. + * The container can be created with a list of arguments, which will be passed to the container constructor to be + * inserted to the container. + * + * Note: + * The returned value must be freed with jerry_release_value + * @return jerry_value_t representing a container with the given type. + */ +jerry_value_t +jerry_create_container (jerry_container_type_t container_type, /**< Type of the container */ + const jerry_value_t *arguments_list_p, /**< arguments list */ + jerry_length_t arguments_list_len) /**< Length of arguments list */ +{ + jerry_assert_api_available (); + +#if ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) + for (jerry_length_t i = 0; i < arguments_list_len; i++) + { + if (ecma_is_value_error_reference (arguments_list_p[i])) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (error_value_msg_p))); + } + } + + switch (container_type) + { +#if ENABLED (JERRY_ES2015_BUILTIN_MAP) + case JERRY_CONTAINER_TYPE_MAP: + { + return ecma_op_container_create (arguments_list_p, + arguments_list_len, + LIT_MAGIC_STRING_MAP_UL, + ECMA_BUILTIN_ID_MAP_PROTOTYPE); + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */ +#if ENABLED (JERRY_ES2015_BUILTIN_SET) + case JERRY_CONTAINER_TYPE_SET: + { + return ecma_op_container_create (arguments_list_p, + arguments_list_len, + LIT_MAGIC_STRING_SET_UL, + ECMA_BUILTIN_ID_SET_PROTOTYPE); + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_SET) */ +#if ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) + case JERRY_CONTAINER_TYPE_WEAKMAP: + { + return ecma_op_container_create (arguments_list_p, + arguments_list_len, + LIT_MAGIC_STRING_WEAKMAP_UL, + ECMA_BUILTIN_ID_WEAKMAP_PROTOTYPE); + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) */ +#if ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) + case JERRY_CONTAINER_TYPE_WEAKSET: + { + return ecma_op_container_create (arguments_list_p, + arguments_list_len, + LIT_MAGIC_STRING_WEAKSET_UL, + ECMA_BUILTIN_ID_WEAKSET_PROTOTYPE); + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) */ + default: + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Invalid container type."))); + } + } +#else /* !ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) */ + JERRY_UNUSED (arguments_list_p); + JERRY_UNUSED (arguments_list_len); + JERRY_UNUSED (container_type); + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Containers are disabled."))); +#endif /* ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) */ +} /* jerry_create_container */ + +/** + * Get the type of the given container object. + * + * @return Corresponding type to the given container object. + */ +jerry_container_type_t +jerry_get_container_type (const jerry_value_t value) /**< the container object */ +{ + jerry_assert_api_available (); + +#if ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) + if (ecma_is_value_object (value)) + { + ecma_object_t *obj_p = ecma_get_object_from_value (value); + + if (ecma_get_object_type (obj_p) == ECMA_OBJECT_TYPE_CLASS) + { + uint16_t type = ((ecma_extended_object_t *) obj_p)->u.class_prop.class_id; + + switch (type) + { +#if ENABLED (JERRY_ES2015_BUILTIN_MAP) + case LIT_MAGIC_STRING_MAP_UL: + { + return JERRY_CONTAINER_TYPE_MAP; + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_MAP) */ +#if ENABLED (JERRY_ES2015_BUILTIN_SET) + case LIT_MAGIC_STRING_SET_UL: + { + return JERRY_CONTAINER_TYPE_SET; + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_SET) */ +#if ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) + case LIT_MAGIC_STRING_WEAKMAP_UL: + { + return JERRY_CONTAINER_TYPE_WEAKMAP; + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKMAP) */ +#if ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) + case LIT_MAGIC_STRING_WEAKSET_UL: + { + return JERRY_CONTAINER_TYPE_WEAKSET; + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_WEAKSET) */ + default: + { + return JERRY_CONTAINER_TYPE_INVALID; + } + } + } + } + +#else /* !ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) */ + JERRY_UNUSED (value); +#endif /* ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) */ + return JERRY_CONTAINER_TYPE_INVALID; +} /* jerry_get_container_type */ + /** * @} */ diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index 1cfbf386f..b71cc355d 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -100,6 +100,10 @@ typedef enum JERRY_FEATURE_SYMBOL, /**< symbol support */ JERRY_FEATURE_DATAVIEW, /**< DataView support */ JERRY_FEATURE_PROXY, /**< Proxy support */ + JERRY_FEATURE_MAP, /**< Map support */ + JERRY_FEATURE_SET, /**< Set support */ + JERRY_FEATURE_WEAKMAP, /**< WeakMap support */ + JERRY_FEATURE_WEAKSET, /**< WeakSet support */ JERRY_FEATURE__COUNT /**< number of features. NOTE: must be at the end of the list */ } jerry_feature_t; @@ -669,6 +673,18 @@ typedef enum JERRY_TYPEDARRAY_FLOAT64, } jerry_typedarray_type_t; +/** + * Container types. + */ +typedef enum +{ + JERRY_CONTAINER_TYPE_INVALID = 0, /**< Invalid container */ + JERRY_CONTAINER_TYPE_MAP, /**< Map type */ + JERRY_CONTAINER_TYPE_SET, /**< Set type */ + JERRY_CONTAINER_TYPE_WEAKMAP, /**< WeakMap type */ + JERRY_CONTAINER_TYPE_WEAKSET, /**< WeakSet type */ +} jerry_container_type_t; + bool jerry_value_is_typedarray (jerry_value_t value); jerry_value_t jerry_create_typedarray (jerry_typedarray_type_t type_name, jerry_length_t length); jerry_value_t jerry_create_typedarray_for_arraybuffer_sz (jerry_typedarray_type_t type_name, @@ -684,6 +700,10 @@ jerry_value_t jerry_get_typedarray_buffer (jerry_value_t value, jerry_length_t *byte_length); jerry_value_t jerry_json_parse (const jerry_char_t *string_p, jerry_size_t string_size); jerry_value_t jerry_json_stringify (const jerry_value_t object_to_stringify); +jerry_value_t jerry_create_container (jerry_container_type_t container_type, + const jerry_value_t *arguments_list_p, + jerry_length_t arguments_list_len); +jerry_container_type_t jerry_get_container_type (const jerry_value_t value); /** * @} diff --git a/tests/unit-core/test-container.c b/tests/unit-core/test-container.c new file mode 100644 index 000000000..031f2b427 --- /dev/null +++ b/tests/unit-core/test-container.c @@ -0,0 +1,86 @@ +/* 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. + */ + +#include "jerryscript.h" +#include "test-common.h" + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + if (!jerry_is_feature_enabled (JERRY_FEATURE_MAP) + || !jerry_is_feature_enabled (JERRY_FEATURE_SET) + || !jerry_is_feature_enabled (JERRY_FEATURE_WEAKMAP) + || !jerry_is_feature_enabled (JERRY_FEATURE_WEAKSET)) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Containers are disabled!\n"); + jerry_cleanup (); + return 0; + } + + jerry_value_t instance_check; + jerry_value_t global = jerry_get_global_object (); + jerry_value_t map_str = jerry_create_string ((jerry_char_t *) "Map"); + jerry_value_t set_str = jerry_create_string ((jerry_char_t *) "Set"); + jerry_value_t weakmap_str = jerry_create_string ((jerry_char_t *) "WeakMap"); + jerry_value_t weakset_str = jerry_create_string ((jerry_char_t *) "WeakSet"); + jerry_value_t global_map = jerry_get_property (global, map_str); + jerry_value_t global_set = jerry_get_property (global, set_str); + jerry_value_t global_weakmap = jerry_get_property (global, weakmap_str); + jerry_value_t global_weakset = jerry_get_property (global, weakset_str); + + jerry_release_value (global); + + jerry_release_value (map_str); + jerry_release_value (set_str); + jerry_release_value (weakmap_str); + jerry_release_value (weakset_str); + + jerry_value_t empty_map = jerry_create_container (JERRY_CONTAINER_TYPE_MAP, NULL, 0); + TEST_ASSERT (jerry_get_container_type (empty_map) == JERRY_CONTAINER_TYPE_MAP); + instance_check = jerry_binary_operation (JERRY_BIN_OP_INSTANCEOF, empty_map, global_map); + TEST_ASSERT (jerry_get_boolean_value (instance_check)); + jerry_release_value (instance_check); + jerry_release_value (global_map); + jerry_release_value (empty_map); + + jerry_value_t empty_set = jerry_create_container (JERRY_CONTAINER_TYPE_SET, NULL, 0); + TEST_ASSERT (jerry_get_container_type (empty_set) == JERRY_CONTAINER_TYPE_SET); + instance_check = jerry_binary_operation (JERRY_BIN_OP_INSTANCEOF, empty_set, global_set); + TEST_ASSERT (jerry_get_boolean_value (instance_check)); + jerry_release_value (instance_check); + jerry_release_value (global_set); + jerry_release_value (empty_set); + + jerry_value_t empty_weakmap = jerry_create_container (JERRY_CONTAINER_TYPE_WEAKMAP, NULL, 0); + TEST_ASSERT (jerry_get_container_type (empty_weakmap) == JERRY_CONTAINER_TYPE_WEAKMAP); + instance_check = jerry_binary_operation (JERRY_BIN_OP_INSTANCEOF, empty_weakmap, global_weakmap); + TEST_ASSERT (jerry_get_boolean_value (instance_check)); + jerry_release_value (instance_check); + jerry_release_value (global_weakmap); + jerry_release_value (empty_weakmap); + + jerry_value_t empty_weakset = jerry_create_container (JERRY_CONTAINER_TYPE_WEAKSET, NULL, 0); + TEST_ASSERT (jerry_get_container_type (empty_weakset) == JERRY_CONTAINER_TYPE_WEAKSET); + instance_check = jerry_binary_operation (JERRY_BIN_OP_INSTANCEOF, empty_weakset, global_weakset); + TEST_ASSERT (jerry_get_boolean_value (instance_check)); + jerry_release_value (instance_check); + jerry_release_value (global_weakset); + jerry_release_value (empty_weakset); + + jerry_cleanup (); + return 0; +} /* main */