mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
Implement Proxy object [[OwnPropertyKeys]] internal method (#3639)
The algorithm is based on ECMA-262 v6, 9.5.12 JerryScript-DCO-1.0-Signed-off-by: Adam Szilagyi aszilagy@inf.u-szeged.hu
This commit is contained in:
parent
60db840cf4
commit
4342c9ea6f
@ -198,7 +198,7 @@ ecma_builtin_reflect_dispatch_routine (uint16_t builtin_routine_id, /**< built-i
|
||||
return ecma_raise_type_error (ECMA_ERR_MSG ("Reflect.construct requires the second argument be an object"));
|
||||
}
|
||||
|
||||
ecma_collection_t *coll_p = ecma_op_create_list_from_array_like (arguments_list[1]);
|
||||
ecma_collection_t *coll_p = ecma_op_create_list_from_array_like (arguments_list[1], false);
|
||||
|
||||
if (coll_p == NULL)
|
||||
{
|
||||
|
||||
@ -977,7 +977,9 @@ ecma_op_to_length (ecma_value_t value, /**< ecma value */
|
||||
* NULL otherwise
|
||||
*/
|
||||
ecma_collection_t *
|
||||
ecma_op_create_list_from_array_like (ecma_value_t arr) /**< array value */
|
||||
ecma_op_create_list_from_array_like (ecma_value_t arr, /**< array value */
|
||||
bool prop_names_only) /**< true - accept only property names
|
||||
false - otherwise */
|
||||
{
|
||||
/* 1. */
|
||||
JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (arr));
|
||||
@ -1010,6 +1012,15 @@ ecma_op_create_list_from_array_like (ecma_value_t arr) /**< array value */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (prop_names_only
|
||||
&& !ecma_is_value_prop_name (next))
|
||||
{
|
||||
ecma_free_value (next);
|
||||
ecma_collection_free (list_ptr);
|
||||
ecma_raise_type_error (ECMA_ERR_MSG ("Property name is neither Symbol nor String."));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ecma_collection_push_back (list_ptr, next);
|
||||
}
|
||||
|
||||
|
||||
@ -52,7 +52,7 @@ ecma_value_t ecma_op_to_object (ecma_value_t value);
|
||||
ecma_value_t ecma_op_to_integer (ecma_value_t value, ecma_number_t *number_p);
|
||||
ecma_value_t ecma_op_to_length (ecma_value_t value, uint32_t *length);
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
ecma_collection_t *ecma_op_create_list_from_array_like (ecma_value_t arr);
|
||||
ecma_collection_t *ecma_op_create_list_from_array_like (ecma_value_t arr, bool prop_names_only);
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
ecma_object_t *ecma_op_from_property_descriptor (const ecma_property_descriptor_t *src_prop_desc_p);
|
||||
|
||||
@ -1131,6 +1131,119 @@ ecma_proxy_object_enumerate (ecma_object_t *obj_p) /**< proxy object */
|
||||
return ecma_raise_type_error (ECMA_ERR_MSG ("UNIMPLEMENTED: Proxy.[[Enumerate]]"));
|
||||
} /* ecma_proxy_object_enumerate */
|
||||
|
||||
/**
|
||||
* Helper method for the Proxy object [[OwnPropertyKeys]] operation
|
||||
*
|
||||
* See also:
|
||||
* ECMAScript v6, 9.5.12 steps 21. 23.
|
||||
*
|
||||
* @return ECMA_VALUE_ERROR - if a target key is not in the unchecked_result_keys collection
|
||||
* ECMA_VALUE_EMPTY - otherwise
|
||||
*/
|
||||
static ecma_value_t
|
||||
ecma_proxy_object_own_property_keys_helper (ecma_collection_t *target_collection, /**< target keys */
|
||||
ecma_collection_t *unchecked_result_keys, /**< unchecked keys */
|
||||
uint32_t *counter) /**< unchecked property counter */
|
||||
{
|
||||
ecma_value_t ret_value = ECMA_VALUE_EMPTY;
|
||||
|
||||
for (uint32_t i = 0; i < target_collection->item_count; i++)
|
||||
{
|
||||
ecma_string_t *current_prop_name = ecma_get_prop_name_from_value (target_collection->buffer_p[i]);
|
||||
|
||||
ret_value = ECMA_VALUE_ERROR;
|
||||
|
||||
for (uint32_t j = 0; j < unchecked_result_keys->item_count; j++)
|
||||
{
|
||||
if (ecma_is_value_empty (unchecked_result_keys->buffer_p[j]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ecma_string_t *unchecked_prop_name = ecma_get_prop_name_from_value (unchecked_result_keys->buffer_p[j]);
|
||||
|
||||
if (ecma_compare_ecma_strings (current_prop_name, unchecked_prop_name))
|
||||
{
|
||||
ecma_deref_ecma_string (unchecked_prop_name);
|
||||
ret_value = ECMA_VALUE_EMPTY;
|
||||
unchecked_result_keys->buffer_p[j] = ECMA_VALUE_EMPTY;
|
||||
(*counter)++;
|
||||
}
|
||||
}
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (ret_value))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret_value;
|
||||
} /* ecma_proxy_object_own_property_keys_helper */
|
||||
|
||||
/**
|
||||
* Helper method for checking the invariants in the Proxy object [[OwnPropertyKeys]] operation
|
||||
*
|
||||
* See also:
|
||||
* ECMAScript v6, 9.5.12 steps 20-25.
|
||||
*
|
||||
* @return true - if none of the invariants got violated
|
||||
* false - otherwise
|
||||
*/
|
||||
static bool
|
||||
ecma_proxy_check_invariants_for_own_prop_keys (ecma_collection_t *trap_result,
|
||||
ecma_collection_t *target_non_configurable_keys,
|
||||
ecma_collection_t *target_configurable_keys,
|
||||
ecma_value_t extensible_target)
|
||||
{
|
||||
/* 20. */
|
||||
ecma_collection_t *unchecked_result_keys = ecma_new_collection ();
|
||||
|
||||
ecma_collection_append (unchecked_result_keys, trap_result->buffer_p, trap_result->item_count);
|
||||
|
||||
for (uint32_t i = 0; i < unchecked_result_keys->item_count; i++)
|
||||
{
|
||||
ecma_string_t *unchecked_prop_name = ecma_get_prop_name_from_value (unchecked_result_keys->buffer_p[i]);
|
||||
ecma_ref_ecma_string (unchecked_prop_name);
|
||||
}
|
||||
|
||||
bool check_ok = false;
|
||||
uint32_t unchecked_prop_name_counter = 0;
|
||||
|
||||
/* 21. */
|
||||
if (ECMA_IS_VALUE_ERROR (ecma_proxy_object_own_property_keys_helper (target_non_configurable_keys,
|
||||
unchecked_result_keys,
|
||||
&unchecked_prop_name_counter)))
|
||||
{
|
||||
ecma_raise_type_error (ECMA_ERR_MSG ("Trap result did not include all non-configurable keys."));
|
||||
}
|
||||
/* 22. */
|
||||
else if (ecma_is_value_true (extensible_target))
|
||||
{
|
||||
check_ok = true;
|
||||
}
|
||||
/* 23. */
|
||||
else if (ECMA_IS_VALUE_ERROR (ecma_proxy_object_own_property_keys_helper (target_configurable_keys,
|
||||
unchecked_result_keys,
|
||||
&unchecked_prop_name_counter)))
|
||||
{
|
||||
ecma_raise_type_error (ECMA_ERR_MSG ("Trap result did not include all configurable keys."));
|
||||
}
|
||||
/* 24. */
|
||||
else if (unchecked_result_keys->item_count != unchecked_prop_name_counter)
|
||||
{
|
||||
ecma_raise_type_error (ECMA_ERR_MSG ("Trap returned extra keys but proxy target is non-extensible"));
|
||||
}
|
||||
/* 25. */
|
||||
else
|
||||
{
|
||||
check_ok = true;
|
||||
}
|
||||
|
||||
ecma_collection_free (unchecked_result_keys);
|
||||
|
||||
return check_ok;
|
||||
} /* ecma_proxy_check_invariants_for_own_prop_keys */
|
||||
|
||||
/**
|
||||
* The Proxy object [[OwnPropertyKeys]] internal routine
|
||||
*
|
||||
@ -1147,9 +1260,141 @@ ecma_collection_t *
|
||||
ecma_proxy_object_own_property_keys (ecma_object_t *obj_p) /**< proxy object */
|
||||
{
|
||||
JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (obj_p));
|
||||
JERRY_UNUSED (obj_p);
|
||||
ecma_raise_type_error (ECMA_ERR_MSG ("UNIMPLEMENTED: Proxy.[[OwnPropertyKeys]]"));
|
||||
return NULL;
|
||||
|
||||
ecma_proxy_object_t *proxy_obj_p = (ecma_proxy_object_t *) obj_p;
|
||||
|
||||
/* 1. */
|
||||
ecma_value_t handler = proxy_obj_p->handler;
|
||||
|
||||
/* 2-5. */
|
||||
ecma_value_t trap = ecma_validate_proxy_object (handler, LIT_MAGIC_STRING_OWN_KEYS_UL);
|
||||
|
||||
/* 6. */
|
||||
if (ECMA_IS_VALUE_ERROR (trap))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ecma_value_t target = proxy_obj_p->target;
|
||||
ecma_object_t *target_obj_p = ecma_get_object_from_value (target);
|
||||
|
||||
/* 7. */
|
||||
if (ecma_is_value_undefined (trap))
|
||||
{
|
||||
return ecma_op_object_get_property_names (target_obj_p, ECMA_LIST_SYMBOLS);
|
||||
}
|
||||
|
||||
ecma_object_t *func_obj_p = ecma_get_object_from_value (trap);
|
||||
|
||||
/* 8. */
|
||||
ecma_value_t trap_result_array = ecma_op_function_call (func_obj_p, handler, &target, 1);
|
||||
|
||||
ecma_deref_object (func_obj_p);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (trap_result_array))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 9. */
|
||||
ecma_collection_t *trap_result = ecma_op_create_list_from_array_like (trap_result_array, true);
|
||||
|
||||
ecma_free_value (trap_result_array);
|
||||
|
||||
/* 10. */
|
||||
if (trap_result == NULL)
|
||||
{
|
||||
return trap_result;
|
||||
}
|
||||
|
||||
/* 11. */
|
||||
ecma_value_t extensible_target = ecma_builtin_object_object_is_extensible (target_obj_p);
|
||||
|
||||
/* 12. */
|
||||
if (ECMA_IS_VALUE_ERROR (extensible_target))
|
||||
{
|
||||
ecma_collection_free (trap_result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 13. */
|
||||
ecma_collection_t *target_keys = ecma_op_object_get_property_names (target_obj_p, ECMA_LIST_SYMBOLS);
|
||||
|
||||
/* 14. */
|
||||
if (target_keys == NULL)
|
||||
{
|
||||
ecma_collection_free (trap_result);
|
||||
return target_keys;
|
||||
}
|
||||
|
||||
/* 16. */
|
||||
ecma_collection_t *target_configurable_keys = ecma_new_collection ();
|
||||
|
||||
/* 17. */
|
||||
ecma_collection_t *target_non_configurable_keys = ecma_new_collection ();
|
||||
|
||||
ecma_collection_t *ret_value = NULL;
|
||||
|
||||
/* 18. */
|
||||
for (uint32_t i = 0; i < target_keys->item_count; i++)
|
||||
{
|
||||
ecma_property_descriptor_t target_desc;
|
||||
|
||||
ecma_string_t *prop_name_p = ecma_get_prop_name_from_value (target_keys->buffer_p[i]);
|
||||
|
||||
ecma_value_t status = ecma_op_object_get_own_property_descriptor (target_obj_p,
|
||||
prop_name_p,
|
||||
&target_desc);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (status))
|
||||
{
|
||||
ecma_collection_free (trap_result);
|
||||
goto free_target_collections;
|
||||
}
|
||||
|
||||
ecma_value_t prop_value = ecma_make_prop_name_value (prop_name_p);
|
||||
|
||||
if (ecma_is_value_true (status)
|
||||
&& !(target_desc.flags & ECMA_PROP_IS_CONFIGURABLE))
|
||||
{
|
||||
ecma_collection_push_back (target_non_configurable_keys, prop_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
ecma_collection_push_back (target_configurable_keys, prop_value);
|
||||
}
|
||||
|
||||
if (ecma_is_value_true (status))
|
||||
{
|
||||
ecma_free_property_descriptor (&target_desc);
|
||||
}
|
||||
}
|
||||
|
||||
/* 19. */
|
||||
if (ecma_is_value_true (extensible_target) && target_non_configurable_keys->item_count == 0)
|
||||
{
|
||||
ret_value = trap_result;
|
||||
}
|
||||
/* 20-25. */
|
||||
else if (ecma_proxy_check_invariants_for_own_prop_keys (trap_result,
|
||||
target_non_configurable_keys,
|
||||
target_configurable_keys,
|
||||
extensible_target))
|
||||
{
|
||||
ret_value = trap_result;
|
||||
}
|
||||
else
|
||||
{
|
||||
JERRY_ASSERT (ret_value == NULL);
|
||||
ecma_collection_free (trap_result);
|
||||
}
|
||||
|
||||
free_target_collections:
|
||||
ecma_collection_destroy (target_keys);
|
||||
ecma_collection_free (target_configurable_keys);
|
||||
ecma_collection_free (target_non_configurable_keys);
|
||||
|
||||
return ret_value;
|
||||
} /* ecma_proxy_object_own_property_keys */
|
||||
|
||||
/**
|
||||
|
||||
@ -470,7 +470,8 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_INDEX_OF_UL, "indexOf")
|
||||
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_ARRAY_UL, "isArray")
|
||||
#endif
|
||||
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MESSAGE, "message")
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_REFLECT)
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_PROXY) \
|
||||
|| ENABLED (JERRY_ES2015_BUILTIN_REFLECT)
|
||||
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_OWN_KEYS_UL, "ownKeys")
|
||||
#endif
|
||||
#if ENABLED (JERRY_BUILTIN_REGEXP) && ENABLED (JERRY_BUILTIN_STRING) \
|
||||
|
||||
@ -37,14 +37,6 @@ try {
|
||||
assert(e instanceof TypeError);
|
||||
}
|
||||
|
||||
try {
|
||||
// 19.1.2.1.5.b.iii
|
||||
Object.assign({}, proxy)
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof TypeError);
|
||||
}
|
||||
|
||||
try {
|
||||
// 19.1.2.6.5
|
||||
Object.getOwnPropertyDescriptor(proxy)
|
||||
|
||||
@ -12,7 +12,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// TODO: Update these tests when the internal routine has been implemented
|
||||
// Copyright 2015 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
var target = {};
|
||||
var handler = { ownKeys (target) {
|
||||
@ -26,7 +28,7 @@ try {
|
||||
Array.prototype.sort.call(proxy);
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof TypeError);
|
||||
assert(e === 42);
|
||||
}
|
||||
|
||||
try {
|
||||
@ -34,7 +36,7 @@ try {
|
||||
Object.keys(proxy);
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof TypeError);
|
||||
assert(e === 42);
|
||||
}
|
||||
|
||||
try {
|
||||
@ -42,13 +44,156 @@ try {
|
||||
Object.getOwnPropertyNames(proxy);
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof TypeError);
|
||||
assert(e === 42);
|
||||
}
|
||||
|
||||
try {
|
||||
// 19.1.2.8.1
|
||||
Object.getOwnPropertySymbols(proxy);
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e === 42);
|
||||
}
|
||||
|
||||
// test basic functionality
|
||||
var target = { prop1: "prop1", prop2: "prop2"};
|
||||
var handler = {
|
||||
ownKeys: function(target) {
|
||||
return ["foo", "bar"];
|
||||
}
|
||||
}
|
||||
|
||||
var proxy = new Proxy(target, handler);
|
||||
|
||||
assert(JSON.stringify(Reflect.ownKeys(proxy)) === '["foo","bar"]');
|
||||
assert(JSON.stringify(Object.getOwnPropertyNames(proxy)) === '["foo","bar"]');
|
||||
assert(JSON.stringify(Object.keys(proxy)) === '["foo","bar"]');
|
||||
assert(JSON.stringify(Object.getOwnPropertySymbols(proxy)) === '["foo","bar"]');
|
||||
|
||||
handler.ownKeys = function(target) {return Object.getOwnPropertyNames(target);};
|
||||
|
||||
assert(JSON.stringify(Reflect.ownKeys(proxy)) === '["prop1","prop2"]');
|
||||
assert(JSON.stringify(Object.getOwnPropertyNames(proxy)) === '["prop1","prop2"]');
|
||||
assert(JSON.stringify(Object.keys(proxy)) === '["prop1","prop2"]');
|
||||
assert(JSON.stringify(Object.getOwnPropertySymbols(proxy)) === '["prop1","prop2"]');
|
||||
|
||||
// test with no trap
|
||||
var target = { prop1: "prop1", prop2: "prop2"};
|
||||
var handler = {};
|
||||
var proxy = new Proxy(target, handler);
|
||||
|
||||
assert(JSON.stringify(Object.getOwnPropertyNames(proxy)) === '["prop1","prop2"]');
|
||||
|
||||
// test wtih "undefined" trap
|
||||
var target = { prop1: "prop1", prop2: "prop2"};
|
||||
var handler = {ownKeys: null};
|
||||
var proxy = new Proxy(target, handler);
|
||||
|
||||
assert(JSON.stringify(Object.getOwnPropertyNames(proxy)) === '["prop1","prop2"]');
|
||||
|
||||
// test with invalid trap
|
||||
var target = { prop1: "prop1", prop2: "prop2"};
|
||||
var handler = {ownKeys: 42};
|
||||
var proxy = new Proxy(target, handler);
|
||||
|
||||
try {
|
||||
Object.getOwnPropertyNames(proxy);
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof TypeError);
|
||||
}
|
||||
|
||||
// test when CreateListFromArrayLike called on non-object
|
||||
var target = { prop1: "prop1", prop2: "prop2"};
|
||||
var handler = {
|
||||
ownKeys: function(target) {
|
||||
return "foo";
|
||||
}
|
||||
};
|
||||
|
||||
var proxy = new Proxy(target, handler);
|
||||
|
||||
try {
|
||||
Object.getOwnPropertyNames(proxy);
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof TypeError);
|
||||
}
|
||||
|
||||
// test with invalid property key
|
||||
var target = {};
|
||||
var handler = {
|
||||
ownKeys: function(target) {
|
||||
return [5];
|
||||
}
|
||||
};
|
||||
|
||||
var proxy = new Proxy(target, handler);
|
||||
|
||||
try {
|
||||
Object.getOwnPropertyNames(proxy);
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof TypeError);
|
||||
}
|
||||
|
||||
// test with duplicated keys
|
||||
var target = { prop1: "prop1", prop2: "prop2"};
|
||||
var handler = {
|
||||
ownKeys: function(target) {
|
||||
return ["a", "a", "a"];
|
||||
}
|
||||
};
|
||||
|
||||
var proxy = new Proxy(target, handler);
|
||||
|
||||
assert(JSON.stringify(Object.getOwnPropertyNames(proxy)) === '["a","a","a"]');
|
||||
|
||||
// test when invariants gets violated
|
||||
var target = {
|
||||
"target_one": 1
|
||||
};
|
||||
Object.defineProperty(target, "nonconf", {value: 1, configurable: false});
|
||||
|
||||
var keys = ["foo"];
|
||||
|
||||
var handler = {
|
||||
ownKeys: function(target) {
|
||||
return keys;
|
||||
}
|
||||
};
|
||||
|
||||
var proxy = new Proxy(target, handler);
|
||||
|
||||
try {
|
||||
Object.getOwnPropertyNames(proxy);
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof TypeError);
|
||||
}
|
||||
|
||||
keys = ["nonconf"];
|
||||
assert(JSON.stringify(Object.getOwnPropertyNames(proxy)) === '["nonconf"]');
|
||||
|
||||
Object.preventExtensions(target);
|
||||
keys = ["foo", "nonconf"];
|
||||
|
||||
try {
|
||||
Object.getOwnPropertyNames(proxy);
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof TypeError);
|
||||
}
|
||||
|
||||
keys = ["target_one", "nonconf"];
|
||||
|
||||
assert(JSON.stringify(Object.getOwnPropertyNames(proxy)) === '["target_one","nonconf"]');
|
||||
|
||||
keys = ["target_one", "nonconf", "foo"];
|
||||
|
||||
try {
|
||||
Object.getOwnPropertyNames(proxy);
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof TypeError);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user