Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disguise accessor properties as data properties #4374

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/02.API-REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -493,6 +509,9 @@ typedef struct
/** [[Configurable]] */
bool is_configurable;

/** Is data accessor property */
bool is_data_accessor;

/** [[Value]] */
jerry_value_t value;

Expand Down
24 changes: 17 additions & 7 deletions jerry-core/api/jerry.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion jerry-core/ecma/base/ecma-globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;

/**
Expand All @@ -1169,7 +1171,6 @@ typedef enum
*/
typedef struct
{

/** any combination of ecma_property_descriptor_status_flags_t bits */
uint16_t flags;

Expand Down
2 changes: 1 addition & 1 deletion jerry-core/ecma/base/ecma-helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
25 changes: 25 additions & 0 deletions jerry-core/ecma/builtin-objects/ecma-builtin-object.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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 */
Expand Down
44 changes: 44 additions & 0 deletions jerry-core/ecma/operations/ecma-conversion.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
1 change: 1 addition & 0 deletions jerry-core/ecma/operations/ecma-conversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/**
* @}
Expand Down
97 changes: 90 additions & 7 deletions jerry-core/ecma/operations/ecma-objects-general.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
9 changes: 7 additions & 2 deletions jerry-core/ecma/operations/ecma-objects.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions jerry-core/include/jerryscript-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ typedef struct
/** [[Configurable]] */
bool is_configurable;

/** Is data accessor property */
bool is_data_accessor;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an API breaking change and should be documented.


/** [[Value]] */
jerry_value_t value;

Expand Down
Loading