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:
@@ -12011,3 +12011,72 @@ main (void)
|
||||
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
@@ -32,6 +32,7 @@
|
||||
#include "ecma-gc.h"
|
||||
#include "ecma-helpers.h"
|
||||
#include "ecma-init-finalize.h"
|
||||
#include "ecma-iterator-object.h"
|
||||
#include "ecma-lex-env.h"
|
||||
#include "lit-char-helpers.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_len);
|
||||
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 */
|
||||
} /* jerry_create_container */
|
||||
|
||||
@@ -6507,6 +6508,119 @@ jerry_get_container_type (const jerry_value_t value) /**< the container object *
|
||||
return JERRY_CONTAINER_TYPE_INVALID;
|
||||
} /* 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 */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
@@ -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";
|
||||
#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
|
||||
/**
|
||||
* Error message, if argument is not a module
|
||||
|
||||
@@ -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;
|
||||
#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
|
||||
extern const char * const ecma_error_not_module_p;
|
||||
extern const char * const ecma_error_unknown_export_p;
|
||||
|
||||
@@ -422,6 +422,7 @@ 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);
|
||||
jerry_value_t jerry_get_array_from_container (jerry_value_t value, bool *is_key_value_p);
|
||||
|
||||
/**
|
||||
* @}
|
||||
|
||||
@@ -34,6 +34,41 @@ static const jerry_object_native_info_t native_info =
|
||||
.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
|
||||
main (void)
|
||||
{
|
||||
@@ -60,6 +95,15 @@ main (void)
|
||||
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 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 (map_str);
|
||||
@@ -122,6 +166,87 @@ main (void)
|
||||
jerry_gc (JERRY_GC_PRESSURE_LOW);
|
||||
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 ();
|
||||
return 0;
|
||||
} /* main */
|
||||
|
||||
Reference in New Issue
Block a user