Add container related API function (#4666)

The new function returns with an array, containing the
elements of the given Container or Container iterator.

JerryScript-DCO-1.0-Signed-off-by: Bela Toth tbela@inf.u-szeged.hu
This commit is contained in:
Tóth Béla
2021-06-16 07:41:13 +02:00
committed by GitHub
parent 5729dd8cec
commit 9471515dae
6 changed files with 321 additions and 1 deletions
+69
View File
@@ -12011,3 +12011,72 @@ main (void)
return 0; return 0;
} }
``` ```
# Container Functions
## jerry_get_array_from_container
**Summary**
Return a new array containing elements from a Container, or a Container Iterator. Sets the is_key_value_p to true.
if the container object contains key-value structure and false if not.
*Notes*
- The return value will be an empty array if the Map/Set or Iterator object was empty or finished.
- 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.
*New in version [[NEXT_RELEASE]]*.
**Prototype**
```c
jerry_value_t
jerry_get_array_from_container(jerry_value_t value,
bool *is_key_value_p);
```
- `value` - Map/Set or iterator object
- `is_key_value` - Will be set to `true` if the given container has key-value pairs, `false` otherwise.
- return value
- jerry_value_t containing an array of values from the Map/Set or iterator object
- Error if the `value` is nor a Container or a Container Iterator.
- `undefined` if the `value` is undefined/null.
**Example**
[doctest]: # ()
```c
#include "jerryscript.h"
int
main (void)
{
jerry_init (JERRY_INIT_EMPTY);
jerry_char_t src[] = "var map = new Map(); map.set(1,2); map.entries()";
jerry_value_t iterable = jerry_eval (src, sizeof (src) - 1, JERRY_PARSE_NO_OPTS);
bool is_key_value_container = false;
jerry_value_t buffer_from_map = jerry_get_array_from_container (iterable, &is_key_value_container);
/*
The buffer_from_map contains two elements: 1 and 2, which is the key/value pair of the only item in the set.
is_key_value set to true, as the original is a key-value structure (a Map Iterator)
*/
jerry_release_value (iterable);
jerry_release_value (buffer_from_map);
jerry_cleanup ();
return 0;
}
```
**See also**
- [jerry_create_container](#jerry_create_container)
- [jerry_container_type_t](#jerry_container_type_t)
+115 -1
View File
@@ -32,6 +32,7 @@
#include "ecma-gc.h" #include "ecma-gc.h"
#include "ecma-helpers.h" #include "ecma-helpers.h"
#include "ecma-init-finalize.h" #include "ecma-init-finalize.h"
#include "ecma-iterator-object.h"
#include "ecma-lex-env.h" #include "ecma-lex-env.h"
#include "lit-char-helpers.h" #include "lit-char-helpers.h"
#include "ecma-literal-storage.h" #include "ecma-literal-storage.h"
@@ -6446,7 +6447,7 @@ jerry_create_container (jerry_container_type_t container_type, /**< Type of the
JERRY_UNUSED (arguments_list_p); JERRY_UNUSED (arguments_list_p);
JERRY_UNUSED (arguments_list_len); JERRY_UNUSED (arguments_list_len);
JERRY_UNUSED (container_type); JERRY_UNUSED (container_type);
return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Containers are disabled"))); return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_container_not_supported_p)));
#endif /* JERRY_BUILTIN_CONTAINER */ #endif /* JERRY_BUILTIN_CONTAINER */
} /* jerry_create_container */ } /* jerry_create_container */
@@ -6507,6 +6508,119 @@ jerry_get_container_type (const jerry_value_t value) /**< the container object *
return JERRY_CONTAINER_TYPE_INVALID; return JERRY_CONTAINER_TYPE_INVALID;
} /* jerry_get_container_type */ } /* jerry_get_container_type */
/**
* Return a new array containing elements from a Container or a Container Iterator.
* Sets the boolean input value to `true` if the container object has key/value pairs.
*
* Note:
* the returned value must be freed with a jerry_release_value call
*
* @return an array of items for maps/sets or their iterators, error otherwise
*/
jerry_value_t
jerry_get_array_from_container (jerry_value_t value, /**< the container or iterator object */
bool *is_key_value_p) /**< [out] is key-value structure */
{
jerry_assert_api_available ();
#if JERRY_BUILTIN_CONTAINER
const char *container_needed = ECMA_ERR_MSG ("Value is not a Container or Iterator");
if (!ecma_is_value_object (value))
{
return jerry_throw (ecma_raise_type_error (container_needed));
}
ecma_object_t *obj_p = ecma_get_object_from_value (value);
if (ecma_get_object_type (obj_p) != ECMA_OBJECT_TYPE_CLASS)
{
return jerry_throw (ecma_raise_type_error (container_needed));
}
ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p;
uint32_t entry_count;
uint8_t entry_size;
uint32_t index = 0;
uint8_t iterator_kind = ECMA_ITERATOR__COUNT;
ecma_value_t *start_p;
*is_key_value_p = false;
if (ext_obj_p->u.cls.type == ECMA_OBJECT_CLASS_MAP_ITERATOR
|| ext_obj_p->u.cls.type == ECMA_OBJECT_CLASS_SET_ITERATOR)
{
ecma_value_t iterated_value = ext_obj_p->u.cls.u3.iterated_value;
if (ecma_is_value_empty (iterated_value))
{
return ecma_op_new_array_object_from_collection (ecma_new_collection (), false);
}
ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) (ecma_get_object_from_value (iterated_value));
ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value);
entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p);
index = ext_obj_p->u.cls.u2.iterator_index;
entry_size = ecma_op_container_entry_size (map_object_p->u.cls.u2.container_id);
start_p = ECMA_CONTAINER_START (container_p);
iterator_kind = ext_obj_p->u.cls.u1.iterator_kind;
}
else if (jerry_get_container_type (value) != JERRY_CONTAINER_TYPE_INVALID)
{
ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, ext_obj_p->u.cls.u3.value);
entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p);
entry_size = ecma_op_container_entry_size (ext_obj_p->u.cls.u2.container_id);
index = 0;
iterator_kind = ECMA_ITERATOR_KEYS;
start_p = ECMA_CONTAINER_START (container_p);
if (ext_obj_p->u.cls.u2.container_id == LIT_MAGIC_STRING_MAP_UL
|| ext_obj_p->u.cls.u2.container_id == LIT_MAGIC_STRING_WEAKMAP_UL)
{
iterator_kind = ECMA_ITERATOR_ENTRIES;
}
}
else
{
return jerry_throw (ecma_raise_type_error (container_needed));
}
*is_key_value_p = (iterator_kind == ECMA_ITERATOR_ENTRIES);
ecma_collection_t *collection_buffer = ecma_new_collection ();
for (uint32_t i = index; i < entry_count; i += entry_size)
{
ecma_value_t *entry_p = start_p + i;
if (ecma_is_value_empty (*entry_p))
{
continue;
}
if (iterator_kind != ECMA_ITERATOR_VALUES)
{
ecma_collection_push_back (collection_buffer, ecma_copy_value_if_not_object (entry_p[0]));
}
if (iterator_kind != ECMA_ITERATOR_KEYS)
{
ecma_collection_push_back (collection_buffer, ecma_copy_value_if_not_object (entry_p[1]));
}
}
return ecma_op_new_array_object_from_collection (collection_buffer, false);
#else /* JERRY_BUILTIN_CONTAINER */
JERRY_UNUSED (value);
JERRY_UNUSED (is_key_value_p);
return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_container_not_supported_p)));
#endif
} /* jerry_get_array_from_container */
/** /**
* @} * @}
*/ */
+7
View File
@@ -76,6 +76,13 @@ const char * const ecma_error_data_view_not_supported_p = "DataView support is d
const char * const ecma_error_bigint_not_supported_p = "BigInt support is disabled"; const char * const ecma_error_bigint_not_supported_p = "BigInt support is disabled";
#endif /* !JERRY_BUILTIN_BIGINT */ #endif /* !JERRY_BUILTIN_BIGINT */
#if !JERRY_BUILTIN_CONTAINER
/**
* Error message, if Container support is disabled
*/
const char * const ecma_error_container_not_supported_p = "Container support is disabled";
#endif /* JERRY_BUILTIN_CONTAINER */
#if JERRY_MODULE_SYSTEM #if JERRY_MODULE_SYSTEM
/** /**
* Error message, if argument is not a module * Error message, if argument is not a module
+4
View File
@@ -51,6 +51,10 @@ extern const char * const ecma_error_data_view_not_supported_p;
extern const char * const ecma_error_bigint_not_supported_p; extern const char * const ecma_error_bigint_not_supported_p;
#endif /* !JERRY_BUILTIN_BIGINT */ #endif /* !JERRY_BUILTIN_BIGINT */
#if !JERRY_BUILTIN_CONTAINER
extern const char * const ecma_error_container_not_supported_p;
#endif /* !JERRY_BUILTIN_CONTAINER */
#if JERRY_MODULE_SYSTEM #if JERRY_MODULE_SYSTEM
extern const char * const ecma_error_not_module_p; extern const char * const ecma_error_not_module_p;
extern const char * const ecma_error_unknown_export_p; extern const char * const ecma_error_unknown_export_p;
+1
View File
@@ -422,6 +422,7 @@ jerry_value_t jerry_create_container (jerry_container_type_t container_type,
const jerry_value_t *arguments_list_p, const jerry_value_t *arguments_list_p,
jerry_length_t arguments_list_len); jerry_length_t arguments_list_len);
jerry_container_type_t jerry_get_container_type (const jerry_value_t value); 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);
/** /**
* @} * @}
+125
View File
@@ -34,6 +34,41 @@ static const jerry_object_native_info_t native_info =
.offset_of_references = 0, .offset_of_references = 0,
}; };
static jerry_value_t
create_array_from_container_handler (const jerry_call_info_t *call_info_p,
const jerry_value_t args_p[],
const jerry_length_t args_count)
{
JERRY_UNUSED (call_info_p);
if (args_count < 2)
{
return jerry_create_undefined ();
}
bool is_key_value_pairs = false;
jerry_value_t result = jerry_get_array_from_container (args_p[0], &is_key_value_pairs);
TEST_ASSERT (is_key_value_pairs == jerry_get_boolean_value (args_p[1]));
return result;
} /* create_array_from_container_handler */
static void
run_eval (const char *source_p)
{
jerry_value_t result = jerry_eval ((const jerry_char_t *) source_p, strlen (source_p), 0);
TEST_ASSERT (!jerry_value_is_error (result));
jerry_release_value (result);
} /* run_eval */
static void
run_eval_error (const char *source_p)
{
jerry_value_t result = jerry_eval ((const jerry_char_t *) source_p, strlen (source_p), 0);
jerry_release_value (result);
} /* run_eval_error */
int int
main (void) main (void)
{ {
@@ -60,6 +95,15 @@ main (void)
jerry_value_t global_weakmap = jerry_get_property (global, weakmap_str); jerry_value_t global_weakmap = jerry_get_property (global, weakmap_str);
jerry_value_t global_weakset = jerry_get_property (global, weakset_str); jerry_value_t global_weakset = jerry_get_property (global, weakset_str);
jerry_value_t function = jerry_create_external_function (create_array_from_container_handler);
jerry_value_t name = jerry_create_string ((const jerry_char_t *) "create_array_from_container");
jerry_value_t res = jerry_set_property (global, name, function);
TEST_ASSERT (!jerry_value_is_error (res));
jerry_release_value (res);
jerry_release_value (name);
jerry_release_value (function);
jerry_release_value (global); jerry_release_value (global);
jerry_release_value (map_str); jerry_release_value (map_str);
@@ -122,6 +166,87 @@ main (void)
jerry_gc (JERRY_GC_PRESSURE_LOW); jerry_gc (JERRY_GC_PRESSURE_LOW);
TEST_ASSERT (global_counter == 1); TEST_ASSERT (global_counter == 1);
run_eval ("function assert(v) {\n"
" if(v !== true)\n"
" throw 'Assertion failed!'\n"
"}");
run_eval ("function test_values(arr1, arr2) {\n"
" assert(Array.isArray(arr1));\n"
" assert(arr1.length == arr2.length);\n"
" for(let i = 0; i < arr1.length; i++) {\n"
" assert(arr1[i] === arr2[i]);\n"
" }\n"
"}\n");
run_eval ("var map = new Map();\n"
"map.set(1, 3.14);\n"
"map.set(2, true);\n"
"map.set(3, 'foo');\n"
"var set = new Set();\n"
"set.add(3.14);\n"
"set.add(true);\n"
"set.add('foo');\n"
"var obj = { x:3, y:'foo'};\n"
"var b_int = 1n;\n"
"var obj_bint_map = new Map();\n"
"obj_bint_map.set(1, obj);\n"
"obj_bint_map.set(2, b_int);\n");
run_eval ("var result = create_array_from_container(map, true);\n"
"test_values(result, [1, 3.14, 2, true, 3, 'foo']);");
run_eval ("var result = create_array_from_container(set, false);\n"
"test_values(result, [3.14, true, 'foo']);");
run_eval ("var result = create_array_from_container(map.entries(), true);\n"
"test_values(result, [1, 3.14, 2, true, 3, 'foo']);");
run_eval ("var result = create_array_from_container(map.keys(), false);\n"
"test_values(result, [1, 2, 3,]);");
run_eval ("var result = create_array_from_container(map.values(), false);\n"
"test_values(result, [3.14, true, 'foo']);");
run_eval ("var result = create_array_from_container(obj_bint_map, true)\n"
"test_values(result, [1, obj, 2, b_int]);");
run_eval ("var map = new Map();\n"
"map.set(1, 1);\n"
"var iter = map.entries();\n"
"iter.next();\n"
"var result = create_array_from_container(iter, true);\n"
"assert(Array.isArray(result));\n"
"assert(result.length == 0);");
run_eval ("var ws = new WeakSet();\n"
"var foo = {};\n"
"var bar = {};\n"
"ws.add(foo);\n"
"ws.add(bar);\n"
"var result = create_array_from_container(ws, false);\n"
"test_values(result, [foo, bar]);\n");
run_eval ("var ws = new WeakMap();\n"
"var foo = {};\n"
"var bar = {};\n"
"ws.set(foo, 37);\n"
"ws.set(bar, 'asd');\n"
"var result = create_array_from_container(ws, true);\n"
"test_values(result, [foo, 37, bar, 'asd']);\n");
run_eval_error ("var iter = null;\n"
"var result = create_array_from_container(iter, false);\n"
"assert(result instanceof Error);");
run_eval_error ("var iter = 3;\n"
"var result = create_array_from_container(iter, false);\n"
"assert(result instanceof Error);");
run_eval_error ("var iter = [3.14, true, 'foo'].entries();\n"
"var result = create_array_from_container(iter, false);\n"
"assert(result instanceof Error);");
jerry_cleanup (); jerry_cleanup ();
return 0; return 0;
} /* main */ } /* main */