diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 4a014e096..eb549ad96 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -478,6 +478,18 @@ typedef struct jerry_context_t jerry_context_t; *New in version 2.0*. +## jerry_container_operation_t + +Enum that contains the supported container operation types + - JERRY_CONTAINER_OP_ADD - Set/WeakSet add operation + - JERRY_CONTAINER_OP_GET - Map/WeakMap get operation + - JERRY_CONTAINER_OP_SET - Map/WeakMap set operation + - JERRY_CONTAINER_OP_HAS - Set/WeakSet/Map/WeakMap has operation + - JERRY_CONTAINER_OP_DELETE - Set/WeakSet/Map/WeakMap delete operation + - JERRY_CONTAINER_OP_SIZE - Set/WeakSet/Map/WeakMap size operation + - JERRY_CONTAINER_OP_CLEAR - Set/Map clear operation + +*New in version [[NEXT_RELEASE]]*. ## jerry_binary_operation_t @@ -12429,3 +12441,78 @@ main (void) - [jerry_create_container](#jerry_create_container) - [jerry_container_type_t](#jerry_container_type_t) + + +## jerry_container_operation + +**Summary** + +Perform container operation on the given operands (add, delete, set, etc.). + +*Note*: +- Returned value must be freed with [jerry_release_value](#jerry_release_value) when it + is no longer needed. +- This API function depends on a build option (`JERRY_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 es.next profile enables this by default. + +**Prototype** + +```c +jerry_value_t +jerry_container_operation (jerry_container_operation_t operation, + jerry_value_t container, + jerry_value_t *arguments, + uint32_t arguments_number) +``` + + - `operation` - container operation + - `container` - this value + - `arguments` - array of arguments + - `arguments_number` - number of arguments + - result if the operation is successful + - error, otherwise + +*New in version [[NEXT_RELEASE]]*. + +**Example** + +[doctest]: # () + +```c +#include "jerryscript.h" + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t map = jerry_create_container (JERRY_CONTAINER_TYPE_MAP, NULL, 0); + jerry_value_t key_str = jerry_create_string ((jerry_char_t *) "number"); + jerry_value_t number = jerry_create_number (10); + jerry_value_t args[2] = {key_str, number}; + + jerry_value_t result = jerry_container_operation (JERRY_CONTAINER_OP_SET, map, args, 2); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_SIZE, map, NULL, 0); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_CLEAR, map, NULL, 0); + jerry_release_value (result); + + jerry_release_value (map); + jerry_release_value (key_str); + jerry_release_value (number); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_create_container](#jerry_create_container) +- [jerry_container_type_t](#jerry_container_type_t) diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index bbf85df4e..386d972a4 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -6708,6 +6708,154 @@ jerry_get_array_from_container (jerry_value_t value, /**< the container or itera #endif } /* jerry_get_array_from_container */ +/** + * Perform container operation on the given operands (add, get, set, has, delete, size, clear). + * + * @return error - if argument is invalid or operation is unsuccessful or unsupported + * result of the container operation - otherwise. + */ +jerry_value_t +jerry_container_operation (jerry_container_operation_t operation, /**< container operation */ + jerry_value_t container, /**< container */ + jerry_value_t *arguments, /**< list of arguments */ + uint32_t arguments_number) /**< number of arguments */ +{ + jerry_assert_api_available (); +#if JERRY_BUILTIN_CONTAINER + if (!ecma_is_value_object (container)) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Container is not an object."))); + } + + ecma_object_t *obj_p = ecma_get_object_from_value (container); + + if (ecma_get_object_type (obj_p) != ECMA_OBJECT_TYPE_CLASS) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Container is not a container object."))); + } + uint16_t type = ((ecma_extended_object_t *) obj_p)->u.cls.u2.container_id; + ecma_extended_object_t *container_object_p = ecma_op_container_get_object (container, type); + + if (container_object_p == NULL) + { + return ecma_create_error_reference_from_context (); + } + + switch (operation) + { + case JERRY_CONTAINER_OP_ADD: + case JERRY_CONTAINER_OP_DELETE: + case JERRY_CONTAINER_OP_GET: + case JERRY_CONTAINER_OP_HAS: + { + if (arguments_number != 1 || ecma_is_value_error_reference (arguments[0])) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_wrong_args_msg_p))); + } + break; + } + case JERRY_CONTAINER_OP_SET: + { + if (arguments_number != 2 + || ecma_is_value_error_reference (arguments[0]) + || ecma_is_value_error_reference (arguments[1])) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_wrong_args_msg_p))); + } + break; + } + case JERRY_CONTAINER_OP_CLEAR: + case JERRY_CONTAINER_OP_SIZE: + { + if (arguments_number != 0) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_wrong_args_msg_p))); + } + break; + } + default: + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_wrong_args_msg_p))); + } + } + + jerry_value_t result; + const char *incorrect_type_call = ECMA_ERR_MSG ("Operator called on incorrect container type"); + + switch (operation) + { + case JERRY_CONTAINER_OP_ADD: + { + if (type == LIT_MAGIC_STRING_MAP_UL || type == LIT_MAGIC_STRING_WEAKMAP_UL) + { + return jerry_throw (ecma_raise_type_error (incorrect_type_call)); + } + result = ecma_op_container_set (container_object_p, arguments[0], arguments[0], type); + break; + } + case JERRY_CONTAINER_OP_GET: + { + if (type == LIT_MAGIC_STRING_SET_UL || type == LIT_MAGIC_STRING_WEAKSET_UL) + { + return jerry_throw (ecma_raise_type_error (incorrect_type_call)); + } + result = ecma_op_container_get (container_object_p, arguments[0], type); + break; + } + case JERRY_CONTAINER_OP_SET: + { + if (type == LIT_MAGIC_STRING_SET_UL || type == LIT_MAGIC_STRING_WEAKSET_UL) + { + return jerry_throw (ecma_raise_type_error (incorrect_type_call)); + } + result = ecma_op_container_set (container_object_p, arguments[0], arguments[1], type); + break; + } + case JERRY_CONTAINER_OP_HAS: + { + result = ecma_op_container_has (container_object_p, arguments[0], type); + break; + } + case JERRY_CONTAINER_OP_DELETE: + { + if (type == LIT_MAGIC_STRING_WEAKMAP_UL || type == LIT_MAGIC_STRING_WEAKSET_UL) + { + result = ecma_op_container_delete_weak (container_object_p, arguments[0], type); + break; + } + result = ecma_op_container_delete (container_object_p, arguments[0], type); + break; + } + case JERRY_CONTAINER_OP_SIZE: + { + result = ecma_op_container_size (container_object_p); + break; + } + case JERRY_CONTAINER_OP_CLEAR: + { + if (type == LIT_MAGIC_STRING_WEAKSET_UL || type == LIT_MAGIC_STRING_WEAKMAP_UL) + { + return jerry_throw (ecma_raise_type_error (incorrect_type_call)); + } + result = ecma_op_container_clear (container_object_p); + break; + } + default: + { + result = jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Unsupported container operation"))); + break; + } + } + return jerry_return (result); +#else /* JERRY_BUILTIN_CONTAINER */ + JERRY_UNUSED (operation); + JERRY_UNUSED (container); + JERRY_UNUSED (arguments); + JERRY_UNUSED (arguments_number); + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_container_not_supported_p))); +#endif /* JERRY_BUILTIN_CONTAINER */ +} /* jerry_container_operation */ + /** * @} */ diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index 386dd5a00..6aa6ae510 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -434,6 +434,10 @@ jerry_value_t jerry_create_container (jerry_container_type_t container_type, jerry_length_t arguments_list_len); jerry_container_type_t jerry_get_container_type (const jerry_value_t value); jerry_value_t jerry_get_array_from_container (jerry_value_t value, bool *is_key_value_p); +jerry_value_t jerry_container_operation (jerry_container_operation_t operation, + jerry_value_t container, + jerry_value_t *arguments, + uint32_t arguments_number); /** * @} diff --git a/jerry-core/include/jerryscript-types.h b/jerry-core/include/jerryscript-types.h index df070a966..320e1000e 100644 --- a/jerry-core/include/jerryscript-types.h +++ b/jerry-core/include/jerryscript-types.h @@ -749,6 +749,20 @@ typedef enum JERRY_CONTAINER_TYPE_WEAKSET, /**< WeakSet type */ } jerry_container_type_t; +/** + * Container operations + */ +typedef enum +{ + JERRY_CONTAINER_OP_ADD, /**< Set/WeakSet add operation */ + JERRY_CONTAINER_OP_GET, /**< Map/WeakMap get operation */ + JERRY_CONTAINER_OP_SET, /**< Map/WeakMap set operation */ + JERRY_CONTAINER_OP_HAS, /**< Set/WeakSet/Map/WeakMap has operation */ + JERRY_CONTAINER_OP_DELETE, /**< Set/WeakSet/Map/WeakMap delete operation */ + JERRY_CONTAINER_OP_SIZE, /**< Set/WeakSet/Map/WeakMap size operation */ + JERRY_CONTAINER_OP_CLEAR, /**< Set/Map clear operation */ +} jerry_container_operation_t; + /** * @} */ diff --git a/tests/unit-core/CMakeLists.txt b/tests/unit-core/CMakeLists.txt index 8699b79d5..f8f288bc8 100644 --- a/tests/unit-core/CMakeLists.txt +++ b/tests/unit-core/CMakeLists.txt @@ -44,6 +44,7 @@ set(SOURCE_UNIT_TEST_MAIN_MODULES test-backtrace.c test-bigint.c test-container.c + test-container-operation.c test-context-data.c test-dataview.c test-date-helpers.c diff --git a/tests/unit-core/test-container-operation.c b/tests/unit-core/test-container-operation.c new file mode 100644 index 000000000..50e72497b --- /dev/null +++ b/tests/unit-core/test-container-operation.c @@ -0,0 +1,183 @@ +/* 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; + } + + // Map container tests + jerry_value_t map = jerry_create_container (JERRY_CONTAINER_TYPE_MAP, NULL, 0); + TEST_ASSERT (jerry_get_container_type (map) == JERRY_CONTAINER_TYPE_MAP); + + jerry_value_t key_str = jerry_create_string ((jerry_char_t *) "number"); + jerry_value_t number = jerry_create_number (10); + jerry_value_t args[2] = {key_str, number}; + jerry_value_t result = jerry_container_operation (JERRY_CONTAINER_OP_SET, map, args, 2); + TEST_ASSERT (!jerry_value_is_error (result)); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_GET, map, &key_str, 1); + TEST_ASSERT (jerry_get_number_value (result) == 10); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_HAS, map, &key_str, 1); + TEST_ASSERT (jerry_value_is_true (result)); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_SIZE, map, NULL, 0); + TEST_ASSERT (jerry_get_number_value (result) == 1); + jerry_release_value (result); + + key_str = jerry_create_string ((jerry_char_t *) "number2"); + number = jerry_create_number (11); + jerry_value_t args2[2] = {key_str, number}; + result = jerry_container_operation (JERRY_CONTAINER_OP_SET, map, args2, 2); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_SIZE, map, NULL, 0); + TEST_ASSERT (jerry_get_number_value (result) == 2); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_DELETE, map, &key_str, 1); + TEST_ASSERT (jerry_value_is_true (result)); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_SIZE, map, NULL, 0); + TEST_ASSERT (jerry_get_number_value (result) == 1); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_CLEAR, map, NULL, 0); + TEST_ASSERT (jerry_value_is_undefined (result)); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_SIZE, map, NULL, 0); + TEST_ASSERT (jerry_get_number_value (result) == 0); + jerry_release_value (result); + + // Set container tests + number = jerry_create_number (10); + jerry_value_t set = jerry_create_container (JERRY_CONTAINER_TYPE_SET, NULL, 0); + TEST_ASSERT (jerry_get_container_type (set) == JERRY_CONTAINER_TYPE_SET); + result = jerry_container_operation (JERRY_CONTAINER_OP_ADD, set, &number, 1); + TEST_ASSERT (!jerry_value_is_error (result)); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_HAS, set, &number, 1); + TEST_ASSERT (jerry_value_is_true (result)); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_SIZE, set, NULL, 0); + TEST_ASSERT (jerry_get_number_value (result) == 1); + jerry_release_value (result); + + number = jerry_create_number (11); + result = jerry_container_operation (JERRY_CONTAINER_OP_ADD, set, &number, 1); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_SIZE, set, NULL, 0); + TEST_ASSERT (jerry_get_number_value (result) == 2); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_DELETE, set, &number, 1); + TEST_ASSERT (jerry_value_is_true (result)); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_SIZE, set, NULL, 0); + TEST_ASSERT (jerry_get_number_value (result) == 1); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_CLEAR, set, NULL, 0); + TEST_ASSERT (jerry_value_is_undefined (result)); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_SIZE, set, NULL, 0); + TEST_ASSERT (jerry_get_number_value (result) == 0); + jerry_release_value (result); + jerry_release_value (set); + + // WeakMap contanier tests + number = jerry_create_number (10); + jerry_value_t weak_map = jerry_create_container (JERRY_CONTAINER_TYPE_WEAKMAP, NULL, 0); + TEST_ASSERT (jerry_get_container_type (weak_map) == JERRY_CONTAINER_TYPE_WEAKMAP); + + jerry_value_t obj = jerry_create_object (); + number = jerry_create_number (10); + jerry_value_t args4[2] = {obj, number}; + result = jerry_container_operation (JERRY_CONTAINER_OP_SET, weak_map, args4, 2); + TEST_ASSERT (!jerry_value_is_error (result)); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_HAS, weak_map, &obj, 1); + TEST_ASSERT (jerry_value_is_true (result)); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_DELETE, weak_map, &obj, 1); + TEST_ASSERT (jerry_value_is_true (result)); + jerry_release_value (result); + jerry_release_value (weak_map); + + // WeakSet contanier tests, + jerry_value_t weak_set = jerry_create_container (JERRY_CONTAINER_TYPE_WEAKSET, NULL, 0); + TEST_ASSERT (jerry_get_container_type (weak_set) == JERRY_CONTAINER_TYPE_WEAKSET); + + result = jerry_container_operation (JERRY_CONTAINER_OP_ADD, weak_set, &obj, 1); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_HAS, weak_set, &obj, 1); + TEST_ASSERT (jerry_value_is_true (result)); + jerry_release_value (result); + + result = jerry_container_operation (JERRY_CONTAINER_OP_DELETE, weak_set, &obj, 1); + TEST_ASSERT (jerry_value_is_true (result)); + jerry_release_value (result); + jerry_release_value (weak_set); + + // container is not a object + jerry_value_t empty_val = jerry_create_undefined (); + result = jerry_container_operation (JERRY_CONTAINER_OP_SET, empty_val, args, 2); + TEST_ASSERT (jerry_value_is_error (result)); + jerry_release_value (result); + + // arguments is a error + const char * const error_message_p = "Random error."; + jerry_value_t error_val = jerry_create_error (JERRY_ERROR_RANGE, (const jerry_char_t *) error_message_p); + jerry_value_t args3[2] = { error_val, error_val }; + result = jerry_container_operation (JERRY_CONTAINER_OP_SET, map, args3, 2); + TEST_ASSERT (jerry_value_is_error (result)); + jerry_release_value (result); + jerry_release_value (error_val); + jerry_release_value (map); + + jerry_release_value (key_str); + jerry_release_value (number); + jerry_release_value (obj); + jerry_cleanup (); + return 0; + +} /* main */