From 29b7c8f8ff5d62872d85e2c47f64489e9f793b4e Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Wed, 3 Mar 2021 18:02:40 +0100 Subject: [PATCH] Implement jerry_get_own_property API function (#4612) JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- docs/02.API-REFERENCE.md | 71 ++++++++ jerry-core/api/jerry.c | 60 +++++++ jerry-core/include/jerryscript-core.h | 2 + tests/unit-core/CMakeLists.txt | 1 + tests/unit-core/test-get-own-property.c | 212 ++++++++++++++++++++++++ 5 files changed, 346 insertions(+) create mode 100644 tests/unit-core/test-get-own-property.c diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 0d230bdc7..2b8f8e46c 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -6994,6 +6994,77 @@ jerry_get_property_by_index (const jerry_value_t obj_val, - [jerry_set_property](#jerry_set_property) - [jerry_set_property_by_index](#jerry_set_property_by_index) +## jerry_get_own_property + +**Summary** + +Get the own property value of an object with the given name. The function tells +whether the property is found, and the receiver object can be specified as well. +The receiver is passed as the `this` argument for getters, and the receiver +argument for Proxy `get` traps. + +*Notes*: + - Returned value must be freed with [jerry_release_value](#jerry_release_value) when it is no longer needed. + - The `found_p` argument is ignored if its value is NULL. + - The target value of `found_p` argument is set to false when the arguments are invalid, e.g. `obj_val` is not an object. + +**Prototype** + +```c +jerry_value_t +jerry_get_own_property (const jerry_value_t obj_val, + const jerry_value_t prop_name_val, + const jerry_value_t receiver_val, + bool *found_p); +``` + +- `obj_val` - object value +- `prop_name_val` - property name +- `receiver_val` - receiver object +- `found_p` - [out] true, if the property is found or obj_val is a Proxy object, false otherwise +- return value + - value of property, if success + - thrown error, otherwise + +**Example** + +[doctest]: # () + +```c +#include "jerryscript.h" +#include "stdio.h" + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t global_object = jerry_get_global_object (); + jerry_value_t prop_name = jerry_create_string ((const jerry_char_t *) "Object"); + + bool found; + jerry_value_t prop_value = jerry_get_own_property (global_object, prop_name, global_object, &found); + + if (found) + { + printf ("Property is found!\n"); + } + + /* use "prop_value" then release it. */ + + jerry_release_value (prop_value); + jerry_release_value (prop_name); + jerry_release_value (global_object); + + return 0; +} +``` + +**See also** + +- [jerry_get_property](#jerry_get_property) +- [jerry_get_property_by_index](#jerry_get_property_by_index) + ## jerry_get_internal_property **Summary** diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 4fb82d6b0..eea7a97f7 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -3030,6 +3030,66 @@ jerry_get_property_by_index (const jerry_value_t obj_val, /**< object value */ return jerry_return (ret_value); } /* jerry_get_property_by_index */ +/** + * Get the own property value of an object with the given name. + * + * Note: + * returned value must be freed with jerry_release_value, when it is no longer needed. + * + * @return value of the property - if success + * value marked with error flag - otherwise + */ +jerry_value_t +jerry_get_own_property (const jerry_value_t obj_val, /**< object value */ + const jerry_value_t prop_name_val, /**< property name (string value) */ + const jerry_value_t receiver_val, /**< receiver object value */ + bool *found_p) /**< [out] true, if the property is found + * or obj_val is a Proxy object, false otherwise */ +{ + jerry_assert_api_available (); + + if (found_p != NULL) + { + *found_p = false; + } + + if (!ecma_is_value_object (obj_val) + || !ecma_is_value_prop_name (prop_name_val) + || !ecma_is_value_object (receiver_val)) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (wrong_args_msg_p))); + } + + ecma_object_t *object_p = ecma_get_object_from_value (obj_val); + ecma_string_t *property_name_p = ecma_get_prop_name_from_value (prop_name_val); + +#if JERRY_BUILTIN_PROXY + if (ECMA_OBJECT_IS_PROXY (object_p)) + { + if (found_p != NULL) + { + *found_p = true; + } + + return jerry_return (ecma_proxy_object_get (object_p, property_name_p, receiver_val)); + } +#endif /* JERRY_BUILTIN_PROXY */ + + ecma_value_t ret_value = ecma_op_object_find_own (receiver_val, object_p, property_name_p); + + if (ecma_is_value_found (ret_value)) + { + if (found_p != NULL) + { + *found_p = true; + } + + return jerry_return (ret_value); + } + + return ECMA_VALUE_UNDEFINED; +} /* jerry_get_own_property */ + /** * Get value of an internal property to the specified object with the given name. * diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index 792711206..7609eb957 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -687,6 +687,8 @@ bool jerry_delete_internal_property (const jerry_value_t obj_val, const jerry_va jerry_value_t jerry_get_property (const jerry_value_t obj_val, const jerry_value_t prop_name_val); jerry_value_t jerry_get_property_by_index (const jerry_value_t obj_val, uint32_t index); +jerry_value_t jerry_get_own_property (const jerry_value_t obj_val, const jerry_value_t prop_name_val, + const jerry_value_t receiver_val, bool *found_p); jerry_value_t jerry_get_internal_property (const jerry_value_t obj_val, const jerry_value_t prop_name_val); jerry_value_t jerry_set_property (const jerry_value_t obj_val, const jerry_value_t prop_name_val, const jerry_value_t value_to_set); diff --git a/tests/unit-core/CMakeLists.txt b/tests/unit-core/CMakeLists.txt index be4bba1cc..4603f6911 100644 --- a/tests/unit-core/CMakeLists.txt +++ b/tests/unit-core/CMakeLists.txt @@ -50,6 +50,7 @@ set(SOURCE_UNIT_TEST_MAIN_MODULES test-exec-stop.c test-external-string.c test-from-property-descriptor.c + test-get-own-property.c test-has-property.c test-internal-properties.c test-jmem.c diff --git a/tests/unit-core/test-get-own-property.c b/tests/unit-core/test-get-own-property.c new file mode 100644 index 000000000..489bd9925 --- /dev/null +++ b/tests/unit-core/test-get-own-property.c @@ -0,0 +1,212 @@ + /* 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" + +static jerry_value_t +create_object (const char *source_p) /**< source script */ +{ + jerry_value_t result = jerry_eval ((const jerry_char_t *) source_p, strlen (source_p), 0); + TEST_ASSERT (jerry_value_is_object (result)); + return result; +} /* create_object */ + +static void +compare_string (jerry_value_t value, /**< value to compare */ + const char *string_p) /**< expected value */ +{ + jerry_char_t string_buffer[64]; + + TEST_ASSERT (jerry_value_is_string (value)); + + size_t size = strlen (string_p); + TEST_ASSERT (size <= sizeof (string_buffer)); + TEST_ASSERT (size == jerry_get_string_size (value)); + + jerry_string_to_char_buffer (value, string_buffer, (jerry_size_t) size); + TEST_ASSERT (memcmp (string_p, string_buffer, size) == 0); +} /* compare_string */ + +int +main (void) +{ + TEST_INIT (); + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t pp_string = jerry_create_string ((const jerry_char_t *) "pp"); + jerry_value_t qq_string = jerry_create_string ((const jerry_char_t *) "qq"); + jerry_value_t rr_string = jerry_create_string ((const jerry_char_t *) "rr"); + + jerry_value_t object = create_object ("'use strict';\n" + "({ pp:'A', get qq() { return 'B' } })"); + + jerry_value_t result = jerry_get_own_property (object, pp_string, object, NULL); + compare_string (result, "A"); + jerry_release_value (result); + + bool found = false; + result = jerry_get_own_property (object, pp_string, object, &found); + compare_string (result, "A"); + TEST_ASSERT (found); + jerry_release_value (result); + + result = jerry_get_own_property (object, qq_string, object, NULL); + compare_string (result, "B"); + jerry_release_value (result); + + found = false; + result = jerry_get_own_property (object, qq_string, object, &found); + compare_string (result, "B"); + TEST_ASSERT (found); + jerry_release_value (result); + + result = jerry_get_own_property (object, rr_string, object, NULL); + TEST_ASSERT (jerry_value_is_undefined (result)); + jerry_release_value (result); + + found = true; + result = jerry_get_own_property (object, rr_string, object, &found); + TEST_ASSERT (jerry_value_is_undefined (result)); + TEST_ASSERT (!found); + jerry_release_value (result); + + jerry_release_value (object); + + object = create_object ("'use strict';\n" + "Object.create({ pp:'Found!' })\n"); + + found = true; + /* Does not check prototype. */ + result = jerry_get_own_property (object, pp_string, object, &found); + TEST_ASSERT (jerry_value_is_undefined (result)); + TEST_ASSERT (!found); + jerry_release_value (result); + + jerry_release_value (object); + + object = create_object ("'use strict';\n" + "var obj = Object.create({ get pp() { return this.qq } })\n" + "Object.defineProperty(obj, 'qq', { value: 'Prop' })\n" + "obj"); + jerry_value_t prototype = jerry_get_prototype (object); + + TEST_ASSERT (jerry_value_is_object (prototype)); + found = false; + result = jerry_get_own_property (prototype, pp_string, object, &found); + compare_string (result, "Prop"); + TEST_ASSERT (found); + jerry_release_value (result); + + jerry_release_value (prototype); + jerry_release_value (object); + + /* Error cases. */ + jerry_value_t invalid_arg = jerry_create_null (); + object = jerry_create_object (); + + found = true; + result = jerry_get_own_property (invalid_arg, pp_string, object, &found); + TEST_ASSERT (jerry_value_is_error (result)); + TEST_ASSERT (!found); + jerry_release_value (result); + + result = jerry_get_own_property (object, pp_string, invalid_arg, NULL); + TEST_ASSERT (jerry_value_is_error (result)); + jerry_release_value (result); + + found = true; + result = jerry_get_own_property (object, invalid_arg, object, &found); + TEST_ASSERT (jerry_value_is_error (result)); + TEST_ASSERT (!found); + jerry_release_value (result); + + jerry_release_value (object); + jerry_release_value (invalid_arg); + + if (jerry_is_feature_enabled (JERRY_FEATURE_PROXY)) + { + object = create_object ("'use strict';\n" + "var proxy = new Proxy({}, {\n" + " get: function(target, prop, receiver) {\n" + " if (prop === 'qq') return\n" + " return receiver[prop]\n" + " }\n" + "})\n" + "var obj = Object.create(proxy)\n" + "Object.defineProperty(obj, 'pp', { value: 'Prop' })\n" + "obj"); + + prototype = jerry_get_prototype (object); + found = false; + result = jerry_get_own_property (prototype, pp_string, object, &found); + compare_string (result, "Prop"); + TEST_ASSERT (found); + jerry_release_value (result); + + found = false; + result = jerry_get_own_property (prototype, qq_string, object, &found); + TEST_ASSERT (jerry_value_is_undefined (result)); + TEST_ASSERT (found); + jerry_release_value (result); + + jerry_release_value (prototype); + jerry_release_value (object); + + object = create_object ("'use strict';\n" + "(new Proxy({}, {\n" + " get: function(target, prop, receiver) {\n" + " throw 'Error'\n" + " }\n" + "}))\n"); + + found = false; + result = jerry_get_own_property (object, qq_string, object, &found); + TEST_ASSERT (jerry_value_is_error (result)); + TEST_ASSERT (found); + jerry_release_value (result); + + jerry_release_value (object); + } + + if (jerry_is_feature_enabled (JERRY_FEATURE_SYMBOL)) + { + object = create_object ("'use strict'\n" + "var sym = Symbol();\n" + "({ pp:sym, [sym]:'Prop' })"); + + found = false; + jerry_value_t symbol = jerry_get_own_property (object, pp_string, object, &found); + TEST_ASSERT (jerry_value_is_symbol (symbol)); + TEST_ASSERT (found); + + found = false; + result = jerry_get_own_property (object, symbol, object, &found); + compare_string (result, "Prop"); + TEST_ASSERT (found); + jerry_release_value (result); + + jerry_release_value (symbol); + jerry_release_value (object); + } + + jerry_release_value (pp_string); + jerry_release_value (qq_string); + jerry_release_value (rr_string); + + jerry_cleanup (); + return 0; +} /* main */