Implement container operators API function (#4697)

Basically re-opening and updating #4275

Co-authored-by: bence gabor kis <kisbg@inf.u-szeged.hu>
JerryScript-DCO-1.0-Signed-off-by: Bela Toth tbela@inf.u-szeged.hu

Co-authored-by: bence gabor kis <kisbg@inf.u-szeged.hu>
This commit is contained in:
Tóth Béla
2021-07-27 12:25:52 +02:00
committed by GitHub
parent d69fe41085
commit d99905aca6
6 changed files with 437 additions and 0 deletions
+87
View File
@@ -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)
+148
View File
@@ -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 */
/**
* @}
*/
+4
View File
@@ -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);
/**
* @}
+14
View File
@@ -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;
/**
* @}
*/
+1
View File
@@ -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
+183
View File
@@ -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 */