diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 43215bb47..e1d2badfe 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -3911,6 +3911,106 @@ example (void) - [jerry_create_promise](#jerry_create_promise) - [jerry_promise_state_t](#jerry_promise_state_t) +## jerry_from_property_descriptor + +**Summary** + +This API function is equivalent to FromPropertyDescriptor operation defined in ECMA 262 v11 6.2.5.4. +It returns with an ECMAScript Object which represents the property attributes. + +*Notes*: +- Returned value must be freed with [jerry_release_value](#jerry_release_value) when it + is no longer needed. + +**Prototype** + +```c +jerry_value_t +jerry_from_property_descriptor (const jerry_property_descriptor_t *src_prop_desc_p) +``` + +- `src_prop_desc_p` - the input property descriptor. +- return + - [jerry_value_t](#jerry_value_t) + - jerry value - if success + - value marked with error flag - otherwise + +**Example** + +[doctest]: # (test="compile") + +```c +#include + +static void +example (void) +{ + jerry_value_t prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "value"); + + jerry_property_descriptor_t prop_desc; + jerry_init_property_descriptor_fields (&prop_desc); + + prop_desc.value = prop_name; + prop_desc.is_value_defined = true; + + jerry_value_t from_object = jerry_from_property_descriptor (&prop_desc); + + jerry_release_value (prop_name); + jerry_release_value (from_object); + jerry_free_property_descriptor_fields (&prop_desc); +} +``` + +## jerry_to_property_descriptor + +**Summary** + +This API function is equivalent to ToPropertyDescriptor operation defined in ECMA 262 v11 6.2.5.5. +It decodes the ECMAScript object and fills the fields of a JerryScript property descriptor. + +*Notes*: +- The es.next profile enables this by default. + +**Prototype** + +```c +bool +jerry_to_property_descriptor (jerry_value_t obj_value, jerry_property_descriptor_t *out_prop_desc_p); +``` + +- `obj_value` - the input object +- `src_prop_desc_p` - the output property descriptor. +- return + - [jerry_value_t](#jerry_value_t) + - true - if success + - false - if error occurs + +**Example** + +[doctest]: # (test="compile") + +```c +#include + +static void +example (void) +{ + jerry_value_t object = jerry_create_object (); + jerry_value_t prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "value"); + jerry_value_t value = jerry_create_boolean (true); + jerry_property_descriptor_t prop_desc; + + jerry_set_property (object, prop_name, prop_name); + + jerry_to_property_descriptor (object, &prop_desc); + + jerry_release_value (object); + jerry_release_value (prop_name); + jerry_release_value (value); + jerry_free_property_descriptor_fields (&prop_desc); +} +``` + ## jerry_resolve_or_reject_promise **Summary** diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 3301375d0..db5871274 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -2952,6 +2952,161 @@ jerry_init_property_descriptor_fields (jerry_property_descriptor_t *prop_desc_p) prop_desc_p->setter = ECMA_VALUE_UNDEFINED; } /* jerry_init_property_descriptor_fields */ +/** + * Convert a ecma_property_descriptor_t to a jerry_property_descriptor_t + * + * if error occurs the property descriptor's value field is filled with ECMA_VALUE_ERROR + * + * @return jerry_property_descriptor_t + */ +static jerry_property_descriptor_t +jerry_property_descriptor_from_ecma (const ecma_property_descriptor_t *prop_desc_p) /**<[out] property_descriptor */ +{ + jerry_property_descriptor_t prop_desc; + jerry_init_property_descriptor_fields (&prop_desc); + + if (prop_desc_p->flags & (ECMA_PROP_IS_ENUMERABLE_DEFINED)) + { + prop_desc.is_enumerable_defined = true; + prop_desc.is_enumerable = prop_desc_p->flags & (ECMA_PROP_IS_ENUMERABLE); + } + + if (prop_desc_p->flags & (ECMA_PROP_IS_CONFIGURABLE_DEFINED)) + { + prop_desc.is_configurable_defined = true; + prop_desc.is_configurable = prop_desc_p->flags & (ECMA_PROP_IS_CONFIGURABLE); + } + + prop_desc.is_value_defined = prop_desc_p->flags & (ECMA_PROP_IS_VALUE_DEFINED); + + if (prop_desc.is_value_defined) + { + prop_desc.value = prop_desc_p->value; + } + + if (prop_desc_p->flags & (ECMA_PROP_IS_WRITABLE_DEFINED)) + { + prop_desc.is_writable_defined = true; + prop_desc.is_writable = prop_desc_p->flags & (ECMA_PROP_IS_WRITABLE); + } + + if (prop_desc_p->flags & (ECMA_PROP_IS_GET_DEFINED)) + { + ecma_value_t getter = ecma_make_object_value (prop_desc_p->get_p); + prop_desc.is_get_defined = true; + + if (ecma_op_is_callable (getter)) + { + prop_desc.getter = getter; + } + } + + if (prop_desc_p->flags & (ECMA_PROP_IS_SET_DEFINED)) + { + ecma_value_t setter = ecma_make_object_value (prop_desc_p->set_p); + prop_desc.is_set_defined = true; + prop_desc.setter = setter; + } + + return prop_desc; +} /* jerry_property_descriptor_from_ecma */ + +/** + * Convert a jerry_property_descriptor_t to a ecma_property_descriptor_t + * + * if error occurs the property descriptor's value field is filled with ECMA_VALUE_ERROR + * + * @return ecma_property_descriptor_t + */ +static ecma_property_descriptor_t +jerry_property_descriptor_to_ecma (const jerry_property_descriptor_t *prop_desc_p) /**< input property_descriptor */ +{ + ecma_property_descriptor_t prop_desc = ecma_make_empty_property_descriptor (); + + uint32_t flags = ECMA_PROP_NO_OPTS; + + if (prop_desc_p->is_enumerable_defined) + { + flags |= (uint32_t) (ECMA_PROP_IS_ENUMERABLE_DEFINED | (prop_desc_p->is_enumerable ? ECMA_PROP_IS_ENUMERABLE + : ECMA_PROP_NO_OPTS)); + } + + if (prop_desc_p->is_configurable_defined) + { + flags |= (uint32_t) (ECMA_PROP_IS_CONFIGURABLE_DEFINED | (prop_desc_p->is_configurable ? ECMA_PROP_IS_CONFIGURABLE + : ECMA_PROP_NO_OPTS)); + } + + /* Copy data property info. */ + flags |= (prop_desc_p->is_value_defined ? ECMA_PROP_IS_VALUE_DEFINED : ECMA_PROP_NO_OPTS); + + if (prop_desc_p->is_value_defined) + { + if (ecma_is_value_error_reference (prop_desc_p->value)) + { + prop_desc.value = ecma_raise_type_error (ECMA_ERR_MSG (wrong_args_msg_p)); + return prop_desc; + } + + prop_desc.value = prop_desc_p->value; + } + + if (prop_desc_p->is_writable_defined) + { + flags |= (uint32_t) (ECMA_PROP_IS_WRITABLE_DEFINED | (prop_desc_p->is_writable ? ECMA_PROP_IS_WRITABLE + : ECMA_PROP_NO_OPTS)); + } + + /* Copy accessor property info. */ + if (prop_desc_p->is_get_defined) + { + ecma_value_t getter = prop_desc_p->getter; + flags |= ECMA_PROP_IS_GET_DEFINED; + + if (ecma_is_value_error_reference (getter)) + { + prop_desc.value = ecma_raise_type_error (ECMA_ERR_MSG (wrong_args_msg_p)); + return prop_desc; + } + + if (ecma_op_is_callable (getter)) + { + prop_desc.get_p = ecma_get_object_from_value (getter); + } + else if (!ecma_is_value_null (getter)) + { + prop_desc.value = ecma_raise_type_error (ECMA_ERR_MSG (wrong_args_msg_p)); + return prop_desc; + } + } + + if (prop_desc_p->is_set_defined) + { + ecma_value_t setter = prop_desc_p->setter; + flags |= ECMA_PROP_IS_SET_DEFINED; + + if (ecma_is_value_error_reference (setter)) + { + prop_desc.value = ecma_raise_type_error (ECMA_ERR_MSG (wrong_args_msg_p)); + return prop_desc; + } + + if (ecma_op_is_callable (setter)) + { + prop_desc.set_p = ecma_get_object_from_value (setter); + } + else if (!ecma_is_value_null (setter)) + { + prop_desc.value = ecma_raise_type_error (ECMA_ERR_MSG (wrong_args_msg_p)); + return prop_desc; + } + } + + prop_desc.flags |= (uint16_t) (flags | ECMA_PROP_IS_THROW); + + return prop_desc; +} /* jerry_property_descriptor_to_ecma */ + /** * Define a property to the specified object with the given name. * @@ -2980,84 +3135,12 @@ jerry_define_own_property (const jerry_value_t obj_val, /**< object value */ return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (wrong_args_msg_p))); } - ecma_property_descriptor_t prop_desc = ecma_make_empty_property_descriptor (); + ecma_property_descriptor_t prop_desc = jerry_property_descriptor_to_ecma (prop_desc_p); - uint32_t flags = ECMA_PROP_NO_OPTS; - - if (prop_desc_p->is_enumerable_defined) + if (ECMA_IS_VALUE_ERROR (prop_desc.value)) { - flags |= (uint32_t) (ECMA_PROP_IS_ENUMERABLE_DEFINED | (prop_desc_p->is_enumerable ? ECMA_PROP_IS_ENUMERABLE - : ECMA_PROP_NO_OPTS)); + return jerry_throw (prop_desc.value); } - - if (prop_desc_p->is_configurable_defined) - { - flags |= (uint32_t) (ECMA_PROP_IS_CONFIGURABLE_DEFINED | (prop_desc_p->is_configurable ? ECMA_PROP_IS_CONFIGURABLE - : ECMA_PROP_NO_OPTS)); - } - - /* Copy data property info. */ - flags |= (prop_desc_p->is_value_defined ? ECMA_PROP_IS_VALUE_DEFINED : ECMA_PROP_NO_OPTS); - - if (prop_desc_p->is_value_defined) - { - if (ecma_is_value_error_reference (prop_desc_p->value)) - { - return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (wrong_args_msg_p))); - } - - prop_desc.value = prop_desc_p->value; - } - - if (prop_desc_p->is_writable_defined) - { - flags |= (uint32_t) (ECMA_PROP_IS_WRITABLE_DEFINED | (prop_desc_p->is_writable ? ECMA_PROP_IS_WRITABLE - : ECMA_PROP_NO_OPTS)); - } - - /* Copy accessor property info. */ - if (prop_desc_p->is_get_defined) - { - ecma_value_t getter = prop_desc_p->getter; - flags |= ECMA_PROP_IS_GET_DEFINED; - - if (ecma_is_value_error_reference (getter)) - { - return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (wrong_args_msg_p))); - } - - if (ecma_op_is_callable (getter)) - { - prop_desc.get_p = ecma_get_object_from_value (getter); - } - else if (!ecma_is_value_null (getter)) - { - return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (wrong_args_msg_p))); - } - } - - if (prop_desc_p->is_set_defined) - { - ecma_value_t setter = prop_desc_p->setter; - flags |= ECMA_PROP_IS_SET_DEFINED; - - if (ecma_is_value_error_reference (setter)) - { - return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (wrong_args_msg_p))); - } - - if (ecma_op_is_callable (setter)) - { - prop_desc.set_p = ecma_get_object_from_value (setter); - } - else if (!ecma_is_value_null (setter)) - { - return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (wrong_args_msg_p))); - } - } - - prop_desc.flags |= (uint16_t) (flags | ECMA_PROP_IS_THROW); - return jerry_return (ecma_op_object_define_own_property (ecma_get_object_from_value (obj_val), ecma_get_prop_name_from_value (prop_name_val), &prop_desc)); @@ -3841,6 +3924,66 @@ jerry_object_get_property_names (const jerry_value_t obj_val, /**< object */ return ecma_op_new_array_object_from_collection (result_p, false); } /* jerry_object_get_property_names */ +/** FromPropertyDescriptor abstract operation. + * + * @return new jerry_value_t - if success + * value marked with error flag - otherwise + */ +jerry_value_t +jerry_from_property_descriptor (const jerry_property_descriptor_t *src_prop_desc_p) /**< property descriptor */ +{ + jerry_assert_api_available (); + + ecma_property_descriptor_t prop_desc = jerry_property_descriptor_to_ecma (src_prop_desc_p); + + if (ECMA_IS_VALUE_ERROR (prop_desc.value)) + { + return jerry_throw (prop_desc.value); + } + + ecma_object_t *desc_obj_p = ecma_op_from_property_descriptor (&prop_desc); + + return ecma_make_object_value (desc_obj_p); +} /* jerry_from_property_descriptor */ + +/** +* ToPropertyDescriptor abstract operation. +* +* @return thrown error - if any conversion error happens +* ECMA_VALUE_TRUE - otherwise. +*/ +bool +jerry_to_property_descriptor (jerry_value_t obj_value, /**< object value */ + jerry_property_descriptor_t *out_prop_desc_p) /**< [out] filled property descriptor + if return value is normal + empty completion value */ +{ + jerry_assert_api_available (); + + if (!ecma_is_value_object (obj_value)) + { + jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (wrong_args_msg_p))); + } + + ecma_property_descriptor_t prop_desc; + jerry_value_t result = ecma_op_to_property_descriptor (obj_value, &prop_desc); + + if (ECMA_IS_VALUE_ERROR (result)) + { + return false; + } + *out_prop_desc_p = jerry_property_descriptor_from_ecma (&prop_desc); + + if (ECMA_IS_VALUE_ERROR (out_prop_desc_p->value)) + { + jerry_release_value (result); + return false; + } + + jerry_release_value (result); + return true; +} /* jerry_to_property_descriptor */ + /** * Resolve or reject the promise with an argument. * diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index 85999c79c..e05e32bdf 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -653,7 +653,8 @@ bool jerry_foreach_object_property (const jerry_value_t obj_val, jerry_object_pr void *user_data_p); jerry_value_t jerry_object_get_property_names (const jerry_value_t obj_val, jerry_property_filter_t filter); - +jerry_value_t jerry_from_property_descriptor (const jerry_property_descriptor_t *src_prop_desc_p); +bool jerry_to_property_descriptor (jerry_value_t obj_value, jerry_property_descriptor_t *out_prop_desc_p); /** * Promise functions. */ diff --git a/tests/unit-core/test-from-property-descriptor.c b/tests/unit-core/test-from-property-descriptor.c new file mode 100644 index 000000000..27b8af78b --- /dev/null +++ b/tests/unit-core/test-from-property-descriptor.c @@ -0,0 +1,67 @@ +/* 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 "config.h" +#include "jerryscript.h" +#include "test-common.h" + +int +main (void) +{ + TEST_INIT (); + + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t object = jerry_create_object (); + jerry_value_t prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "length"); + jerry_value_t value = jerry_create_boolean (true); + + TEST_ASSERT (jerry_set_property (object, prop_name, prop_name)); + TEST_ASSERT (jerry_has_property (object, prop_name)); + TEST_ASSERT (jerry_has_own_property (object, prop_name)); + + jerry_property_descriptor_t prop_desc; + TEST_ASSERT (jerry_get_own_property_descriptor (object, prop_name, &prop_desc)); + + jerry_value_t from_object = jerry_from_property_descriptor (&prop_desc); + + prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "value"); + value = jerry_get_property (from_object, prop_name); + TEST_ASSERT (value == prop_desc.value); + + prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "writable"); + value = jerry_get_property (from_object, prop_name); + TEST_ASSERT (jerry_get_boolean_value (value) == prop_desc.is_writable); + + prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "writable"); + value = jerry_get_property (from_object, prop_name); + TEST_ASSERT (jerry_get_boolean_value (value) == prop_desc.is_writable); + + prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "enumerable"); + value = jerry_get_property (from_object, prop_name); + TEST_ASSERT (jerry_get_boolean_value (value) == prop_desc.is_enumerable); + + prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "configurable"); + value = jerry_get_property (from_object, prop_name); + TEST_ASSERT (jerry_get_boolean_value (value) == prop_desc.is_configurable); + + jerry_release_value (object); + jerry_release_value (prop_name); + jerry_release_value (value); + jerry_release_value (from_object); + jerry_free_property_descriptor_fields (&prop_desc); + jerry_cleanup (); + return 0; +} /* main */ diff --git a/tests/unit-core/test-to-property-descriptor.c b/tests/unit-core/test-to-property-descriptor.c new file mode 100644 index 000000000..410e0b28e --- /dev/null +++ b/tests/unit-core/test-to-property-descriptor.c @@ -0,0 +1,76 @@ +/* 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 "config.h" +#include "jerryscript.h" +#include "test-common.h" + +int +main (void) +{ + TEST_INIT (); + + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t object = jerry_create_object (); + jerry_value_t prop_name; + jerry_value_t value = jerry_create_boolean (true); + jerry_property_descriptor_t prop_desc; + + prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "value"); + TEST_ASSERT (jerry_set_property (object, prop_name, value)); + + prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "writable"); + TEST_ASSERT (jerry_set_property (object, prop_name, value)); + + prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "writable"); + TEST_ASSERT (jerry_set_property (object, prop_name, value)); + + prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "enumerable"); + TEST_ASSERT (jerry_set_property (object, prop_name, value)); + + prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "configurable"); + TEST_ASSERT (jerry_set_property (object, prop_name, value)); + + bool result = jerry_to_property_descriptor (object, &prop_desc); + TEST_ASSERT (result); + + prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "value"); + value = jerry_get_property (object, prop_name); + TEST_ASSERT (value == prop_desc.value); + + prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "writable"); + value = jerry_get_property (object, prop_name); + TEST_ASSERT (jerry_get_boolean_value (value) == prop_desc.is_writable); + + prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "writable"); + value = jerry_get_property (object, prop_name); + TEST_ASSERT (jerry_get_boolean_value (value) == prop_desc.is_writable); + + prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "enumerable"); + value = jerry_get_property (object, prop_name); + TEST_ASSERT (jerry_get_boolean_value (value) == prop_desc.is_enumerable); + + prop_name = jerry_create_string_from_utf8 ((jerry_char_t *) "configurable"); + value = jerry_get_property (object, prop_name); + TEST_ASSERT (jerry_get_boolean_value (value) == prop_desc.is_configurable); + + jerry_release_value (object); + jerry_release_value (prop_name); + jerry_release_value (value); + jerry_free_property_descriptor_fields (&prop_desc); + jerry_cleanup (); + return 0; +} /* main */