diff --git a/src-input/duk_prop_set.c b/src-input/duk_prop_set.c index d84f3ce4..cc4ef556 100644 --- a/src-input/duk_prop_set.c +++ b/src-input/duk_prop_set.c @@ -2,10 +2,10 @@ * [[Set]] and PutValue() for properties * * The specification [[Set]] algorithm is tail recursive, progressing - * from target to target up the inheritance chain. Except for special + * from object to object up the inheritance chain. Except for special * cases like getters and Proxies, once an inherited property is found * or the inheritance chain ends, the final property write goes to the - * original receiver. + * original receiver (not the current object in the inheritance chain). * * To avoid a mandatory dependence on the compiler optimizing tail calls * (which is not guaranteed), the algorithm here is reworked into a "check" @@ -14,10 +14,13 @@ * algorithm terminates. Some cases are handled inline in the "check" phase. * A tail recursive implementation might be preferable because it avoids * the overhead of returning and checking a return code for each step, but - * it would need to depend on a config option. + * it would need to depend on a config option. (Support for optimizing + * "sibling calls" should be enough to implement the recursion efficiently.) * * As for [[Get]], side effects are difficult to handle correctly, especially - * for Proxies and Arguments objects. + * for Proxies and Arguments objects. On the other hand performance of + * [[Set]] is important, so similar stabilization approaches as for [[Get]] + * are used here. */ #include "duk_internal.h" @@ -32,48 +35,55 @@ #define DUK__SETCHECK_DONE_SUCCESS 3 /* handled inline, success result */ #define DUK__SETCHECK_HANDLE_SPECIAL 4 /* special handling for Proxy and Arguments in "check" loop */ -DUK_LOCAL_DECL duk_bool_t duk__prop_set_str_safe(duk_hthread *thr, - duk_hobject *target, - duk_hstring *key, +typedef duk_bool_t (*duk__setcheck_idxkey_htype)(duk_hthread *thr, + duk_hobject *obj, + duk_uarridx_t idx, duk_idx_t idx_val, duk_idx_t idx_recv, duk_bool_t throw_flag); -DUK_LOCAL_DECL duk_bool_t duk__prop_set_str_unsafe(duk_hthread *thr, - duk_hobject *target, - duk_hstring *key, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag); -DUK_LOCAL_DECL duk_bool_t duk__prop_set_idx_safe(duk_hthread *thr, - duk_hobject *target, - duk_uarridx_t idx, +typedef duk_bool_t (*duk__setcheck_strkey_htype)(duk_hthread *thr, + duk_hobject *obj, + duk_hstring *key, duk_idx_t idx_val, duk_idx_t idx_recv, duk_bool_t throw_flag); -DUK_LOCAL_DECL duk_bool_t duk__prop_set_idx_unsafe(duk_hthread *thr, - duk_hobject *target, - duk_uarridx_t idx, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag); -DUK_LOCAL_DECL duk_bool_t duk__setfinal_own_prop_idxkey_ordinary(duk_hthread *thr, - duk_hobject *obj, - duk_uarridx_t idx, - duk_idx_t idx_val); -DUK_LOCAL_DECL duk_bool_t duk__setfinal_own_prop_idxkey(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_val); -DUK_LOCAL_DECL duk_bool_t duk__setcheck_own_prop_idxkey_ordinary(duk_hthread *thr, - duk_hobject *obj, - duk_uarridx_t idx, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag); - -DUK_NORETURN(DUK_LOCAL duk_bool_t - duk__prop_set_error_objidx_str(duk_hthread *thr, duk_idx_t idx_obj, duk_hstring *key, duk_bool_t throw_flag)); -DUK_NORETURN(DUK_LOCAL duk_bool_t - duk__prop_set_error_objidx_idx(duk_hthread *thr, duk_idx_t idx_obj, duk_uarridx_t idx, duk_bool_t throw_flag)); -DUK_NORETURN(DUK_LOCAL duk_bool_t - duk__prop_set_error_objidx_tvkey(duk_hthread *thr, duk_idx_t idx_obj, duk_tval *tv_key, duk_bool_t throw_flag)); +typedef duk_bool_t (*duk__setfinal_idxkey_htype)(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_val); +typedef duk_bool_t (*duk__setfinal_strkey_htype)(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_val); + +DUK_LOCAL_DECL duk_bool_t duk__prop_set_strkey_safe(duk_hthread *thr, + duk_hobject *target, + duk_hstring *key, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag); +DUK_LOCAL_DECL duk_bool_t duk__prop_set_strkey_unsafe(duk_hthread *thr, + duk_hobject *target, + duk_hstring *key, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag); +DUK_LOCAL_DECL duk_bool_t duk__prop_set_idxkey_safe(duk_hthread *thr, + duk_hobject *target, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag); +DUK_LOCAL_DECL duk_bool_t duk__prop_set_idxkey_unsafe(duk_hthread *thr, + duk_hobject *target, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag); +DUK_LOCAL_DECL duk_bool_t duk__setfinal_strkey_ordinary(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_val); +DUK_LOCAL_DECL duk_bool_t duk__setfinal_idxkey_ordinary(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_val); +DUK_LOCAL_DECL duk_bool_t duk__setfinal_idxkey(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_val); + +DUK_LOCAL_DECL duk_bool_t duk__setcheck_idxkey_ordinary(duk_hthread *thr, + duk_hobject *obj, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag); #if defined(DUK_USE_PARANOID_ERRORS) DUK_LOCAL duk_bool_t duk__prop_set_error_shared(duk_hthread *thr, duk_idx_t idx_obj, duk_bool_t throw_flag) { @@ -83,17 +93,17 @@ DUK_LOCAL duk_bool_t duk__prop_set_error_shared(duk_hthread *thr, duk_idx_t idx_ } return 0; } -DUK_LOCAL DUK_COLD duk_bool_t duk__prop_set_error_objidx_str(duk_hthread *thr, - duk_idx_t idx_obj, - duk_hstring *key, - duk_bool_t throw_flag) { +DUK_LOCAL DUK_COLD duk_bool_t duk__prop_set_error_objidx_strkey(duk_hthread *thr, + duk_idx_t idx_obj, + duk_hstring *key, + duk_bool_t throw_flag) { DUK_UNREF(key); return duk__prop_set_error_shared(thr, idx_obj, throw_flag); } -DUK_LOCAL DUK_COLD duk_bool_t duk__prop_set_error_objidx_idx(duk_hthread *thr, - duk_idx_t idx_obj, - duk_uarridx_t idx, - duk_bool_t throw_flag) { +DUK_LOCAL DUK_COLD duk_bool_t duk__prop_set_error_objidx_idxkey(duk_hthread *thr, + duk_idx_t idx_obj, + duk_uarridx_t idx, + duk_bool_t throw_flag) { DUK_UNREF(idx); return duk__prop_set_error_shared(thr, idx_obj, throw_flag); } @@ -105,7 +115,10 @@ DUK_LOCAL DUK_COLD duk_bool_t duk__prop_set_error_objidx_tvkey(duk_hthread *thr, return duk__prop_set_error_shared(thr, idx_obj, throw_flag); } #elif defined(DUK_USE_VERBOSE_ERRORS) -DUK_LOCAL duk_bool_t duk__prop_set_error_objidx_str(duk_hthread *thr, duk_idx_t idx_obj, duk_hstring *key, duk_bool_t throw_flag) { +DUK_LOCAL duk_bool_t duk__prop_set_error_objidx_strkey(duk_hthread *thr, + duk_idx_t idx_obj, + duk_hstring *key, + duk_bool_t throw_flag) { if (throw_flag) { const char *str1 = duk_push_readable_idx(thr, idx_obj); const char *str2 = duk_push_readable_hstring(thr, key); @@ -113,7 +126,10 @@ DUK_LOCAL duk_bool_t duk__prop_set_error_objidx_str(duk_hthread *thr, duk_idx_t } return 0; } -DUK_LOCAL duk_bool_t duk__prop_set_error_objidx_idx(duk_hthread *thr, duk_idx_t idx_obj, duk_uarridx_t idx, duk_bool_t throw_flag) { +DUK_LOCAL duk_bool_t duk__prop_set_error_objidx_idxkey(duk_hthread *thr, + duk_idx_t idx_obj, + duk_uarridx_t idx, + duk_bool_t throw_flag) { if (throw_flag) { const char *str1 = duk_push_readable_idx(thr, idx_obj); DUK_ERROR_FMT2(thr, DUK_ERR_TYPE_ERROR, "cannot write property %lu of %s", (unsigned long) idx, str1); @@ -138,17 +154,17 @@ DUK_LOCAL duk_bool_t duk__prop_set_error_shared(duk_hthread *thr, duk_idx_t idx_ } return 0; } -DUK_LOCAL DUK_COLD duk_bool_t duk__prop_set_error_objidx_str(duk_hthread *thr, - duk_idx_t idx_obj, - duk_hstring *key, - duk_bool_t throw_flag) { +DUK_LOCAL DUK_COLD duk_bool_t duk__prop_set_error_objidx_strkey(duk_hthread *thr, + duk_idx_t idx_obj, + duk_hstring *key, + duk_bool_t throw_flag) { DUK_UNREF(key); return duk__prop_set_error_shared(thr, idx_obj, throw_flag); } -DUK_LOCAL DUK_COLD duk_bool_t duk__prop_set_error_objidx_idx(duk_hthread *thr, - duk_idx_t idx_obj, - duk_uarridx_t idx, - duk_bool_t throw_flag) { +DUK_LOCAL DUK_COLD duk_bool_t duk__prop_set_error_objidx_idxkey(duk_hthread *thr, + duk_idx_t idx_obj, + duk_uarridx_t idx, + duk_bool_t throw_flag) { DUK_UNREF(idx); return duk__prop_set_error_shared(thr, idx_obj, throw_flag); } @@ -219,7 +235,8 @@ DUK_LOCAL duk_bool_t duk__prop_assume_no_inherited_array_indices(duk_hthread *th } /* Return true if 'obj' is the original receiver of the operation. This - * allows some common case shortcuts. + * allows some common case shortcuts. The check should be fast and with + * minimal branches. */ DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__prop_recv_direct(duk_hthread *thr, duk_idx_t idx_recv, duk_hobject *obj) { duk_hobject *recv; @@ -237,9 +254,10 @@ DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__prop_recv_direct(duk_hthread *thr, d return 0; } -/* Final [[Set]] processing for an index write to an Array with no items - * part, in essence Array exotic [[DefineOwnProperty]] for index keys. +/* + * Setfinal helpers. */ + DUK_LOCAL duk_bool_t duk__setfinal_write_array_abandoned_idxkey(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, @@ -282,7 +300,7 @@ DUK_LOCAL duk_bool_t duk__setfinal_write_array_abandoned_idxkey(duk_hthread *thr /* Ordinary [[DefineOwnProperty]] for index part. May fail if item * exists and is write protected. See notes on side effects below. */ - rc = duk__setfinal_own_prop_idxkey_ordinary(thr, obj, idx, idx_val); + rc = duk__setfinal_idxkey_ordinary(thr, obj, idx, idx_val); /* Update array length if written idx extends array and the ordinary * [[DefineOwnProperty]] was successful. @@ -294,7 +312,8 @@ DUK_LOCAL duk_bool_t duk__setfinal_write_array_abandoned_idxkey(duk_hthread *thr * 1. 'idx' being written doesn't extend the array. There may be * arbitrary side effects by e.g. an existing setter, a finalizer * triggered by a normal property write, etc. There's no need - * to update .length. + * to update .length so it doesn't matter if the target's .length + * has changed by such side effects. * * 2. 'idx' extends the array and there is no previous property. * There can be no side effects from a previous property, but @@ -303,7 +322,7 @@ DUK_LOCAL duk_bool_t duk__setfinal_write_array_abandoned_idxkey(duk_hthread *thr * able to update .length safely. */ if (new_len > 0U) { - DUK_ASSERT(DUK_HARRAY_GET_LENGTH(a) == old_len); + DUK_ASSERT(DUK_HARRAY_GET_LENGTH(a) == old_len); /* Resize should guarantee. */ if (rc) { DUK_HARRAY_SET_LENGTH(a, new_len); } @@ -313,7 +332,6 @@ DUK_LOCAL duk_bool_t duk__setfinal_write_array_abandoned_idxkey(duk_hthread *thr return rc; fail_length_not_writable: -fail_not_extensible: return 0; } @@ -349,7 +367,6 @@ DUK_LOCAL duk_small_int_t duk__setfinal_write_array_arrayitems_idxkey(duk_hthrea goto fail_length_not_writable; } if (DUK_UNLIKELY(!DUK_HOBJECT_HAS_EXTENSIBLE(obj))) { - /* Don't try to extend if we're going to fail anyway. */ goto fail_not_extensible; } new_len = idx + 1; @@ -459,19 +476,26 @@ fail_not_extensible: return 0; } -/* Helper for handling setter calls. - * Return value: 1=found and handled, 0=not found, fail write. +/* + * Setcheck helpers. + */ + +/* Helper for handling setter calls. Return value: + * + * 0 = not found, fail write + * 1 = found and handled + * * Setter may also throw. */ -DUK_LOCAL duk_bool_t duk__setcheck_own_prop_found_setter_helper(duk_hthread *thr, - duk_hobject *obj, - duk_hstring *key, - duk_uarridx_t idx, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_propvalue *pv, - duk_uint8_t attrs, - duk_bool_t use_key) { +DUK_LOCAL duk_bool_t duk__setcheck_found_setter_helper(duk_hthread *thr, + duk_hobject *obj, + duk_hstring *key, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_propvalue *pv, + duk_uint8_t attrs, + duk_bool_t use_key) { duk_propaccessor *pa; DUK_ASSERT(thr != NULL); @@ -513,13 +537,13 @@ DUK_LOCAL duk_bool_t duk__setcheck_own_prop_found_setter_helper(duk_hthread *thr } #if defined(DUK_USE_NONSTD_SETTER_KEY_ARGUMENT) -DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__setcheck_own_prop_found_setter_withkey(duk_hthread *thr, - duk_hobject *obj, - duk_hstring *key, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_propvalue *pv, - duk_uint8_t attrs) { +DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__setcheck_found_setter_withkey(duk_hthread *thr, + duk_hobject *obj, + duk_hstring *key, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_propvalue *pv, + duk_uint8_t attrs) { DUK_ASSERT(thr != NULL); DUK_ASSERT(obj != NULL); DUK_ASSERT(key != NULL); @@ -528,15 +552,15 @@ DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__setcheck_own_prop_found_setter_w DUK_ASSERT(duk_is_valid_posidx(thr, idx_recv)); DUK_ASSERT(pv != NULL); - return duk__setcheck_own_prop_found_setter_helper(thr, obj, key, 0, idx_val, idx_recv, pv, attrs, 1 /*use_key*/); + return duk__setcheck_found_setter_helper(thr, obj, key, 0, idx_val, idx_recv, pv, attrs, 1 /*use_key*/); } -DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__setcheck_own_prop_found_setter_withidx(duk_hthread *thr, - duk_hobject *obj, - duk_uarridx_t idx, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_propvalue *pv, - duk_uint8_t attrs) { +DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__setcheck_found_setter_withidx(duk_hthread *thr, + duk_hobject *obj, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_propvalue *pv, + duk_uint8_t attrs) { DUK_ASSERT(thr != NULL); DUK_ASSERT(obj != NULL); DUK_ASSERT_ARRIDX_VALID(idx); @@ -544,31 +568,31 @@ DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__setcheck_own_prop_found_setter_w DUK_ASSERT(duk_is_valid_posidx(thr, idx_recv)); DUK_ASSERT(pv != NULL); - return duk__setcheck_own_prop_found_setter_helper(thr, obj, NULL, idx, idx_val, idx_recv, pv, attrs, 0 /*use_key*/); + return duk__setcheck_found_setter_helper(thr, obj, NULL, idx, idx_val, idx_recv, pv, attrs, 0 /*use_key*/); } #else -DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__setcheck_own_prop_found_setter_nokey(duk_hthread *thr, - duk_hobject *obj, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_propvalue *pv, - duk_uint8_t attrs) { +DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__setcheck_found_setter_nokey(duk_hthread *thr, + duk_hobject *obj, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_propvalue *pv, + duk_uint8_t attrs) { DUK_ASSERT(thr != NULL); DUK_ASSERT(obj != NULL); DUK_ASSERT(duk_is_valid_posidx(thr, idx_val)); DUK_ASSERT(duk_is_valid_posidx(thr, idx_recv)); DUK_ASSERT(pv != NULL); - return duk__setcheck_own_prop_found_setter_helper(thr, obj, NULL, 0, idx_val, idx_recv, pv, attrs, 0 /*use_key*/); + return duk__setcheck_found_setter_helper(thr, obj, NULL, 0, idx_val, idx_recv, pv, attrs, 0 /*use_key*/); } #endif -DUK_LOCAL duk_bool_t duk__setcheck_own_prop_strkey_ordinary(duk_hthread *thr, - duk_hobject *obj, - duk_hstring *key, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag) { +DUK_LOCAL duk_bool_t duk__setcheck_strkey_ordinary(duk_hthread *thr, + duk_hobject *obj, + duk_hstring *key, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { duk_propvalue *pv; duk_uint8_t attrs; @@ -598,11 +622,11 @@ DUK_LOCAL duk_bool_t duk__setcheck_own_prop_strkey_ordinary(duk_hthread *thr, /* Accessor can be handled inline also for inherited case. */ if ((attrs & DUK_PROPDESC_FLAG_ACCESSOR) != 0) { #if defined(DUK_USE_NONSTD_SETTER_KEY_ARGUMENT) - if (duk__setcheck_own_prop_found_setter_withkey(thr, obj, key, idx_val, idx_recv, pv, attrs) == 0) { + if (duk__setcheck_found_setter_withkey(thr, obj, key, idx_val, idx_recv, pv, attrs) == 0) { goto fail_not_writable; } #else - if (duk__setcheck_own_prop_found_setter_nokey(thr, obj, idx_val, idx_val, pv, attrs) == 0) { + if (duk__setcheck_found_setter_nokey(thr, obj, idx_val, idx_val, pv, attrs) == 0) { goto fail_not_writable; } #endif @@ -620,145 +644,13 @@ fail_not_writable: return DUK__SETCHECK_DONE_FAILURE; } -DUK_LOCAL duk_bool_t duk__setcheck_own_prop_strkey(duk_hthread *thr, - duk_hobject *obj, - duk_hstring *key, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag) { - duk_small_uint_t htype; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(obj != NULL); - DUK_ASSERT(key != NULL); - DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key)); - DUK_ASSERT(duk_is_valid_index(thr, idx_val)); - DUK_ASSERT(duk_is_valid_index(thr, idx_recv)); - DUK_ASSERT(throw_flag == 0 || throw_flag == 1); - - htype = DUK_HEAPHDR_GET_HTYPE((duk_heaphdr *) obj); - DUK_ASSERT(DUK_HTYPE_IS_ANY_OBJECT(htype)); - - switch (htype) { - case DUK_HTYPE_ARRAY: { - duk_harray *a = (duk_harray *) obj; - if (DUK_HSTRING_HAS_LENGTH(key)) { - if (DUK_UNLIKELY(DUK_HARRAY_LENGTH_NONWRITABLE(a))) { - goto fail_not_writable; - } - /* For arrays the common case is a direct non-inherited - * write. Writing to .length is common but usually not - * performance critical, so no fast path yet. - */ - return DUK__SETCHECK_FOUND; - } - break; - } - case DUK_HTYPE_ARGUMENTS: - /* Special arguments behavior only triggers for index keys, - * so no special behavior here. - */ - break; - case DUK_HTYPE_STRING_OBJECT: - /* String objects don't have exotic [[Set]] behavior; the - * ordinary [[Set]] algorithm uses [[GetOwnProperty]]. - * The String [[GetOwnProperty]] uses an ordinary property - * lookup, and if not found, then checks for String .length - * and indices. - * - * In practice it's easiest to handle it as exotic behavior - * for valid indices and .length before the ordinary property - * lookup as it shouldn't be possible to establish conflicting - * ordinary properties. - */ - if (DUK_HSTRING_HAS_LENGTH(key)) { - goto fail_not_writable; - } - break; - case DUK_HTYPE_PROXY: - /* Handled by the caller in a special way to allow target - * reference stabilization, see comments in duk_prop_get.c. - */ - return DUK__SETCHECK_HANDLE_SPECIAL; - case DUK_HTYPE_COMPFUNC: - case DUK_HTYPE_NATFUNC: - case DUK_HTYPE_BOUNDFUNC: - /* No exotic [[Set]] or [[GetOwnProperty]] behavior. */ - break; -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - case DUK_HTYPE_ARRAYBUFFER: - case DUK_HTYPE_DATAVIEW: - /* ArrayBuffer and DataView .byteLength is an accessor, - * handle without fast path. - */ - 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: { - /* Typed array .length is an inherited accessor but we present a - * virtual own property. Fail a write attempt (even with SameValue() - * compatible value). - * - * Special [[Set]] behavior for CanonicalNumericIndexStrings - * when in direct receiver case. - */ - - duk_hbufobj *h = (duk_hbufobj *) obj; - - if (DUK_HSTRING_HAS_LENGTH_OR_CANNUM(key)) { - if (DUK_HSTRING_HAS_CANNUM(key)) { - /* Specification bug, exotic [[Set]] behavior should only - * trigger for direct receiver case (ES2016+ don't have a - * receiver check but it is implemented in practice). - */ - if (duk__prop_recv_direct(thr, idx_recv, obj)) { - if (DUK_HBUFOBJ_IS_DETACHED(h)) { - /* If detached, unconditional TypeError for any canonical - * numerix index string (even if out of range). - */ - duk__prop_set_error_objidx_str(thr, idx_recv, key, 1 /*throw_flag*/); - } - /* Else fall through to shared error. */ - } else { - /* Not direct receiver, no exotic behavior. - * Ordinary lookup would return not found because - * we don't allow canonical numeric index string - * properties to be established, so just shortcut - * it here. - */ - return DUK__SETCHECK_NOTFOUND; - } - } else { - DUK_ASSERT(DUK_HSTRING_HAS_LENGTH(key)); - } - goto fail_not_writable; - } - break; - } -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - default: - break; - } - - return duk__setcheck_own_prop_strkey_ordinary(thr, obj, key, idx_val, idx_recv, throw_flag); - -fail_not_writable: - return DUK__SETCHECK_DONE_FAILURE; -} - -DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__setcheck_own_prop_idxkey_arguments(duk_hthread *thr, - duk_hobject *obj, - duk_uarridx_t idx, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag, - duk_bool_t check_only) { +DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__setcheck_idxkey_arguments_helper(duk_hthread *thr, + duk_hobject *obj, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag, + duk_bool_t check_only) { duk_harray *a = (duk_harray *) obj; duk_hstring *varname; duk_hobject *map; @@ -781,7 +673,7 @@ DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__setcheck_own_prop_idxkey_argumen * 2. the property key exists in the Arguments map. * * If the exotic behavior is triggered, the Arguments map write is - * done followed by an ordinary [[Set]] for the Arguments object. + * done first, followed by an ordinary [[Set]] for the Arguments object. * * In the common case the ordinary [[Set]] will terminate because * the property exists in the Arguments object (it would not be @@ -867,15 +759,15 @@ ordinary_set_check: } } - return duk__setcheck_own_prop_idxkey_ordinary(thr, obj, idx, idx_val, idx_recv, throw_flag); + return duk__setcheck_idxkey_ordinary(thr, obj, idx, idx_val, idx_recv, throw_flag); } -DUK_LOCAL duk_bool_t duk__setcheck_own_prop_idxkey_typedarray(duk_hthread *thr, - duk_hobject *obj, - duk_uarridx_t idx, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag) { +DUK_LOCAL duk_bool_t duk__setcheck_idxkey_typedarray(duk_hthread *thr, + duk_hobject *obj, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { duk_hbufobj *h = (duk_hbufobj *) obj; duk_size_t byte_off; duk_small_uint_t elem_size; @@ -903,6 +795,7 @@ DUK_LOCAL duk_bool_t duk__setcheck_own_prop_idxkey_typedarray(duk_hthread *thr, * not yet a clear resolution: * * - https://github.com/tc39/ecma262/issues/1541 + * - https://github.com/tc39/ecma262/pull/1556 * * Best behavior for now is to add back the receiver check from ES2015 * for [[Set]], but omit it from [[Get]]. @@ -914,7 +807,7 @@ DUK_LOCAL duk_bool_t duk__setcheck_own_prop_idxkey_typedarray(duk_hthread *thr, * handled here reliably. */ - /* Don't expose exotic [[Set]] behavior if obj is not a direct + /* Don't expose exotic [[Set]] behavior if 'obj' is not a direct * receiver. This differs from [[Get]] which does allow indices * to be read via inheritance (in ES2016+). */ @@ -961,7 +854,10 @@ DUK_LOCAL duk_bool_t duk__setcheck_own_prop_idxkey_typedarray(duk_hthread *thr, } rc = DUK__SETCHECK_DONE_SUCCESS; } else { - rc = DUK__SETCHECK_DONE_FAILURE; + /* For a direct, [[Set]], even if writing past end-of-buffer, + * pretend to succeed. + */ + rc = DUK__SETCHECK_DONE_SUCCESS; } duk_pop_2_unsafe(thr); return rc; /* Never continue lookup. */ @@ -970,16 +866,16 @@ fail_detached: /* If detached, unconditional TypeError for any canonical * numerix index string (even if out of range). */ - duk__prop_set_error_objidx_idx(thr, idx_recv, idx, 1 /*throw_flag*/); + DUK_ERROR_TYPE_BUFFER_DETACHED(thr); DUK_WO_NORETURN(return 0;); } -DUK_LOCAL duk_small_int_t duk__setcheck_own_prop_idxkey_array(duk_hthread *thr, - duk_hobject *obj, - duk_uarridx_t idx, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag) { +DUK_LOCAL DUK_ALWAYS_INLINE duk_small_int_t duk__setcheck_idxkey_array_attempt(duk_hthread *thr, + duk_hobject *obj, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { duk_harray *a = (duk_harray *) obj; DUK_UNREF(throw_flag); @@ -1001,16 +897,26 @@ DUK_LOCAL duk_small_int_t duk__setcheck_own_prop_idxkey_array(duk_hthread *thr, old_len = DUK_HARRAY_GET_LENGTH(a); if (idx >= old_len) { + /* Because .length is found we can terminate search here. */ if (DUK_UNLIKELY(DUK_HARRAY_LENGTH_NONWRITABLE(a))) { goto fail_length_not_writable; } + + /* For an index key >= .length inline handling isn't possible + * here because an inherited setter might capture the write. + * This applies both to success case (i.e. want to extend and + * write and the setter cancels it) and failure case (i.e. + * array is non-extensible but setter must still be called). + */ + return DUK__SETCHECK_NOTFOUND; +#if 0 if (DUK_UNLIKELY(!DUK_HOBJECT_HAS_EXTENSIBLE(obj))) { - /* Don't try to extend if we're going to fail anyway. */ goto fail_not_extensible; } new_len = idx + 1; DUK_ASSERT(new_len > idx); DUK_ASSERT(new_len >= 1U); +#endif } else { new_len = 0U; /* Marker: no length update. */ } @@ -1056,7 +962,11 @@ DUK_LOCAL duk_small_int_t duk__setcheck_own_prop_idxkey_array(duk_hthread *thr, if (idx < DUK_HARRAY_GET_LENGTH(a)) { duk_tval *tv_val = DUK_HARRAY_GET_ITEMS(thr->heap, a) + idx; if (DUK_TVAL_IS_UNUSED(tv_val)) { - return DUK__SETCHECK_NOTFOUND; + if (!duk__prop_assume_no_inherited_array_indices(thr, obj)) { + return DUK__SETCHECK_NOTFOUND; + } else { + return DUK__SETCHECK_NOTFOUND; + } } else { return DUK__SETCHECK_FOUND; } @@ -1064,86 +974,930 @@ DUK_LOCAL duk_small_int_t duk__setcheck_own_prop_idxkey_array(duk_hthread *thr, return DUK__SETCHECK_NOTFOUND; } - /* Never here. */ - DUK_ASSERT(0); - return DUK__SETCHECK_NOTFOUND; + /* Never here. */ + DUK_ASSERT(0); + return DUK__SETCHECK_NOTFOUND; + +abandoned: + return -1; + +fail_not_extensible: +fail_length_not_writable: + return DUK__SETCHECK_DONE_FAILURE; +} + +DUK_LOCAL duk_bool_t duk__setcheck_idxkey_ordinary(duk_hthread *thr, + duk_hobject *obj, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { + duk_propvalue *pv; + duk_uint8_t attrs; + + DUK_ASSERT(thr != NULL); + DUK_ASSERT(obj != NULL); + DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)); + DUK_ASSERT_ARRIDX_VALID(idx); + DUK_ASSERT(duk_is_valid_index(thr, idx_val)); + DUK_ASSERT(duk_is_valid_index(thr, idx_recv)); + DUK_ASSERT(throw_flag == 0 || throw_flag == 1); + DUK_UNREF(throw_flag); + + if (duk_hobject_lookup_idxprop_val_attrs(thr, obj, idx, &pv, &attrs) != 0) { + /* Fast path for write allowed data property case, single branch. */ + if (DUK_LIKELY((attrs & (DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_ACCESSOR)) == DUK_PROPDESC_FLAG_WRITABLE)) { + /* Not an accessor, writable property. Fast path direct writes + * which are relatively common. + */ +#if !defined(DUK_USE_PREFER_SIZE) + if (duk__prop_recv_direct(thr, idx_recv, obj)) { + duk__prop_set_write_tval(thr, idx_val, &pv->v); + return DUK__SETCHECK_DONE_SUCCESS; + } +#endif + return DUK__SETCHECK_FOUND; + } else { + /* Accessor can be handled inline also for inherited case. */ + if ((attrs & DUK_PROPDESC_FLAG_ACCESSOR) != 0) { +#if defined(DUK_USE_NONSTD_SETTER_KEY_ARGUMENT) + if (duk__setcheck_found_setter_withidx(thr, obj, idx, idx_val, idx_recv, pv, attrs) == 0) { + goto fail_not_writable; + } +#else + if (duk__setcheck_found_setter_nokey(thr, obj, idx_val, idx_val, pv, attrs) == 0) { + goto fail_not_writable; + } +#endif + return DUK__SETCHECK_DONE_SUCCESS; + } else { + DUK_ASSERT((attrs & DUK_PROPDESC_FLAG_WRITABLE) == 0); + goto fail_not_writable; + } + } + } + + return DUK__SETCHECK_NOTFOUND; + +fail_not_writable: + return DUK__SETCHECK_DONE_FAILURE; +} + +DUK_LOCAL duk_bool_t duk__setcheck_strkey_switch(duk_hthread *thr, + duk_hobject *obj, + duk_hstring *key, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { + duk_small_uint_t htype; + + DUK_ASSERT(thr != NULL); + DUK_ASSERT(obj != NULL); + DUK_ASSERT(key != NULL); + DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key)); + DUK_ASSERT(duk_is_valid_index(thr, idx_val)); + DUK_ASSERT(duk_is_valid_index(thr, idx_recv)); + DUK_ASSERT(throw_flag == 0 || throw_flag == 1); + + htype = DUK_HEAPHDR_GET_HTYPE((duk_heaphdr *) obj); + DUK_ASSERT(DUK_HTYPE_IS_ANY_OBJECT(htype)); + + switch (htype) { + case DUK_HTYPE_ARRAY: { + duk_harray *a = (duk_harray *) obj; + if (DUK_HSTRING_HAS_LENGTH(key)) { + if (DUK_UNLIKELY(DUK_HARRAY_LENGTH_NONWRITABLE(a))) { + goto fail_not_writable; + } + /* For arrays the common case is a direct non-inherited + * write. Writing to .length is common but usually not + * performance critical, so no fast path yet. + */ + return DUK__SETCHECK_FOUND; + } + break; + } + case DUK_HTYPE_ARGUMENTS: + /* Special arguments behavior only triggers for index keys, + * so no special behavior here. + */ + break; + case DUK_HTYPE_STRING_OBJECT: + /* String objects don't have exotic [[Set]] behavior; the + * ordinary [[Set]] algorithm uses [[GetOwnProperty]]. + * The String [[GetOwnProperty]] uses an ordinary property + * lookup, and if not found, then checks for String .length + * and indices. + * + * In practice it's easiest to handle it as exotic behavior + * for valid indices and .length before the ordinary property + * lookup as it shouldn't be possible to establish conflicting + * ordinary properties. + */ + if (DUK_HSTRING_HAS_LENGTH(key)) { + goto fail_not_writable; + } + break; + case DUK_HTYPE_PROXY: + /* Handled by the caller in a special way to allow target + * reference stabilization, see comments in duk_prop_get.c. + */ + return DUK__SETCHECK_HANDLE_SPECIAL; + case DUK_HTYPE_COMPFUNC: + case DUK_HTYPE_NATFUNC: + case DUK_HTYPE_BOUNDFUNC: + /* No exotic [[Set]] or [[GetOwnProperty]] behavior. */ + break; +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) + case DUK_HTYPE_ARRAYBUFFER: + case DUK_HTYPE_DATAVIEW: + /* ArrayBuffer and DataView .byteLength is an accessor, + * handle without fast path. + */ + 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: { + /* Typed array .length is an inherited accessor but we present a + * virtual own property. Fail a write attempt (even with SameValue() + * compatible value). + * + * Special [[Set]] behavior for CanonicalNumericIndexStrings + * when in direct receiver case. + */ + + duk_hbufobj *h = (duk_hbufobj *) obj; + + if (DUK_HSTRING_HAS_LENGTH_OR_CANNUM(key)) { + if (DUK_HSTRING_HAS_CANNUM(key)) { + /* Specification bug, exotic [[Set]] behavior should only + * trigger for direct receiver case (ES2016+ don't have a + * receiver check but it is implemented in practice). + */ + if (duk__prop_recv_direct(thr, idx_recv, obj)) { + if (DUK_HBUFOBJ_IS_DETACHED(h)) { + /* If detached, unconditional TypeError for any canonical + * numerix index string (even if out of range). + */ + duk__prop_set_error_objidx_strkey(thr, idx_recv, key, 1 /*throw_flag*/); + } + /* Else fall through to shared error. */ + } else { + /* Not direct receiver, no exotic behavior. + * Ordinary lookup would return not found because + * we don't allow canonical numeric index string + * properties to be established, so just shortcut + * it here. + */ + return DUK__SETCHECK_NOTFOUND; + } + } else { + DUK_ASSERT(DUK_HSTRING_HAS_LENGTH(key)); + } + goto fail_not_writable; + } + break; + } +#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ + default: + break; + } + + return duk__setcheck_strkey_ordinary(thr, obj, key, idx_val, idx_recv, throw_flag); + +fail_not_writable: + return DUK__SETCHECK_DONE_FAILURE; +} + +DUK_LOCAL duk_bool_t duk__setcheck_idxkey_switch(duk_hthread *thr, + duk_hobject *obj, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { + duk_small_uint_t htype; + + DUK_ASSERT(thr != NULL); + DUK_ASSERT(obj != NULL); + DUK_ASSERT_ARRIDX_VALID(idx); + DUK_ASSERT(duk_is_valid_index(thr, idx_val)); + DUK_ASSERT(duk_is_valid_index(thr, idx_recv)); + DUK_ASSERT(throw_flag == 0 || throw_flag == 1); + + htype = DUK_HEAPHDR_GET_HTYPE((duk_heaphdr *) obj); + DUK_ASSERT(DUK_HTYPE_IS_ANY_OBJECT(htype)); + + switch (htype) { + case DUK_HTYPE_ARRAY: { + duk_small_int_t set_rc; + + set_rc = duk__setcheck_idxkey_array_attempt(thr, obj, idx, idx_val, idx_recv, throw_flag); + if (DUK_LIKELY(set_rc >= 0)) { + return (duk_bool_t) set_rc; + } + DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)); + break; /* Check from index part if abandoned. */ + } + case DUK_HTYPE_ARGUMENTS: + /* Arguments exotic [[Set]] is tricky, see detailed comments + * in the helper. + */ + return duk__setcheck_idxkey_arguments_helper(thr, obj, idx, idx_val, idx_recv, throw_flag, 1 /*check_only*/); + case DUK_HTYPE_STRING_OBJECT: { + /* String objects don't have exotic [[Set]] behavior; the + * ordinary [[Set]] algorithm uses [[GetOwnProperty]]. + * In practice it's easiest to handle it as exotic + * behavior for valid indices and .length. + */ + duk_hstring *h; + + h = duk_hobject_lookup_intvalue_hstring(thr, obj); + if (h != NULL && idx < duk_hstring_get_charlen(h)) { + goto fail_not_writable; + } + /* Out of bounds, go to normal property table. */ + break; + } + case DUK_HTYPE_PROXY: + /* Handled by the caller in the NULL prototype path, see + * comments in duk_prop_get.c. + */ + return DUK__SETCHECK_HANDLE_SPECIAL; + case DUK_HTYPE_COMPFUNC: + case DUK_HTYPE_NATFUNC: + case DUK_HTYPE_BOUNDFUNC: + /* No exotic [[Set]] or [[GetOwnProperty]] behavior. */ + break; +#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: + return duk__setcheck_idxkey_typedarray(thr, obj, idx, idx_val, idx_recv, throw_flag); +#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ + default: + break; + } + + return duk__setcheck_idxkey_ordinary(thr, obj, idx, idx_val, idx_recv, throw_flag); + +fail_not_writable: + return DUK__SETCHECK_DONE_FAILURE; +} + +DUK_LOCAL duk_bool_t duk__setcheck_idxkey_error(duk_hthread *thr, + duk_hobject *obj, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { + DUK_UNREF(thr); + DUK_UNREF(obj); + DUK_UNREF(idx); + DUK_UNREF(idx_val); + DUK_UNREF(idx_recv); + DUK_UNREF(throw_flag); + return DUK__SETCHECK_DONE_FAILURE; +} + +DUK_LOCAL duk_bool_t duk__setcheck_idxkey_array(duk_hthread *thr, + duk_hobject *obj, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { + duk_small_int_t set_rc; + + set_rc = duk__setcheck_idxkey_array_attempt(thr, obj, idx, idx_val, idx_recv, throw_flag); + if (DUK_LIKELY(set_rc >= 0)) { + return (duk_bool_t) set_rc; + } + + DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)); + return duk__setcheck_idxkey_ordinary(thr, obj, idx, idx_val, idx_recv, throw_flag); +} + +DUK_LOCAL duk_bool_t duk__setcheck_idxkey_arguments(duk_hthread *thr, + duk_hobject *obj, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { + return duk__setcheck_idxkey_arguments_helper(thr, obj, idx, idx_val, idx_recv, throw_flag, 1 /*check_only*/); +} + +DUK_LOCAL duk_bool_t duk__setcheck_idxkey_proxy(duk_hthread *thr, + duk_hobject *obj, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { + DUK_UNREF(thr); + DUK_UNREF(obj); + DUK_UNREF(idx); + DUK_UNREF(idx_val); + DUK_UNREF(idx_recv); + DUK_UNREF(throw_flag); + return DUK__SETCHECK_HANDLE_SPECIAL; +} + +DUK_LOCAL duk_bool_t duk__setcheck_idxkey_stringobj(duk_hthread *thr, + duk_hobject *obj, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { + /* String objects don't have exotic [[Set]] behavior; the + * ordinary [[Set]] algorithm uses [[GetOwnProperty]]. + * In practice it's easiest to handle it as exotic + * behavior for valid indices and .length. + */ + duk_hstring *h; + + h = duk_hobject_lookup_intvalue_hstring(thr, obj); + if (h != NULL && idx < duk_hstring_get_charlen(h)) { + goto fail_not_writable; + } + + /* Out of bounds, go to normal property table. */ + return duk__setcheck_idxkey_ordinary(thr, obj, idx, idx_val, idx_recv, throw_flag); + +fail_not_writable: + return DUK__SETCHECK_DONE_FAILURE; +} + +DUK_LOCAL duk_bool_t duk__setcheck_strkey_array(duk_hthread *thr, + duk_hobject *obj, + duk_hstring *key, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { + duk_harray *a = (duk_harray *) obj; + if (DUK_HSTRING_HAS_LENGTH(key)) { + if (DUK_UNLIKELY(DUK_HARRAY_LENGTH_NONWRITABLE(a))) { + goto fail_not_writable; + } + /* For arrays the common case is a direct non-inherited + * write. Writing to .length is common but usually not + * performance critical, so no fast path yet. + */ + return DUK__SETCHECK_FOUND; + } + return duk__setcheck_strkey_ordinary(thr, obj, key, idx_val, idx_recv, throw_flag); + +fail_not_writable: + return DUK__SETCHECK_DONE_FAILURE; +} + +DUK_LOCAL duk_bool_t duk__setcheck_strkey_stringobj(duk_hthread *thr, + duk_hobject *obj, + duk_hstring *key, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { + /* String objects don't have exotic [[Set]] behavior; the + * ordinary [[Set]] algorithm uses [[GetOwnProperty]]. + * The String [[GetOwnProperty]] uses an ordinary property + * lookup, and if not found, then checks for String .length + * and indices. + * + * In practice it's easiest to handle it as exotic behavior + * for valid indices and .length before the ordinary property + * lookup as it shouldn't be possible to establish conflicting + * ordinary properties. + */ + if (DUK_HSTRING_HAS_LENGTH(key)) { + goto fail_not_writable; + } + return duk__setcheck_strkey_ordinary(thr, obj, key, idx_val, idx_recv, throw_flag); + +fail_not_writable: + return DUK__SETCHECK_DONE_FAILURE; +} + +DUK_LOCAL duk_bool_t duk__setcheck_strkey_proxy(duk_hthread *thr, + duk_hobject *obj, + duk_hstring *key, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { + DUK_UNREF(thr); + DUK_UNREF(obj); + DUK_UNREF(key); + DUK_UNREF(idx_val); + DUK_UNREF(idx_recv); + DUK_UNREF(throw_flag); + return DUK__SETCHECK_HANDLE_SPECIAL; +} + +DUK_LOCAL duk_bool_t duk__setcheck_strkey_typedarray(duk_hthread *thr, + duk_hobject *obj, + duk_hstring *key, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { + /* Typed array .length is an inherited accessor but we present a + * virtual own property. Fail a write attempt (even with SameValue() + * compatible value). + * + * Special [[Set]] behavior for CanonicalNumericIndexStrings + * when in direct receiver case. + */ + duk_hbufobj *h = (duk_hbufobj *) obj; + + if (DUK_HSTRING_HAS_LENGTH_OR_CANNUM(key)) { + if (DUK_HSTRING_HAS_CANNUM(key)) { + /* Specification bug, exotic [[Set]] behavior should only + * trigger for direct receiver case (ES2016+ don't have a + * receiver check but it is implemented in practice). + */ + if (duk__prop_recv_direct(thr, idx_recv, obj)) { + if (DUK_HBUFOBJ_IS_DETACHED(h)) { + /* If detached, unconditional TypeError for any canonical + * numerix index string (even if out of range). + */ + duk__prop_set_error_objidx_strkey(thr, idx_recv, key, 1 /*throw_flag*/); + } + /* Else fall through to shared error. */ + } else { + /* Not direct receiver, no exotic behavior. + * Ordinary lookup would return not found because + * we don't allow canonical numeric index string + * properties to be established, so just shortcut + * it here. + */ + return DUK__SETCHECK_NOTFOUND; + } + } else { + DUK_ASSERT(DUK_HSTRING_HAS_LENGTH(key)); + } + goto fail_not_writable; + } + return duk__setcheck_strkey_ordinary(thr, obj, key, idx_val, idx_recv, throw_flag); + +fail_not_writable: + return DUK__SETCHECK_DONE_FAILURE; +} + +DUK_LOCAL duk_bool_t duk__setcheck_strkey_error(duk_hthread *thr, + duk_hobject *obj, + duk_hstring *key, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { + DUK_UNREF(thr); + DUK_UNREF(obj); + DUK_UNREF(key); + DUK_UNREF(idx_val); + DUK_UNREF(idx_recv); + DUK_UNREF(throw_flag); + return DUK__SETCHECK_DONE_FAILURE; +} + +DUK_LOCAL duk_bool_t duk__setfinal_strkey_error(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_val) { + DUK_UNREF(thr); + DUK_UNREF(obj); + DUK_UNREF(key); + DUK_UNREF(idx_val); + return 0; +} + +DUK_LOCAL duk_bool_t duk__setfinal_strkey_array(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_val) { + /* Array does not have exotic [[Set]] behavior. Conceptually + * ordinary [[Set]] finds Array .length descriptor and checks + * its writability. If writable, [[DefineOwnProperty]] is + * invoked. If not writable, [[DefineOwnProperty]] is not + * invoked at all (even if value would be SameValue to existing). + */ + if (DUK_HSTRING_HAS_LENGTH(key)) { + duk_harray *a = (duk_harray *) obj; + duk_uint32_t new_len; + + /* Recheck in case of side effects mutating object. */ + if (DUK_UNLIKELY(DUK_HARRAY_LENGTH_NONWRITABLE(a))) { + goto fail_not_writable; + } + + /* 'obj' is stabilized in value stack, so length coercion + * side effects are OK here. + */ + new_len = duk_harray_to_array_length_checked(thr, DUK_GET_TVAL_POSIDX(thr, idx_val)); + return duk_harray_put_array_length_u32(thr, obj, new_len, 0 /*force_flag*/); + } + return duk__setfinal_strkey_ordinary(thr, obj, key, idx_val); + +fail_not_writable: + return 0; +} + +DUK_LOCAL duk_bool_t duk__setfinal_strkey_stringobj(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_val) { + /* We should have already rejected a .length write in the + * "check" phase. + */ + DUK_ASSERT(!DUK_HSTRING_HAS_LENGTH(key)); +#if 1 + if (DUK_UNLIKELY(DUK_HSTRING_HAS_LENGTH(key))) { + /* Fail safely. */ + DUK_D(DUK_DPRINT("String object setfinal for 'length' reached, should not happen, fail write")); + goto fail_not_writable; + } +#endif + return duk__setfinal_strkey_ordinary(thr, obj, key, idx_val); + +fail_not_writable: + return 0; +} + +DUK_LOCAL duk_bool_t duk__setfinal_strkey_proxy(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_val) { + /* We may come here when a Proxy is the original receiver + * but it doesn't contain an applicable trap. [[Set]] + * processing propagates through the target, and the final + * write comes here. + * + * The final [[Set]] behavior is from the ultimate target + * (ordinary Set) but it calls [[GetOwnProperty]] and + * [[DefineProperty]] which are applied to the Proxy. + * Both are visible as side effects which must be respected. + */ + duk_small_int_t attrs; + duk_uint_t defprop_flags; + duk_bool_t rc; + + attrs = duk_prop_getowndesc_obj_strkey(thr, obj, key); + duk_prop_pop_propdesc(thr, attrs); + if (attrs >= 0) { + duk_small_uint_t uattrs = (duk_small_uint_t) attrs; + + if (uattrs & DUK_PROPDESC_FLAG_ACCESSOR) { + goto fail_accessor; + } + if (!(uattrs & DUK_PROPDESC_FLAG_WRITABLE)) { + goto fail_not_writable; + } + defprop_flags = DUK_DEFPROP_HAVE_VALUE; /* Keep attributes as is. */ + } else { + defprop_flags = DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WEC; + } + duk_dup(thr, idx_val); + rc = duk_prop_defown_strkey(thr, obj, key, duk_get_top_index(thr), defprop_flags); + duk_pop_unsafe(thr); + return rc; + +fail_accessor: +fail_not_writable: + return 0; +} + +DUK_LOCAL duk_bool_t duk__setfinal_strkey_typedarray(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_val) { + /* We should normally not come here: the .length + * or canonical numeric index string case has + * already been checked and rejected in the 'check' + * phase for this object (as direct receiver). + */ + DUK_ASSERT(!DUK_HSTRING_HAS_LENGTH(key)); + DUK_ASSERT(!DUK_HSTRING_HAS_CANNUM(key)); +#if 1 + if (DUK_UNLIKELY(DUK_HSTRING_HAS_LENGTH_OR_CANNUM(key))) { + /* Fail safely. */ + DUK_D(DUK_DPRINT("typed array setfinal for 'length' or cannum reached, should not happen, fail write")); + goto fail_not_writable; +#endif + } + return duk__setfinal_strkey_ordinary(thr, obj, key, idx_val); + +fail_not_writable: + return 0; +} + +DUK_LOCAL duk_bool_t duk__setfinal_idxkey_error(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_val) { + DUK_UNREF(thr); + DUK_UNREF(obj); + DUK_UNREF(idx); + DUK_UNREF(idx_val); + return 0; +} + +DUK_LOCAL duk_bool_t duk__setfinal_idxkey_array(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_val) { + /* Array does not have exotic [[Set]] behavior. Conceptually + * ordinary [[Set]] finds Array index descriptors and checks + * their writability. If writable, [[DefineOwnProperty]] is + * invoked (which then has some exotic behaviors, e.g. to update + * 'length'). If not writable, [[DefineOwnProperty]] is not + * invoked at all, even if value would be SameValue to existing. + */ + if (DUK_LIKELY(DUK_HOBJECT_HAS_ARRAY_ITEMS(obj))) { + duk_small_int_t setfin_rc; + + setfin_rc = duk__setfinal_write_array_arrayitems_idxkey(thr, obj, idx, idx_val); + DUK_ASSERT(setfin_rc == 0 || setfin_rc == 1 || setfin_rc == -1); + if (setfin_rc > 0) { + DUK_ASSERT(setfin_rc == 1); + DUK_ASSERT(DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)); + return (duk_bool_t) setfin_rc; + } else if (setfin_rc == 0) { + goto fail_array; + } else { + DUK_ASSERT(setfin_rc == -1); + DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)); + } + } + DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)); + return duk__setfinal_write_array_abandoned_idxkey(thr, obj, idx, idx_val); + +fail_array: + return 0; +} + +DUK_LOCAL duk_bool_t duk__setfinal_idxkey_arguments(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_val) { + /* Similar to Array case, but no exotic 'length'. Array magic + * arguments map behavior was already applied in the 'check' + * phase. + */ + if (DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)) { + duk_small_int_t setfin_rc; + + setfin_rc = duk__setfinal_write_arguments_arrayitems_idxkey(thr, obj, idx, idx_val); + DUK_ASSERT(setfin_rc == 0 || setfin_rc == 1 || setfin_rc == -1); + if (setfin_rc > 0) { + DUK_ASSERT(setfin_rc == 1); + DUK_ASSERT(DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)); + return (duk_bool_t) setfin_rc; + } else if (setfin_rc == 0) { + goto fail_arguments; + } else { + DUK_ASSERT(setfin_rc == -1); + DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)); + } + DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)); + } + /* No array items, handle in shared index part path. */ + return duk__setfinal_idxkey_ordinary(thr, obj, idx, idx_val); + +fail_arguments: + return 0; +} + +DUK_LOCAL duk_bool_t duk__setfinal_idxkey_stringobj(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_val) { + /* "Check" path should have rejected writes to all exotic + * index properties (0 <= idx < .length) and .length. So + * here only ordinary behavior should remain. + */ + return duk__setfinal_idxkey_ordinary(thr, obj, idx, idx_val); +} + +DUK_LOCAL duk_bool_t duk__setfinal_idxkey_proxy(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_val) { + duk_small_int_t attrs; + duk_uint_t defprop_flags; + duk_bool_t rc; + + attrs = duk_prop_getowndesc_obj_idxkey(thr, obj, idx); + duk_prop_pop_propdesc(thr, attrs); + if (attrs >= 0) { + duk_small_uint_t uattrs = (duk_small_uint_t) attrs; + + if (uattrs & DUK_PROPDESC_FLAG_ACCESSOR) { + goto fail_accessor; + } + if (!(uattrs & DUK_PROPDESC_FLAG_WRITABLE)) { + goto fail_not_writable; + } + defprop_flags = DUK_DEFPROP_HAVE_VALUE; /* Keep attributes as is. */ + } else { + defprop_flags = DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WEC; + } + duk_dup(thr, idx_val); + rc = duk_prop_defown_idxkey(thr, obj, idx, duk_get_top_index(thr), defprop_flags); + duk_pop_unsafe(thr); + return rc; + +fail_accessor: +fail_not_writable: + return 0; +} + +DUK_LOCAL duk_bool_t duk__setfinal_idxkey_typedarray(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_val) { + /* If [[Set]] is applied to a typed array with a valid + * CanonicalNumericIndexString (all arridx are such strings, + * but also many other strings like 'NaN'), the [[Set]] + * operation won't return to the original receiver call site, + * i.e. it terminates in the check phase. + * + * While typed arrays have a custom [[DefineOwnProperty]] we + * should never come here. Side effects during [[Set]] lookup + * (e.g. Proxy trap checks) can modify the typed array but + * cannot change its [[Set]] capturing nature. + */ + DUK_UNREF(thr); + DUK_UNREF(obj); + DUK_UNREF(idx); + DUK_UNREF(idx_val); + +#if 1 + DUK_D(DUK_DPRINT("typed array setfinal for index key reached, should not happen, fail write")); + goto fail_internal; + +fail_internal: +#endif + return 0; +} + +DUK_LOCAL const duk__setfinal_strkey_htype duk__setfinal_strkey_handlers[64] = { + duk__setfinal_strkey_error, duk__setfinal_strkey_error, duk__setfinal_strkey_error, + duk__setfinal_strkey_error, duk__setfinal_strkey_error, duk__setfinal_strkey_error, + duk__setfinal_strkey_error, duk__setfinal_strkey_error, + + duk__setfinal_strkey_array, duk__setfinal_strkey_ordinary, /* For string keys, simplifies to ordinary algorithm for + Arguments. */ + duk__setfinal_strkey_ordinary, duk__setfinal_strkey_error, duk__setfinal_strkey_ordinary, + duk__setfinal_strkey_ordinary, duk__setfinal_strkey_ordinary, duk__setfinal_strkey_error, + + duk__setfinal_strkey_ordinary, duk__setfinal_strkey_ordinary, duk__setfinal_strkey_ordinary, + duk__setfinal_strkey_ordinary, duk__setfinal_strkey_ordinary, duk__setfinal_strkey_ordinary, + duk__setfinal_strkey_ordinary, duk__setfinal_strkey_stringobj, /* Check path should have rejected writes, so only ordinary + behavior should remain for stringobjs. */ + + duk__setfinal_strkey_ordinary, duk__setfinal_strkey_ordinary, duk__setfinal_strkey_ordinary, + duk__setfinal_strkey_ordinary, duk__setfinal_strkey_ordinary, duk__setfinal_strkey_ordinary, + duk__setfinal_strkey_proxy, duk__setfinal_strkey_error, + + duk__setfinal_strkey_error, duk__setfinal_strkey_error, duk__setfinal_strkey_error, + duk__setfinal_strkey_error, duk__setfinal_strkey_error, duk__setfinal_strkey_error, + duk__setfinal_strkey_error, duk__setfinal_strkey_error, + + duk__setfinal_strkey_error, duk__setfinal_strkey_error, duk__setfinal_strkey_error, + duk__setfinal_strkey_error, duk__setfinal_strkey_error, duk__setfinal_strkey_error, + duk__setfinal_strkey_error, duk__setfinal_strkey_error, + + duk__setfinal_strkey_ordinary, duk__setfinal_strkey_error, duk__setfinal_strkey_ordinary, + duk__setfinal_strkey_typedarray, duk__setfinal_strkey_typedarray, duk__setfinal_strkey_typedarray, + duk__setfinal_strkey_typedarray, duk__setfinal_strkey_typedarray, + + duk__setfinal_strkey_typedarray, duk__setfinal_strkey_typedarray, duk__setfinal_strkey_typedarray, + duk__setfinal_strkey_typedarray, duk__setfinal_strkey_error, duk__setfinal_strkey_error, + duk__setfinal_strkey_error, duk__setfinal_strkey_error +}; + +DUK_LOCAL const duk__setfinal_idxkey_htype duk__setfinal_idxkey_handlers[64] = { + duk__setfinal_idxkey_error, duk__setfinal_idxkey_error, duk__setfinal_idxkey_error, + duk__setfinal_idxkey_error, duk__setfinal_idxkey_error, duk__setfinal_idxkey_error, + duk__setfinal_idxkey_error, duk__setfinal_idxkey_error, + + duk__setfinal_idxkey_array, duk__setfinal_idxkey_arguments, duk__setfinal_idxkey_ordinary, + duk__setfinal_idxkey_error, duk__setfinal_idxkey_ordinary, duk__setfinal_idxkey_ordinary, + duk__setfinal_idxkey_ordinary, duk__setfinal_idxkey_error, + + duk__setfinal_idxkey_ordinary, duk__setfinal_idxkey_ordinary, duk__setfinal_idxkey_ordinary, + duk__setfinal_idxkey_ordinary, duk__setfinal_idxkey_ordinary, duk__setfinal_idxkey_ordinary, + duk__setfinal_idxkey_ordinary, duk__setfinal_idxkey_stringobj, + + duk__setfinal_idxkey_ordinary, duk__setfinal_idxkey_ordinary, duk__setfinal_idxkey_ordinary, + duk__setfinal_idxkey_ordinary, duk__setfinal_idxkey_ordinary, duk__setfinal_idxkey_ordinary, + duk__setfinal_idxkey_proxy, duk__setfinal_idxkey_error, + + duk__setfinal_idxkey_error, duk__setfinal_idxkey_error, duk__setfinal_idxkey_error, + duk__setfinal_idxkey_error, duk__setfinal_idxkey_error, duk__setfinal_idxkey_error, + duk__setfinal_idxkey_error, duk__setfinal_idxkey_error, + + duk__setfinal_idxkey_error, duk__setfinal_idxkey_error, duk__setfinal_idxkey_error, + duk__setfinal_idxkey_error, duk__setfinal_idxkey_error, duk__setfinal_idxkey_error, + duk__setfinal_idxkey_error, duk__setfinal_idxkey_error, + + duk__setfinal_idxkey_ordinary, duk__setfinal_idxkey_error, duk__setfinal_idxkey_ordinary, + duk__setfinal_idxkey_typedarray, duk__setfinal_idxkey_typedarray, duk__setfinal_idxkey_typedarray, + duk__setfinal_idxkey_typedarray, duk__setfinal_idxkey_typedarray, + + duk__setfinal_idxkey_typedarray, duk__setfinal_idxkey_typedarray, duk__setfinal_idxkey_typedarray, + duk__setfinal_idxkey_typedarray, duk__setfinal_idxkey_error, duk__setfinal_idxkey_error, + duk__setfinal_idxkey_error, duk__setfinal_idxkey_error +}; + +DUK_LOCAL const duk__setcheck_strkey_htype duk__setcheck_strkey_handlers[64] = { + duk__setcheck_strkey_error, duk__setcheck_strkey_error, duk__setcheck_strkey_error, + duk__setcheck_strkey_error, duk__setcheck_strkey_error, duk__setcheck_strkey_error, + duk__setcheck_strkey_error, duk__setcheck_strkey_error, + + duk__setcheck_strkey_array, duk__setcheck_strkey_ordinary, /* no exotic Arguments behaviors for string keys */ + duk__setcheck_strkey_ordinary, duk__setcheck_strkey_error, duk__setcheck_strkey_ordinary, + duk__setcheck_strkey_ordinary, duk__setcheck_strkey_ordinary, duk__setcheck_strkey_error, + + duk__setcheck_strkey_ordinary, duk__setcheck_strkey_ordinary, duk__setcheck_strkey_ordinary, + duk__setcheck_strkey_ordinary, duk__setcheck_strkey_ordinary, duk__setcheck_strkey_ordinary, + duk__setcheck_strkey_ordinary, duk__setcheck_strkey_stringobj, + + duk__setcheck_strkey_ordinary, duk__setcheck_strkey_ordinary, duk__setcheck_strkey_ordinary, + duk__setcheck_strkey_ordinary, duk__setcheck_strkey_ordinary, duk__setcheck_strkey_ordinary, + duk__setcheck_strkey_proxy, duk__setcheck_strkey_error, + + duk__setcheck_strkey_error, duk__setcheck_strkey_error, duk__setcheck_strkey_error, + duk__setcheck_strkey_error, duk__setcheck_strkey_error, duk__setcheck_strkey_error, + duk__setcheck_strkey_error, duk__setcheck_strkey_error, + + duk__setcheck_strkey_error, duk__setcheck_strkey_error, duk__setcheck_strkey_error, + duk__setcheck_strkey_error, duk__setcheck_strkey_error, duk__setcheck_strkey_error, + duk__setcheck_strkey_error, duk__setcheck_strkey_error, + + duk__setcheck_strkey_ordinary, duk__setcheck_strkey_error, duk__setcheck_strkey_ordinary, + duk__setcheck_strkey_typedarray, duk__setcheck_strkey_typedarray, duk__setcheck_strkey_typedarray, + duk__setcheck_strkey_typedarray, duk__setcheck_strkey_typedarray, + + duk__setcheck_strkey_typedarray, duk__setcheck_strkey_typedarray, duk__setcheck_strkey_typedarray, + duk__setcheck_strkey_typedarray, duk__setcheck_strkey_error, duk__setcheck_strkey_error, + duk__setcheck_strkey_error, duk__setcheck_strkey_error +}; + +DUK_LOCAL const duk__setcheck_idxkey_htype duk__setcheck_idxkey_handlers[64] = { + duk__setcheck_idxkey_error, duk__setcheck_idxkey_error, duk__setcheck_idxkey_error, + duk__setcheck_idxkey_error, duk__setcheck_idxkey_error, duk__setcheck_idxkey_error, + duk__setcheck_idxkey_error, duk__setcheck_idxkey_error, + + duk__setcheck_idxkey_array, duk__setcheck_idxkey_arguments, duk__setcheck_idxkey_ordinary, + duk__setcheck_idxkey_error, duk__setcheck_idxkey_ordinary, duk__setcheck_idxkey_ordinary, + duk__setcheck_idxkey_ordinary, duk__setcheck_idxkey_error, + + duk__setcheck_idxkey_ordinary, duk__setcheck_idxkey_ordinary, duk__setcheck_idxkey_ordinary, + duk__setcheck_idxkey_ordinary, duk__setcheck_idxkey_ordinary, duk__setcheck_idxkey_ordinary, + duk__setcheck_idxkey_ordinary, duk__setcheck_idxkey_stringobj, + + duk__setcheck_idxkey_ordinary, duk__setcheck_idxkey_ordinary, duk__setcheck_idxkey_ordinary, + duk__setcheck_idxkey_ordinary, duk__setcheck_idxkey_ordinary, duk__setcheck_idxkey_ordinary, + duk__setcheck_idxkey_proxy, duk__setcheck_idxkey_error, + + duk__setcheck_idxkey_error, duk__setcheck_idxkey_error, duk__setcheck_idxkey_error, + duk__setcheck_idxkey_error, duk__setcheck_idxkey_error, duk__setcheck_idxkey_error, + duk__setcheck_idxkey_error, duk__setcheck_idxkey_error, + + duk__setcheck_idxkey_error, duk__setcheck_idxkey_error, duk__setcheck_idxkey_error, + duk__setcheck_idxkey_error, duk__setcheck_idxkey_error, duk__setcheck_idxkey_error, + duk__setcheck_idxkey_error, duk__setcheck_idxkey_error, + + duk__setcheck_idxkey_ordinary, duk__setcheck_idxkey_error, duk__setcheck_idxkey_ordinary, + duk__setcheck_idxkey_typedarray, duk__setcheck_idxkey_typedarray, duk__setcheck_idxkey_typedarray, + duk__setcheck_idxkey_typedarray, duk__setcheck_idxkey_typedarray, + + duk__setcheck_idxkey_typedarray, duk__setcheck_idxkey_typedarray, duk__setcheck_idxkey_typedarray, + duk__setcheck_idxkey_typedarray, duk__setcheck_idxkey_error, duk__setcheck_idxkey_error, + duk__setcheck_idxkey_error, duk__setcheck_idxkey_error +}; + +DUK_LOCAL duk_bool_t duk__setfinal_strkey_htypejump(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_val) { + duk_small_uint_t htype; -abandoned: - return -1; + DUK_ASSERT(thr != NULL); + DUK_ASSERT(obj != NULL); + DUK_ASSERT(key != NULL); + DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key)); + DUK_ASSERT(duk_is_valid_posidx(thr, idx_val)); -fail_not_extensible: -fail_length_not_writable: - return DUK__SETCHECK_DONE_FAILURE; + /* Here 'obj' is always the receiver, and is stabilized automatically + * by the duk_tval at 'idx_recv'. + */ + + htype = DUK_HEAPHDR_GET_HTYPE((duk_heaphdr *) obj); + DUK_ASSERT(DUK_HTYPE_IS_ANY_OBJECT(htype)); + + return duk__setfinal_strkey_handlers[htype](thr, obj, key, idx_val); } -DUK_LOCAL duk_bool_t duk__setcheck_own_prop_idxkey_ordinary(duk_hthread *thr, - duk_hobject *obj, - duk_uarridx_t idx, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag) { - duk_propvalue *pv; - duk_uint8_t attrs; +DUK_LOCAL duk_bool_t duk__setfinal_idxkey_htypejump(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_val) { + duk_small_uint_t htype; DUK_ASSERT(thr != NULL); DUK_ASSERT(obj != NULL); - DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)); DUK_ASSERT_ARRIDX_VALID(idx); - DUK_ASSERT(duk_is_valid_index(thr, idx_val)); - DUK_ASSERT(duk_is_valid_index(thr, idx_recv)); - DUK_ASSERT(throw_flag == 0 || throw_flag == 1); - DUK_UNREF(throw_flag); - - if (duk_hobject_lookup_idxprop_val_attrs(thr, obj, idx, &pv, &attrs) != 0) { - /* Fast path for write allowed case, single branch. */ - if (DUK_LIKELY((attrs & (DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_ACCESSOR)) == DUK_PROPDESC_FLAG_WRITABLE)) { - /* Not an accessor, writable property. Fast path direct writes - * which are relatively common. - */ -#if !defined(DUK_USE_PREFER_SIZE) - if (duk__prop_recv_direct(thr, idx_recv, obj)) { - duk__prop_set_write_tval(thr, idx_val, &pv->v); - return DUK__SETCHECK_DONE_SUCCESS; - } -#endif - return DUK__SETCHECK_FOUND; - } else { - /* Accessor can be handled inline also for inherited case. */ - if ((attrs & DUK_PROPDESC_FLAG_ACCESSOR) != 0) { -#if defined(DUK_USE_NONSTD_SETTER_KEY_ARGUMENT) - if (duk__setcheck_own_prop_found_setter_withidx(thr, obj, idx, idx_val, idx_recv, pv, attrs) == 0) { - goto fail_not_writable; - } -#else - if (duk__setcheck_own_prop_found_setter_nokey(thr, obj, idx_val, idx_val, pv, attrs) == 0) { - goto fail_not_writable; - } -#endif - return DUK__SETCHECK_DONE_SUCCESS; - } else { - DUK_ASSERT((attrs & DUK_PROPDESC_FLAG_WRITABLE) == 0); - goto fail_not_writable; - } - } - } + DUK_ASSERT(duk_is_valid_posidx(thr, idx_val)); - return DUK__SETCHECK_NOTFOUND; + htype = DUK_HEAPHDR_GET_HTYPE((duk_heaphdr *) obj); + DUK_ASSERT(DUK_HTYPE_IS_ANY_OBJECT(htype)); -fail_not_writable: - return DUK__SETCHECK_DONE_FAILURE; + return duk__setfinal_idxkey_handlers[htype](thr, obj, idx, idx_val); } -DUK_LOCAL duk_bool_t duk__setcheck_own_prop_idxkey(duk_hthread *thr, - duk_hobject *obj, - duk_uarridx_t idx, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag) { +DUK_LOCAL duk_bool_t duk__setcheck_strkey_htypejump(duk_hthread *thr, + duk_hobject *obj, + duk_hstring *key, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { duk_small_uint_t htype; DUK_ASSERT(thr != NULL); DUK_ASSERT(obj != NULL); - DUK_ASSERT_ARRIDX_VALID(idx); + DUK_ASSERT(key != NULL); + DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key)); DUK_ASSERT(duk_is_valid_index(thr, idx_val)); DUK_ASSERT(duk_is_valid_index(thr, idx_recv)); DUK_ASSERT(throw_flag == 0 || throw_flag == 1); @@ -1151,76 +1905,31 @@ DUK_LOCAL duk_bool_t duk__setcheck_own_prop_idxkey(duk_hthread *thr, htype = DUK_HEAPHDR_GET_HTYPE((duk_heaphdr *) obj); DUK_ASSERT(DUK_HTYPE_IS_ANY_OBJECT(htype)); - switch (htype) { - case DUK_HTYPE_ARRAY: { - duk_small_int_t set_rc; + return duk__setcheck_strkey_handlers[htype](thr, obj, key, idx_val, idx_recv, throw_flag); +} - set_rc = duk__setcheck_own_prop_idxkey_array(thr, obj, idx, idx_val, idx_recv, throw_flag); - if (DUK_LIKELY(set_rc >= 0)) { - return (duk_bool_t) set_rc; - } - DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)); - break; /* Check from index part if abandoned. */ - } - case DUK_HTYPE_ARGUMENTS: - /* Arguments exotic [[Set]] is tricky, see detailed comments - * in the helper. - */ - return duk__setcheck_own_prop_idxkey_arguments(thr, obj, idx, idx_val, idx_recv, throw_flag, 1 /*check_only*/); - case DUK_HTYPE_STRING_OBJECT: { - /* String objects don't have exotic [[Set]] behavior; the - * ordinary [[Set]] algorithm uses [[GetOwnProperty]]. - * In practice it's easiest to handle it as exotic - * behavior for valid indices and .length. - */ - duk_hstring *h; +DUK_LOCAL duk_bool_t duk__setcheck_idxkey_htypejump(duk_hthread *thr, + duk_hobject *obj, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { + duk_small_uint_t htype; - h = duk_hobject_lookup_intvalue_hstring(thr, obj); - if (h != NULL && idx < duk_hstring_get_charlen(h)) { - goto fail_not_writable; - } - /* Out of bounds, go to normal property table. */ - break; - } - case DUK_HTYPE_PROXY: - /* Handled by the caller in the NULL prototype path, see - * comments in duk_prop_get.c. - */ - return DUK__SETCHECK_NOTFOUND; - case DUK_HTYPE_COMPFUNC: - case DUK_HTYPE_NATFUNC: - case DUK_HTYPE_BOUNDFUNC: - /* No exotic [[Set]] or [[GetOwnProperty]] behavior. */ - break; -#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: - return duk__setcheck_own_prop_idxkey_typedarray(thr, obj, idx, idx_val, idx_recv, throw_flag); -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - default: - break; - } + DUK_ASSERT(thr != NULL); + DUK_ASSERT(obj != NULL); + DUK_ASSERT_ARRIDX_VALID(idx); + DUK_ASSERT(duk_is_valid_index(thr, idx_val)); + DUK_ASSERT(duk_is_valid_index(thr, idx_recv)); + DUK_ASSERT(throw_flag == 0 || throw_flag == 1); - return duk__setcheck_own_prop_idxkey_ordinary(thr, obj, idx, idx_val, idx_recv, throw_flag); + htype = DUK_HEAPHDR_GET_HTYPE((duk_heaphdr *) obj); + DUK_ASSERT(DUK_HTYPE_IS_ANY_OBJECT(htype)); -fail_not_writable: - return DUK__SETCHECK_DONE_FAILURE; + return duk__setcheck_idxkey_handlers[htype](thr, obj, idx, idx_val, idx_recv, throw_flag); } -DUK_LOCAL duk_bool_t duk__setfinal_own_prop_strkey_ordinary(duk_hthread *thr, - duk_hobject *obj, - duk_hstring *key, - duk_idx_t idx_val) { +DUK_LOCAL duk_bool_t duk__setfinal_strkey_ordinary(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_val) { duk_propvalue *pv; duk_uint8_t attrs; @@ -1235,7 +1944,7 @@ DUK_LOCAL duk_bool_t duk__setfinal_own_prop_strkey_ordinary(duk_hthread *thr, * the final step: if the receiver has an own accessor property, * it is NOT invoked but causes a write failure. This is * probably intended to standardize behavior for cases where - * the prototype chain or objects are modified by side effect + * the prototype chain or objects are modified by side effects * during handling of [[Set]]. * * This case can only happen if we first (1) pass through the @@ -1250,6 +1959,8 @@ DUK_LOCAL duk_bool_t duk__setfinal_own_prop_strkey_ordinary(duk_hthread *thr, * However, it's possible for a 'set' trap lookup to fail to * find a property but have side effects, e.g. if the handler * object is a Proxy or has a 'set' getter. + * + * A similar issue exists for the 'writable' attribute. */ if (DUK_UNLIKELY((attrs & (DUK_PROPDESC_FLAG_ACCESSOR | DUK_PROPDESC_FLAG_WRITABLE)) != DUK_PROPDESC_FLAG_WRITABLE)) { @@ -1257,8 +1968,12 @@ DUK_LOCAL duk_bool_t duk__setfinal_own_prop_strkey_ordinary(duk_hthread *thr, if (attrs & DUK_PROPDESC_FLAG_ACCESSOR) { DUK_D(DUK_DPRINT("receiver property is an accessor in final step of [[Set]], fail property write")); } + if (!(attrs & DUK_PROPDESC_FLAG_WRITABLE)) { + DUK_D( + DUK_DPRINT("receiver property is non-writable in final step of [[Set]], fail property write")); + } #endif - goto fail_not_writable; + goto fail_final_sanity; } duk__prop_set_write_tval(thr, idx_val, &pv->v); @@ -1299,7 +2014,7 @@ DUK_LOCAL duk_bool_t duk__setfinal_own_prop_strkey_ordinary(duk_hthread *thr, return 1; fail_not_extensible: -fail_not_writable: +fail_final_sanity: return 0; } @@ -1308,7 +2023,7 @@ fail_not_writable: * handles it as [[DefineOwnProperty]] (possibly via CreateDataProperty), * so all [[DefineOwnProperty]] exotic behaviors apply. */ -DUK_LOCAL duk_bool_t duk__setfinal_own_prop_strkey(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_val) { +DUK_LOCAL duk_bool_t duk__setfinal_strkey_switch(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_val) { duk_small_uint_t htype; DUK_ASSERT(thr != NULL); @@ -1365,10 +2080,13 @@ DUK_LOCAL duk_bool_t duk__setfinal_own_prop_strkey(duk_hthread *thr, duk_hobject * "check" phase. */ DUK_ASSERT(!DUK_HSTRING_HAS_LENGTH(key)); +#if 1 if (DUK_UNLIKELY(DUK_HSTRING_HAS_LENGTH(key))) { /* Fail safely. */ + DUK_D(DUK_DPRINT("String object setfinal for 'length' reached, should not happen, fail write")); goto fail_not_writable; } +#endif break; case DUK_HTYPE_PROXY: /* We should not come here: Proxy [[Set]] either continues @@ -1376,6 +2094,8 @@ DUK_LOCAL duk_bool_t duk__setfinal_own_prop_strkey(duk_hthread *thr, duk_hobject * invoked and the [[Set]] process finishes there. Cause * an internal error for safety here. */ + DUK_D(DUK_DPRINT("Proxy setfinal, should not happen")); + DUK_ASSERT(0); goto fail_internal; #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) case DUK_HTYPE_ARRAYBUFFER: @@ -1397,9 +2117,12 @@ DUK_LOCAL duk_bool_t duk__setfinal_own_prop_strkey(duk_hthread *thr, duk_hobject */ DUK_ASSERT(!DUK_HSTRING_HAS_LENGTH(key)); DUK_ASSERT(!DUK_HSTRING_HAS_CANNUM(key)); +#if 1 if (DUK_UNLIKELY(DUK_HSTRING_HAS_LENGTH_OR_CANNUM(key))) { /* Fail safely. */ + DUK_D(DUK_DPRINT("typed array setfinal for 'length' or cannum reached, should not happen, fail write")); goto fail_not_writable; +#endif } break; } @@ -1408,17 +2131,14 @@ DUK_LOCAL duk_bool_t duk__setfinal_own_prop_strkey(duk_hthread *thr, duk_hobject break; } - return duk__setfinal_own_prop_strkey_ordinary(thr, obj, key, idx_val); + return duk__setfinal_strkey_ordinary(thr, obj, key, idx_val); fail_not_writable: fail_internal: return 0; } -DUK_LOCAL duk_bool_t duk__setfinal_own_prop_idxkey_ordinary(duk_hthread *thr, - duk_hobject *obj, - duk_uarridx_t idx, - duk_idx_t idx_val) { +DUK_LOCAL duk_bool_t duk__setfinal_idxkey_ordinary(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_val) { duk_propvalue *pv; duk_uint8_t attrs; @@ -1434,8 +2154,12 @@ DUK_LOCAL duk_bool_t duk__setfinal_own_prop_idxkey_ordinary(duk_hthread *thr, DUK_D( DUK_DPRINT("receiver property is an accessor in final step of [[Set]] => fail property write")); } + if (!(attrs & DUK_PROPDESC_FLAG_WRITABLE)) { + DUK_D( + DUK_DPRINT("receiver property is non-writable in final step of [[Set]], fail property write")); + } #endif - goto fail_not_writable; + goto fail_final_sanity; } duk__prop_set_write_tval(thr, idx_val, &pv->v); DUK_GC_TORTURE(thr->heap); @@ -1467,11 +2191,11 @@ DUK_LOCAL duk_bool_t duk__setfinal_own_prop_idxkey_ordinary(duk_hthread *thr, return 1; fail_not_extensible: -fail_not_writable: +fail_final_sanity: return 0; } -DUK_LOCAL duk_bool_t duk__setfinal_own_prop_idxkey(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_val) { +DUK_LOCAL duk_bool_t duk__setfinal_idxkey_switch(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_val) { duk_small_uint_t htype; DUK_ASSERT(thr != NULL); @@ -1501,6 +2225,7 @@ DUK_LOCAL duk_bool_t duk__setfinal_own_prop_idxkey(duk_hthread *thr, duk_hobject duk_small_int_t setfin_rc; setfin_rc = duk__setfinal_write_array_arrayitems_idxkey(thr, obj, idx, idx_val); + DUK_ASSERT(setfin_rc == 0 || setfin_rc == 1 || setfin_rc == -1); if (setfin_rc > 0) { DUK_ASSERT(setfin_rc == 1); DUK_ASSERT(DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)); @@ -1523,6 +2248,7 @@ DUK_LOCAL duk_bool_t duk__setfinal_own_prop_idxkey(duk_hthread *thr, duk_hobject duk_small_int_t setfin_rc; setfin_rc = duk__setfinal_write_arguments_arrayitems_idxkey(thr, obj, idx, idx_val); + DUK_ASSERT(setfin_rc == 0 || setfin_rc == 1 || setfin_rc == -1); if (setfin_rc > 0) { DUK_ASSERT(setfin_rc == 1); DUK_ASSERT(DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)); @@ -1530,7 +2256,7 @@ DUK_LOCAL duk_bool_t duk__setfinal_own_prop_idxkey(duk_hthread *thr, duk_hobject } else if (setfin_rc == 0) { goto fail_array; } else { - DUK_ASSERT(setfin_rc < 0); + DUK_ASSERT(setfin_rc == -1); DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)); } DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_ITEMS(obj)); @@ -1548,7 +2274,7 @@ DUK_LOCAL duk_bool_t duk__setfinal_own_prop_idxkey(duk_hthread *thr, duk_hobject * invoked and the [[Set]] process finishes there. Fail * safely. */ - DUK_D(DUK_DPRINT("Proxy final [[Set]], should not happen")); + DUK_D(DUK_DPRINT("Proxy setfinal, should not happen")); DUK_ASSERT(0); goto fail_internal; #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) @@ -1584,7 +2310,7 @@ DUK_LOCAL duk_bool_t duk__setfinal_own_prop_idxkey(duk_hthread *thr, duk_hobject break; } - return duk__setfinal_own_prop_idxkey_ordinary(thr, obj, idx, idx_val); + return duk__setfinal_idxkey_ordinary(thr, obj, idx, idx_val); #if defined(DUK_USE_ROM_OBJECTS) fail_not_writable: @@ -1594,29 +2320,90 @@ fail_array: return 0; } -DUK_LOCAL duk_bool_t duk__prop_set_proxy_tail(duk_hthread *thr, duk_idx_t idx_val, duk_idx_t idx_recv) { +DUK_LOCAL void duk__prop_set_proxy_policy(duk_hthread *thr, duk_hobject *obj, duk_idx_t idx_val, duk_bool_t trap_rc) { + duk_hobject *target; + duk_small_int_t attrs; + + if (trap_rc == 0) { + return; + } + + /* [ ... key ] */ + + target = duk_proxy_get_target_autothrow(thr, (duk_hproxy *) obj); + DUK_ASSERT(target != NULL); + + attrs = duk_prop_getowndesc_obj_tvkey(thr, target, duk_get_tval(thr, -1 /*trap key*/)); + target = NULL; /* Potentially invalidated. */ + + /* [ ... key value ] OR [ ... key get set ] */ + + if (attrs >= 0) { + duk_small_uint_t uattrs = (duk_small_uint_t) attrs; + if ((uattrs & (DUK_PROPDESC_FLAG_ACCESSOR | DUK_PROPDESC_FLAG_WRITABLE | DUK_PROPDESC_FLAG_CONFIGURABLE)) == 0) { + /* Non-configurable, data property, non-writable: the value V already + * given to the 'set' trap must match the target [[Value]]. If not, + * TypeError unconditionally, but note that the 'set' trap still gets + * called. + */ + if (!duk_samevalue(thr, -1 /*target value*/, idx_val /*[[Set]] value*/)) { + goto reject; + } + } else if ((uattrs & (DUK_PROPDESC_FLAG_ACCESSOR | DUK_PROPDESC_FLAG_CONFIGURABLE)) == DUK_PROPDESC_FLAG_ACCESSOR) { + /* Non-configurable, accessor property; if [[Set]] is null, always an + * unconditional TypeError, but note that the 'set' trap still gets called. + */ + if (duk_is_nullish(thr, -1 /*set*/)) { + goto reject; + } + } + } else { + /* If target has no 'key', then no restrictions are applied. */ + } + + duk_prop_pop_propdesc(thr, attrs); + return; + +reject: + DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REJECTED); +} + +DUK_LOCAL duk_bool_t duk__prop_set_proxy_tail(duk_hthread *thr, duk_hobject *obj, duk_idx_t idx_val, duk_idx_t idx_recv) { + duk_bool_t trap_rc; + DUK_ASSERT(thr != NULL); DUK_ASSERT(duk_is_valid_posidx(thr, idx_val)); DUK_ASSERT(duk_is_valid_posidx(thr, idx_recv)); + /* [ ... trap handler target key ] */ + + duk_dup_top(thr); + duk_insert(thr, -5); /* Stash key for policy check. */ + + /* [ ... key trap handler target key ] */ + duk_dup(thr, idx_val); duk_dup(thr, idx_recv); - duk_call_method(thr, 4); /* [ ... trap handler target key val receiver ] -> [ ... result ] */ - duk_pop_unsafe(thr); + duk_call_method(thr, 4); /* [ ... key trap handler target key val receiver ] -> [ ... key result ] */ + trap_rc = duk_to_boolean_top_pop(thr); -#if 0 - /* XXX: proxy policy check */ +#if defined(DUK_USE_PROXY_POLICY) + duk__prop_set_proxy_policy(thr, obj, idx_val, trap_rc); +#else + DUK_DD(DUK_DDPRINT("proxy policy check for 'set' trap disabled in configuration")); #endif - return 1; + duk_pop_unsafe(thr); + + return trap_rc; } -DUK_LOCAL DUK_NOINLINE duk_bool_t duk__setcheck_strkey_proxy(duk_hthread *thr, - duk_hobject *obj, - duk_hstring *key, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag) { +DUK_LOCAL DUK_NOINLINE duk_small_int_t duk__setcheck_strkey_proxy_actual(duk_hthread *thr, + duk_hobject *obj, + duk_hstring *key, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { DUK_ASSERT(thr != NULL); DUK_ASSERT(obj != NULL); DUK_ASSERT(key != NULL); @@ -1628,18 +2415,19 @@ DUK_LOCAL DUK_NOINLINE duk_bool_t duk__setcheck_strkey_proxy(duk_hthread *thr, if (duk_proxy_trap_check_strkey(thr, (duk_hproxy *) obj, key, DUK_STRIDX_SET)) { duk_push_hstring(thr, key); - return duk__prop_set_proxy_tail(thr, idx_val, idx_recv); + return (duk_small_int_t) duk__prop_set_proxy_tail(thr, obj, idx_val, idx_recv); } else { - return 0; + /* -1 indicates: no trap present. */ + return -1; } } -DUK_LOCAL DUK_NOINLINE duk_bool_t duk__setcheck_idxkey_proxy(duk_hthread *thr, - duk_hobject *obj, - duk_uarridx_t idx, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag) { +DUK_LOCAL DUK_NOINLINE duk_small_int_t duk__setcheck_idxkey_proxy_actual(duk_hthread *thr, + duk_hobject *obj, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { DUK_ASSERT(thr != NULL); DUK_ASSERT(obj != NULL); DUK_ASSERT(duk_is_valid_posidx(thr, idx_val)); @@ -1649,9 +2437,10 @@ DUK_LOCAL DUK_NOINLINE duk_bool_t duk__setcheck_idxkey_proxy(duk_hthread *thr, if (duk_proxy_trap_check_idxkey(thr, (duk_hproxy *) obj, idx, DUK_STRIDX_SET)) { (void) duk_push_u32_tostring(thr, idx); - return duk__prop_set_proxy_tail(thr, idx_val, idx_recv); + return (duk_small_int_t) duk__prop_set_proxy_tail(thr, obj, idx_val, idx_recv); } else { - return 0; + /* -1 indicates: no trap present. */ + return -1; } } @@ -1682,27 +2471,55 @@ DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__prop_set_stroridx_helper(duk_hthread if (side_effect_safe) { duk_push_hobject(thr, target); +#if 0 + tv_target = thr->valstack_top++; + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv_target)); + DUK_TVAL_SET_OBJECT_INCREF(thr, tv_target, target); +#endif } sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY; do { duk_hobject *next; + if (use_key) { + DUK_DDD(DUK_DDDPRINT("set main loop, sanity=%ld, key %!O, current target: %!O, receiver: %!T", + (long) sanity, + key, + target, + duk_get_tval(thr, idx_recv))); + } else { + DUK_DDD(DUK_DDDPRINT("set main loop, sanity=%ld, key %lu, current target: %!O, receiver: %!T", + (long) sanity, + (unsigned long) idx, + target, + duk_get_tval(thr, idx_recv))); + } + #if defined(DUK_USE_ASSERTIONS) DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) target) > 0); if (side_effect_safe && DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) target) == 1) { - DUK_D(DUK_DPRINT("'target' is only reachable via stabilized value stack slot")); + DUK_DD(DUK_DDPRINT("'target' is only reachable via stabilized value stack slot")); } #endif DUK_GC_TORTURE(thr->heap); if (use_key) { - rc = duk__setcheck_own_prop_strkey(thr, target, key, idx_val, idx_recv, throw_flag); + DUK_UNREF(duk__setcheck_strkey_switch); +#if 0 + rc = duk__setcheck_strkey_switch(thr, target, key, idx_val, idx_recv, throw_flag); +#else + rc = duk__setcheck_strkey_htypejump(thr, target, key, idx_val, idx_recv, throw_flag); +#endif } else { - rc = duk__setcheck_own_prop_idxkey(thr, target, idx, idx_val, idx_recv, throw_flag); + DUK_UNREF(duk__setcheck_idxkey_switch); +#if 0 + rc = duk__setcheck_idxkey_switch(thr, target, idx, idx_val, idx_recv, throw_flag); +#else + rc = duk__setcheck_idxkey_htypejump(thr, target, idx, idx_val, idx_recv, throw_flag); +#endif } DUK_GC_TORTURE(thr->heap); - recheck_rc: if (rc == DUK__SETCHECK_NOTFOUND) { /* Not found, continue lookup with ordinary [[Set]]. */ @@ -1728,34 +2545,53 @@ DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__prop_set_stroridx_helper(duk_hthread if (side_effect_safe) { if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(target)) { + duk_small_int_t proxy_rc; + DUK_ASSERT(DUK_HOBJECT_GET_HTYPE(target) == DUK_HTYPE_PROXY); if (use_key) { - rc = duk__setcheck_strkey_proxy(thr, target, key, idx_val, idx_recv, throw_flag); + proxy_rc = duk__setcheck_strkey_proxy_actual(thr, + target, + key, + idx_val, + idx_recv, + throw_flag); } else { - rc = duk__setcheck_idxkey_proxy(thr, target, idx, idx_val, idx_recv, throw_flag); + proxy_rc = duk__setcheck_idxkey_proxy_actual(thr, + target, + idx, + idx_val, + idx_recv, + throw_flag); } - DUK_ASSERT(rc == 0 || rc == 1); - if (rc) { - DUK_ASSERT(rc == 1); - goto success; - } else { + DUK_ASSERT(proxy_rc == 0 || proxy_rc == 1 || proxy_rc == -1); /* -1=no trap */ + if (proxy_rc < 0) { DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(target)); next = duk_proxy_get_target_autothrow(thr, (duk_hproxy *) target); DUK_ASSERT(next != NULL); goto go_next; + } else if (proxy_rc > 0) { +#if 0 + rc = (duk_bool_t) proxy_rc; +#endif + goto success; + } else { +#if 0 + rc = (duk_bool_t) proxy_rc; +#endif + goto fail; } } else { DUK_ASSERT(DUK_HOBJECT_GET_HTYPE(target) == DUK_HTYPE_ARGUMENTS); if (use_key) { DUK_ASSERT(0); } else { - rc = duk__setcheck_own_prop_idxkey_arguments(thr, - target, - idx, - idx_val, - idx_recv, - throw_flag, - 0 /*check_only*/); + rc = duk__setcheck_idxkey_arguments_helper(thr, + target, + idx, + idx_val, + idx_recv, + throw_flag, + 0 /*check_only*/); goto recheck_rc; } } @@ -1767,7 +2603,7 @@ DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__prop_set_stroridx_helper(duk_hthread #if defined(DUK_USE_ASSERTIONS) DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) target) > 0); if (side_effect_safe && DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) target) == 1) { - DUK_D(DUK_DPRINT("'target' is only reachable via stabilized value stack slot")); + DUK_DD(DUK_DDPRINT("'target' is only reachable via stabilized value stack slot")); } #endif @@ -1781,16 +2617,10 @@ DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__prop_set_stroridx_helper(duk_hthread go_next: DUK_ASSERT(next != NULL); if (side_effect_safe) { - duk_tval *tv_target; - - 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 = duk_prop_switch_stabilized_target_top(thr, target, next); + } else { + target = next; } - target = next; } while (--sanity > 0); DUK_ERROR_RANGE_PROTO_SANITY(thr); @@ -1804,13 +2634,24 @@ allow_write: * effects may have altered the receiver during the prototype walk. */ tv_recv = thr->valstack_bottom + idx_recv; + if (DUK_LIKELY(DUK_TVAL_IS_OBJECT(tv_recv))) { duk_hobject *recv = DUK_TVAL_GET_OBJECT(tv_recv); target = NULL; if (key) { - rc = duk__setfinal_own_prop_strkey(thr, recv, key, idx_val); + DUK_UNREF(duk__setfinal_strkey_switch); +#if 0 + rc = duk__setfinal_strkey_switch(thr, recv, key, idx_val); +#else + rc = duk__setfinal_strkey_htypejump(thr, recv, key, idx_val); +#endif } else { - rc = duk__setfinal_own_prop_idxkey(thr, recv, idx, idx_val); + DUK_UNREF(duk__setfinal_idxkey_switch); +#if 0 + rc = duk__setfinal_idxkey_switch(thr, recv, idx, idx_val); +#else + rc = duk__setfinal_idxkey_htypejump(thr, recv, idx, idx_val); +#endif } if (rc == 0) { goto fail; @@ -1834,25 +2675,25 @@ fail: duk_pop_unsafe(thr); } if (use_key) { - return duk__prop_set_error_objidx_str(thr, idx_recv, key, throw_flag); + return duk__prop_set_error_objidx_strkey(thr, idx_recv, key, throw_flag); } else { - return duk__prop_set_error_objidx_idx(thr, idx_recv, idx, throw_flag); + return duk__prop_set_error_objidx_idxkey(thr, idx_recv, idx, throw_flag); } switch_to_safe: if (use_key) { - return duk__prop_set_str_safe(thr, target, key, idx_val, idx_recv, throw_flag); + return duk__prop_set_strkey_safe(thr, target, key, idx_val, idx_recv, throw_flag); } else { - return duk__prop_set_idx_safe(thr, target, idx, idx_val, idx_recv, throw_flag); + return duk__prop_set_idxkey_safe(thr, target, idx, idx_val, idx_recv, throw_flag); } } -DUK_LOCAL DUK_NOINLINE duk_bool_t duk__prop_set_str_safe(duk_hthread *thr, - duk_hobject *target, - duk_hstring *key, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag) { +DUK_LOCAL DUK_NOINLINE duk_bool_t duk__prop_set_strkey_safe(duk_hthread *thr, + duk_hobject *target, + duk_hstring *key, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { return duk__prop_set_stroridx_helper(thr, target, key, @@ -1864,14 +2705,14 @@ DUK_LOCAL DUK_NOINLINE duk_bool_t duk__prop_set_str_safe(duk_hthread *thr, 1 /*side_effect_safe*/); } -DUK_LOCAL duk_bool_t duk__prop_set_str_unsafe(duk_hthread *thr, - duk_hobject *target, - duk_hstring *key, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag) { +DUK_LOCAL duk_bool_t duk__prop_set_strkey_unsafe(duk_hthread *thr, + duk_hobject *target, + duk_hstring *key, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { #if defined(DUK_USE_PREFER_SIZE) - return duk__prop_set_str_safe(thr, target, key, 0, idx_val, idx_recv, throw_flag); + return duk__prop_set_strkey_safe(thr, target, key, 0, idx_val, idx_recv, throw_flag); #else return duk__prop_set_stroridx_helper(thr, target, @@ -1885,12 +2726,12 @@ DUK_LOCAL duk_bool_t duk__prop_set_str_unsafe(duk_hthread *thr, #endif } -DUK_LOCAL DUK_NOINLINE duk_bool_t duk__prop_set_idx_safe(duk_hthread *thr, - duk_hobject *target, - duk_uarridx_t idx, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag) { +DUK_LOCAL DUK_NOINLINE duk_bool_t duk__prop_set_idxkey_safe(duk_hthread *thr, + duk_hobject *target, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { return duk__prop_set_stroridx_helper(thr, target, NULL, @@ -1902,14 +2743,14 @@ DUK_LOCAL DUK_NOINLINE duk_bool_t duk__prop_set_idx_safe(duk_hthread *thr, 1 /*side_effect_safe*/); } -DUK_LOCAL duk_bool_t duk__prop_set_idx_unsafe(duk_hthread *thr, - duk_hobject *target, - duk_uarridx_t idx, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag) { +DUK_LOCAL duk_bool_t duk__prop_set_idxkey_unsafe(duk_hthread *thr, + duk_hobject *target, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { #if defined(DUK_USE_PREFER_SIZE) - return duk__prop_set_idx_safe(thr, target, NULL, idx, idx_val, idx_recv, throw_flag); + return duk__prop_set_idxkey_safe(thr, target, NULL, idx, idx_val, idx_recv, throw_flag); #else return duk__prop_set_stroridx_helper(thr, target, @@ -1923,12 +2764,12 @@ DUK_LOCAL duk_bool_t duk__prop_set_idx_unsafe(duk_hthread *thr, #endif } -DUK_LOCAL duk_bool_t duk__prop_set_str(duk_hthread *thr, - duk_hobject *obj, - duk_hstring *key, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag) { +DUK_LOCAL duk_bool_t duk__prop_set_strkey(duk_hthread *thr, + duk_hobject *obj, + duk_hstring *key, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { DUK_ASSERT(thr != NULL); DUK_ASSERT(obj != NULL); DUK_ASSERT(key != NULL); @@ -1939,15 +2780,15 @@ DUK_LOCAL duk_bool_t duk__prop_set_str(duk_hthread *thr, DUK_STATS_INC(thr->heap, stats_set_strkey_count); - return duk__prop_set_str_unsafe(thr, obj, key, idx_val, idx_recv, throw_flag); + return duk__prop_set_strkey_unsafe(thr, obj, key, idx_val, idx_recv, throw_flag); } -DUK_LOCAL duk_bool_t duk__prop_set_idx(duk_hthread *thr, - duk_hobject *obj, - duk_uarridx_t idx, - duk_idx_t idx_val, - duk_idx_t idx_recv, - duk_bool_t throw_flag) { +DUK_LOCAL duk_bool_t duk__prop_set_idxkey(duk_hthread *thr, + duk_hobject *obj, + duk_uarridx_t idx, + duk_idx_t idx_val, + duk_idx_t idx_recv, + duk_bool_t throw_flag) { DUK_ASSERT(thr != NULL); DUK_ASSERT(obj != NULL); DUK_ASSERT_ARRIDX_VALID(idx); @@ -1957,11 +2798,11 @@ DUK_LOCAL duk_bool_t duk__prop_set_idx(duk_hthread *thr, DUK_STATS_INC(thr->heap, stats_set_idxkey_count); - return duk__prop_set_idx_unsafe(thr, obj, idx, idx_val, idx_recv, throw_flag); + return duk__prop_set_idxkey_unsafe(thr, obj, idx, idx_val, idx_recv, throw_flag); } DUK_LOCAL duk_bool_t -duk__prop_putvalue_idx_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_uarridx_t idx, duk_idx_t idx_val, duk_bool_t throw_flag) { +duk__prop_putvalue_idxkey_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_uarridx_t idx, duk_idx_t idx_val, duk_bool_t throw_flag) { duk_hobject *next; duk_small_uint_t next_bidx; duk_tval *tv_recv; @@ -1992,7 +2833,7 @@ duk__prop_putvalue_idx_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_uarridx_t case DUK_TAG_UNUSED: case DUK_TAG_UNDEFINED: case DUK_TAG_NULL: - return duk__prop_set_error_objidx_idx(thr, idx_recv, idx, 1 /*unconditional*/); + return duk__prop_set_error_objidx_idxkey(thr, idx_recv, idx, 1 /*unconditional*/); case DUK_TAG_STRING: { /* For arridx in string valid range a TypeError is required: * [[GetOwnProperty]] for ToObject coerced value (String @@ -2052,7 +2893,10 @@ duk__prop_putvalue_idx_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_uarridx_t } else #endif { - coerced_val = duk_to_uint32(thr, idx_val); /* arbitrary side effects */ + /* Cannot alter value at 'idx_val' so must dup for now. */ + duk_dup(thr, idx_val); + coerced_val = duk_to_uint32(thr, -1); /* arbitrary side effects */ + duk_pop_unsafe(thr); } if (DUK_LIKELY(idx < DUK_HBUFFER_GET_SIZE(h))) { @@ -2082,14 +2926,14 @@ duk__prop_putvalue_idx_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_uarridx_t next = thr->builtins[next_bidx]; /* fall thru */ go_next: - return duk__prop_set_idx(thr, next, idx, idx_val, idx_recv, throw_flag); + return duk__prop_set_idxkey(thr, next, idx, idx_val, idx_recv, throw_flag); fail_not_writable: - return duk__prop_set_error_objidx_idx(thr, idx_recv, idx, throw_flag); + return duk__prop_set_error_objidx_idxkey(thr, idx_recv, idx, throw_flag); } DUK_LOCAL duk_bool_t -duk__prop_putvalue_str_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_hstring *key, duk_idx_t idx_val, duk_bool_t throw_flag) { +duk__prop_putvalue_strkey_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_hstring *key, duk_idx_t idx_val, duk_bool_t throw_flag) { duk_hobject *next; duk_small_uint_t next_bidx; duk_tval *tv_recv; @@ -2132,7 +2976,7 @@ duk__prop_putvalue_str_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_hstring * case DUK_TAG_UNUSED: case DUK_TAG_UNDEFINED: case DUK_TAG_NULL: - return duk__prop_set_error_objidx_str(thr, idx_recv, key, 1 /*unconditional*/); + return duk__prop_set_error_objidx_strkey(thr, idx_recv, key, 1 /*unconditional*/); case DUK_TAG_STRING: { duk_hstring *h = DUK_TVAL_GET_STRING(tv_recv); @@ -2189,35 +3033,39 @@ duk__prop_putvalue_str_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_hstring * next = thr->builtins[next_bidx]; /* fall thru */ go_next: - return duk__prop_set_str(thr, next, key, idx_val, idx_recv, throw_flag); + return duk__prop_set_strkey(thr, next, key, idx_val, idx_recv, throw_flag); fail_not_writable: - return duk__prop_set_error_objidx_str(thr, idx_recv, key, throw_flag); + return duk__prop_set_error_objidx_strkey(thr, idx_recv, key, throw_flag); } +/* + * Exposed internal APIs + */ + DUK_INTERNAL duk_bool_t -duk_prop_putvalue_idx_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_uarridx_t idx, duk_idx_t idx_val, duk_bool_t throw_flag) { +duk_prop_putvalue_idxkey_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_uarridx_t idx, duk_idx_t idx_val, duk_bool_t throw_flag) { DUK_ASSERT(thr != NULL); DUK_ASSERT(duk_is_valid_posidx(thr, idx_recv)); DUK_ASSERT(duk_is_valid_posidx(thr, idx_val)); DUK_ASSERT(throw_flag == 0 || throw_flag == 1); if (DUK_LIKELY(idx <= DUK_ARRIDX_MAX)) { - return duk__prop_putvalue_idx_inidx(thr, idx_recv, idx, idx_val, throw_flag); + return duk__prop_putvalue_idxkey_inidx(thr, idx_recv, idx, idx_val, throw_flag); } 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")); + 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_putvalue_str_inidx(thr, idx_recv, key, idx_val, throw_flag); + rc = duk__prop_putvalue_strkey_inidx(thr, idx_recv, key, idx_val, throw_flag); duk_pop_unsafe(thr); return rc; } } DUK_INTERNAL duk_bool_t -duk_prop_putvalue_str_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_hstring *key, duk_idx_t idx_val, duk_bool_t throw_flag) { +duk_prop_putvalue_strkey_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_hstring *key, duk_idx_t idx_val, duk_bool_t throw_flag) { DUK_ASSERT(thr != NULL); DUK_ASSERT(duk_is_valid_posidx(thr, idx_recv)); DUK_ASSERT(key != NULL); @@ -2225,9 +3073,9 @@ duk_prop_putvalue_str_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_hstring *k DUK_ASSERT(throw_flag == 0 || throw_flag == 1); if (DUK_UNLIKELY(DUK_HSTRING_HAS_ARRIDX(key))) { - return duk__prop_putvalue_idx_inidx(thr, idx_recv, duk_hstring_get_arridx_fast_known(key), idx_val, throw_flag); + return duk__prop_putvalue_idxkey_inidx(thr, idx_recv, duk_hstring_get_arridx_fast_known(key), idx_val, throw_flag); } else { - return duk__prop_putvalue_str_inidx(thr, idx_recv, key, idx_val, throw_flag); + return duk__prop_putvalue_strkey_inidx(thr, idx_recv, key, idx_val, throw_flag); } } @@ -2240,10 +3088,6 @@ duk_prop_putvalue_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_tval *tv_key, DUK_ASSERT(thr != NULL); DUK_ASSERT(duk_is_valid_posidx(thr, idx_recv)); 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). - */ DUK_ASSERT(duk_is_valid_posidx(thr, idx_val)); /* 'idx_val' is in value stack but we're not allowed to mutate it * because it might be a VM register source. @@ -2311,15 +3155,15 @@ duk_prop_putvalue_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_tval *tv_key, duk_push_tval(thr, tv_key); tv_key = NULL; key = duk_to_property_key_hstring(thr, -1); - rc = duk_prop_putvalue_str_inidx(thr, idx_recv, key, idx_val, throw_flag); + rc = duk_prop_putvalue_strkey_inidx(thr, idx_recv, key, idx_val, throw_flag); duk_pop_unsafe(thr); return rc; use_idx: DUK_ASSERT_ARRIDX_VALID(idx); - return duk__prop_putvalue_idx_inidx(thr, idx_recv, idx, idx_val, throw_flag); + return duk__prop_putvalue_idxkey_inidx(thr, idx_recv, idx, idx_val, throw_flag); use_str: DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key)); - return duk__prop_putvalue_str_inidx(thr, idx_recv, key, idx_val, throw_flag); + return duk__prop_putvalue_strkey_inidx(thr, idx_recv, key, idx_val, throw_flag); }