Browse Source

Add rewritten property [[Delete]] code draft

pull/2309/head
Sami Vaarala 5 years ago
parent
commit
d9f70e8b5a
  1. 910
      src-input/duk_prop_delete.c

910
src-input/duk_prop_delete.c

@ -0,0 +1,910 @@
/*
* [[Delete]] and 'delete' operator for properties (not super references).
*
* The [[Delete]] algorithm returns true (success) or false (failure) but
* doesn't throw explicitly. Errors may also be thrown explicitly by e.g.
* a Proxy trap. In non-strict mode the 'delete' operator returns true/false
* and in strict mode returns true or throws. In practice the throwing must
* be inlined into the [[Delete]] algorithms to provide good error messages.
*
* Stabilization is needed for Proxies because side effects may revoke some
* Proxies in a Proxy chain, stranding the current target object.
*/
#include "duk_internal.h"
DUK_LOCAL_DECL duk_bool_t duk__prop_delete_obj_strkey_ordinary(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t delprop_flags);
DUK_LOCAL_DECL duk_bool_t duk__prop_delete_obj_idxkey_ordinary(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_small_uint_t delprop_flags);
DUK_LOCAL_DECL duk_bool_t duk__prop_delete_obj_strkey_unsafe(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t delprop_flags);
DUK_LOCAL_DECL duk_bool_t duk__prop_delete_obj_strkey_safe(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t delprop_flags);
DUK_LOCAL_DECL duk_bool_t duk__prop_delete_obj_idxkey_unsafe(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_small_uint_t delprop_flags);
DUK_LOCAL_DECL duk_bool_t duk__prop_delete_obj_idxkey_safe(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_small_uint_t delprop_flags);
#if defined(DUK_USE_PARANOID_ERRORS)
DUK_LOCAL duk_bool_t duk__prop_delete_error_shared_obj(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t delprop_flags) {
if (delprop_flags & DUK_DELPROP_FLAG_THROW) {
DUK_TYPE_ERROR(thr, "cannot delete property of object");
}
return 0;
}
DUK_LOCAL duk_bool_t duk__prop_delete_error_shared_objidx(duk_hthread *thr, duk_idx_t idx_obj, duk_hstring *key, duk_small_uint_t delprop_flags) {
if (delprop_flags & DUK_DELPROP_FLAG_THROW) {
const char *str1 = duk_get_type_name(thr, idx_obj);
DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "cannot delete property of %s", str1);
}
return 0;
}
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_delete_error_obj_strkey(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t delprop_flags) {
return duk__prop_delete_error_shared_obj(thr, obj, key, delprop_flags);
}
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_delete_error_objidx_strkey(duk_hthread *thr, duk_idx_t idx_obj, duk_hstring *key, duk_small_uint_t delprop_flags) {
return duk__prop_delete_error_shared_objidx(thr, idx_obj, key, delprop_flags);
}
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_delete_error_obj_idxkey(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_small_uint_t delprop_flags) {
return duk__prop_delete_error_shared_obj(thr, obj, key, delprop_flags);
}
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_delete_error_objidx_idxkey(duk_hthread *thr, duk_idx_t idx_obj, duk_uarridx_t idx, duk_small_uint_t delprop_flags) {
return duk__prop_delete_error_shared_objidx(thr, idx_obj, key, delprop_flags);
}
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_delete_error_objidx_tvkey(duk_hthread *thr, duk_idx_t idx_obj, duk_tval *tv_key, duk_small_uint_t delprop_flags) {
return duk__prop_delete_error_shared_objidx(thr, idx_obj, key, delprop_flags);
}
#elif defined(DUK_USE_VERBOSE_ERRORS)
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_delete_error_obj_strkey(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t delprop_flags) {
if (delprop_flags & DUK_DELPROP_FLAG_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 delete property %s of %s", str2, str1);
}
return 0;
}
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_delete_error_objidx_strkey(duk_hthread *thr, duk_idx_t idx_obj, duk_hstring *key, duk_small_uint_t delprop_flags) {
if (delprop_flags & DUK_DELPROP_FLAG_THROW) {
const char *str1 = duk_push_readable_idx(thr, idx_obj);
const char *str2 = duk_push_readable_hstring(thr, key);
DUK_ERROR_FMT2(thr, DUK_ERR_TYPE_ERROR, "cannot delete property %s of %s", str2, str1);
}
return 0;
}
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_delete_error_obj_idxkey(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_small_uint_t delprop_flags) {
if (delprop_flags & DUK_DELPROP_FLAG_THROW) {
const char *str1 = duk_push_readable_hobject(thr, obj);
DUK_ERROR_FMT2(thr, DUK_ERR_TYPE_ERROR, "cannot delete property %lu of %s", (unsigned long) idx, str1);
}
return 0;
}
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_delete_error_objidx_idxkey(duk_hthread *thr, duk_idx_t idx_obj, duk_uarridx_t idx, duk_small_uint_t delprop_flags) {
if (delprop_flags & DUK_DELPROP_FLAG_THROW) {
const char *str1 = duk_push_readable_idx(thr, idx_obj);
DUK_ERROR_FMT2(thr, DUK_ERR_TYPE_ERROR, "cannot delete property %lu of %s", (unsigned long) idx, str1);
}
return 0;
}
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_delete_error_objidx_tvkey(duk_hthread *thr, duk_idx_t idx_obj, duk_tval *tv_key, duk_small_uint_t delprop_flags) {
if (delprop_flags & DUK_DELPROP_FLAG_THROW) {
const char *str1 = duk_push_readable_idx(thr, idx_obj);
const char *str2 = duk_push_readable_tval(thr, tv_key);
DUK_ERROR_FMT2(thr, DUK_ERR_TYPE_ERROR, "cannot delete property %s of %s", str2, str1);
}
return 0;
}
#else
DUK_LOCAL duk_bool_t duk__prop_delete_error_shared(duk_hthread *thr, duk_small_uint_t delprop_flags) {
if (delprop_flags & DUK_DELPROP_FLAG_THROW) {
DUK_ERROR_TYPE(thr, "cannot delete property");
}
return 0;
}
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_delete_error_obj_strkey(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t delprop_flags) {
DUK_UNREF(obj);
DUK_UNREF(key);
return duk__prop_delete_error_shared(thr, delprop_flags);
}
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_delete_error_objidx_strkey(duk_hthread *thr, duk_idx_t idx_obj, duk_hstring *key, duk_small_uint_t delprop_flags) {
DUK_UNREF(idx_obj);
DUK_UNREF(key);
return duk__prop_delete_error_shared(thr, delprop_flags);
}
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_delete_error_obj_idxkey(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_small_uint_t delprop_flags) {
DUK_UNREF(obj);
DUK_UNREF(idx);
return duk__prop_delete_error_shared(thr, delprop_flags);
}
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_delete_error_objidx_idxkey(duk_hthread *thr, duk_idx_t idx_obj, duk_uarridx_t idx, duk_small_uint_t delprop_flags) {
DUK_UNREF(idx_obj);
DUK_UNREF(idx);
return duk__prop_delete_error_shared(thr, delprop_flags);
}
DUK_LOCAL DUK_COLD duk_bool_t duk__prop_delete_error_objidx_tvkey(duk_hthread *thr, duk_idx_t idx_obj, duk_tval *tv_key, duk_small_uint_t delprop_flags) {
DUK_UNREF(idx_obj);
DUK_UNREF(tv_key);
return duk__prop_delete_error_shared(thr, delprop_flags);
}
#endif /* error model */
DUK_LOCAL void duk__prop_delete_ent_shared(duk_hthread *thr, duk_propvalue *pv_slot, duk_uint8_t attrs) {
if (DUK_UNLIKELY(attrs & DUK_PROPDESC_FLAG_ACCESSOR)) {
duk_hobject *tmp;
tmp = pv_slot->a.get;
DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, tmp);
tmp = pv_slot->a.set;
DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, tmp);
} else {
duk_tval *tmp;
tmp = &pv_slot->v;
DUK_TVAL_DECREF_NORZ(thr, tmp);
}
DUK_REFZERO_CHECK_SLOW(thr);
/* Slot is left as garbage. */
}
DUK_LOCAL duk_bool_t duk__prop_delete_proxy_tail(duk_hthread *thr) {
duk_bool_t res;
DUK_ASSERT(thr != NULL);
duk_call_method(thr, 2); /* [ ... trap handler target key ] -> [ ... result ] */
res = duk_to_boolean(thr, -1);
duk_pop_unsafe(thr);
/* XXX: Proxy policy */
return res;
}
DUK_LOCAL duk_small_int_t duk__prop_delete_obj_strkey_proxy(duk_hthread *thr, duk_hobject *obj, duk_hstring *key) {
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
DUK_ASSERT(DUK_HOBJECT_GET_HTYPE(obj) == DUK_HTYPE_PROXY);
DUK_ASSERT(key != NULL);
DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key));
if (duk_proxy_trap_check_strkey(thr, (duk_hproxy *) obj, key, DUK_STRIDX_DELETE_PROPERTY)) {
duk_push_hstring(thr, key);
return duk__prop_delete_proxy_tail(thr);
} else {
return -1;
}
}
DUK_LOCAL duk_small_int_t duk__prop_delete_obj_idxkey_proxy(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx) {
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
DUK_ASSERT(DUK_HOBJECT_GET_HTYPE(obj) == DUK_HTYPE_PROXY);
DUK_ASSERT_ARRIDX_VALID(idx);
if (duk_proxy_trap_check_idxkey(thr, (duk_hproxy *) obj, idx, DUK_STRIDX_DELETE_PROPERTY)) {
(void) duk_push_u32_tostring(thr, idx);
return duk__prop_delete_proxy_tail(thr);
} else {
return -1;
}
}
DUK_LOCAL duk_bool_t duk__prop_delete_obj_strkey_ordinary(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t delprop_flags) {
duk_uint_fast32_t ent_idx;
duk_int_fast32_t hash_idx;
if (duk_hobject_lookup_strprop_indices(thr, obj, key, &ent_idx, &hash_idx) != 0) {
duk_propvalue *val_base;
duk_hstring **key_base;
duk_uint8_t *attr_base;
duk_hstring **key_slot;
duk_propvalue *pv_slot;
duk_uint8_t attrs;
duk_uint32_t *hash_base;
duk_uint32_t *hash_slot;
attr_base = DUK_HOBJECT_E_GET_FLAGS_BASE(thr->heap, obj);
attrs = attr_base[ent_idx];
if (DUK_UNLIKELY(!(attrs & DUK_PROPDESC_FLAG_CONFIGURABLE) && !(delprop_flags & DUK_DELPROP_FLAG_FORCE))) {
goto fail_not_configurable;
}
val_base = DUK_HOBJECT_E_GET_VALUE_BASE(thr->heap, obj);
pv_slot = val_base + ent_idx;
key_base = DUK_HOBJECT_E_GET_KEY_BASE(thr->heap, obj);
key_slot = key_base + ent_idx;
DUK_ASSERT(*key_slot != NULL);
DUK_ASSERT(*key_slot == key);
DUK_HSTRING_DECREF_NORZ(thr, key);
*key_slot = NULL;
if (hash_idx >= 0) {
hash_base = DUK_HOBJECT_GET_HASH(thr->heap, obj);
DUK_ASSERT(hash_base != NULL);
DUK_ASSERT((duk_uint_fast32_t) hash_idx < (duk_uint_fast32_t) hash_base[0]);
hash_slot = hash_base + 1 + hash_idx;
*hash_slot = DUK_HOBJECT_HASHIDX_DELETED;
}
/* Attrs are left as garbage. */
duk__prop_delete_ent_shared(thr, pv_slot, attrs);
}
return 1;
fail_not_configurable:
return duk__prop_delete_error_obj_strkey(thr, obj, key, delprop_flags);
}
/* [[Delete]] for duk_hobject, with string key. */
DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__prop_delete_obj_strkey_helper(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t delprop_flags, duk_bool_t side_effect_safe) {
duk_small_uint_t htype;
duk_hobject *target;
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:
/* Array has no special [[Delete]] operation, but we must
* handle the 'length' property here because it is stored
* outside of strprops.
*/
if (DUK_UNLIKELY(DUK_HSTRING_HAS_LENGTH(key))) {
goto fail_not_configurable;
}
break;
case DUK_HTYPE_ARGUMENTS:
/* For arguments objects, 'length' is tracked as an ordinary
* property (and it is, by default, configurable) and no mapped
* indices come here. The Arguments [[Delete]] simplifies to
* OrdinaryDelete() here.
*/
break;
case DUK_HTYPE_PROXY: {
/* Unlike get/set, delete doesn't follow the inheritance chain.
* There may be a Proxy chain however, and Proxy trap lookups
* can have arbitrary side effects, including stranding the
* current 'target' by revoking Proxies in the chain. Current
* 'target' is stabilized on the value stack.
*
* P1 --> P2 --> P3 --> target
*
* Above P1 is the original 'obj'. Suppose we've progressed
* to P3, and a side effect of looking up the P3 proxy trap
* revokes proxy P1, i.e. NULLs the 'target' pointer of P1.
* This may cause P2, P3, and target to be garbage collected.
*/
if (side_effect_safe) {
duk_small_int_t rc = duk__prop_delete_obj_strkey_proxy(thr, target, key);
if (rc >= 0) {
DUK_ASSERT(rc == 0 || rc == 1);
if (rc) {
goto success;
}
goto fail_proxy;
} else {
duk_hobject *next;
duk_tval *tv_target;
next = duk_proxy_get_target_autothrow(thr, (duk_hproxy *) target);
DUK_ASSERT(next != NULL);
tv_target = thr->valstack_top - 1;
DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_target));
DUK_ASSERT(DUK_TVAL_GET_OBJECT(tv_target) == target);
DUK_HOBJECT_INCREF(thr, next);
DUK_TVAL_UPDATE_OBJECT(tv_target, next);
DUK_HOBJECT_DECREF(thr, target);
target = next;
goto retry_target;
}
} else {
goto switch_to_safe;
}
}
case DUK_HTYPE_STRING_OBJECT:
if (DUK_UNLIKELY(DUK_HSTRING_HAS_LENGTH(key))) {
goto fail_not_configurable;
}
break;
case DUK_HTYPE_ARRAYBUFFER:
break;
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)) {
/* Non-standard behavior: because we present a virtual
* non-configurable .length property, fail the delete.
*/
goto fail_not_configurable;
} else {
/* CanonicalNumericIndexString (not arridx); no exotic
* behavior but standard OrdinaryDelete() calls
* [[GetOwnProperty]] which is special and short
* circuits all CanonicalNumericIndexStrings. Here
* the CanonicalNumericIndexString is always out of
* bounds, so [[GetOwnProperty]] returns undefined
* (no descriptor), which causes OrdinaryDelete() to
* return true (= success).
*/
DUK_ASSERT(DUK_HSTRING_HAS_CANNUM(key));
goto success;
}
}
break;
default:
break;
}
/* No virtual properties preventing delete, look up concrete
* property table.
*/
if (side_effect_safe) {
duk_bool_t rc = duk__prop_delete_obj_strkey_ordinary(thr, target, key, delprop_flags);
duk_pop_unsafe(thr);
return rc;
} else {
return duk__prop_delete_obj_strkey_ordinary(thr, target, key, delprop_flags);
}
success:
if (side_effect_safe) {
duk_pop_unsafe(thr);
}
return 1;
fail_not_configurable:
fail_proxy:
if (side_effect_safe) {
duk_pop_unsafe(thr);
}
return duk__prop_delete_error_obj_strkey(thr, target, key, delprop_flags);
switch_to_safe:
return duk__prop_delete_obj_strkey_safe(thr, obj, key, delprop_flags);
}
DUK_LOCAL duk_bool_t duk__prop_delete_obj_strkey_unsafe(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t delprop_flags) {
#if defined(DUK_USE_PREFER_SIZE)
return duk__prop_delete_obj_strkey_safe(thr, obj, key, delprop_flags);
#else
return duk__prop_delete_obj_strkey_helper(thr, obj, key, delprop_flags, 0 /*side_effect_safe*/);
#endif
}
DUK_LOCAL DUK_NOINLINE duk_bool_t duk__prop_delete_obj_strkey_safe(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t delprop_flags) {
return duk__prop_delete_obj_strkey_helper(thr, obj, key, delprop_flags, 1 /*side_effect_safe*/);
}
DUK_LOCAL duk_bool_t duk__prop_delete_obj_idxkey_array(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx) {
duk_harray *a = (duk_harray *) obj;
/* Delete from array items part never causes it to be abandoned
* at present, which allows one to craft an Array which is very
* sparse but keeps its array items.
*/
if (DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)) {
if (idx < DUK_HARRAY_GET_LENGTH(a)) {
if (idx < DUK_HARRAY_GET_ITEMS_LENGTH(a)) {
duk_tval *tv = DUK_HARRAY_GET_ITEMS(thr->heap, a) + idx;
DUK_TVAL_SET_UNUSED_UPDREF(thr, tv);
return 1;
} else {
/* Technically within .length, but no
* concrete allocation, so non-existent.
*/
return 1;
}
} else {
/* No index keys should be elsewhere. */
return 1;
}
}
DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_ITEMS(obj));
return 0;
}
DUK_LOCAL duk_bool_t duk__prop_delete_obj_idxkey_arguments(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_small_uint_t delprop_flags) {
duk_harray *a = (duk_harray *) obj;
duk_hobject *map;
duk_hobject *env;
duk_hstring *varname;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
DUK_ASSERT(DUK_HEAPHDR_GET_HTYPE((duk_heaphdr *) obj) == DUK_HTYPE_ARGUMENTS);
/* Conceptual algorithm:
* 1. Check if mapped.
* 2. Ordinary delete (arbitrary side effects).
* 3. If was mapped in 1, and ordinary delete succeeds, delete from map.
*/
varname = duk_prop_arguments_map_prep_idxkey(thr, obj, idx, &map, &env);
if (DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)) {
if (idx < DUK_HARRAY_GET_ITEMS_LENGTH(a)) {
duk_tval *tv = DUK_HARRAY_GET_ITEMS(thr->heap, a) + idx;
DUK_TVAL_SET_UNUSED_UPDREF(thr, tv);
} else {
/* No index keys should be elsewhere. */
}
/* Always succeeds. As for arrays, never abandoned here. */
} else {
duk_bool_t del_rc = duk__prop_delete_obj_idxkey_ordinary(thr, obj, idx, delprop_flags);
if (del_rc == 0) {
return 0;
}
}
/* Ordinary delete successful, delete from map if was mapped
* in the beginning. Map delete should always succeed; its
* retval is ignored.
*/
if (varname != NULL) {
(void) duk__prop_delete_obj_idxkey_ordinary(thr, map, idx, 0 /*delprop_flags*/);
}
return 1;
}
DUK_LOCAL duk_bool_t duk__prop_delete_obj_idxkey_ordinary(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_small_uint_t delprop_flags) {
duk_uint_fast32_t ent_idx;
duk_int_fast32_t hash_idx;
if (duk_hobject_lookup_idxprop_indices(thr, obj, idx, &ent_idx, &hash_idx) != 0) {
duk_propvalue *val_base;
duk_uarridx_t *key_base;
duk_uint8_t *attr_base;
duk_uarridx_t *key_slot;
duk_propvalue *pv_slot;
duk_uint8_t attrs;
duk_uint32_t *hash_base;
duk_uint32_t *hash_slot;
val_base = (duk_propvalue *) (void *) obj->idx_props;
key_base = (duk_uarridx_t *) (void *) (val_base + obj->i_size);
attr_base = (duk_uint8_t *) (void *) (key_base + obj->i_size);
attrs = attr_base[ent_idx];
if (DUK_UNLIKELY(!(attrs & DUK_PROPDESC_FLAG_CONFIGURABLE) && !(delprop_flags & DUK_DELPROP_FLAG_FORCE))) {
goto fail_not_configurable;
}
key_slot = key_base + ent_idx;
DUK_ASSERT(*key_slot == idx);
*key_slot = DUK_ARRIDX_NONE;
/* Attrs are left as garbage. */
pv_slot = val_base + ent_idx;
if (hash_idx >= 0) {
hash_base = obj->idx_hash;
DUK_ASSERT(hash_base != NULL);
DUK_ASSERT((duk_uint_fast32_t) hash_idx < (duk_uint_fast32_t) hash_base[0]);
hash_slot = hash_base + 1 + hash_idx;
*hash_slot = DUK_HOBJECT_HASHIDX_DELETED;
}
duk__prop_delete_ent_shared(thr, pv_slot, attrs);
}
return 1;
fail_not_configurable:
return duk__prop_delete_error_obj_idxkey(thr, obj, idx, delprop_flags);
}
DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__prop_delete_obj_idxkey_helper(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_small_uint_t delprop_flags, duk_bool_t side_effect_safe) {
duk_small_uint_t htype;
duk_hobject *target;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(thr->heap != NULL);
DUK_ASSERT(obj != NULL);
DUK_ASSERT(idx != DUK_ARRIDX_NONE);
target = obj;
if (side_effect_safe) {
duk_push_hobject(thr, target);
}
retry_target:
/* Check HTYPE specific properties first. Some special properties
* cannot be deleted, even with force flag.
*/
htype = DUK_HEAPHDR_GET_HTYPE((duk_heaphdr *) target);
DUK_ASSERT(DUK_HTYPE_IS_ANY_OBJECT(htype));
switch (htype) {
case DUK_HTYPE_ARRAY:
if (duk__prop_delete_obj_idxkey_array(thr, target, idx)) {
goto success;
}
/* Continue to index properties if abandoned. */
DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_ITEMS(target));
break;
case DUK_HTYPE_ARGUMENTS:
/* Argument object indices are stored either in array items
* or index part. There's an exotic [[Delete]] algorithm
* which first runs OrdinaryDelete(); if it succeeds, the
* arguments map is checked and updated.
*/
return duk__prop_delete_obj_idxkey_arguments(thr, target, idx, delprop_flags);
case DUK_HTYPE_PROXY: {
/* Proxy chains may be revoked by side effects so we
* must stabilize the current 'target' once side effects
* become possible.
*/
if (side_effect_safe) {
duk_small_int_t rc = duk__prop_delete_obj_idxkey_proxy(thr, target, idx);
if (rc >= 0) {
DUK_ASSERT(rc == 0 || rc == 1);
if (rc) {
goto success;
}
goto fail_proxy;
} else {
duk_hobject *next;
duk_tval *tv_target;
next = duk_proxy_get_target_autothrow(thr, (duk_hproxy *) target);
DUK_ASSERT(next != NULL);
tv_target = thr->valstack_top - 1;
DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_target));
DUK_ASSERT(DUK_TVAL_GET_OBJECT(tv_target) == target);
DUK_HOBJECT_INCREF(thr, next);
DUK_TVAL_UPDATE_OBJECT(tv_target, next);
DUK_HOBJECT_DECREF(thr, target);
target = next;
goto retry_target;
}
} else {
goto switch_to_safe;
}
}
case DUK_HTYPE_STRING_OBJECT: {
duk_hstring *h;
h = duk_hobject_lookup_intvalue_hstring(thr, target);
if (DUK_LIKELY(h != NULL)) {
if (DUK_LIKELY(!DUK_HSTRING_HAS_SYMBOL(h) && idx < DUK_HSTRING_GET_CHARLEN(h))) {
goto fail_not_configurable;
}
}
break;
}
case DUK_HTYPE_ARRAYBUFFER:
break;
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: {
duk_hbufobj *h = (duk_hbufobj *) target;
if (idx < DUK_HBUFOBJ_GET_LOGICAL_LENGTH(h)) {
goto fail_not_configurable;
} else {
/* Any canonical numeric index string outside of
* length is treated as missing ([[GetOwnProperty]]
* returns undefined), with no concrete lookup.
*/
goto success;
}
break;
}
default:
break;
}
/* No virtual properties preventing delete, look up concrete
* property table.
*/
if (side_effect_safe) {
duk_bool_t rc = duk__prop_delete_obj_idxkey_ordinary(thr, target, idx, delprop_flags);
duk_pop_unsafe(thr);
return rc;
} else {
return duk__prop_delete_obj_idxkey_ordinary(thr, target, idx, delprop_flags);
}
success:
if (side_effect_safe) {
duk_pop_unsafe(thr);
}
return 1;
fail_not_configurable:
fail_proxy:
if (side_effect_safe) {
duk_pop_unsafe(thr);
}
return duk__prop_delete_error_obj_idxkey(thr, target, idx, delprop_flags);
switch_to_safe:
return duk__prop_delete_obj_idxkey_safe(thr, obj, idx, delprop_flags);
}
DUK_LOCAL duk_bool_t duk__prop_delete_obj_idxkey_unsafe(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_small_uint_t delprop_flags) {
#if defined(DUK_USE_PREFER_SIZE)
return duk__prop_delete_obj_idxkey_safe(thr, obj, idx, delprop_flags);
#else
return duk__prop_delete_obj_idxkey_helper(thr, obj, idx, delprop_flags, 0 /*side_effect_safe*/);
#endif
}
DUK_LOCAL DUK_NOINLINE duk_bool_t duk__prop_delete_obj_idxkey_safe(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_small_uint_t delprop_flags) {
return duk__prop_delete_obj_idxkey_helper(thr, obj, idx, delprop_flags, 1 /*side_effect_safe*/);
}
DUK_INTERNAL duk_bool_t duk_prop_delete_obj_strkey(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t delprop_flags) {
if (DUK_UNLIKELY(DUK_HSTRING_HAS_ARRIDX(key))) {
return duk__prop_delete_obj_idxkey_unsafe(thr, obj, DUK_HSTRING_GET_ARRIDX_FAST_KNOWN(key), delprop_flags);
} else {
return duk__prop_delete_obj_strkey_unsafe(thr, obj, key, delprop_flags);
}
}
DUK_INTERNAL duk_bool_t duk_prop_delete_obj_idxkey(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_small_uint_t delprop_flags) {
if (DUK_LIKELY(idx <= DUK_ARRIDX_MAX)) {
return duk__prop_delete_obj_idxkey_unsafe(thr, obj, idx, delprop_flags);
} else {
duk_bool_t rc;
duk_hstring *key;
DUK_D(DUK_DPRINT("corner case, input idx 0xffffffff is not an arridx, must coerce to string"));
key = duk_push_u32_tohstring(thr, idx);
rc = duk__prop_delete_obj_strkey_unsafe(thr, obj, key, delprop_flags);
duk_pop_unsafe(thr);
return rc;
}
}
DUK_LOCAL duk_bool_t duk__prop_delete_strkey(duk_hthread *thr, duk_idx_t idx_obj, duk_hstring *key, duk_small_uint_t delprop_flags) {
duk_tval *tv_obj;
DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key));
tv_obj = thr->valstack_bottom + idx_obj;
/* The 'delete' operator conceptually performs a ToObject() and then
* runs the normal [[Delete]]. No actual property is deleted if the
* delete argument is a primitive value, but false/throw may be needed.
*/
switch (DUK_TVAL_GET_TAG(tv_obj)) {
case DUK_TAG_UNUSED:
case DUK_TAG_UNDEFINED:
case DUK_TAG_NULL:
/* Conceptually this error happens in ToObject() coercion of
* the delete operator algorithm, so it's unconditional.
*/
goto fail_invalid_base_uncond;
case DUK_TAG_STRING: {
duk_hstring *h = DUK_TVAL_GET_STRING(tv_obj);
if (!DUK_HSTRING_HAS_SYMBOL(h) && DUK_HSTRING_HAS_LENGTH(key)) {
goto fail_not_configurable;
}
break;
}
case DUK_TAG_OBJECT:
/* Typical case. */
return duk__prop_delete_obj_strkey_unsafe(thr, DUK_TVAL_GET_OBJECT(tv_obj), key, delprop_flags);
case DUK_TAG_BUFFER:
/* Mimic an actual Uint8Array object, for which we present a
* non-standard own .length property.
*/
if (DUK_HSTRING_HAS_LENGTH(key)) {
goto fail_not_configurable;
}
break;
case DUK_TAG_BOOLEAN:
break;
case DUK_TAG_POINTER:
break;
case DUK_TAG_LIGHTFUNC:
break;
default:
DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_obj));
}
/* No property found, success. */
return 1;
fail_not_configurable:
return duk__prop_delete_error_objidx_strkey(thr, idx_obj, key, delprop_flags);
fail_invalid_base_uncond:
return duk__prop_delete_error_objidx_strkey(thr, idx_obj, key, DUK_DELPROP_FLAG_THROW);
}
DUK_LOCAL duk_bool_t duk__prop_delete_idxkey(duk_hthread *thr, duk_idx_t idx_obj, duk_uarridx_t idx, duk_small_uint_t delprop_flags) {
duk_tval *tv_obj;
tv_obj = thr->valstack_bottom + idx_obj;
switch (DUK_TVAL_GET_TAG(tv_obj)) {
case DUK_TAG_UNUSED:
case DUK_TAG_UNDEFINED:
case DUK_TAG_NULL:
/* Conceptually this error happens in ToObject() coercion of
* the delete operator algorithm, so it's unconditional.
*/
goto fail_invalid_base_uncond;
case DUK_TAG_STRING: {
duk_hstring *h = DUK_TVAL_GET_STRING(tv_obj);
if (!DUK_HSTRING_HAS_SYMBOL(h) && idx < DUK_HSTRING_GET_CHARLEN(h)) {
goto fail_not_configurable;
}
break;
}
case DUK_TAG_OBJECT:
/* Typical case. */
return duk__prop_delete_obj_idxkey_unsafe(thr, DUK_TVAL_GET_OBJECT(tv_obj), idx, delprop_flags);
case DUK_TAG_BUFFER: {
duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv_obj);
if (idx < DUK_HBUFFER_GET_SIZE(h)) {
goto fail_not_configurable;
}
break;
}
case DUK_TAG_BOOLEAN:
break;
case DUK_TAG_POINTER:
break;
case DUK_TAG_LIGHTFUNC:
break;
default:
DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_obj));
}
/* No property found, success. */
return 1;
fail_not_configurable:
return duk__prop_delete_error_objidx_idxkey(thr, idx_obj, idx, delprop_flags);
fail_invalid_base_uncond:
return duk__prop_delete_error_objidx_idxkey(thr, idx_obj, idx, DUK_DELPROP_FLAG_THROW);
}
DUK_INTERNAL duk_bool_t duk_prop_delete_strkey(duk_hthread *thr, duk_idx_t idx_obj, duk_hstring *key, duk_small_uint_t delprop_flags) {
if (DUK_UNLIKELY(DUK_HSTRING_HAS_ARRIDX(key))) {
return duk__prop_delete_idxkey(thr, idx_obj, DUK_HSTRING_GET_ARRIDX_FAST_KNOWN(key), delprop_flags);
} else {
return duk__prop_delete_strkey(thr, idx_obj, key, delprop_flags);
}
}
DUK_INTERNAL duk_bool_t duk_prop_delete_idxkey(duk_hthread *thr, duk_idx_t idx_obj, duk_uarridx_t idx, duk_small_uint_t delprop_flags) {
if (DUK_LIKELY(idx <= DUK_ARRIDX_MAX)) {
return duk__prop_delete_idxkey(thr, idx_obj, idx, delprop_flags);
} else {
duk_bool_t rc;
duk_hstring *key;
DUK_D(DUK_DPRINT("corner case, input idx 0xffffffff is not an arridx, must coerce to string"));
key = duk_push_u32_tohstring(thr, idx);
rc = duk__prop_delete_strkey(thr, idx_obj, key, delprop_flags);
duk_pop_unsafe(thr);
return rc;
}
}
DUK_INTERNAL duk_bool_t duk_prop_deleteoper(duk_hthread *thr, duk_idx_t idx_obj, duk_tval *tv_key, duk_small_uint_t delprop_flags) {
duk_bool_t rc;
duk_hstring *key;
duk_uarridx_t idx;
DUK_ASSERT(duk_is_valid_index(thr, idx_obj));
DUK_ASSERT(tv_key != NULL);
/* 'tv_key' is not necessarily in value stack (may be a const). It
* must remain reachable despite side effects, but the 'tv_key' pointer
* itself may be unstable (e.g. in value stack).
*/
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 {
DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key));
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));
d = DUK_TVAL_GET_DOUBLE(tv_key);
if (duk_prop_double_idx_check(d, &idx)) {
goto use_idx;
}
#endif
break;
}
}
/* For undefined and null, 'delete' operator first does a ToObject()
* which causes a TypeError. This must happen before any side effects
* caused by key coercion.
*/
if (DUK_UNLIKELY(duk_is_null_or_undefined(thr, idx_obj))) {
return duk__prop_delete_error_objidx_tvkey(thr, idx_obj, tv_key, DUK_DELPROP_FLAG_THROW);
}
duk_push_tval(thr, tv_key);
tv_key = NULL;
key = duk_to_property_key_hstring(thr, -1);
DUK_ASSERT(key != NULL);
rc = duk_prop_delete_strkey(thr, idx_obj, key, delprop_flags);
duk_pop_unsafe(thr);
return rc;
use_idx:
return duk__prop_delete_idxkey(thr, idx_obj, idx, delprop_flags);
use_str:
return duk__prop_delete_strkey(thr, idx_obj, key, delprop_flags);
}
Loading…
Cancel
Save