From ca1fff711edbae8114025833169460ffda362429 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Tue, 29 Dec 2020 00:54:43 -0800 Subject: [PATCH] Disguise accessor properties as data properties JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- docs/02.API-REFERENCE.md | 19 ++ jerry-core/api/jerry.c | 24 +- jerry-core/ecma/base/ecma-globals.h | 3 +- jerry-core/ecma/base/ecma-helpers.c | 2 +- .../builtin-objects/ecma-builtin-object.c | 25 ++ jerry-core/ecma/operations/ecma-conversion.c | 44 +++ jerry-core/ecma/operations/ecma-conversion.h | 1 + .../ecma/operations/ecma-objects-general.c | 97 +++++- jerry-core/ecma/operations/ecma-objects.c | 9 +- jerry-core/include/jerryscript-core.h | 3 + tests/unit-core/CMakeLists.txt | 1 + tests/unit-core/test-hide-accessor.c | 282 ++++++++++++++++++ 12 files changed, 492 insertions(+), 18 deletions(-) create mode 100644 tests/unit-core/test-hide-accessor.c diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 946f98a756..ea0a78a345 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -461,6 +461,22 @@ The naming scheme is similar to the JavaScript `Object.defineProperty` method. Fields should be used in pairs. That is if the `is_value_defined` is set to `true` the `value` field should contain the value for the property. +The `is_data_accessor` value is a non-standard extension. When an accessor property is +defined, and the `is_data_accessor` is set to true, the `Object.getOwnPropertyDescriptor` +and `Object.getOwnPropertyDescriptors` ECMAScript functions returns the property as a +data property. The `writable` flag in the returned property descriptor is set to true, +if the property has a `setter` accessor. These types of properties can only be defined +using the JerryScript API. The property is not converted to data property when it is +retrieved using the JerryScript API. Property re-definition has the following rules: + +* When `is_configurable` is set, the property can be re-defined as a real data property +later. + +* When `is_configurable` is not set, the `setter` accessor function is called, and the +`value` provided in the data property descriptor is passed as an argument. If `setter` +is not available, the `value` is compared to the value returned by the `getter` accessor +function. + **Prototype** ```c @@ -493,6 +509,9 @@ typedef struct /** [[Configurable]] */ bool is_configurable; + /** Is data accessor property */ + bool is_data_accessor; + /** [[Value]] */ jerry_value_t value; diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 61db554c4f..e288410fa6 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -3065,6 +3065,7 @@ jerry_init_property_descriptor_fields (jerry_property_descriptor_t *prop_desc_p) prop_desc_p->is_enumerable = false; prop_desc_p->is_configurable_defined = false; prop_desc_p->is_configurable = false; + prop_desc_p->is_data_accessor = false; prop_desc_p->is_get_defined = false; prop_desc_p->getter = ECMA_VALUE_UNDEFINED; prop_desc_p->is_set_defined = false; @@ -3084,31 +3085,33 @@ jerry_property_descriptor_from_ecma (const ecma_property_descriptor_t *prop_desc jerry_property_descriptor_t prop_desc; jerry_init_property_descriptor_fields (&prop_desc); - if (prop_desc_p->flags & (ECMA_PROP_IS_ENUMERABLE_DEFINED)) + 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); + prop_desc.is_enumerable = (prop_desc_p->flags & ECMA_PROP_IS_ENUMERABLE) != 0; } - if (prop_desc_p->flags & (ECMA_PROP_IS_CONFIGURABLE_DEFINED)) + 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_configurable = (prop_desc_p->flags & ECMA_PROP_IS_CONFIGURABLE) != 0; } - prop_desc.is_value_defined = prop_desc_p->flags & (ECMA_PROP_IS_VALUE_DEFINED); + prop_desc.is_value_defined = (prop_desc_p->flags & ECMA_PROP_IS_VALUE_DEFINED) != 0; if (prop_desc.is_value_defined) { prop_desc.value = prop_desc_p->value; } - if (prop_desc_p->flags & (ECMA_PROP_IS_WRITABLE_DEFINED)) + 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); + prop_desc.is_writable = (prop_desc_p->flags & ECMA_PROP_IS_WRITABLE) != 0; } + prop_desc.is_data_accessor = (prop_desc_p->flags & ECMA_PROP_IS_DATA_ACCESSOR) != 0; + if (prop_desc_p->flags & (ECMA_PROP_IS_GET_DEFINED)) { ecma_value_t getter = ecma_make_object_value (prop_desc_p->get_p); @@ -3176,6 +3179,11 @@ jerry_property_descriptor_to_ecma (const jerry_property_descriptor_t *prop_desc_ : ECMA_PROP_NO_OPTS)); } + if (prop_desc_p->is_data_accessor) + { + flags |= ECMA_PROP_IS_DATA_ACCESSOR; + } + /* Copy accessor property info. */ if (prop_desc_p->is_get_defined) { @@ -3311,6 +3319,8 @@ jerry_get_own_property_descriptor (const jerry_value_t obj_val, /**< object val prop_desc_p->is_writable_defined = (prop_desc.flags & ECMA_PROP_IS_WRITABLE_DEFINED) != 0; prop_desc_p->is_writable = prop_desc_p->is_writable_defined ? (prop_desc.flags & ECMA_PROP_IS_WRITABLE) != 0 : false; + prop_desc_p->is_data_accessor = (prop_desc.flags & ECMA_PROP_IS_DATA_ACCESSOR) != 0; + prop_desc_p->is_value_defined = (prop_desc.flags & ECMA_PROP_IS_VALUE_DEFINED) != 0; prop_desc_p->is_get_defined = (prop_desc.flags & ECMA_PROP_IS_GET_DEFINED) != 0; prop_desc_p->is_set_defined = (prop_desc.flags & ECMA_PROP_IS_SET_DEFINED) != 0; diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index d85759f0ab..6eb1da37c7 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -398,6 +398,7 @@ typedef enum ECMA_PROPERTY_FLAG_CONFIGURABLE = 1u << 2, /**< property is configurable */ ECMA_PROPERTY_FLAG_ENUMERABLE = 1u << 3, /**< property is enumerable */ ECMA_PROPERTY_FLAG_WRITABLE = 1u << 4, /**< property is writable */ + ECMA_PROPERTY_FLAG_DATA_ACCESSOR = 1u << 4, /**< property is data accessor */ ECMA_PROPERTY_FLAG_DATA = 1u << 5, /**< property contains data */ } ecma_property_flags_t; @@ -1155,6 +1156,7 @@ typedef enum ECMA_PROP_IS_CONFIGURABLE_DEFINED = (1 << 7), /** Is [[Configurable]] defined? */ ECMA_PROP_IS_ENUMERABLE_DEFINED = (1 << 8), /** Is [[Enumerable]] defined? */ ECMA_PROP_IS_WRITABLE_DEFINED = (1 << 9), /** Is [[Writable]] defined? */ + ECMA_PROP_IS_DATA_ACCESSOR = (1 << 10), /** Is data accessor */ } ecma_property_descriptor_status_flags_t; /** @@ -1169,7 +1171,6 @@ typedef enum */ typedef struct { - /** any combination of ecma_property_descriptor_status_flags_t bits */ uint16_t flags; diff --git a/jerry-core/ecma/base/ecma-helpers.c b/jerry-core/ecma/base/ecma-helpers.c index cac1e9a7e6..c16a4235b1 100644 --- a/jerry-core/ecma/base/ecma-helpers.c +++ b/jerry-core/ecma/base/ecma-helpers.c @@ -548,7 +548,7 @@ ecma_create_named_accessor_property (ecma_object_t *object_p, /**< object */ JERRY_ASSERT (ecma_is_lexical_environment (object_p) || !ecma_op_object_is_fast_array (object_p)); JERRY_ASSERT (ecma_find_named_property (object_p, name_p) == NULL); - JERRY_ASSERT ((prop_attributes & ~ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE) == 0); + JERRY_ASSERT ((prop_attributes & ~(ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE | ECMA_PROPERTY_FLAG_DATA_ACCESSOR)) == 0); uint8_t type_and_flags = prop_attributes; diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-object.c b/jerry-core/ecma/builtin-objects/ecma-builtin-object.c index b96d3ce326..c07bb2782d 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-object.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-object.c @@ -725,6 +725,17 @@ ecma_builtin_object_object_get_own_property_descriptor (ecma_object_t *obj_p, /* if (ecma_is_value_true (status)) { + if (JERRY_UNLIKELY (prop_desc.flags & ECMA_PROP_IS_DATA_ACCESSOR)) + { + status = ecma_op_to_data_property (obj_p, &prop_desc); + + if (ECMA_IS_VALUE_ERROR (status)) + { + ecma_free_property_descriptor (&prop_desc); + return status; + } + } + /* 4. */ ecma_object_t *desc_obj_p = ecma_op_from_property_descriptor (&prop_desc); @@ -786,6 +797,20 @@ ecma_builtin_object_object_get_own_property_descriptors (ecma_object_t *obj_p) / if (ecma_is_value_true (status)) { + if (JERRY_UNLIKELY (prop_desc.flags & ECMA_PROP_IS_DATA_ACCESSOR)) + { + status = ecma_op_to_data_property (obj_p, &prop_desc); + + if (ECMA_IS_VALUE_ERROR (status)) + { + ecma_free_property_descriptor (&prop_desc); + ecma_deref_object (descriptors_p); + ecma_collection_free (prop_names_p); + + return status; + } + } + /* 4.b */ ecma_object_t *desc_obj_p = ecma_op_from_property_descriptor (&prop_desc); /* 4.c */ diff --git a/jerry-core/ecma/operations/ecma-conversion.c b/jerry-core/ecma/operations/ecma-conversion.c index bec94f76f8..ec877f2e9c 100644 --- a/jerry-core/ecma/operations/ecma-conversion.c +++ b/jerry-core/ecma/operations/ecma-conversion.c @@ -907,6 +907,50 @@ ecma_op_to_property_descriptor (ecma_value_t obj_value, /**< object value */ return ret_value; } /* ecma_op_to_property_descriptor */ +/** + * Convert accessor to data property. + * + * @return ECMA_VALUE_EMPTY - if the opertation is successful + * error - otherwise + */ +ecma_value_t +ecma_op_to_data_property (ecma_object_t *object_p, /**< base object */ + ecma_property_descriptor_t *prop_desc_p) /**< [in/out] property descriptor */ +{ + JERRY_ASSERT (prop_desc_p->flags & ECMA_PROP_IS_DATA_ACCESSOR); + + ecma_value_t result = ECMA_VALUE_UNDEFINED; + uint16_t flags = prop_desc_p->flags; + + if ((flags & ECMA_PROP_IS_GET_DEFINED) && prop_desc_p->get_p != NULL) + { + result = ecma_op_function_call (prop_desc_p->get_p, ecma_make_object_value (object_p), NULL, 0); + + if (ECMA_IS_VALUE_ERROR (result)) + { + return result; + } + + ecma_deref_object (prop_desc_p->get_p); + prop_desc_p->get_p = NULL; + } + + if ((flags & ECMA_PROP_IS_SET_DEFINED) && prop_desc_p->set_p != NULL) + { + ecma_deref_object (prop_desc_p->set_p); + prop_desc_p->set_p = NULL; + + flags |= ECMA_PROP_IS_WRITABLE; + } + + flags |= ECMA_PROP_IS_WRITABLE_DEFINED | ECMA_PROP_IS_VALUE_DEFINED; + flags &= (uint16_t) ~(ECMA_PROP_IS_GET_DEFINED | ECMA_PROP_IS_SET_DEFINED | ECMA_PROP_IS_DATA_ACCESSOR); + + prop_desc_p->value = result; + prop_desc_p->flags = flags; + return ECMA_VALUE_EMPTY; +} /* ecma_op_to_data_property */ + /** * IsInteger operation. * diff --git a/jerry-core/ecma/operations/ecma-conversion.h b/jerry-core/ecma/operations/ecma-conversion.h index 32de03757a..63100f0f7f 100644 --- a/jerry-core/ecma/operations/ecma-conversion.h +++ b/jerry-core/ecma/operations/ecma-conversion.h @@ -72,6 +72,7 @@ ecma_collection_t *ecma_op_create_list_from_array_like (ecma_value_t arr, bool p ecma_object_t *ecma_op_from_property_descriptor (const ecma_property_descriptor_t *src_prop_desc_p); ecma_value_t ecma_op_to_property_descriptor (ecma_value_t obj_value, ecma_property_descriptor_t *out_prop_desc_p); +ecma_value_t ecma_op_to_data_property (ecma_object_t *object_p, ecma_property_descriptor_t *prop_desc_p); /** * @} diff --git a/jerry-core/ecma/operations/ecma-objects-general.c b/jerry-core/ecma/operations/ecma-objects-general.c index bbc7f7a351..7e5d44453a 100644 --- a/jerry-core/ecma/operations/ecma-objects-general.c +++ b/jerry-core/ecma/operations/ecma-objects-general.c @@ -411,6 +411,11 @@ ecma_op_general_object_define_own_property (ecma_object_t *object_p, /**< the ob else { /* b. */ + if (property_desc_p->flags & ECMA_PROP_IS_DATA_ACCESSOR) + { + prop_attributes |= ECMA_PROPERTY_FLAG_DATA_ACCESSOR; + } + ecma_create_named_accessor_property (object_p, property_name_p, property_desc_p->get_p, @@ -503,15 +508,9 @@ ecma_op_general_object_define_own_property (ecma_object_t *object_p, /**< the ob } } } - else + else if (is_current_configurable) { /* 9. */ - if (!is_current_configurable) - { - /* a. */ - return ecma_reject (property_desc_p->flags & ECMA_PROP_IS_THROW); - } - ecma_property_value_t *value_p = ext_property_ref.property_ref.value_p; if (property_desc_type == ECMA_OP_OBJECT_DEFINE_ACCESSOR) @@ -548,6 +547,81 @@ ecma_op_general_object_define_own_property (ecma_object_t *object_p, /**< the ob prop_flags ^= ECMA_PROPERTY_FLAG_DATA; *(ext_property_ref.property_p) = prop_flags; } + else + { + /* Property is non-configurable. */ + if ((current_prop & ECMA_PROPERTY_FLAG_DATA) + || !(current_prop & ECMA_PROPERTY_FLAG_DATA_ACCESSOR)) + { + /* a. */ + return ecma_reject (property_desc_p->flags & ECMA_PROP_IS_THROW); + } + + /* Non-standard extension. */ + ecma_getter_setter_pointers_t *getter_setter_pair_p; + getter_setter_pair_p = ecma_get_named_accessor_property (ext_property_ref.property_ref.value_p); + + if (getter_setter_pair_p->setter_cp == JMEM_CP_NULL) + { + const uint16_t mask = ECMA_PROP_IS_WRITABLE_DEFINED | ECMA_PROP_IS_WRITABLE; + + if ((property_desc_p->flags & mask) == mask) + { + return ecma_reject (property_desc_p->flags & ECMA_PROP_IS_THROW); + } + + if (!(property_desc_p->flags & ECMA_PROP_IS_VALUE_DEFINED)) + { + return ECMA_VALUE_TRUE; + } + + ecma_value_t result = ECMA_VALUE_UNDEFINED; + + if (getter_setter_pair_p->getter_cp != JMEM_CP_NULL) + { + ecma_object_t *getter_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, getter_setter_pair_p->getter_cp); + result = ecma_op_function_call (getter_p, ecma_make_object_value (object_p), NULL, 0); + + if (ECMA_IS_VALUE_ERROR (result)) + { + return result; + } + } + + bool same_value = ecma_op_same_value (property_desc_p->value, result); + ecma_free_value (result); + + if (!same_value) + { + return ecma_reject (property_desc_p->flags & ECMA_PROP_IS_THROW); + } + return ECMA_VALUE_TRUE; + } + + if (property_desc_p->flags & ECMA_PROP_IS_VALUE_DEFINED) + { + ecma_object_t *setter_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, getter_setter_pair_p->setter_cp); + + ecma_value_t result; + result = ecma_op_function_call (setter_p, ecma_make_object_value (object_p), &property_desc_p->value, 1); + + if (ECMA_IS_VALUE_ERROR (result)) + { + return result; + } + + ecma_free_value (result); + } + + /* Because the property is non-configurable, it cannot be modified. */ + if ((property_desc_p->flags & ECMA_PROP_IS_WRITABLE_DEFINED) + && !(property_desc_p->flags & ECMA_PROP_IS_WRITABLE)) + { + getter_setter_pair_p->setter_cp = JMEM_CP_NULL; + } + + return ECMA_VALUE_TRUE; + } /* 12. */ if (property_desc_type == ECMA_OP_OBJECT_DEFINE_DATA) @@ -583,6 +657,15 @@ ecma_op_general_object_define_own_property (ecma_object_t *object_p, /**< the ob ext_property_ref.property_ref.value_p, property_desc_p->set_p); } + + if (property_desc_p->flags & ECMA_PROP_IS_DATA_ACCESSOR) + { + *ext_property_ref.property_p |= ECMA_PROPERTY_FLAG_DATA_ACCESSOR; + } + else + { + *ext_property_ref.property_p &= (uint8_t) ~ECMA_PROPERTY_FLAG_DATA_ACCESSOR; + } } if (property_desc_p->flags & ECMA_PROP_IS_ENUMERABLE_DEFINED) diff --git a/jerry-core/ecma/operations/ecma-objects.c b/jerry-core/ecma/operations/ecma-objects.c index 96648844af..4f63476193 100644 --- a/jerry-core/ecma/operations/ecma-objects.c +++ b/jerry-core/ecma/operations/ecma-objects.c @@ -65,8 +65,8 @@ * See also: * ECMA-262 v5, 8.6.2; ECMA-262 v5, Table 8 * - * @return pointer to a property - if it exists, - * NULL (i.e. ecma-undefined) - otherwise. + * @return property descriptor - if it exists, + * ECMA_PROPERTY_TYPE_NOT_FOUND / ECMA_PROPERTY_TYPE_NOT_FOUND_AND_STOP - otherwise. */ ecma_property_t ecma_op_object_get_own_property (ecma_object_t *object_p, /**< the object */ @@ -1935,6 +1935,11 @@ ecma_op_object_get_own_property_descriptor (ecma_object_t *object_p, /**< the ob prop_desc_p->set_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->setter_cp); ecma_ref_object (prop_desc_p->set_p); } + + if (property & ECMA_PROPERTY_FLAG_DATA_ACCESSOR) + { + prop_desc_p->flags |= ECMA_PROP_IS_DATA_ACCESSOR; + } } return ECMA_VALUE_TRUE; diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index 5a5097def9..bcb7888a6d 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -194,6 +194,9 @@ typedef struct /** [[Configurable]] */ bool is_configurable; + /** Is data accessor property */ + bool is_data_accessor; + /** [[Value]] */ jerry_value_t value; diff --git a/tests/unit-core/CMakeLists.txt b/tests/unit-core/CMakeLists.txt index 6981df4522..e982e55cc0 100644 --- a/tests/unit-core/CMakeLists.txt +++ b/tests/unit-core/CMakeLists.txt @@ -51,6 +51,7 @@ set(SOURCE_UNIT_TEST_MAIN_MODULES test-external-string.c test-from-property-descriptor.c test-has-property.c + test-hide-accessor.c test-internal-properties.c test-jmem.c test-json.c diff --git a/tests/unit-core/test-hide-accessor.c b/tests/unit-core/test-hide-accessor.c new file mode 100644 index 0000000000..432264293e --- /dev/null +++ b/tests/unit-core/test-hide-accessor.c @@ -0,0 +1,282 @@ +/* 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 jerry_value_t +define_da_handler (const jerry_value_t func_obj_val, /**< function object */ + const jerry_value_t this_val, /**< this arg */ + const jerry_value_t args_p[], /**< function arguments */ + const jerry_length_t args_cnt) /**< number of function arguments */ +{ + (void) func_obj_val; + (void) this_val; + + if (args_cnt < 4) + { + return jerry_create_error (JERRY_ERROR_RANGE, (const jerry_char_t *) "Four arguments expected"); + } + + jerry_property_descriptor_t descriptor; + jerry_init_property_descriptor_fields (&descriptor); + + descriptor.is_get_defined = true; + descriptor.is_set_defined = true; + descriptor.is_enumerable_defined = true; + descriptor.is_enumerable = true; + descriptor.is_configurable_defined = true; + descriptor.is_configurable = (args_cnt >= 4 + && jerry_value_is_boolean (args_p[4]) + && jerry_get_boolean_value (args_p[4])); + descriptor.is_data_accessor = true; + descriptor.getter = jerry_acquire_value (args_p[2]); + descriptor.setter = jerry_acquire_value (args_p[3]); + + jerry_value_t result = jerry_define_own_property (args_p[0], args_p[1], &descriptor); + + jerry_free_property_descriptor_fields (&descriptor); + return result; +} /* define_da_handler */ + +static jerry_value_t +check_da_handler (const jerry_value_t func_obj_val, /**< function object */ + const jerry_value_t this_val, /**< this arg */ + const jerry_value_t args_p[], /**< function arguments */ + const jerry_length_t args_cnt) /**< number of function arguments */ +{ + (void) func_obj_val; + (void) this_val; + + if (args_cnt < 2) + { + return jerry_create_error (JERRY_ERROR_RANGE, (const jerry_char_t *) "Two arguments expected"); + } + + jerry_property_descriptor_t descriptor; + + TEST_ASSERT (jerry_get_own_property_descriptor (args_p[0], args_p[1], &descriptor)); + TEST_ASSERT (descriptor.is_data_accessor); + + jerry_free_property_descriptor_fields (&descriptor); + return jerry_create_undefined (); +} /* check_da_handler */ + +static jerry_value_t +assert_handler (const jerry_value_t func_obj_val, /**< function object */ + const jerry_value_t this_val, /**< this arg */ + const jerry_value_t args_p[], /**< function arguments */ + const jerry_length_t args_cnt) /**< number of function arguments */ +{ + (void) func_obj_val; + (void) this_val; + + TEST_ASSERT (args_cnt == 1); + TEST_ASSERT (jerry_value_is_boolean (args_p[0])); + TEST_ASSERT (jerry_get_boolean_value (args_p[0])); + return jerry_create_undefined (); +} /* assert_handler */ + +static void +register_function (char *name_p, /**< function name */ + jerry_external_handler_t handler_p) /**< function handler */ +{ + jerry_value_t global_value = jerry_get_global_object (); + jerry_value_t function_value = jerry_create_external_function (handler_p); + jerry_value_t name_value = jerry_create_string ((const jerry_char_t *) name_p); + jerry_value_t result_value = jerry_set_property (global_value, name_value, function_value); + TEST_ASSERT (!jerry_value_is_error (result_value)); + jerry_release_value (result_value); + jerry_release_value (name_value); + jerry_release_value (function_value); + jerry_release_value (global_value); +} /* register_function */ + +static void +eval_and_check_error (char *script_p) /**< script source */ +{ + jerry_value_t result_value; + result_value = jerry_eval ((const jerry_char_t *) script_p, strlen (script_p), JERRY_PARSE_NO_OPTS); + + TEST_ASSERT (!jerry_value_is_error (result_value)); + jerry_release_value (result_value); +} /* eval_and_check_error */ + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + register_function ("defineDA", define_da_handler); + register_function ("checkDA", check_da_handler); + register_function ("assert", assert_handler); + + /* Check getting property descriptors. */ + + eval_and_check_error ("var counter = 0\n" + "var o = { }\n" + "defineDA(o, 'prop',\n" + " function() { counter++; return 5.5 },\n" + " null)\n" + "assert(o.prop === 5.5)\n" + "var d = Object.getOwnPropertyDescriptor(o, 'prop')\n" + "assert(d.value === 5.5)\n" + "assert(d.writable === false)\n" + "assert(!d.hasOwnProperty('get'))\n" + "assert(!d.hasOwnProperty('set'))\n" + "checkDA(o, 'prop')\n" + "assert(counter === 2)\n"); + + eval_and_check_error ("var counter = 0\n" + "var o = { }\n" + "defineDA(o, 'prop',\n" + " function() { counter++; return this.v },\n" + " function(v) { counter++; this.v = v })\n" + "o.prop = 'X'\n" + "assert(o.v === 'X')\n" + "var d = Object.getOwnPropertyDescriptor(o, 'prop')\n" + "assert(d.value === 'X')\n" + "assert(d.writable === true)\n" + "assert(!d.hasOwnProperty('get'))\n" + "assert(!d.hasOwnProperty('set'))\n" + "checkDA(o, 'prop')\n" + "assert(counter === 2)\n"); + + eval_and_check_error ("if (Object.getOwnPropertyDescriptors) {" + " var counter = 0\n" + " var o = { }\n" + " defineDA(o, 'prop',\n" + " function() { counter++; return o },\n" + " function(v) { counter++ })\n" + " var d = Object.getOwnPropertyDescriptors(o)\n" + " assert(d.prop.value === o)\n" + " assert(d.prop.writable === true)\n" + " assert(!d.prop.hasOwnProperty('get'))\n" + " assert(!d.prop.hasOwnProperty('set'))\n" + " checkDA(o, 'prop')\n" + " assert(counter === 1)\n" + "}"); + + /* Check getting property descriptor errors. */ + + eval_and_check_error ("var counter = 0\n" + "var o = { }\n" + "defineDA(o, 'prop',\n" + " function() { counter++; throw 'E1' },\n" + " null)\n" + "try {" + " var d = Object.getOwnPropertyDescriptor(o, 'prop')\n" + " throw 'E2'\n" + "} catch (e) {\n" + " assert(e === 'E1')" + "}\n" + "checkDA(o, 'prop')\n" + "assert(counter === 1)\n"); + + eval_and_check_error ("if (Object.getOwnPropertyDescriptors) {" + " var counter = 0\n" + " var o = { p1:4,\n" + " p2:5 }\n" + " defineDA(o, 'prop',\n" + " function() { counter++; throw 'E1' },\n" + " null)\n" + " try {" + " var d = Object.getOwnPropertyDescriptors(o)\n" + " throw 'E2'\n" + " } catch (e) {\n" + " assert(e === 'E1')" + " }\n" + " checkDA(o, 'prop')\n" + " assert(counter === 1)\n" + "}"); + + /* Check redefining property descriptors. */ + + eval_and_check_error ("var counter = 0\n" + "var o = { }\n" + "defineDA(o, 'prop',\n" + " function() { counter++; return 0 },\n" + " function(v) { counter++ },\n" + " true)\n" + "Object.defineProperty(o, 'prop', { value:'V', writable:true })\n" + "var d = Object.getOwnPropertyDescriptor(o, 'prop')\n" + "assert(d.value === 'V')\n" + "assert(d.writable === true)\n" + "assert(!d.hasOwnProperty('get'))\n" + "assert(!d.hasOwnProperty('set'))\n" + "assert(counter === 0)\n"); + + eval_and_check_error ("var counter = 0\n" + "var o = {}\n" + "defineDA(o, 'prop',\n" + " function() { counter++; return this.v },\n" + " function(v) { counter++; this.v = v })\n" + "Object.defineProperty(o, 'prop', { value:'V', writable:true })\n" + "assert(o.v === 'V')\n" + "var d = Object.getOwnPropertyDescriptor(o, 'prop')\n" + "assert(d.value === 'V')\n" + "assert(d.writable === true)\n" + "assert(!d.hasOwnProperty('get'))\n" + "assert(!d.hasOwnProperty('set'))\n" + "checkDA(o, 'prop')\n" + "assert(counter === 2)\n"); + + eval_and_check_error ("var counter = 0\n" + "var o = {}\n" + "defineDA(o, 'prop',\n" + " function() { counter++; return -7.25 },\n" + " null)\n" + "Object.defineProperty(o, 'prop', { value:-7.25 })\n" + "var d = Object.getOwnPropertyDescriptor(o, 'prop')\n" + "assert(d.value === -7.25)\n" + "assert(d.writable === false)\n" + "assert(!d.hasOwnProperty('get'))\n" + "assert(!d.hasOwnProperty('set'))\n" + "checkDA(o, 'prop')\n" + "assert(counter === 2)\n"); + + eval_and_check_error ("var counter = 0\n" + "var o = {}\n" + "defineDA(o, 'prop',\n" + " function() { counter++; return -7.25 },\n" + " null)\n" + "try {\n" + " Object.defineProperty(o, 'prop', { value:1 })\n" + "} catch (e) {\n" + " assert(e instanceof TypeError)\n" + " counter++" + "}\n" + "assert(counter === 2)\n"); + + eval_and_check_error ("if (Object.defineProperties) {\n" + " var counter = 0\n" + " var o = {}\n" + " defineDA(o, 'prop',\n" + " function() { counter++; return -7.25 },\n" + " null)\n" + " try {\n" + " Object.defineProperties(o, { prop: { value:1 } })\n" + " } catch (e) {\n" + " assert(e instanceof TypeError)\n" + " counter++" + " }\n" + " assert(counter === 2)\n" + "}\n"); + + jerry_cleanup (); + return 0; +} /* main */