Property key filter API (#4311)

Co-authored-by: Robert Fancsik <frobert@inf.u-szeged.hu>

JerryScript-DCO-1.0-Signed-off-by: Daniel Balla dballa@inf.u-szeged.hu
This commit is contained in:
Daniel Balla
2020-10-29 11:10:21 +01:00
committed by GitHub
parent e681adce05
commit b7ca097436
4 changed files with 462 additions and 0 deletions
+54
View File
@@ -80,6 +80,22 @@ Enum that contains JerryScript **iterator** value types:
*New in version [[NEXT_RELEASE]]*.
## jerry_property_filter_t
Enum that contains JerryScript **property filter** options bits:
- JERRY_PROPERTY_FILTER_ALL - List all property keys independently from key type or property value attributes (equivalent to Reflect.ownKeys call)
- JERRY_PROPERTY_FILTER_TRAVERSE_PROTOTYPE_CHAIN - Include keys from the objects's prototype chain as well
- JERRY_PROPERTY_FILTER_EXLCUDE_NON_CONFIGURABLE - Exclude property key if the property is non-configurable
- JERRY_PROPERTY_FILTER_EXLCUDE_NON_ENUMERABLE - Exclude property key if the property is non-enumerable
- JERRY_PROPERTY_FILTER_EXLCUDE_NON_WRITABLE - Exclude property key if the property is non-writable
- JERRY_PROPERTY_FILTER_EXLCUDE_STRINGS - Exclude property key if it is a string
- JERRY_PROPERTY_FILTER_EXLCUDE_SYMBOLS - Exclude property key if it is a symbol
- JERRY_PROPERTY_FILTER_EXLCUDE_INTEGER_INDICES - Exclude property key if it is an integer index
- JERRY_PROPERTY_FILTER_INTEGER_INDICES_AS_NUMBER - By default integer index property keys are converted to string. Enabling this flags keeps integer index property keys as numbers
*New in version [[NEXT_RELEASE]]*.
## jerry_error_t
Possible types of an error:
@@ -7263,6 +7279,44 @@ best-practice example.
- [jerry_object_native_info_t](#jerry_object_native_info_t)
## jerry_object_get_property_names
**Summary**
Gets the property keys for the given object using the selected filters.
**Prototype**
```c
jerry_value_t
jerry_object_get_property_names (jerry_value_t obj_val,
jerry_property_filter_t filter);
```
- `obj_val` - object value
- `filter` - any combination of [jerry_property_filter_t](#jerry_property_filter_t) options
- return value
- array containing the filtered property keys in successful operation
- error marked with error flag, otherwise
**Example**
```c
{
jerry_value_t global_object = jerry_get_global_object ();
jerry_value_t keys = jerry_object_get_property_names (object, JERRY_PROPERTY_FILTER_ALL);
... // usage of keys
jerry_release_value (keys);
jerry_release_value (global_object);
}
```
**See also**
- [jerry_property_filter_t](#jerry_property_filter_t)
## jerry_foreach_object_property
**Summary**
+180
View File
@@ -3661,6 +3661,186 @@ jerry_foreach_object_property (const jerry_value_t obj_val, /**< object value */
return false;
} /* jerry_foreach_object_property */
/**
* Gets the property keys for the given object using the selected filters.
*
* @return array containing the filtered property keys in successful operation
* value marked with error flag - otherwise
*/
jerry_value_t
jerry_object_get_property_names (const jerry_value_t obj_val, /**< object */
jerry_property_filter_t filter) /**< property filter options */
{
jerry_assert_api_available ();
if (!ecma_is_value_object (obj_val))
{
return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (wrong_args_msg_p)));
}
ecma_object_t *obj_p = ecma_get_object_from_value (obj_val);
ecma_object_t *obj_iter_p = obj_p;
ecma_collection_t *result_p = ecma_new_collection ();
while (true)
{
/* Step 1. Get Object.[[OwnKeys]] */
ecma_collection_t *prop_names_p = ecma_op_object_own_property_keys (obj_iter_p);
#if ENABLED (JERRY_BUILTIN_PROXY)
if (prop_names_p == NULL)
{
return jerry_throw (ECMA_VALUE_ERROR);
}
#endif /* ENABLED (JERRY_BUILTIN_PROXY) */
for (uint32_t i = 0; i < prop_names_p->item_count; i++)
{
ecma_value_t key = prop_names_p->buffer_p[i];
ecma_string_t *key_p = ecma_get_prop_name_from_value (key);
uint32_t index = ecma_string_get_array_index (key_p);
/* Step 2. Filter by key type */
if (filter & (JERRY_PROPERTY_FILTER_EXLCUDE_STRINGS
| JERRY_PROPERTY_FILTER_EXLCUDE_SYMBOLS
| JERRY_PROPERTY_FILTER_EXLCUDE_INTEGER_INDICES))
{
if (ecma_is_value_symbol (key))
{
if (filter & JERRY_PROPERTY_FILTER_EXLCUDE_SYMBOLS)
{
continue;
}
}
else if (index != ECMA_STRING_NOT_ARRAY_INDEX)
{
if ((filter & JERRY_PROPERTY_FILTER_EXLCUDE_INTEGER_INDICES)
|| ((filter & JERRY_PROPERTY_FILTER_EXLCUDE_STRINGS)
&& !(filter & JERRY_PROPERTY_FILTER_INTEGER_INDICES_AS_NUMBER)))
{
continue;
}
}
else if (filter & JERRY_PROPERTY_FILTER_EXLCUDE_STRINGS)
{
continue;
}
}
/* Step 3. Filter property attributes */
if (filter & (JERRY_PROPERTY_FILTER_EXLCUDE_NON_CONFIGURABLE
| JERRY_PROPERTY_FILTER_EXLCUDE_NON_ENUMERABLE
| JERRY_PROPERTY_FILTER_EXLCUDE_NON_WRITABLE))
{
ecma_property_descriptor_t prop_desc;
ecma_value_t status = ecma_op_object_get_own_property_descriptor (obj_iter_p, key_p, &prop_desc);
#if ENABLED (JERRY_BUILTIN_PROXY)
if (ECMA_IS_VALUE_ERROR (status))
{
ecma_collection_free (prop_names_p);
ecma_collection_free (result_p);
return jerry_throw (ECMA_VALUE_ERROR);
}
#endif /* ENABLED (JERRY_BUILTIN_PROXY) */
JERRY_ASSERT (ecma_is_value_true (status));
uint16_t flags = prop_desc.flags;
ecma_free_property_descriptor (&prop_desc);
if ((!(flags & ECMA_PROP_IS_CONFIGURABLE)
&& (filter & JERRY_PROPERTY_FILTER_EXLCUDE_NON_CONFIGURABLE))
|| (!(flags & ECMA_PROP_IS_ENUMERABLE)
&& (filter & JERRY_PROPERTY_FILTER_EXLCUDE_NON_ENUMERABLE))
|| (!(flags & ECMA_PROP_IS_WRITABLE)
&& (filter & JERRY_PROPERTY_FILTER_EXLCUDE_NON_WRITABLE)))
{
continue;
}
}
if (index != ECMA_STRING_NOT_ARRAY_INDEX
&& (filter & JERRY_PROPERTY_FILTER_INTEGER_INDICES_AS_NUMBER))
{
ecma_deref_ecma_string (key_p);
key = ecma_make_uint32_value (index);
}
else
{
ecma_ref_ecma_string (key_p);
}
if ((filter & JERRY_PROPERTY_FILTER_TRAVERSE_PROTOTYPE_CHAIN) && obj_iter_p != obj_p)
{
uint32_t duplicate_idx = 0;
while (duplicate_idx < result_p->item_count)
{
ecma_value_t value = result_p->buffer_p[duplicate_idx];
JERRY_ASSERT (ecma_is_value_prop_name (value) || ecma_is_value_number (value));
if (JERRY_UNLIKELY (ecma_is_value_number (value)))
{
if (ecma_get_number_from_value (value) == ecma_get_number_from_value (key))
{
break;
}
}
else if (ecma_compare_ecma_strings (ecma_get_prop_name_from_value (value), key_p))
{
break;
}
duplicate_idx++;
}
if (duplicate_idx == result_p->item_count)
{
ecma_collection_push_back (result_p, key);
}
}
else
{
ecma_collection_push_back (result_p, key);
}
}
ecma_collection_free (prop_names_p);
/* Step 4: Traverse prototype chain */
jmem_cpointer_t parent_cp = JMEM_CP_NULL;
if (filter & JERRY_PROPERTY_FILTER_TRAVERSE_PROTOTYPE_CHAIN)
{
#if ENABLED (JERRY_BUILTIN_PROXY)
if (ECMA_OBJECT_IS_PROXY (obj_iter_p))
{
ecma_value_t parent = ecma_proxy_object_get_prototype_of (obj_iter_p);
if (ECMA_IS_VALUE_ERROR (parent))
{
ecma_collection_free (result_p);
return jerry_throw (ECMA_VALUE_ERROR);
}
parent_cp = ecma_proxy_object_prototype_to_cp (parent);
}
else
#endif /* ENABLED (JERRY_BUILTIN_PROXY) */
{
parent_cp = ecma_op_ordinary_object_get_prototype_of (obj_iter_p);
}
}
if (parent_cp == JMEM_CP_NULL)
{
break;
}
obj_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, parent_cp);
}
return ecma_op_new_array_object_from_collection (result_p, false);
} /* jerry_object_get_property_names */
/**
* Resolve or reject the promise with an argument.
*
+26
View File
@@ -459,6 +459,30 @@ typedef enum
JERRY_ITERATOR_TYPE_SET, /**< Set iterator */
} jerry_iterator_type_t;
/**
* JerryScript object property filter options.
*/
typedef enum
{
JERRY_PROPERTY_FILTER_ALL = 0, /**< List all property keys independently
* from key type or property value attributes
* (equivalent to Reflect.ownKeys call) */
JERRY_PROPERTY_FILTER_TRAVERSE_PROTOTYPE_CHAIN = (1 << 0), /**< Include keys from the objects's
* prototype chain as well */
JERRY_PROPERTY_FILTER_EXLCUDE_NON_CONFIGURABLE = (1 << 1), /**< Exclude property key if
* the property is non-configurable */
JERRY_PROPERTY_FILTER_EXLCUDE_NON_ENUMERABLE = (1 << 2), /**< Exclude property key if
* the property is non-enumerable */
JERRY_PROPERTY_FILTER_EXLCUDE_NON_WRITABLE = (1 << 3), /**< Exclude property key if
* the property is non-writable */
JERRY_PROPERTY_FILTER_EXLCUDE_STRINGS = (1 << 4), /**< Exclude property key if it is a string */
JERRY_PROPERTY_FILTER_EXLCUDE_SYMBOLS = (1 << 5), /**< Exclude property key if it is a symbol */
JERRY_PROPERTY_FILTER_EXLCUDE_INTEGER_INDICES = (1 << 6), /**< Exclude property key if it is an integer index */
JERRY_PROPERTY_FILTER_INTEGER_INDICES_AS_NUMBER = (1 << 7), /**< By default integer index property keys are
* converted to string. Enabling this flags keeps
* integer index property keys as numbers. */
} jerry_property_filter_t;
jerry_type_t jerry_value_get_type (const jerry_value_t value);
jerry_object_type_t jerry_object_get_type (const jerry_value_t value);
jerry_function_type_t jerry_function_get_type (const jerry_value_t value);
@@ -628,6 +652,8 @@ bool jerry_objects_foreach_by_native_info (const jerry_object_native_info_t *nat
bool jerry_foreach_object_property (const jerry_value_t obj_val, jerry_object_property_foreach_t foreach_p,
void *user_data_p);
jerry_value_t jerry_object_get_property_names (const jerry_value_t obj_val, jerry_property_filter_t filter);
/**
* Promise functions.
*/
@@ -0,0 +1,202 @@
/* 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 "jerryscript-port.h"
#include "jerryscript-port-default.h"
#include "test-common.h"
static const char *prop_names[] =
{
"val1",
"val2",
"val3",
"val4",
"val5",
"37",
"symbol"
};
static jerry_char_t buffer[256] = { 0 };
static void create_and_set_property (const jerry_value_t object, const char *prop_name)
{
jerry_value_t jprop_name = jerry_create_string ((const jerry_char_t *) prop_name);
jerry_value_t ret_val = jerry_set_property (object, jprop_name, jerry_create_undefined ());
jerry_release_value (jprop_name);
jerry_release_value (ret_val);
} /* create_and_set_property */
static void compare_prop_name (const jerry_value_t object, const char *prop_name, uint32_t idx)
{
jerry_value_t name = jerry_get_property_by_index (object, idx);
TEST_ASSERT (jerry_value_is_string (name) || jerry_value_is_number (name));
if (jerry_value_is_string (name))
{
jerry_size_t name_size = jerry_get_string_size (name);
TEST_ASSERT (name_size < sizeof (buffer));
jerry_size_t ret_size = jerry_string_to_char_buffer (name, buffer, sizeof (buffer));
TEST_ASSERT (name_size == ret_size);
buffer[name_size] = '\0';
TEST_ASSERT (strcmp ((const char *) buffer, prop_name) == 0);
}
else
{
TEST_ASSERT ((int) jerry_get_number_value (name) == atoi (prop_name));
}
jerry_release_value (name);
} /* compare_prop_name */
static void define_property (const jerry_value_t object,
const char *prop_name,
jerry_property_descriptor_t *prop_desc_p,
bool is_symbol)
{
jerry_value_t jname = jerry_create_string ((const jerry_char_t *) prop_name);
jerry_value_t ret_val;
if (is_symbol)
{
jerry_value_t symbol = jerry_create_symbol (jname);
ret_val = jerry_define_own_property (object, symbol, prop_desc_p);
jerry_release_value (symbol);
}
else
{
ret_val = jerry_define_own_property (object, jname, prop_desc_p);
}
jerry_release_value (jname);
jerry_release_value (ret_val);
} /* define_property */
int
main (void)
{
if (!jerry_is_feature_enabled (JERRY_FEATURE_SYMBOL))
{
return 0;
}
TEST_INIT ();
jerry_init (JERRY_INIT_EMPTY);
jerry_value_t error_value = jerry_object_get_property_names (jerry_create_undefined (), JERRY_PROPERTY_FILTER_ALL);
TEST_ASSERT (jerry_value_is_error (error_value) && jerry_get_error_type (error_value) == JERRY_ERROR_TYPE);
jerry_release_value (error_value);
jerry_value_t test_object = jerry_create_object ();
create_and_set_property (test_object, prop_names[0]);
create_and_set_property (test_object, prop_names[1]);
jerry_value_t names;
jerry_property_descriptor_t prop_desc;
jerry_init_property_descriptor_fields (&prop_desc);
prop_desc.is_configurable_defined = true;
prop_desc.is_configurable = true;
prop_desc.is_writable_defined = true;
prop_desc.is_writable = true;
prop_desc.is_enumerable_defined = true;
// Test enumerable - non-enumerable filter
prop_desc.is_enumerable = false;
define_property (test_object, prop_names[2], &prop_desc, false);
names = jerry_object_get_property_names (test_object,
JERRY_PROPERTY_FILTER_ALL | JERRY_PROPERTY_FILTER_EXLCUDE_NON_ENUMERABLE);
TEST_ASSERT (jerry_get_array_length (names) == (uint32_t) 2);
jerry_release_value (names);
names = jerry_object_get_property_names (test_object, JERRY_PROPERTY_FILTER_ALL);
TEST_ASSERT (jerry_get_array_length (names) == (uint32_t) 3);
compare_prop_name (names, prop_names[2], 2);
jerry_release_value (names);
prop_desc.is_enumerable = true;
// Test configurable - non-configurable filter
prop_desc.is_configurable = false;
define_property (test_object, prop_names[3], &prop_desc, false);
names = jerry_object_get_property_names (test_object,
JERRY_PROPERTY_FILTER_ALL | JERRY_PROPERTY_FILTER_EXLCUDE_NON_CONFIGURABLE);
TEST_ASSERT (jerry_get_array_length (names) == (uint32_t) 3);
jerry_release_value (names);
names = jerry_object_get_property_names (test_object, JERRY_PROPERTY_FILTER_ALL);
TEST_ASSERT (jerry_get_array_length (names) == (uint32_t) 4);
compare_prop_name (names, prop_names[3], 3);
jerry_release_value (names);
prop_desc.is_configurable = true;
// Test writable - non-writable filter
prop_desc.is_writable = false;
define_property (test_object, prop_names[4], &prop_desc, false);
names = jerry_object_get_property_names (test_object,
JERRY_PROPERTY_FILTER_ALL | JERRY_PROPERTY_FILTER_EXLCUDE_NON_WRITABLE);
TEST_ASSERT (jerry_get_array_length (names) == (uint32_t) 4);
jerry_release_value (names);
names = jerry_object_get_property_names (test_object, JERRY_PROPERTY_FILTER_ALL);
TEST_ASSERT (jerry_get_array_length (names) == (uint32_t) 5);
compare_prop_name (names, prop_names[4], 4);
jerry_release_value (names);
prop_desc.is_writable = true;
// Test all property filter
names = jerry_object_get_property_names (test_object, JERRY_PROPERTY_FILTER_ALL);
jerry_length_t array_len = jerry_get_array_length (names);
TEST_ASSERT (array_len == (uint32_t) 5);
for (uint32_t i = 0; i < array_len; i++)
{
compare_prop_name (names, prop_names[i], i);
}
jerry_release_value (names);
// Test number and string index exclusion
define_property (test_object, prop_names[5], &prop_desc, false);
names = jerry_object_get_property_names (test_object, JERRY_PROPERTY_FILTER_ALL
| JERRY_PROPERTY_FILTER_EXLCUDE_STRINGS
| JERRY_PROPERTY_FILTER_INTEGER_INDICES_AS_NUMBER);
TEST_ASSERT (jerry_get_array_length (names) == (uint32_t) 1);
compare_prop_name (names, prop_names[5], 0);
jerry_release_value (names);
names = jerry_object_get_property_names (test_object,
JERRY_PROPERTY_FILTER_ALL | JERRY_PROPERTY_FILTER_EXLCUDE_INTEGER_INDICES);
TEST_ASSERT (jerry_get_array_length (names) == (uint32_t) 5);
jerry_release_value (names);
// Test prototype chain traversion
names = jerry_object_get_property_names (test_object, JERRY_PROPERTY_FILTER_ALL);
TEST_ASSERT (jerry_get_array_length (names) == (uint32_t) 6);
jerry_release_value (names);
names = jerry_object_get_property_names (test_object,
JERRY_PROPERTY_FILTER_ALL | JERRY_PROPERTY_FILTER_TRAVERSE_PROTOTYPE_CHAIN);
TEST_ASSERT (jerry_get_array_length (names) == (uint32_t) 18);
jerry_release_value (names);
// Test symbol exclusion
define_property (test_object, prop_names[6], &prop_desc, true);
names = jerry_object_get_property_names (test_object,
JERRY_PROPERTY_FILTER_ALL | JERRY_PROPERTY_FILTER_EXLCUDE_SYMBOLS);
TEST_ASSERT (jerry_get_array_length (names) == (uint32_t) 6);
jerry_release_value (names);
names = jerry_object_get_property_names (test_object, JERRY_PROPERTY_FILTER_ALL);
TEST_ASSERT (jerry_get_array_length (names) == (uint32_t) 7);
jerry_release_value (names);
jerry_free_property_descriptor_fields (&prop_desc);
jerry_release_value (test_object);
jerry_cleanup ();
return 0;
} /* main */