mirror of https://github.com/svaarala/duktape.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1521 lines
55 KiB
1521 lines
55 KiB
/*
|
|
* [[DefineOwnProperty]]
|
|
*
|
|
* Special handling is needed when an object has exotic [[DefineOwnProperty]]
|
|
* or when a property is stored internally in a special way. Special storage
|
|
* may also mean that only certain attribute configurations are possible.
|
|
*
|
|
* Force flag is a custom feature for the C API which should enable any
|
|
* property attribute change to be made except when prevented by internal
|
|
* limitations (e.g. for Array 'length' only writability attribute can be
|
|
* controlled).
|
|
*
|
|
* [[DefineOwnProperty]] is different from most other property operations
|
|
* in that it deals with potentially partial descriptors, i.e. a descriptor
|
|
* may contain a certain boolean or be missing the key entirely.
|
|
*
|
|
* Performance matters somewhat (but not as much as for [[Get]] and [[Set]])
|
|
* because [[DefineOwnProperty]] is used in many internal algorithms.
|
|
*
|
|
* Stabilization is required for Proxy chains (Proxy revocations can strand
|
|
* the current object), and for Arguments.
|
|
*/
|
|
|
|
#include "duk_internal.h"
|
|
|
|
DUK_LOCAL_DECL duk_bool_t
|
|
duk__prop_defown_strkey_unsafe(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_desc, duk_uint_t defprop_flags);
|
|
DUK_LOCAL_DECL duk_bool_t
|
|
duk__prop_defown_strkey_safe(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_desc, duk_uint_t defprop_flags);
|
|
|
|
DUK_LOCAL_DECL duk_bool_t
|
|
duk__prop_defown_idxkey_unsafe(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_desc, duk_uint_t defprop_flags);
|
|
DUK_LOCAL_DECL duk_bool_t
|
|
duk__prop_defown_idxkey_safe(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_desc, duk_uint_t defprop_flags);
|
|
|
|
#if defined(DUK_USE_PARANOID_ERRORS)
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_error_shared(duk_hthread *thr, duk_uint_t defprop_flags) {
|
|
if (defprop_flags & DUK_DEFPROP_THROW) {
|
|
DUK_ERROR_TYPE(thr, "cannot (re)define property of object");
|
|
}
|
|
return 0;
|
|
}
|
|
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_defown_error_obj_idxkey(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_uarridx_t idx,
|
|
duk_uint_t defprop_flags) {
|
|
DUK_UNREF(obj);
|
|
DUK_UNREF(idx);
|
|
return duk__prop_defown_error_shared(thr, defprop_flags);
|
|
}
|
|
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_defown_error_obj_strkey(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_hstring *key,
|
|
duk_uint_t defprop_flags) {
|
|
DUK_UNREF(obj);
|
|
DUK_UNREF(key);
|
|
|
|
return duk__prop_defown_error_shared(thr, defprop_flags);
|
|
}
|
|
#elif defined(DUK_USE_VERBOSE_ERRORS)
|
|
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_defown_error_obj_idxkey(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_uarridx_t idx,
|
|
duk_uint_t defprop_flags) {
|
|
if (defprop_flags & DUK_DEFPROP_THROW) {
|
|
const char *str1 = duk_push_readable_hobject(thr, obj);
|
|
DUK_ERROR_FMT2(thr, DUK_ERR_TYPE_ERROR, "cannot (re)define property %lu of %s", (unsigned long) idx, str1);
|
|
}
|
|
return 0;
|
|
}
|
|
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_defown_error_obj_strkey(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_hstring *key,
|
|
duk_uint_t defprop_flags) {
|
|
if (defprop_flags & DUK_DEFPROP_THROW) {
|
|
const char *str1 = duk_push_readable_hobject(thr, obj);
|
|
const char *str2 = duk_push_readable_hstring(thr, key);
|
|
DUK_ERROR_FMT2(thr, DUK_ERR_TYPE_ERROR, "cannot (re)define property %s of %s", str2, str1);
|
|
}
|
|
return 0;
|
|
}
|
|
#else
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_error_shared(duk_hthread *thr, duk_uint_t defprop_flags) {
|
|
if (defprop_flags & DUK_DEFPROP_THROW) {
|
|
DUK_ERROR_TYPE(thr, "cannot (re)define property of object");
|
|
}
|
|
return 0;
|
|
}
|
|
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_defown_error_obj_idxkey(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_uarridx_t idx,
|
|
duk_uint_t defprop_flags) {
|
|
DUK_UNREF(obj);
|
|
DUK_UNREF(idx);
|
|
return duk__prop_defown_error_shared(thr, defprop_flags);
|
|
}
|
|
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_defown_error_obj_strkey(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_hstring *key,
|
|
duk_uint_t defprop_flags) {
|
|
DUK_UNREF(obj);
|
|
DUK_UNREF(key);
|
|
return duk__prop_defown_error_shared(thr, defprop_flags);
|
|
}
|
|
#endif /* error model */
|
|
|
|
DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__prop_is_accessor_descriptor(duk_uint_t defprop_flags) {
|
|
duk_bool_t rc = ((defprop_flags & (DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER)) != 0U);
|
|
return rc;
|
|
}
|
|
|
|
DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__prop_is_data_descriptor(duk_uint_t defprop_flags) {
|
|
duk_bool_t rc = ((defprop_flags & (DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE)) != 0U);
|
|
return rc;
|
|
}
|
|
|
|
DUK_LOCAL DUK_ALWAYS_INLINE duk_idx_t duk__prop_defown_getter_index(duk_idx_t idx_desc, duk_uint_t defprop_flags) {
|
|
DUK_UNREF(defprop_flags);
|
|
return idx_desc;
|
|
}
|
|
|
|
DUK_LOCAL DUK_ALWAYS_INLINE duk_idx_t duk__prop_defown_setter_index(duk_idx_t idx_desc, duk_uint_t defprop_flags) {
|
|
return (defprop_flags & DUK_DEFPROP_HAVE_GETTER) ? idx_desc + 1 : idx_desc;
|
|
}
|
|
|
|
/* For a set of immutable attributes of a data property, check whether defprop_flags
|
|
* is trying to (1) change any of the immutable attributes or (2) trying to make the
|
|
* property an accessor. Used to check [[DefineOwnProperty]] handling of virtualized
|
|
* properties where full attribute control is not possible.
|
|
*/
|
|
DUK_LOCAL duk_bool_t duk__prop_validate_immutable_data_desc(duk_uint_t immutable_flags, duk_uint_t defprop_flags) {
|
|
duk_uint_t test_mask;
|
|
duk_uint_t t;
|
|
|
|
DUK_ASSERT((immutable_flags & DUK_PROPDESC_FLAG_ACCESSOR) == 0U);
|
|
|
|
DUK_ASSERT((DUK_DEFPROP_HAVE_WRITABLE >> DUK_DEFPROP_HAVE_SHIFT_COUNT) == DUK_DEFPROP_WRITABLE);
|
|
DUK_ASSERT((DUK_DEFPROP_HAVE_ENUMERABLE >> DUK_DEFPROP_HAVE_SHIFT_COUNT) == DUK_DEFPROP_ENUMERABLE);
|
|
DUK_ASSERT((DUK_DEFPROP_HAVE_CONFIGURABLE >> DUK_DEFPROP_HAVE_SHIFT_COUNT) == DUK_DEFPROP_CONFIGURABLE);
|
|
|
|
test_mask = ((defprop_flags & DUK_DEFPROP_HAVE_WEC) >> DUK_DEFPROP_HAVE_SHIFT_COUNT) |
|
|
(DUK_DEFPROP_HAVE_SETTER | DUK_DEFPROP_HAVE_GETTER);
|
|
t = (defprop_flags ^ immutable_flags) & test_mask;
|
|
if (t != 0U) {
|
|
/* Some WEC flag differs in the 'have flag' set, i.e. we're
|
|
* trying to modify the flag, or get/set is present.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Create new accessor or data property. Missing attributes default to false,
|
|
* so we can just ignore the HAVE mask (any attribute with no HAVE bit MUST
|
|
* be zero).
|
|
*/
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_write_new_slot(duk_hthread *thr,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags,
|
|
duk_propvalue *pv_slot,
|
|
duk_uint8_t *attr_slot) {
|
|
if (DUK_UNLIKELY(defprop_flags & (DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER))) {
|
|
*attr_slot = (defprop_flags & DUK_DEFPROP_EC) | DUK_PROPDESC_FLAG_ACCESSOR;
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_GETTER) {
|
|
pv_slot->a.get = duk_get_hobject(thr, duk__prop_defown_getter_index(idx_desc, defprop_flags));
|
|
} else {
|
|
pv_slot->a.get = NULL;
|
|
}
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_SETTER) {
|
|
pv_slot->a.set = duk_get_hobject(thr, duk__prop_defown_setter_index(idx_desc, defprop_flags));
|
|
} else {
|
|
pv_slot->a.set = NULL;
|
|
}
|
|
DUK_HOBJECT_INCREF_ALLOWNULL(thr, pv_slot->a.get);
|
|
DUK_HOBJECT_INCREF_ALLOWNULL(thr, pv_slot->a.set);
|
|
} else {
|
|
/* Default attributes are 'false', overwrite based on
|
|
* DUK_DEFPROP_HAVE_xxx flags.
|
|
*/
|
|
duk_uint8_t new_attrs;
|
|
|
|
DUK_ASSERT((DUK_DEFPROP_WRITABLE << DUK_DEFPROP_HAVE_SHIFT_COUNT) == DUK_DEFPROP_HAVE_WRITABLE);
|
|
DUK_ASSERT((DUK_DEFPROP_ENUMERABLE << DUK_DEFPROP_HAVE_SHIFT_COUNT) == DUK_DEFPROP_HAVE_ENUMERABLE);
|
|
DUK_ASSERT((DUK_DEFPROP_CONFIGURABLE << DUK_DEFPROP_HAVE_SHIFT_COUNT) == DUK_DEFPROP_HAVE_CONFIGURABLE);
|
|
DUK_ASSERT((DUK_DEFPROP_WEC & 0x07U) == DUK_DEFPROP_WEC);
|
|
|
|
new_attrs = (duk_uint8_t) ((defprop_flags & (defprop_flags >> DUK_DEFPROP_HAVE_SHIFT_COUNT)) & DUK_DEFPROP_WEC);
|
|
*attr_slot = new_attrs;
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_VALUE) {
|
|
duk_tval *tv_src = duk_require_tval(thr, idx_desc);
|
|
DUK_TVAL_SET_TVAL_INCREF(thr, &pv_slot->v, tv_src);
|
|
} else {
|
|
DUK_TVAL_SET_UNDEFINED(&pv_slot->v);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Convert an existing data property into an accessor. */
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_update_convert_to_accessor(duk_hthread *thr,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags,
|
|
duk_propvalue *pv_slot,
|
|
duk_uint8_t *attr_slot,
|
|
duk_uint8_t attrs,
|
|
duk_uint8_t curr_configurable,
|
|
duk_uint_t have_shifted) {
|
|
duk_tval tv_old;
|
|
duk_hobject *new_get = NULL;
|
|
duk_hobject *new_set = NULL;
|
|
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_GETTER) {
|
|
new_get = duk_get_hobject(thr, duk__prop_defown_getter_index(idx_desc, defprop_flags));
|
|
}
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_SETTER) {
|
|
new_set = duk_get_hobject(thr, duk__prop_defown_setter_index(idx_desc, defprop_flags));
|
|
}
|
|
|
|
if (DUK_UNLIKELY(!curr_configurable && !(defprop_flags & DUK_DEFPROP_FORCE))) {
|
|
goto fail_not_configurable;
|
|
}
|
|
|
|
have_shifted &= DUK_DEFPROP_EC; /* Restrict HAVE flags to EC. */
|
|
attrs &= DUK_PROPDESC_FLAGS_EC; /* Keep EC, zero rest. */
|
|
attrs &= ~((duk_uint8_t) have_shifted); /* Zero anything provided. */
|
|
attrs |= (duk_uint8_t) (defprop_flags & have_shifted); /* Set anything provided. */
|
|
attrs |= DUK_PROPDESC_FLAG_ACCESSOR;
|
|
*attr_slot = attrs;
|
|
DUK_TVAL_SET_TVAL(&tv_old, &pv_slot->v);
|
|
pv_slot->a.get = new_get;
|
|
pv_slot->a.set = new_set;
|
|
DUK_HOBJECT_INCREF_ALLOWNULL(thr, new_get);
|
|
DUK_HOBJECT_INCREF_ALLOWNULL(thr, new_set);
|
|
DUK_TVAL_DECREF_NORZ(thr, &tv_old);
|
|
DUK_REFZERO_CHECK_SLOW(thr);
|
|
return 1;
|
|
|
|
fail_not_configurable:
|
|
return 0;
|
|
}
|
|
|
|
/* Convert an existing accessor property to a data property. */
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_update_convert_to_data(duk_hthread *thr,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags,
|
|
duk_propvalue *pv_slot,
|
|
duk_uint8_t *attr_slot,
|
|
duk_uint8_t attrs,
|
|
duk_uint8_t curr_configurable,
|
|
duk_uint_t have_shifted) {
|
|
duk_hobject *old_get;
|
|
duk_hobject *old_set;
|
|
|
|
if (DUK_UNLIKELY(!curr_configurable && !(defprop_flags & DUK_DEFPROP_FORCE))) {
|
|
goto fail_not_configurable;
|
|
}
|
|
|
|
have_shifted &= DUK_DEFPROP_WEC; /* Restrict HAVE flags to WEC. */
|
|
attrs &= DUK_PROPDESC_FLAGS_EC; /* Keep EC, zero rest. */
|
|
attrs &= ~((duk_uint8_t) have_shifted); /* Zero anything provided. */
|
|
attrs |= (duk_uint8_t) (defprop_flags & have_shifted); /* Set anything provided. */
|
|
*attr_slot = attrs;
|
|
old_get = pv_slot->a.get;
|
|
old_set = pv_slot->a.set;
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_VALUE) {
|
|
duk_tval *tv_src = duk_require_tval(thr, idx_desc);
|
|
DUK_TVAL_SET_TVAL_INCREF(thr, &pv_slot->v, tv_src);
|
|
} else {
|
|
DUK_TVAL_SET_UNDEFINED(&pv_slot->v);
|
|
}
|
|
DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, old_get);
|
|
DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, old_set);
|
|
DUK_REFZERO_CHECK_SLOW(thr);
|
|
return 1;
|
|
|
|
fail_not_configurable:
|
|
return 0;
|
|
}
|
|
|
|
/* Update an existing accessor property. */
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_update_keep_accessor(duk_hthread *thr,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags,
|
|
duk_propvalue *pv_slot,
|
|
duk_uint8_t *attr_slot,
|
|
duk_uint8_t attrs,
|
|
duk_uint8_t curr_configurable,
|
|
duk_uint_t have_shifted) {
|
|
duk_hobject *new_get = NULL;
|
|
duk_hobject *new_set = NULL;
|
|
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_GETTER) {
|
|
new_get = duk_get_hobject(thr, duk__prop_defown_getter_index(idx_desc, defprop_flags));
|
|
}
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_SETTER) {
|
|
new_set = duk_get_hobject(thr, duk__prop_defown_setter_index(idx_desc, defprop_flags));
|
|
}
|
|
|
|
have_shifted &= DUK_DEFPROP_EC; /* Restrict HAVE flags to EC. */
|
|
if (DUK_UNLIKELY(!curr_configurable && !(defprop_flags & DUK_DEFPROP_FORCE))) {
|
|
/* Configurable: must not change.
|
|
* Enumerable: must not change.
|
|
* Getter: must not change.
|
|
* Setter: must not change.
|
|
*/
|
|
if (((attrs ^ (duk_uint8_t) (defprop_flags & 0xffU)) & have_shifted) != 0U) {
|
|
goto fail_not_configurable;
|
|
}
|
|
if ((defprop_flags & DUK_DEFPROP_HAVE_GETTER) && (new_get != pv_slot->a.get)) {
|
|
goto fail_not_configurable;
|
|
}
|
|
if ((defprop_flags & DUK_DEFPROP_HAVE_SETTER) && (new_set != pv_slot->a.set)) {
|
|
goto fail_not_configurable;
|
|
}
|
|
}
|
|
|
|
attrs &= ~((duk_uint8_t) have_shifted); /* Zero anything provided. */
|
|
attrs |= (duk_uint8_t) (defprop_flags & have_shifted); /* Set anything provided. */
|
|
*attr_slot = attrs;
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_GETTER) {
|
|
duk_hobject *old_get = pv_slot->a.get;
|
|
pv_slot->a.get = duk_get_hobject(thr, duk__prop_defown_getter_index(idx_desc, defprop_flags));
|
|
DUK_HOBJECT_INCREF_ALLOWNULL(thr, pv_slot->a.get);
|
|
DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, old_get);
|
|
}
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_SETTER) {
|
|
duk_hobject *old_set = pv_slot->a.set;
|
|
pv_slot->a.set = duk_get_hobject(thr, duk__prop_defown_setter_index(idx_desc, defprop_flags));
|
|
DUK_HOBJECT_INCREF_ALLOWNULL(thr, pv_slot->a.set);
|
|
DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, old_set);
|
|
}
|
|
DUK_REFZERO_CHECK_SLOW(thr);
|
|
return 1;
|
|
|
|
fail_not_configurable:
|
|
return 0;
|
|
}
|
|
|
|
/* Update an existing data property. */
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_update_keep_data(duk_hthread *thr,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags,
|
|
duk_propvalue *pv_slot,
|
|
duk_uint8_t *attr_slot,
|
|
duk_uint8_t attrs,
|
|
duk_uint8_t curr_configurable,
|
|
duk_uint_t have_shifted) {
|
|
have_shifted &= DUK_DEFPROP_WEC; /* Restrict HAVE flags to WEC. */
|
|
if (DUK_UNLIKELY(!curr_configurable && !(defprop_flags & DUK_DEFPROP_FORCE))) {
|
|
/* Configurable: must not change.
|
|
* Enumerable: must not change.
|
|
* Writable: can go from true to false, but not vice versa.
|
|
* Value: must not change.
|
|
*/
|
|
duk_uint_t have_shifted_ec = (have_shifted & DUK_DEFPROP_EC);
|
|
|
|
if (((attrs ^ (duk_uint8_t) (defprop_flags & 0xffU)) & have_shifted_ec) != 0U) {
|
|
DUK_DD(DUK_DDPRINT("EC flag conflict"));
|
|
goto fail_not_configurable;
|
|
}
|
|
if ((attrs & DUK_PROPDESC_FLAG_WRITABLE) == 0U) {
|
|
if ((defprop_flags & (DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE)) ==
|
|
(DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE)) {
|
|
DUK_DD(DUK_DDPRINT("W flag conflict"));
|
|
goto fail_not_configurable;
|
|
}
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_VALUE) {
|
|
duk_tval *tv_src = duk_require_tval(thr, idx_desc);
|
|
if (!duk_js_samevalue(&pv_slot->v, tv_src)) {
|
|
DUK_DD(DUK_DDPRINT("[[Value]] conflict"));
|
|
goto fail_not_configurable;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
attrs &= ~((duk_uint8_t) have_shifted); /* Zero anything provided. */
|
|
attrs |= (duk_uint8_t) (defprop_flags & have_shifted); /* Set anything provided. */
|
|
*attr_slot = attrs;
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_VALUE) {
|
|
duk_tval *tv_src = duk_require_tval(thr, idx_desc);
|
|
DUK_TVAL_SET_TVAL_UPDREF_NORZ(thr, &pv_slot->v, tv_src);
|
|
}
|
|
DUK_REFZERO_CHECK_SLOW(thr);
|
|
return 1;
|
|
|
|
fail_not_configurable:
|
|
return 0;
|
|
}
|
|
|
|
/* Update a property in an existing slot, possibly converting it from a data
|
|
* property to an accessor property or vice versa.
|
|
*/
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_update_existing_slot(duk_hthread *thr,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags,
|
|
duk_propvalue *pv_slot,
|
|
duk_uint8_t *attr_slot) {
|
|
duk_uint8_t attrs;
|
|
duk_uint8_t curr_configurable;
|
|
duk_uint_t have_shifted;
|
|
|
|
/* In principle there is a separate validation step followed by an
|
|
* update step, see ValidateAndApplyPropertyDescriptor(). The
|
|
* algorithm here is modified in two ways:
|
|
*
|
|
* - The validation and application step are combined.
|
|
* - There is a 4-way initial branch deciding whether we're
|
|
* a) updating a data property,
|
|
* b) updating an accessor property,
|
|
* c) converting a data property to an accessor property, or
|
|
* d) converting an accessor property to a data property.
|
|
* All attribute checks are inlined for each case.
|
|
*
|
|
* The general principles are:
|
|
*
|
|
* - If a property is configurable, any changes are allowed.
|
|
* - If a property is not configurable, no changes are allowed, except:
|
|
* 1. writing SameValue() to existing attribute is allowed; and
|
|
* 2. a writable property can be made non-writable.
|
|
*/
|
|
|
|
attrs = *attr_slot;
|
|
curr_configurable = (attrs & DUK_PROPDESC_FLAG_CONFIGURABLE);
|
|
have_shifted = (defprop_flags & DUK_DEFPROP_HAVE_WEC) >> DUK_DEFPROP_HAVE_SHIFT_COUNT;
|
|
|
|
if (DUK_UNLIKELY(attrs & DUK_PROPDESC_FLAG_ACCESSOR)) {
|
|
DUK_ASSERT((attrs & DUK_PROPDESC_FLAG_WRITABLE) == 0U);
|
|
if (defprop_flags & (DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE)) {
|
|
/* Convert from accessor to data property. */
|
|
return duk__prop_defown_update_convert_to_data(thr,
|
|
idx_desc,
|
|
defprop_flags,
|
|
pv_slot,
|
|
attr_slot,
|
|
attrs,
|
|
curr_configurable,
|
|
have_shifted);
|
|
} else {
|
|
/* Keep as an accessor. */
|
|
return duk__prop_defown_update_keep_accessor(thr,
|
|
idx_desc,
|
|
defprop_flags,
|
|
pv_slot,
|
|
attr_slot,
|
|
attrs,
|
|
curr_configurable,
|
|
have_shifted);
|
|
}
|
|
} else {
|
|
if (DUK_UNLIKELY(defprop_flags & (DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER))) {
|
|
/* Convert from data property to accessor. */
|
|
return duk__prop_defown_update_convert_to_accessor(thr,
|
|
idx_desc,
|
|
defprop_flags,
|
|
pv_slot,
|
|
attr_slot,
|
|
attrs,
|
|
curr_configurable,
|
|
have_shifted);
|
|
} else {
|
|
/* Keep as a data property. */
|
|
return duk__prop_defown_update_keep_data(thr,
|
|
idx_desc,
|
|
defprop_flags,
|
|
pv_slot,
|
|
attr_slot,
|
|
attrs,
|
|
curr_configurable,
|
|
have_shifted);
|
|
}
|
|
}
|
|
}
|
|
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_strkey_ordinary(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_hstring *key,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags) {
|
|
duk_uint_fast32_t ent_idx;
|
|
|
|
DUK_ASSERT(thr != NULL);
|
|
DUK_ASSERT(obj != NULL);
|
|
DUK_ASSERT(key != NULL);
|
|
DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key));
|
|
|
|
if (DUK_UNLIKELY(duk_hobject_lookup_strprop_index(thr, obj, key, &ent_idx))) {
|
|
duk_propvalue *val_base;
|
|
duk_hstring **key_base;
|
|
duk_uint8_t *attr_base;
|
|
duk_propvalue *pv_slot;
|
|
duk_uint8_t *attr_slot;
|
|
|
|
duk_hobject_get_strprops_key_attr(thr->heap, obj, &val_base, &key_base, &attr_base);
|
|
|
|
DUK_ASSERT(key_base[ent_idx] == key);
|
|
pv_slot = val_base + ent_idx;
|
|
attr_slot = attr_base + ent_idx;
|
|
|
|
return duk__prop_defown_update_existing_slot(thr, idx_desc, defprop_flags, pv_slot, attr_slot);
|
|
} else {
|
|
duk_propvalue *val_base;
|
|
duk_hstring **key_base;
|
|
duk_uint8_t *attr_base;
|
|
duk_propvalue *pv_slot;
|
|
duk_uint8_t *attr_slot;
|
|
|
|
if (DUK_UNLIKELY(!DUK_HOBJECT_HAS_EXTENSIBLE(obj) && !(defprop_flags & DUK_DEFPROP_FORCE))) {
|
|
goto fail_not_extensible;
|
|
}
|
|
|
|
ent_idx = (duk_uint_fast32_t) duk_hobject_alloc_strentry_checked(thr, obj, key);
|
|
duk_hobject_get_strprops_key_attr(thr->heap, obj, &val_base, &key_base, &attr_base);
|
|
DUK_ASSERT(key_base[ent_idx] == key);
|
|
pv_slot = val_base + ent_idx;
|
|
attr_slot = attr_base + ent_idx;
|
|
|
|
return duk__prop_defown_write_new_slot(thr, idx_desc, defprop_flags, pv_slot, attr_slot);
|
|
}
|
|
|
|
fail_not_extensible:
|
|
return 0;
|
|
}
|
|
|
|
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_defown_strkey_stringobj_length(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags) {
|
|
duk_hstring *h;
|
|
|
|
DUK_ASSERT(thr != NULL);
|
|
DUK_ASSERT(obj != NULL);
|
|
DUK_ASSERT(DUK_HOBJECT_GET_HTYPE(obj) == DUK_HTYPE_STRING_OBJECT);
|
|
|
|
h = duk_hobject_lookup_intvalue_hstring(thr, obj);
|
|
if (h != NULL) {
|
|
if (!duk__prop_validate_immutable_data_desc(DUK_PROPDESC_FLAGS_NONE, defprop_flags)) {
|
|
goto fail_invalid_desc;
|
|
}
|
|
DUK_ASSERT((defprop_flags & (DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER)) == 0U);
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_VALUE) {
|
|
duk_tval tv_tmp;
|
|
DUK_TVAL_SET_U32(&tv_tmp, duk_hstring_get_charlen(h));
|
|
if (!duk_js_samevalue(duk_require_tval(thr, idx_desc), &tv_tmp)) {
|
|
goto fail_invalid_desc;
|
|
}
|
|
}
|
|
} else {
|
|
goto fail_internal;
|
|
}
|
|
return 1;
|
|
|
|
fail_invalid_desc:
|
|
fail_internal:
|
|
return 0;
|
|
}
|
|
|
|
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
|
|
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_defown_strkey_bufobj_length(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags) {
|
|
duk_hbufobj *h;
|
|
|
|
DUK_ASSERT(DUK_HOBJECT_IS_ANY_BUFOBJ(obj));
|
|
|
|
h = (duk_hbufobj *) obj;
|
|
if (!duk__prop_validate_immutable_data_desc(DUK_PROPDESC_FLAGS_NONE, defprop_flags)) {
|
|
goto invalid_desc;
|
|
}
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_VALUE) {
|
|
duk_tval tv_tmp;
|
|
DUK_TVAL_SET_U32(&tv_tmp, (duk_uint32_t) DUK_HBUFOBJ_GET_LOGICAL_LENGTH(h));
|
|
if (!duk_js_samevalue(duk_require_tval(thr, idx_desc), &tv_tmp)) {
|
|
goto invalid_desc;
|
|
}
|
|
}
|
|
return 1;
|
|
|
|
invalid_desc:
|
|
return 0;
|
|
}
|
|
#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
|
|
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_strkey_array_length(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags) {
|
|
duk_harray *a = (duk_harray *) obj;
|
|
duk_bool_t want_write_protect;
|
|
duk_bool_t rc;
|
|
|
|
DUK_ASSERT(thr != NULL);
|
|
DUK_ASSERT(obj != NULL);
|
|
DUK_ASSERT(DUK_HOBJECT_GET_HTYPE(obj) == DUK_HTYPE_ARRAY);
|
|
|
|
/* Descriptor compatibility:
|
|
* - length is always non-configurable: cannot change to configurable
|
|
* - length is always non-enumerable: cannot change to enumerable
|
|
* - length cannot be made an accessor
|
|
* - length may be writable or non-writable;
|
|
* + if writable, can be changed to non-writable (but length updated first)
|
|
* + if not writable, cannot change to writable
|
|
*/
|
|
|
|
if ((defprop_flags & (DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_CONFIGURABLE)) ==
|
|
(DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_CONFIGURABLE)) {
|
|
DUK_DD(DUK_DDPRINT("cannot make .length configurable"));
|
|
goto fail_invalid_desc;
|
|
}
|
|
if ((defprop_flags & (DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE)) ==
|
|
(DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE)) {
|
|
DUK_DD(DUK_DDPRINT("cannot make .length enumerable"));
|
|
goto fail_invalid_desc;
|
|
}
|
|
if (DUK_HARRAY_LENGTH_NONWRITABLE(a) && (defprop_flags & (DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE)) ==
|
|
(DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE)) {
|
|
if (DUK_UNLIKELY(defprop_flags & DUK_DEFPROP_FORCE)) {
|
|
/* XXX: Allow forcing? */
|
|
}
|
|
DUK_DD(DUK_DDPRINT("cannot make .length writable again"));
|
|
goto fail_invalid_desc;
|
|
}
|
|
if (duk__prop_is_accessor_descriptor(defprop_flags)) {
|
|
DUK_DD(DUK_DDPRINT("cannot make .length an accessor"));
|
|
goto fail_invalid_desc;
|
|
}
|
|
want_write_protect = ((defprop_flags & (DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE)) == DUK_DEFPROP_HAVE_WRITABLE);
|
|
|
|
rc = 1;
|
|
|
|
/* The specification steps are quite different here. Main points are:
|
|
* - When reducing .length: if a non-configurable element at index >= new_len
|
|
* cannot be deleted, we stop at that position, and update the length to
|
|
* match that element.
|
|
* - If a write protect is pending, it is applied EVEN if the length
|
|
* update succeeds only partially (or not at all, i.e. first element
|
|
* cannot be deleted).
|
|
*/
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_VALUE) {
|
|
duk_uint32_t new_len;
|
|
|
|
new_len = duk_harray_to_array_length_checked(thr, duk_require_tval(thr, idx_desc));
|
|
if (DUK_LIKELY(new_len != DUK_HARRAY_GET_LENGTH(a))) {
|
|
/* Technically a SameValue() comparison. */
|
|
if (DUK_UNLIKELY(DUK_HARRAY_LENGTH_NONWRITABLE(a) && !(defprop_flags & DUK_DEFPROP_FORCE))) {
|
|
DUK_DD(DUK_DDPRINT("length non-writable, not SameValue()"));
|
|
goto fail_length_nonwritable;
|
|
}
|
|
|
|
rc = duk_harray_put_array_length_u32(thr, obj, new_len, defprop_flags & DUK_DEFPROP_FORCE /*force_flag*/);
|
|
}
|
|
}
|
|
|
|
if (want_write_protect) {
|
|
DUK_HARRAY_SET_LENGTH_NONWRITABLE(a);
|
|
}
|
|
|
|
return rc;
|
|
|
|
fail_invalid_desc:
|
|
fail_length_nonwritable:
|
|
return 0;
|
|
}
|
|
|
|
#if defined(DUK_USE_PROXY_POLICY)
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_proxy_policy(duk_hthread *thr, duk_hobject *obj, duk_bool_t trap_rc) {
|
|
duk_hobject *target;
|
|
duk_small_int_t attrs;
|
|
|
|
DUK_ASSERT(trap_rc == 1);
|
|
DUK_UNREF(trap_rc);
|
|
|
|
target = duk_proxy_get_target_autothrow(thr, (duk_hproxy *) obj);
|
|
DUK_ASSERT(target != NULL);
|
|
|
|
attrs = duk_prop_getowndesc_obj_tvkey(thr, target, duk_require_tval(thr, -1));
|
|
duk_prop_pop_propdesc(thr, attrs);
|
|
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_proxy_tail(duk_hthread *thr, duk_hobject *obj, duk_idx_t idx_desc, duk_uint_t defprop_flags) {
|
|
duk_bool_t trap_rc;
|
|
duk_bool_t rc;
|
|
|
|
DUK_ASSERT(thr != NULL);
|
|
|
|
/* [ ... trap handler target key ] */
|
|
|
|
duk_dup_top(thr);
|
|
duk_insert(thr, -5); /* Stash key for policy check. */
|
|
|
|
/* [ ... key trap handler target key ] */
|
|
|
|
duk_prop_frompropdesc_with_idx(thr, idx_desc, (duk_int_t) defprop_flags);
|
|
duk_call_method(thr, 3); /* [ ... key trap handler target key desc ] -> [ ... key result ] */
|
|
|
|
trap_rc = duk_to_boolean_top_pop(thr);
|
|
if (!trap_rc) {
|
|
duk_pop_known(thr);
|
|
return 0;
|
|
}
|
|
|
|
#if defined(DUK_USE_PROXY_POLICY)
|
|
rc = duk__prop_defown_proxy_policy(thr, obj, trap_rc);
|
|
#else
|
|
DUK_DD(DUK_DDPRINT("proxy policy check for 'defineProperty' trap disabled in configuration"));
|
|
rc = 1;
|
|
#endif
|
|
|
|
duk_pop_known(thr);
|
|
return rc;
|
|
}
|
|
|
|
DUK_LOCAL duk_small_int_t
|
|
duk__prop_defown_strkey_proxy(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_desc, duk_uint_t defprop_flags) {
|
|
DUK_ASSERT(thr != NULL);
|
|
DUK_ASSERT(obj != NULL);
|
|
DUK_ASSERT(DUK_HOBJECT_GET_HTYPE(obj) == DUK_HTYPE_PROXY);
|
|
DUK_ASSERT(key != NULL);
|
|
|
|
if (duk_proxy_trap_check_strkey(thr, (duk_hproxy *) obj, key, DUK_STRIDX_DEFINE_PROPERTY)) {
|
|
duk_push_hstring(thr, key);
|
|
return (duk_small_int_t) duk__prop_defown_proxy_tail(thr, obj, idx_desc, defprop_flags);
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_strkey_helper(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_hstring *key,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags,
|
|
duk_bool_t side_effect_safe) {
|
|
duk_small_uint_t htype;
|
|
duk_hobject *target;
|
|
duk_bool_t rc;
|
|
|
|
DUK_ASSERT(thr != NULL);
|
|
DUK_ASSERT(obj != NULL);
|
|
DUK_ASSERT(key != NULL);
|
|
DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key));
|
|
|
|
target = obj;
|
|
if (side_effect_safe) {
|
|
duk_push_hobject(thr, target);
|
|
}
|
|
|
|
retry_target:
|
|
htype = DUK_HEAPHDR_GET_HTYPE((duk_heaphdr *) target);
|
|
DUK_ASSERT(DUK_HTYPE_IS_ANY_OBJECT(htype));
|
|
|
|
switch (htype) {
|
|
case DUK_HTYPE_ARRAY:
|
|
if (DUK_HSTRING_HAS_LENGTH(key)) {
|
|
rc = duk__prop_defown_strkey_array_length(thr, target, idx_desc, defprop_flags);
|
|
goto check_rc;
|
|
}
|
|
break;
|
|
case DUK_HTYPE_ARGUMENTS:
|
|
/* Arguments [[DefineOwnProperty]] simplifies to
|
|
* OrdinaryDefineOwnProperty() for non-index keys.
|
|
*/
|
|
break;
|
|
case DUK_HTYPE_STRING_OBJECT:
|
|
/* No exotic behavior for [[Length]] but because it is a
|
|
* virtual property, check for descriptor compatibility
|
|
* and ignore possible write.
|
|
*/
|
|
if (DUK_HSTRING_HAS_LENGTH(key)) {
|
|
rc = duk__prop_defown_strkey_stringobj_length(thr, target, idx_desc, defprop_flags);
|
|
goto check_rc;
|
|
}
|
|
break;
|
|
case DUK_HTYPE_PROXY:
|
|
if (side_effect_safe) {
|
|
duk_small_int_t proxy_rc;
|
|
proxy_rc = duk__prop_defown_strkey_proxy(thr, target, key, idx_desc, defprop_flags);
|
|
if (proxy_rc < 0) {
|
|
/* No trap, continue to target. */
|
|
duk_hobject *next;
|
|
|
|
next = duk_proxy_get_target_autothrow(thr, (duk_hproxy *) target);
|
|
DUK_ASSERT(next != NULL);
|
|
|
|
target = duk_prop_switch_stabilized_target_top(thr, target, next);
|
|
goto retry_target;
|
|
} else {
|
|
rc = (duk_bool_t) proxy_rc;
|
|
goto check_rc;
|
|
}
|
|
} else {
|
|
goto switch_to_safe;
|
|
}
|
|
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
|
|
case DUK_HTYPE_ARRAYBUFFER:
|
|
case DUK_HTYPE_DATAVIEW:
|
|
break;
|
|
case DUK_HTYPE_INT8ARRAY:
|
|
case DUK_HTYPE_UINT8ARRAY:
|
|
case DUK_HTYPE_UINT8CLAMPEDARRAY:
|
|
case DUK_HTYPE_INT16ARRAY:
|
|
case DUK_HTYPE_UINT16ARRAY:
|
|
case DUK_HTYPE_INT32ARRAY:
|
|
case DUK_HTYPE_UINT32ARRAY:
|
|
case DUK_HTYPE_FLOAT32ARRAY:
|
|
case DUK_HTYPE_FLOAT64ARRAY:
|
|
if (DUK_HSTRING_HAS_LENGTH_OR_CANNUM(key)) {
|
|
if (DUK_HSTRING_HAS_LENGTH(key)) {
|
|
/* Custom own .length property. */
|
|
rc = duk__prop_defown_strkey_bufobj_length(thr, target, idx_desc, defprop_flags);
|
|
goto check_rc;
|
|
} else {
|
|
DUK_ASSERT(DUK_HSTRING_HAS_CANNUM(key));
|
|
/* Never accepted, short circuited as error. */
|
|
goto fail_invalid_index;
|
|
}
|
|
}
|
|
break;
|
|
#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
|
|
default:
|
|
break;
|
|
}
|
|
|
|
rc = duk__prop_defown_strkey_ordinary(thr, target, key, idx_desc, defprop_flags);
|
|
/* fall through */
|
|
|
|
check_rc:
|
|
if (side_effect_safe) {
|
|
duk_pop_known(thr);
|
|
}
|
|
if (DUK_UNLIKELY(rc == 0)) {
|
|
return duk__prop_defown_error_obj_strkey(thr, target, key, defprop_flags);
|
|
}
|
|
return rc;
|
|
|
|
fail_invalid_index:
|
|
rc = 0;
|
|
goto check_rc;
|
|
|
|
switch_to_safe:
|
|
return duk__prop_defown_strkey_safe(thr, obj, key, idx_desc, defprop_flags);
|
|
}
|
|
|
|
DUK_LOCAL duk_bool_t
|
|
duk__prop_defown_strkey_unsafe(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_desc, duk_uint_t defprop_flags) {
|
|
#if defined(DUK_USE_PREFER_SIZE)
|
|
return duk__prop_defown_strkey_safe(thr, obj, key, idx_desc, defprop_flags);
|
|
#else
|
|
return duk__prop_defown_strkey_helper(thr, obj, key, idx_desc, defprop_flags, 0 /*side_effect_safe*/);
|
|
#endif
|
|
}
|
|
|
|
DUK_LOCAL duk_bool_t
|
|
duk__prop_defown_strkey_safe(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_desc, duk_uint_t defprop_flags) {
|
|
return duk__prop_defown_strkey_helper(thr, obj, key, idx_desc, defprop_flags, 1 /*side_effect_safe*/);
|
|
}
|
|
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_bufobj_write(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_val) {
|
|
duk_hbufobj *h = (duk_hbufobj *) obj;
|
|
duk_bool_t our_rc;
|
|
|
|
duk_push_hobject(thr, obj);
|
|
duk_dup(thr, idx_val);
|
|
(void) duk_to_number_m1(thr);
|
|
|
|
our_rc = duk_hbufobj_validate_and_write_top(thr, h, idx);
|
|
duk_pop_2_known(thr);
|
|
return our_rc;
|
|
}
|
|
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_idxkey_ordinary(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_uarridx_t idx,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags) {
|
|
duk_uint_fast32_t ent_idx;
|
|
|
|
DUK_ASSERT(thr != NULL);
|
|
DUK_ASSERT(obj != NULL);
|
|
|
|
if (DUK_UNLIKELY(duk_hobject_lookup_idxprop_index(thr, obj, idx, &ent_idx))) {
|
|
duk_propvalue *val_base;
|
|
duk_uarridx_t *key_base;
|
|
duk_uint8_t *attr_base;
|
|
duk_propvalue *pv_slot;
|
|
duk_uint8_t *attr_slot;
|
|
|
|
duk_hobject_get_idxprops_key_attr(thr->heap, obj, &val_base, &key_base, &attr_base);
|
|
|
|
DUK_ASSERT(key_base[ent_idx] == idx);
|
|
pv_slot = val_base + ent_idx;
|
|
attr_slot = attr_base + ent_idx;
|
|
|
|
return duk__prop_defown_update_existing_slot(thr, idx_desc, defprop_flags, pv_slot, attr_slot);
|
|
} else {
|
|
duk_propvalue *val_base;
|
|
duk_uarridx_t *key_base;
|
|
duk_uint8_t *attr_base;
|
|
duk_propvalue *pv_slot;
|
|
duk_uint8_t *attr_slot;
|
|
|
|
if (DUK_UNLIKELY(!DUK_HOBJECT_HAS_EXTENSIBLE(obj) && !(defprop_flags & DUK_DEFPROP_FORCE))) {
|
|
goto fail_not_extensible;
|
|
}
|
|
ent_idx = (duk_uint_fast32_t) duk_hobject_alloc_idxentry_checked(thr, obj, idx);
|
|
duk_hobject_get_idxprops_key_attr(thr->heap, obj, &val_base, &key_base, &attr_base);
|
|
DUK_ASSERT(key_base[ent_idx] == idx);
|
|
pv_slot = val_base + ent_idx;
|
|
attr_slot = attr_base + ent_idx;
|
|
|
|
return duk__prop_defown_write_new_slot(thr, idx_desc, defprop_flags, pv_slot, attr_slot);
|
|
}
|
|
|
|
fail_not_extensible:
|
|
return 0;
|
|
}
|
|
|
|
/* Attempt to write to array items part, caller handles .length. */
|
|
DUK_LOCAL duk_small_int_t duk__prop_defown_idxkey_array_items_attempt(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_uarridx_t idx,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags) {
|
|
duk_tval *tv_slot;
|
|
duk_tval *tv_val;
|
|
|
|
tv_slot = duk_hobject_obtain_arridx_slot(thr, idx, obj);
|
|
|
|
if (DUK_UNLIKELY(tv_slot == NULL)) {
|
|
/* Failed to extend, now abandoned. */
|
|
DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_ITEMS(obj));
|
|
return -1;
|
|
}
|
|
DUK_ASSERT(DUK_HOBJECT_HAS_ARRAY_ITEMS(obj));
|
|
|
|
if (DUK_TVAL_IS_UNUSED(tv_slot)) {
|
|
/* Writing a new entry; defaults attributes are all
|
|
* false, so the only allowed combination for a linear
|
|
* array part is that all attributes are present with
|
|
* true value.
|
|
*
|
|
* No side effects affecting Array .length can happen
|
|
* here so it should safe to update .length later.
|
|
*/
|
|
if (DUK_UNLIKELY(!DUK_HOBJECT_HAS_EXTENSIBLE(obj) && !(defprop_flags & DUK_DEFPROP_FORCE))) {
|
|
goto fail_not_extensible;
|
|
}
|
|
if (!duk__prop_validate_immutable_data_desc(DUK_PROPDESC_FLAGS_WEC, defprop_flags)) {
|
|
DUK_DDD(DUK_DDDPRINT("new attrs not compatible"));
|
|
goto abandon_items;
|
|
}
|
|
DUK_ASSERT((defprop_flags & (DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER)) == 0U);
|
|
|
|
/* Must write 'undefined' even without a value. */
|
|
DUK_TVAL_SET_UNDEFINED(tv_slot);
|
|
} else {
|
|
/* Existing entry is already WEC, so check that we're
|
|
* not trying to modify any attribute to incompatible
|
|
* value.
|
|
*/
|
|
if (!duk__prop_validate_immutable_data_desc(DUK_PROPDESC_FLAGS_WEC, defprop_flags)) {
|
|
DUK_DDD(DUK_DDDPRINT("existing attrs not compatible"));
|
|
goto abandon_items;
|
|
}
|
|
DUK_ASSERT((defprop_flags & (DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER)) == 0U);
|
|
}
|
|
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_VALUE) {
|
|
/* When writing to an existing entry, the DECREF can cause
|
|
* arbitrary side effects, e.g. the Array we're working on
|
|
* can be modified. But in this case we don't need to update
|
|
* .length.
|
|
*/
|
|
tv_val = DUK_GET_TVAL_POSIDX(thr, idx_desc);
|
|
DUK_TVAL_SET_TVAL_UPDREF(thr, tv_slot, tv_val);
|
|
}
|
|
return 1;
|
|
|
|
abandon_items:
|
|
duk_hobject_abandon_array_items(thr, obj);
|
|
return -1;
|
|
|
|
fail_not_extensible:
|
|
return 0;
|
|
}
|
|
|
|
DUK_LOCAL duk_bool_t
|
|
duk__prop_defown_idxkey_array(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_desc, duk_uint_t defprop_flags) {
|
|
duk_harray *a = (duk_harray *) obj;
|
|
duk_uint32_t old_len;
|
|
duk_uint32_t new_len;
|
|
duk_bool_t def_rc;
|
|
|
|
DUK_ASSERT(thr != NULL);
|
|
DUK_ASSERT(obj != NULL);
|
|
DUK_ASSERT(DUK_HOBJECT_GET_HTYPE(obj) == DUK_HTYPE_ARRAY || DUK_HOBJECT_GET_HTYPE(obj) == DUK_HTYPE_ARGUMENTS);
|
|
|
|
old_len = DUK_HARRAY_GET_LENGTH(a);
|
|
if (idx >= old_len) {
|
|
if (DUK_UNLIKELY(DUK_HARRAY_LENGTH_NONWRITABLE(a) && !(defprop_flags & DUK_DEFPROP_FORCE))) {
|
|
goto fail_length_not_writable;
|
|
}
|
|
new_len = idx + 1;
|
|
DUK_ASSERT(new_len > idx);
|
|
} else {
|
|
new_len = 0U; /* Marker: no length update. */
|
|
}
|
|
|
|
/* If we still have linear array items, simulate ordinary
|
|
* [[DefineOwnProperty]], bailing out if any criterion is
|
|
* not met.
|
|
*/
|
|
|
|
if (DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)) {
|
|
duk_small_int_t items_rc = duk__prop_defown_idxkey_array_items_attempt(thr, obj, idx, idx_desc, defprop_flags);
|
|
if (items_rc < 0) {
|
|
/* Abandoned, fall through to ordinary idxprops. */
|
|
DUK_DDD(DUK_DDDPRINT("abandoned, use ordinary idxprops"));
|
|
DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_ITEMS(obj));
|
|
} else if (items_rc != 0) {
|
|
goto length_update;
|
|
} else {
|
|
goto fail_not_extensible;
|
|
}
|
|
}
|
|
|
|
/* No array items part, run ordinary algorithm first, and update
|
|
* length afterwards. Length writability already checked above
|
|
* so it should never fail.
|
|
*/
|
|
DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_ITEMS(obj));
|
|
def_rc = duk__prop_defown_idxkey_ordinary(thr, obj, idx, idx_desc, defprop_flags);
|
|
if (def_rc == 0) {
|
|
goto fail_unknown; /* Several possible reasons. */
|
|
}
|
|
/* pass through */
|
|
|
|
length_update:
|
|
if (new_len > 0U) {
|
|
DUK_ASSERT(DUK_HARRAY_GET_LENGTH(a) == old_len);
|
|
DUK_HARRAY_SET_LENGTH(a, new_len);
|
|
}
|
|
return 1;
|
|
|
|
fail_length_not_writable:
|
|
fail_not_extensible:
|
|
fail_unknown:
|
|
return 0;
|
|
}
|
|
|
|
DUK_LOCAL duk_small_int_t duk__prop_defown_idxkey_stringobj(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_uarridx_t idx,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags) {
|
|
duk_hstring *h;
|
|
|
|
DUK_ASSERT(thr != NULL);
|
|
DUK_ASSERT(obj != NULL);
|
|
DUK_ASSERT(DUK_HOBJECT_GET_HTYPE(obj) == DUK_HTYPE_STRING_OBJECT);
|
|
DUK_ASSERT_ARRIDX_VALID(idx);
|
|
DUK_ASSERT(idx_desc >= 0);
|
|
|
|
/* String index properties [0,len[ are individual characters
|
|
* with writable=false, enumerable=true, configurable=false.
|
|
* Test descriptor compatibility.
|
|
*/
|
|
|
|
h = duk_hobject_lookup_intvalue_hstring(thr, obj);
|
|
if (DUK_LIKELY(h != NULL)) {
|
|
if (DUK_LIKELY(!DUK_HSTRING_HAS_SYMBOL(h) && idx < duk_hstring_get_charlen(h))) {
|
|
if (duk__prop_validate_immutable_data_desc(DUK_PROPDESC_FLAGS_E, defprop_flags) == 0) {
|
|
goto fail_invalid_desc;
|
|
}
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_VALUE) {
|
|
duk_bool_t match;
|
|
|
|
duk_prop_push_plainstr_idx(thr, h, idx);
|
|
match = duk_samevalue(thr, idx_desc, -1);
|
|
duk_pop_known(thr);
|
|
if (!match) {
|
|
goto fail_invalid_desc;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
} else {
|
|
goto fail_internal;
|
|
}
|
|
return -1;
|
|
|
|
fail_invalid_desc:
|
|
fail_internal:
|
|
return 0;
|
|
}
|
|
|
|
DUK_LOCAL DUK_NOINLINE duk_bool_t duk__prop_defown_idxkey_arguments(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_uarridx_t idx,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags) {
|
|
duk_hobject *map;
|
|
duk_hobject *env;
|
|
duk_hstring *varname;
|
|
duk_bool_t is_mapped;
|
|
duk_bool_t rc_ordinary;
|
|
|
|
DUK_ASSERT(thr != NULL);
|
|
DUK_ASSERT(obj != NULL);
|
|
DUK_ASSERT(DUK_HOBJECT_GET_HTYPE(obj) == DUK_HTYPE_ARGUMENTS);
|
|
|
|
/* Special behavior when property is arguments-mapped and input descriptor
|
|
* is a data descriptor trying to write protect the property. If the
|
|
* descriptor is missing a [[Value]], read the mapped variable value and
|
|
* force it into the descriptor, so that the write protected value will
|
|
* reflect an up-to-date variable value (this is not very important because
|
|
* the two can then diverge anyway, but it's an explicit requirement in
|
|
* the specification algorithm).
|
|
*
|
|
* Use array code as a helper; it validates and updates 'length' but that
|
|
* doesn't matter because it's ignored for arguments.
|
|
*
|
|
* Getvar can have arbitrary side effects (it may be captured e.g. by a
|
|
* with(proxy) statement) so caller must stabilize 'obj'.
|
|
*/
|
|
|
|
varname = duk_prop_arguments_map_prep_idxkey(thr, obj, idx, &map, &env);
|
|
is_mapped = (varname != NULL);
|
|
|
|
if (is_mapped != 0 && duk__prop_is_data_descriptor(defprop_flags) &&
|
|
(defprop_flags & (DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE)) ==
|
|
(DUK_DEFPROP_HAVE_WRITABLE)) {
|
|
duk_uint_t modified_flags = defprop_flags | DUK_DEFPROP_HAVE_VALUE;
|
|
|
|
/* Getvar can have arbitrary side effects, as it may be captured
|
|
* e.g. by a with(proxy).
|
|
*/
|
|
DUK_D(DUK_DPRINT("write protecting arguments mapped idxkey with no [[Value]], force current variable value"));
|
|
(void) duk_js_getvar_envrec(thr, env, varname, 1 /*throw*/); /* -> [ ... value this_binding ] */
|
|
rc_ordinary = duk__prop_defown_idxkey_array(thr, obj, idx, duk_get_top(thr) - 2, modified_flags);
|
|
duk_pop_2_known(thr);
|
|
} else {
|
|
rc_ordinary = duk__prop_defown_idxkey_array(thr, obj, idx, idx_desc, defprop_flags);
|
|
}
|
|
|
|
if (rc_ordinary == 0) {
|
|
DUK_DD(DUK_DDPRINT("ordinary defprop failed, no map update"));
|
|
return 0;
|
|
}
|
|
|
|
if (is_mapped) {
|
|
/* Specification algorithm will both update the
|
|
* map entry and delete it if [[Value]] is present
|
|
* and [[Writable]]=false. Only the deletion
|
|
* should be necessary in this case.
|
|
*/
|
|
duk_bool_t delete_mapping = 0;
|
|
|
|
if (duk__prop_is_accessor_descriptor(defprop_flags)) {
|
|
DUK_D(DUK_DPRINT("delete arguments mapping for index %ld because converting to accessor", (long) idx));
|
|
delete_mapping = 1;
|
|
} else {
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_VALUE) {
|
|
/* Technically we're calling Set() on the map, but the map
|
|
* contains getter/setter functions which operate on the
|
|
* variables.
|
|
*/
|
|
DUK_DD(DUK_DDPRINT("update variable after arguments defprop, index %ld", (long) idx));
|
|
duk_dup(thr, idx_desc);
|
|
duk_js_putvar_envrec(thr, env, varname, DUK_GET_TVAL_NEGIDX(thr, -1), 0 /*throw_flag*/);
|
|
duk_pop_known(thr);
|
|
}
|
|
if ((defprop_flags & (DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE)) == DUK_DEFPROP_HAVE_WRITABLE) {
|
|
DUK_D(DUK_DPRINT("delete arguments mapping for index %ld because setting writable=false",
|
|
(long) idx));
|
|
delete_mapping = 1;
|
|
}
|
|
}
|
|
if (delete_mapping) {
|
|
duk_push_hobject(thr, map);
|
|
(void) duk_prop_delete_idxkey(thr, duk_get_top_index_known(thr), idx, 0 /*delprop_flags*/);
|
|
duk_pop_known(thr);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
DUK_LOCAL duk_small_int_t
|
|
duk__prop_defown_idxkey_proxy(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_desc, duk_uint_t defprop_flags) {
|
|
DUK_ASSERT(thr != NULL);
|
|
DUK_ASSERT(obj != NULL);
|
|
DUK_ASSERT(DUK_HOBJECT_GET_HTYPE(obj) == DUK_HTYPE_PROXY);
|
|
|
|
if (duk_proxy_trap_check_idxkey(thr, (duk_hproxy *) obj, idx, DUK_STRIDX_DEFINE_PROPERTY)) {
|
|
(void) duk_push_u32_tostring(thr, idx);
|
|
return (duk_small_int_t) duk__prop_defown_proxy_tail(thr, obj, idx_desc, defprop_flags);
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_idxkey_bufobj(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_uarridx_t idx,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags) {
|
|
duk_hbufobj *h = (duk_hbufobj *) obj;
|
|
|
|
DUK_ASSERT(thr != NULL);
|
|
DUK_ASSERT(obj != NULL);
|
|
|
|
/* Index validation happens before descriptor validation in the
|
|
* specification.
|
|
*/
|
|
if (DUK_UNLIKELY(idx >= DUK_HBUFOBJ_GET_LOGICAL_LENGTH(h))) {
|
|
goto fail_invalid_index;
|
|
}
|
|
if (!duk__prop_validate_immutable_data_desc(DUK_DEFPROP_WEC, defprop_flags)) {
|
|
/* NOTE: Somewhat bizarrely, typed array indices cannot be deleted
|
|
* but they are still configurable.
|
|
*/
|
|
goto fail_invalid_desc;
|
|
}
|
|
if (defprop_flags & DUK_DEFPROP_HAVE_VALUE) {
|
|
/* Value checking and coercion should happen after validation. */
|
|
if (!duk__prop_defown_bufobj_write(thr, obj, idx, idx_desc)) {
|
|
goto fail_invalid_index;
|
|
}
|
|
}
|
|
/* Short circuited, never proceed to standard [[DefineOwnProperty]]. */
|
|
return 1;
|
|
|
|
fail_invalid_index:
|
|
fail_invalid_desc:
|
|
return 0;
|
|
}
|
|
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_idxkey_helper(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_uarridx_t idx,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags,
|
|
duk_bool_t side_effect_safe) {
|
|
duk_small_uint_t htype;
|
|
duk_hobject *target;
|
|
duk_bool_t rc;
|
|
|
|
DUK_ASSERT(thr != NULL);
|
|
DUK_ASSERT(obj != NULL);
|
|
DUK_ASSERT_ARRIDX_VALID(idx);
|
|
DUK_ASSERT(idx_desc >= 0);
|
|
|
|
target = obj;
|
|
if (side_effect_safe) {
|
|
duk_push_hobject(thr, target);
|
|
}
|
|
|
|
retry_target:
|
|
htype = DUK_HEAPHDR_GET_HTYPE((duk_heaphdr *) target);
|
|
DUK_ASSERT(DUK_HTYPE_IS_ANY_OBJECT(htype));
|
|
|
|
switch (htype) {
|
|
case DUK_HTYPE_ARRAY:
|
|
rc = duk__prop_defown_idxkey_array(thr, target, idx, idx_desc, defprop_flags);
|
|
goto check_rc;
|
|
case DUK_HTYPE_ARGUMENTS:
|
|
if (side_effect_safe) {
|
|
rc = duk__prop_defown_idxkey_arguments(thr, target, idx, idx_desc, defprop_flags);
|
|
goto check_rc;
|
|
} else {
|
|
goto switch_to_safe;
|
|
}
|
|
case DUK_HTYPE_STRING_OBJECT: {
|
|
duk_small_int_t str_rc;
|
|
str_rc = duk__prop_defown_idxkey_stringobj(thr, target, idx, idx_desc, defprop_flags);
|
|
if (str_rc < 0) {
|
|
break;
|
|
}
|
|
rc = (duk_bool_t) str_rc;
|
|
goto check_rc;
|
|
}
|
|
case DUK_HTYPE_PROXY:
|
|
if (side_effect_safe) {
|
|
duk_small_int_t proxy_rc;
|
|
proxy_rc = duk__prop_defown_idxkey_proxy(thr, target, idx, idx_desc, defprop_flags);
|
|
if (proxy_rc < 0) {
|
|
/* No trap, continue to target. */
|
|
duk_hobject *next;
|
|
|
|
next = duk_proxy_get_target_autothrow(thr, (duk_hproxy *) target);
|
|
DUK_ASSERT(next != NULL);
|
|
|
|
target = duk_prop_switch_stabilized_target_top(thr, target, next);
|
|
goto retry_target;
|
|
} else {
|
|
rc = (duk_bool_t) proxy_rc;
|
|
goto check_rc;
|
|
}
|
|
} else {
|
|
goto switch_to_safe;
|
|
}
|
|
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
|
|
case DUK_HTYPE_ARRAYBUFFER:
|
|
case DUK_HTYPE_DATAVIEW:
|
|
break;
|
|
case DUK_HTYPE_INT8ARRAY:
|
|
case DUK_HTYPE_UINT8ARRAY:
|
|
case DUK_HTYPE_UINT8CLAMPEDARRAY:
|
|
case DUK_HTYPE_INT16ARRAY:
|
|
case DUK_HTYPE_UINT16ARRAY:
|
|
case DUK_HTYPE_INT32ARRAY:
|
|
case DUK_HTYPE_UINT32ARRAY:
|
|
case DUK_HTYPE_FLOAT32ARRAY:
|
|
case DUK_HTYPE_FLOAT64ARRAY:
|
|
rc = duk__prop_defown_idxkey_bufobj(thr, target, idx, idx_desc, defprop_flags);
|
|
goto check_rc;
|
|
#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
|
|
default:
|
|
break;
|
|
}
|
|
|
|
rc = duk__prop_defown_idxkey_ordinary(thr, target, idx, idx_desc, defprop_flags);
|
|
/* fall through */
|
|
|
|
check_rc:
|
|
if (side_effect_safe) {
|
|
duk_pop_known(thr);
|
|
}
|
|
if (DUK_UNLIKELY(rc == 0)) {
|
|
return duk__prop_defown_error_obj_idxkey(thr, target, idx, defprop_flags);
|
|
}
|
|
return rc;
|
|
|
|
switch_to_safe:
|
|
return duk__prop_defown_idxkey_safe(thr, obj, idx, idx_desc, defprop_flags);
|
|
}
|
|
|
|
DUK_LOCAL duk_bool_t duk__prop_defown_idxkey_unsafe(duk_hthread *thr,
|
|
duk_hobject *obj,
|
|
duk_uarridx_t idx,
|
|
duk_idx_t idx_desc,
|
|
duk_uint_t defprop_flags) {
|
|
#if defined(DUK_USE_PREFER_SIZE)
|
|
return duk__prop_defown_idxkey_safe(thr, obj, idx, idx_desc, defprop_flags);
|
|
#else
|
|
return duk__prop_defown_idxkey_helper(thr, obj, idx, idx_desc, defprop_flags, 0 /*side_effect_safe*/);
|
|
#endif
|
|
}
|
|
|
|
DUK_LOCAL duk_bool_t
|
|
duk__prop_defown_idxkey_safe(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_desc, duk_uint_t defprop_flags) {
|
|
return duk__prop_defown_idxkey_helper(thr, obj, idx, idx_desc, defprop_flags, 1 /*side_effect_safe*/);
|
|
}
|
|
|
|
DUK_INTERNAL duk_bool_t
|
|
duk_prop_defown_strkey(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_desc, duk_uint_t defprop_flags) {
|
|
#if defined(DUK_USE_ASSERTIONS)
|
|
duk_idx_t entry_top;
|
|
#endif
|
|
|
|
DUK_ASSERT(thr != NULL);
|
|
DUK_ASSERT(obj != NULL);
|
|
DUK_ASSERT(key != NULL);
|
|
DUK_ASSERT(idx_desc >= 0);
|
|
DUK_ASSERT(duk_is_valid_posidx(thr, idx_desc));
|
|
#if defined(DUK_USE_ASSERTIONS)
|
|
entry_top = duk_get_top(thr);
|
|
#endif
|
|
|
|
if (DUK_UNLIKELY(DUK_HSTRING_HAS_ARRIDX(key))) {
|
|
duk_bool_t rc;
|
|
|
|
rc = duk__prop_defown_idxkey_unsafe(thr, obj, duk_hstring_get_arridx_fast_known(key), idx_desc, defprop_flags);
|
|
DUK_ASSERT(duk_get_top(thr) == entry_top);
|
|
return rc;
|
|
} else {
|
|
duk_bool_t rc;
|
|
|
|
DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key));
|
|
rc = duk__prop_defown_strkey_unsafe(thr, obj, key, idx_desc, defprop_flags);
|
|
DUK_ASSERT(duk_get_top(thr) == entry_top);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
DUK_INTERNAL duk_bool_t
|
|
duk_prop_defown_idxkey(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_desc, duk_uint_t defprop_flags) {
|
|
#if defined(DUK_USE_ASSERTIONS)
|
|
duk_idx_t entry_top;
|
|
#endif
|
|
|
|
DUK_ASSERT(thr != NULL);
|
|
DUK_ASSERT(obj != NULL);
|
|
DUK_ASSERT(idx_desc >= 0);
|
|
DUK_ASSERT(duk_is_valid_posidx(thr, idx_desc));
|
|
#if defined(DUK_USE_ASSERTIONS)
|
|
entry_top = duk_get_top(thr);
|
|
#endif
|
|
|
|
if (DUK_LIKELY(idx <= DUK_ARRIDX_MAX)) {
|
|
duk_bool_t rc = duk__prop_defown_idxkey_unsafe(thr, obj, idx, idx_desc, defprop_flags);
|
|
DUK_ASSERT(duk_get_top(thr) == entry_top);
|
|
return rc;
|
|
} else {
|
|
duk_bool_t rc;
|
|
duk_hstring *key;
|
|
|
|
DUK_DD(DUK_DDPRINT("corner case, input idx 0xffffffff is not an arridx, must coerce to string"));
|
|
key = duk_push_u32_tohstring(thr, idx);
|
|
rc = duk__prop_defown_strkey_unsafe(thr, obj, key, idx_desc, defprop_flags);
|
|
duk_pop_known(thr);
|
|
DUK_ASSERT(duk_get_top(thr) == entry_top);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
DUK_INTERNAL duk_bool_t
|
|
duk_prop_defown(duk_hthread *thr, duk_hobject *obj, duk_tval *tv_key, duk_idx_t idx_desc, duk_uint_t defprop_flags) {
|
|
#if defined(DUK_USE_ASSERTIONS)
|
|
duk_idx_t entry_top;
|
|
#endif
|
|
duk_bool_t rc;
|
|
duk_hstring *key;
|
|
duk_uarridx_t idx;
|
|
|
|
DUK_ASSERT(thr != NULL);
|
|
DUK_ASSERT(obj != NULL); /* Must be stable against side effects. */
|
|
DUK_ASSERT(tv_key != NULL);
|
|
/* tv_key may not be in value stack but it must be reachable and
|
|
* remain reachable despite arbitrary side effects (e.g. function
|
|
* constant table).
|
|
*/
|
|
/* 'idx_desc' must be valid, but only if defprop_flags indicates
|
|
* there is a value or getter/setter.
|
|
*/
|
|
DUK_ASSERT(duk_is_valid_posidx(thr, idx_desc));
|
|
/* 'throw_flag' is embedded into defprop_flags. */
|
|
#if defined(DUK_USE_ASSERTIONS)
|
|
entry_top = duk_get_top(thr);
|
|
#endif
|
|
|
|
switch (DUK_TVAL_GET_TAG(tv_key)) {
|
|
case DUK_TAG_STRING:
|
|
key = DUK_TVAL_GET_STRING(tv_key);
|
|
if (DUK_UNLIKELY(DUK_HSTRING_HAS_ARRIDX(key))) {
|
|
idx = duk_hstring_get_arridx_fast_known(key);
|
|
goto use_idx;
|
|
} else {
|
|
goto use_str;
|
|
}
|
|
#if defined(DUK_USE_FASTINT)
|
|
case DUK_TAG_FASTINT: {
|
|
duk_int64_t fi = DUK_TVAL_GET_FASTINT(tv_key);
|
|
if (fi >= 0 && fi <= (duk_int64_t) DUK_ARRIDX_MAX) {
|
|
idx = (duk_uarridx_t) fi;
|
|
goto use_idx;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
#if !defined(DUK_USE_PACKED_TVAL)
|
|
case DUK_TAG_NUMBER: {
|
|
duk_double_t d = DUK_TVAL_GET_DOUBLE(tv_key);
|
|
if (duk_prop_double_idx_check(d, &idx)) {
|
|
goto use_idx;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
case DUK_TAG_UNUSED:
|
|
case DUK_TAG_UNDEFINED:
|
|
case DUK_TAG_NULL:
|
|
case DUK_TAG_BOOLEAN:
|
|
case DUK_TAG_POINTER:
|
|
case DUK_TAG_LIGHTFUNC:
|
|
case DUK_TAG_OBJECT:
|
|
case DUK_TAG_BUFFER:
|
|
default: {
|
|
#if defined(DUK_USE_PACKED_TVAL)
|
|
duk_double_t d;
|
|
DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_key));
|
|
#if defined(DUK_USE_FASTINT)
|
|
DUK_ASSERT(!DUK_TVAL_IS_FASTINT(tv_key));
|
|
#endif
|
|
d = DUK_TVAL_GET_DOUBLE(tv_key);
|
|
if (duk_prop_double_idx_check(d, &idx)) {
|
|
goto use_idx;
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
duk_push_tval(thr, tv_key);
|
|
tv_key = NULL;
|
|
key = duk_to_property_key_hstring(thr, -1);
|
|
DUK_ASSERT(key != NULL);
|
|
rc = duk_prop_defown_strkey(thr, obj, key, idx_desc, defprop_flags);
|
|
duk_pop_known(thr);
|
|
DUK_ASSERT(duk_get_top(thr) == entry_top);
|
|
return rc;
|
|
|
|
use_idx:
|
|
DUK_ASSERT_ARRIDX_VALID(idx);
|
|
rc = duk__prop_defown_idxkey_unsafe(thr, obj, idx, idx_desc, defprop_flags);
|
|
DUK_ASSERT(duk_get_top(thr) == entry_top);
|
|
return rc;
|
|
|
|
use_str:
|
|
DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key));
|
|
rc = duk__prop_defown_strkey_unsafe(thr, obj, key, idx_desc, defprop_flags);
|
|
DUK_ASSERT(duk_get_top(thr) == entry_top);
|
|
return rc;
|
|
}
|
|
|