Browse Source

Fixes and improvements to new property code

* Implement Proxy.revocable() and add test case coverage for some
  Proxy revocation basics.

* Fix revoked Proxy handling in instanceof and Array.isArray(), and
  add test coverage.

* Fix revoked Proxy handling in call setup, and add test coverage.
  Chained Proxies still not supported in call handling.

* Add stack top asserts for internal duk_prop_xxx.c file APIs:
  defown, delete, enum, get, getown, has, ownpropkeys, set.

* Fix unused duk_hobject layouts from property table init code
  in src-tools.
pull/2487/head
Sami Vaarala 2 years ago
parent
commit
aad8d7e8c1
  1. 9
      src-input/duk_api_readable.c
  2. 2
      src-input/duk_api_stack.c
  3. 6
      src-input/duk_bi_array.c
  4. 6
      src-input/duk_bi_json.c
  5. 4
      src-input/duk_bi_object.c
  6. 28
      src-input/duk_bi_proxy.c
  7. 5
      src-input/duk_hobject.h
  8. 10
      src-input/duk_hobject_props.c
  9. 38
      src-input/duk_hobject_proxy.c
  10. 4
      src-input/duk_js.h
  11. 10
      src-input/duk_js_call.c
  12. 17
      src-input/duk_js_ops.c
  13. 51
      src-input/duk_prop_defown.c
  14. 60
      src-input/duk_prop_delete.c
  15. 29
      src-input/duk_prop_enum.c
  16. 50
      src-input/duk_prop_get.c
  17. 67
      src-input/duk_prop_getown.c
  18. 50
      src-input/duk_prop_has.c
  19. 8
      src-input/duk_prop_ownpropkeys.c
  20. 48
      src-input/duk_prop_set.c
  21. 19
      src-tools/lib/builtins/initdata/property_table_initializers.js
  22. 40
      tests/ecmascript/test-bi-proxy-revocable-creation.js
  23. 49
      tests/ecmascript/test-bi-proxy-revoked-func-call.js
  24. 23
      tests/ecmascript/test-bi-proxy-revoked-instanceof.js
  25. 23
      tests/ecmascript/test-bi-proxy-revoked-isarray.js

9
src-input/duk_api_readable.c

@ -120,9 +120,12 @@ DUK_INTERNAL void duk_push_class_string_tval(duk_hthread *thr, duk_tval *tv, duk
tv = NULL;
DUK_ASSERT(h != NULL);
h_resolved = duk_hobject_resolve_proxy_target(h);
DUK_ASSERT(h_resolved != NULL);
if (DUK_HOBJECT_IS_ARRAY(h_resolved)) {
/* Here we want to detect a revoked Proxy without throwing. */
h_resolved = duk_hobject_resolve_proxy_target_nothrow(thr, h);
if (h_resolved == NULL) {
duk_push_string(thr, "Proxy(revoked)");
goto tag_pushed;
} else if (DUK_HOBJECT_IS_ARRAY(h_resolved)) {
/* IsArray() resolves Proxy chain target recursively. */
stridx = DUK_STRIDX_UC_ARRAY;
} else {

2
src-input/duk_api_stack.c

@ -4129,7 +4129,7 @@ DUK_EXTERNAL duk_bool_t duk_is_array(duk_hthread *thr, duk_idx_t idx) {
tv = duk_get_tval(thr, idx);
if (tv) {
return duk_js_isarray(tv);
return duk_js_isarray(thr, tv);
}
return 0;
}

6
src-input/duk_bi_array.c

@ -181,7 +181,7 @@ DUK_INTERNAL duk_ret_t duk_bi_array_constructor(duk_hthread *thr) {
DUK_INTERNAL duk_ret_t duk_bi_array_constructor_is_array(duk_hthread *thr) {
DUK_ASSERT_TOP(thr, 1);
return duk_push_boolean_return1(thr, duk_js_isarray(DUK_GET_TVAL_POSIDX(thr, 0)));
return duk_push_boolean_return1(thr, duk_js_isarray(thr, DUK_GET_TVAL_POSIDX(thr, 0)));
}
/*
@ -262,13 +262,13 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_concat(duk_hthread *thr) {
#if defined(DUK_USE_SYMBOL_BUILTIN)
duk_get_prop_stridx(thr, i, DUK_STRIDX_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE);
if (duk_is_undefined(thr, -1)) {
spreadable = duk_js_isarray_hobject(h);
spreadable = duk_js_isarray_hobject(thr, h);
} else {
spreadable = duk_to_boolean(thr, -1);
}
duk_pop_nodecref_unsafe(thr);
#else
spreadable = duk_js_isarray_hobject(h);
spreadable = duk_js_isarray_hobject(thr, h);
#endif
}

6
src-input/duk_bi_json.c

@ -1015,7 +1015,7 @@ DUK_LOCAL void duk__json_dec_reviver_walk(duk_json_dec_ctx *js_ctx) {
h = duk_get_hobject(thr, -1);
if (h != NULL) {
if (duk_js_isarray_hobject(h)) {
if (duk_js_isarray_hobject(thr, h)) {
arr_len = (duk_uarridx_t) duk_get_length(thr, -1);
for (i = 0; i < arr_len; i++) {
/* [ ... holder name val ] */
@ -2266,7 +2266,7 @@ DUK_LOCAL duk_bool_t duk__json_enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx
*/
DUK_ASSERT(!DUK_HOBJECT_IS_CALLABLE(h));
if (duk_js_isarray_hobject(h)) {
if (duk_js_isarray_hobject(thr, h)) {
duk__json_enc_array(js_ctx);
} else {
duk__json_enc_object(js_ctx);
@ -3125,7 +3125,7 @@ void duk_bi_json_stringify_helper(duk_hthread *thr,
if (h != NULL) {
if (DUK_HOBJECT_IS_CALLABLE(h)) {
js_ctx->h_replacer = h;
} else if (duk_js_isarray_hobject(h)) {
} else if (duk_js_isarray_hobject(thr, h)) {
duk__json_setup_plist_from_array(thr, js_ctx, idx_replacer);
}
}

4
src-input/duk_bi_object.c

@ -479,7 +479,6 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_property(duk_hthread *th
duk_small_uint_t magic;
duk_bool_t throw_flag;
duk_bool_t ret;
duk_idx_t idx_desc;
DUK_ASSERT(thr != NULL);
DUK_ASSERT_TOP(thr, 3);
@ -506,8 +505,7 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_property(duk_hthread *th
if (magic == 0U) {
defprop_flags |= DUK_DEFPROP_THROW;
}
idx_desc = 2;
ret = duk_prop_defown(thr, obj, DUK_GET_TVAL_POSIDX(thr, 1), idx_desc, defprop_flags);
ret = duk_prop_defown(thr, obj, DUK_GET_TVAL_POSIDX(thr, 1), 2 /*idx_desc*/, defprop_flags);
/* Ignore the property descriptor conversion outputs on the value stack,
* they're popped automatically.

28
src-input/duk_bi_proxy.c

@ -15,15 +15,33 @@ DUK_INTERNAL duk_ret_t duk_bi_proxy_constructor(duk_hthread *thr) {
#endif /* DUK_USE_ES6_PROXY */
#if defined(DUK_USE_ES6_PROXY)
DUK_LOCAL duk_ret_t duk__bi_proxy_revoker(duk_hthread *thr) {
duk_hobject *obj;
duk_hobject *h;
duk_push_current_function(thr);
obj = duk_require_hobject(thr, -1);
h = duk_hobject_get_internal_value_object(thr->heap, obj);
if (h != NULL) {
DUK_ASSERT(h != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_PROXY(h));
duk_proxy_revoke(thr, (duk_hproxy *) h);
}
return 0;
}
DUK_INTERNAL duk_ret_t duk_bi_proxy_constructor_revocable(duk_hthread *thr) {
DUK_ASSERT(!duk_is_constructor_call(thr));
DUK_ASSERT_TOP(thr, 2); /* [ target handler ] */
DUK_ASSERT(!duk_is_constructor_call(thr));
duk_push_object(thr);
duk_push_proxy(thr, 0 /*flags*/); /* [ target handler ] -> [ proxy ] */
duk_put_prop_literal(thr, -2, "proxy");
duk_eval_string(thr, "(function () { throw new TypeError('unimplemented'); })");
duk_put_prop_literal(thr, -2, "revoke");
duk_push_object(thr);
duk_push_c_function(thr, duk__bi_proxy_revoker, 0);
duk_dup(thr, 0); /* -> [ proxy retval revoker proxy ] */
duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
duk_pull(thr, 0); /* -> [ retval revoker proxy ] */
duk_put_prop_literal(thr, 0, "proxy");
duk_put_prop_literal(thr, 0, "revoke"); /* -> [ retval ] */
return 1;
}
#endif /* DUK_USE_ES6_PROXY */

5
src-input/duk_hobject.h

@ -639,6 +639,7 @@ DUK_INTERNAL_DECL duk_bool_t duk_hobject_object_is_sealed_frozen_helper(duk_hthr
/* internal properties */
DUK_INTERNAL_DECL duk_tval *duk_hobject_get_internal_value_tval_ptr(duk_heap *heap, duk_hobject *obj);
DUK_INTERNAL_DECL duk_hstring *duk_hobject_get_internal_value_string(duk_heap *heap, duk_hobject *obj);
DUK_INTERNAL_DECL duk_hobject *duk_hobject_get_internal_value_object(duk_heap *heap, duk_hobject *obj);
DUK_INTERNAL_DECL duk_harray *duk_hobject_get_formals(duk_hthread *thr, duk_hobject *obj);
DUK_INTERNAL_DECL duk_hobject *duk_hobject_get_varmap(duk_hthread *thr, duk_hobject *obj);
@ -646,7 +647,8 @@ DUK_INTERNAL_DECL duk_hobject *duk_hobject_get_varmap(duk_hthread *thr, duk_hobj
DUK_INTERNAL_DECL void duk_hobject_compact_object(duk_hthread *thr, duk_hobject *obj);
/* Proxy */
DUK_INTERNAL_DECL duk_hobject *duk_hobject_resolve_proxy_target(duk_hobject *obj);
DUK_INTERNAL_DECL duk_hobject *duk_hobject_resolve_proxy_target_nothrow(duk_hthread *thr, duk_hobject *obj);
DUK_INTERNAL_DECL duk_hobject *duk_hobject_resolve_proxy_target_autothrow(duk_hthread *thr, duk_hobject *obj);
DUK_INTERNAL_DECL duk_hobject *duk_proxy_get_target_autothrow(duk_hthread *thr, duk_hproxy *h);
#if defined(DUK_USE_ES6_PROXY)
DUK_INTERNAL_DECL duk_bool_t duk_proxy_trap_check_strkey(duk_hthread *thr,
@ -659,6 +661,7 @@ DUK_INTERNAL_DECL duk_bool_t duk_proxy_trap_check_idxkey(duk_hthread *thr,
duk_small_uint_t trap_stridx);
DUK_INTERNAL_DECL duk_bool_t duk_proxy_trap_check_nokey(duk_hthread *thr, duk_hproxy *h, duk_small_uint_t trap_stridx);
#endif
DUK_INTERNAL_DECL void duk_proxy_revoke(duk_hthread *thr, duk_hproxy *h);
/* macros */
DUK_INTERNAL_DECL duk_hobject *duk_hobject_get_proto_raw(duk_heap *heap, duk_hobject *h);

10
src-input/duk_hobject_props.c

@ -259,6 +259,16 @@ DUK_INTERNAL duk_hstring *duk_hobject_get_internal_value_string(duk_heap *heap,
return h;
}
DUK_INTERNAL duk_hobject *duk_hobject_get_internal_value_object(duk_heap *heap, duk_hobject *obj) {
duk_hobject *h;
h = (duk_hobject *) duk_hobject_get_internal_value_heaphdr(heap, obj);
if (h != NULL) {
DUK_ASSERT(DUK_HEAPHDR_IS_ANY_OBJECT((duk_heaphdr *) h));
}
return h;
}
DUK_LOCAL duk_hobject *duk__hobject_get_entry_object_stridx(duk_heap *heap, duk_hobject *obj, duk_small_uint_t stridx) {
duk_tval *tv;
duk_hobject *h;

38
src-input/duk_hobject_proxy.c

@ -102,9 +102,10 @@ DUK_INTERNAL duk_hobject *duk_proxy_get_target_autothrow(duk_hthread *thr, duk_h
#endif /* DUK_USE_ES6_PROXY */
/* Get Proxy target object. If the argument is not a Proxy, return it as is.
* If a Proxy is revoked, an error is thrown.
* If a Proxy is revoked, return NULL.
*/
DUK_INTERNAL duk_hobject *duk_hobject_resolve_proxy_target(duk_hobject *obj) {
DUK_INTERNAL duk_hobject *duk_hobject_resolve_proxy_target_nothrow(duk_hthread *thr, duk_hobject *obj) {
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
/* Resolve Proxy targets until Proxy chain ends. No explicit check for
@ -119,6 +120,10 @@ DUK_INTERNAL duk_hobject *duk_hobject_resolve_proxy_target(duk_hobject *obj) {
h_proxy = (duk_hproxy *) obj;
DUK_HPROXY_ASSERT_VALID(h_proxy);
obj = h_proxy->target;
if (obj == NULL) {
/* Revoked. */
return NULL;
}
DUK_ASSERT(obj != NULL);
}
#endif /* DUK_USE_ES6_PROXY */
@ -126,3 +131,32 @@ DUK_INTERNAL duk_hobject *duk_hobject_resolve_proxy_target(duk_hobject *obj) {
DUK_ASSERT(obj != NULL);
return obj;
}
/* Get Proxy target object. If the argument is not a Proxy, return it as is.
* If a Proxy is revoked, throw a TypeError.
*/
DUK_INTERNAL duk_hobject *duk_hobject_resolve_proxy_target_autothrow(duk_hthread *thr, duk_hobject *obj) {
duk_hobject *res;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
res = duk_hobject_resolve_proxy_target_nothrow(thr, obj);
if (res) {
return res;
} else {
DUK_ERROR_TYPE_PROXY_REVOKED(thr);
}
}
DUK_INTERNAL void duk_proxy_revoke(duk_hthread *thr, duk_hproxy *h) {
DUK_ASSERT(thr != NULL);
DUK_ASSERT(h != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_PROXY((duk_hobject *) h));
DUK_UNREF(thr);
h->target = NULL;
h->handler = NULL;
}

4
src-input/duk_js.h

@ -58,8 +58,8 @@ DUK_INTERNAL_DECL duk_bool_t duk_js_instanceof_ordinary(duk_hthread *thr, duk_tv
#endif
DUK_INTERNAL_DECL duk_bool_t duk_js_in(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y);
DUK_INTERNAL_DECL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x);
DUK_INTERNAL_DECL duk_bool_t duk_js_isarray_hobject(duk_hobject *h);
DUK_INTERNAL_DECL duk_bool_t duk_js_isarray(duk_tval *tv);
DUK_INTERNAL_DECL duk_bool_t duk_js_isarray_hobject(duk_hthread *thr, duk_hobject *h);
DUK_INTERNAL_DECL duk_bool_t duk_js_isarray(duk_hthread *thr, duk_tval *tv);
/* arithmetic */
DUK_INTERNAL_DECL double duk_js_arith_pow(double x, double y);

10
src-input/duk_js_call.c

@ -861,13 +861,18 @@ DUK_LOCAL void duk__handle_proxy_for_call(duk_hthread *thr, duk_idx_t idx_func,
* (original 'this' binding) is dropped and ignored.
*/
if (h_proxy->handler == NULL) {
DUK_ERROR_TYPE_PROXY_REVOKED(thr);
}
duk_push_hobject(thr, h_proxy->handler);
rc = duk_get_prop_stridx_short(thr, -1, (*call_flags & DUK_CALL_FLAG_CONSTRUCT) ? DUK_STRIDX_CONSTRUCT : DUK_STRIDX_APPLY);
if (rc == 0) {
/* Not found, continue to target. If this is a construct
* call, update default instance prototype using the Proxy,
* not the target.
* not the target. Side effects in trap check may have
* revoked our Proxy so must recheck revocation status.
*/
if (*call_flags & DUK_CALL_FLAG_CONSTRUCT) {
if (!(*call_flags & DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED)) {
*call_flags |= DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED;
@ -875,6 +880,9 @@ DUK_LOCAL void duk__handle_proxy_for_call(duk_hthread *thr, duk_idx_t idx_func,
}
}
duk_pop_2(thr);
if (h_proxy->target == NULL) {
DUK_ERROR_TYPE_PROXY_REVOKED(thr);
}
duk_push_hobject(thr, h_proxy->target);
duk_replace(thr, idx_func);
return;

17
src-input/duk_js_ops.c

@ -1144,7 +1144,9 @@ DUK_LOCAL duk_bool_t duk__js_instanceof_helper(duk_hthread *thr, duk_tval *tv_x,
DUK_ASSERT(val != NULL);
#if defined(DUK_USE_ES6_PROXY)
val = duk_hobject_resolve_proxy_target(val);
/* Here we must throw for revoked proxy. */
val = duk_hobject_resolve_proxy_target_autothrow(thr, val);
DUK_ASSERT(val != NULL);
#endif
if (skip_first) {
@ -1302,18 +1304,23 @@ DUK_INTERNAL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x) {
* IsArray()
*/
DUK_INTERNAL duk_bool_t duk_js_isarray_hobject(duk_hobject *h) {
DUK_INTERNAL duk_bool_t duk_js_isarray_hobject(duk_hthread *thr, duk_hobject *h) {
DUK_ASSERT(h != NULL);
#if defined(DUK_USE_ES6_PROXY)
h = duk_hobject_resolve_proxy_target(h);
/* Here we must throw for revoked proxy. */
h = duk_hobject_resolve_proxy_target_autothrow(thr, h);
DUK_ASSERT(h != NULL);
#else
DUK_UNREF(thr);
#endif
return (DUK_HEAPHDR_IS_ARRAY((duk_heaphdr *) h) ? 1 : 0);
}
DUK_INTERNAL duk_bool_t duk_js_isarray(duk_tval *tv) {
DUK_INTERNAL duk_bool_t duk_js_isarray(duk_hthread *thr, duk_tval *tv) {
DUK_ASSERT(tv != NULL);
if (DUK_TVAL_IS_OBJECT(tv)) {
return duk_js_isarray_hobject(DUK_TVAL_GET_OBJECT(tv));
return duk_js_isarray_hobject(thr, DUK_TVAL_GET_OBJECT(tv));
}
return 0;
}

51
src-input/duk_prop_defown.c

@ -1364,26 +1364,53 @@ duk__prop_defown_idxkey_safe(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t i
DUK_INTERNAL duk_bool_t
duk_prop_defown_strkey(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_idx_t idx_desc, duk_uint_t defprop_flags) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top;
#endif
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
DUK_ASSERT(key != NULL);
DUK_ASSERT(idx_desc >= 0);
DUK_ASSERT(duk_is_valid_posidx(thr, idx_desc));
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
if (DUK_UNLIKELY(DUK_HSTRING_HAS_ARRIDX(key))) {
return duk__prop_defown_idxkey_unsafe(thr, obj, duk_hstring_get_arridx_fast_known(key), idx_desc, defprop_flags);
duk_bool_t rc;
rc = duk__prop_defown_idxkey_unsafe(thr, obj, duk_hstring_get_arridx_fast_known(key), idx_desc, defprop_flags);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
} else {
return duk__prop_defown_strkey_unsafe(thr, obj, key, idx_desc, defprop_flags);
duk_bool_t rc;
DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key));
rc = duk__prop_defown_strkey_unsafe(thr, obj, key, idx_desc, defprop_flags);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
}
}
DUK_INTERNAL duk_bool_t
duk_prop_defown_idxkey(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, duk_idx_t idx_desc, duk_uint_t defprop_flags) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top;
#endif
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
DUK_ASSERT(idx_desc >= 0);
DUK_ASSERT(duk_is_valid_posidx(thr, idx_desc));
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
if (DUK_LIKELY(idx <= DUK_ARRIDX_MAX)) {
return duk__prop_defown_idxkey_unsafe(thr, obj, idx, idx_desc, defprop_flags);
duk_bool_t rc = duk__prop_defown_idxkey_unsafe(thr, obj, idx, idx_desc, defprop_flags);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
} else {
duk_bool_t rc;
duk_hstring *key;
@ -1392,12 +1419,16 @@ duk_prop_defown_idxkey(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx, du
key = duk_push_u32_tohstring(thr, idx);
rc = duk__prop_defown_strkey_unsafe(thr, obj, key, idx_desc, defprop_flags);
duk_pop_unsafe(thr);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
}
}
DUK_INTERNAL duk_bool_t
duk_prop_defown(duk_hthread *thr, duk_hobject *obj, duk_tval *tv_key, duk_idx_t idx_desc, duk_uint_t defprop_flags) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top;
#endif
duk_bool_t rc;
duk_hstring *key;
duk_uarridx_t idx;
@ -1412,8 +1443,11 @@ duk_prop_defown(duk_hthread *thr, duk_hobject *obj, duk_tval *tv_key, duk_idx_t
/* 'idx_desc' must be valid, but only if defprop_flags indicates
* there is a value or getter/setter.
*/
DUK_ASSERT(idx_desc >= 0);
DUK_ASSERT(duk_is_valid_posidx(thr, idx_desc));
/* 'throw_flag' is embedded into defprop_flags. */
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
switch (DUK_TVAL_GET_TAG(tv_key)) {
case DUK_TAG_STRING:
@ -1473,13 +1507,18 @@ duk_prop_defown(duk_hthread *thr, duk_hobject *obj, duk_tval *tv_key, duk_idx_t
DUK_ASSERT(key != NULL);
rc = duk_prop_defown_strkey(thr, obj, key, idx_desc, defprop_flags);
duk_pop_unsafe(thr);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
use_idx:
DUK_ASSERT_ARRIDX_VALID(idx);
return duk__prop_defown_idxkey_unsafe(thr, obj, idx, idx_desc, defprop_flags);
rc = duk__prop_defown_idxkey_unsafe(thr, obj, idx, idx_desc, defprop_flags);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
use_str:
DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key));
return duk__prop_defown_strkey_unsafe(thr, obj, key, idx_desc, defprop_flags);
rc = duk__prop_defown_strkey_unsafe(thr, obj, key, idx_desc, defprop_flags);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
}

60
src-input/duk_prop_delete.c

@ -979,10 +979,30 @@ 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 defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top;
#endif
DUK_ASSERT(thr != NULL);
DUK_ASSERT(duk_is_valid_posidx(thr, idx_obj));
DUK_ASSERT(key != NULL);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
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);
duk_bool_t rc;
rc = duk__prop_delete_idxkey(thr, idx_obj, duk_hstring_get_arridx_fast_known(key), delprop_flags);
DUK_ASSERT(duk_get_top(thr) == rc);
return rc;
} else {
return duk__prop_delete_strkey(thr, idx_obj, key, delprop_flags);
duk_bool_t rc;
DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key));
rc = duk__prop_delete_strkey(thr, idx_obj, key, delprop_flags);
DUK_ASSERT(duk_get_top(thr) == rc);
return rc;
}
}
@ -990,8 +1010,21 @@ 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 defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top;
#endif
DUK_ASSERT(thr != NULL);
DUK_ASSERT(duk_is_valid_posidx(thr, idx_obj));
DUK_ASSERT_ARRIDX_VALID(idx);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
if (DUK_LIKELY(idx <= DUK_ARRIDX_MAX)) {
return duk__prop_delete_idxkey(thr, idx_obj, idx, delprop_flags);
duk_bool_t rc = duk__prop_delete_idxkey(thr, idx_obj, idx, delprop_flags);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
} else {
duk_bool_t rc;
duk_hstring *key;
@ -1000,21 +1033,29 @@ DUK_INTERNAL duk_bool_t duk_prop_delete_idxkey(duk_hthread *thr,
key = duk_push_u32_tohstring(thr, idx);
rc = duk__prop_delete_strkey(thr, idx_obj, key, delprop_flags);
duk_pop_unsafe(thr);
DUK_ASSERT(duk_get_top(thr) == entry_top);
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) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top;
#endif
duk_bool_t rc;
duk_hstring *key;
duk_uarridx_t idx;
DUK_ASSERT(duk_is_valid_index(thr, idx_obj));
DUK_ASSERT(thr != NULL);
DUK_ASSERT(duk_is_valid_posidx(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).
*/
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
switch (DUK_TVAL_GET_TAG(tv_key)) {
case DUK_TAG_STRING:
@ -1081,11 +1122,18 @@ DUK_INTERNAL duk_bool_t duk_prop_deleteoper(duk_hthread *thr, duk_idx_t idx_obj,
DUK_ASSERT(key != NULL);
rc = duk_prop_delete_strkey(thr, idx_obj, key, delprop_flags);
duk_pop_unsafe(thr);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
use_idx:
return duk__prop_delete_idxkey(thr, idx_obj, idx, delprop_flags);
DUK_ASSERT_ARRIDX_VALID(idx);
rc = duk__prop_delete_idxkey(thr, idx_obj, idx, delprop_flags);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
use_str:
return duk__prop_delete_strkey(thr, idx_obj, key, delprop_flags);
DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key));
rc = duk__prop_delete_strkey(thr, idx_obj, key, delprop_flags);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
}

29
src-input/duk_prop_enum.c

@ -53,6 +53,9 @@ DUK_LOCAL duk_uint_t duk__enum_convert_public_api_flags(duk_uint_t enum_flags) {
* bare array with the enumerated keys.
*/
DUK_INTERNAL void duk_prop_enum_keylist(duk_hthread *thr, duk_hobject *obj, duk_uint_t enum_flags) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top;
#endif
duk_idx_t idx_res;
duk_idx_t idx_visited;
duk_idx_t idx_obj;
@ -62,6 +65,9 @@ DUK_INTERNAL void duk_prop_enum_keylist(duk_hthread *thr, duk_hobject *obj, duk_
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
idx_res = duk_push_bare_array(thr);
idx_visited = duk_push_bare_object(thr);
@ -165,12 +171,23 @@ DUK_INTERNAL void duk_prop_enum_keylist(duk_hthread *thr, duk_hobject *obj, duk_
DUK_ASSERT(duk_get_top(thr) == idx_base_top);
duk_pop_2_unsafe(thr);
DUK_ASSERT(duk_get_top(thr) == entry_top + 1);
/* [ ... res ] */
}
/* Create an internal enumerator object for a target object with specified enum flags. */
DUK_INTERNAL void duk_prop_enum_create_enumerator(duk_hthread *thr, duk_hobject *obj, duk_uint_t enum_flags) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top;
#endif
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
duk_push_bare_object(thr);
duk_prop_enum_keylist(thr, obj, enum_flags);
duk_put_prop_literal(thr, -2, "keys");
@ -178,14 +195,25 @@ DUK_INTERNAL void duk_prop_enum_create_enumerator(duk_hthread *thr, duk_hobject
duk_put_prop_literal(thr, -2, "target");
duk_push_uint(thr, 0U);
duk_put_prop_literal(thr, -2, "index");
DUK_ASSERT(duk_get_top(thr) == entry_top + 1);
}
/* Get next key (and optionally value) from an internal enumerator object and push
* them on the value stack.
*/
DUK_INTERNAL duk_bool_t duk_prop_enum_next(duk_hthread *thr, duk_idx_t idx_enum, duk_bool_t get_value) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top;
#endif
duk_uarridx_t idx_next;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(duk_is_valid_index(thr, idx_enum)); /* May also be a negative index. */
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
idx_enum = duk_require_normalize_index(thr, idx_enum);
duk_get_prop_literal(thr, idx_enum, "keys");
@ -225,5 +253,6 @@ DUK_INTERNAL duk_bool_t duk_prop_enum_next(duk_hthread *thr, duk_idx_t idx_enum,
/* [ ... key val? ] */
DUK_ASSERT(duk_get_top(thr) == entry_top + 1 + (get_value ? 1 : 0));
return 1;
}

50
src-input/duk_prop_get.c

@ -966,8 +966,10 @@ duk__get_ownprop_idxkey_uint8array(duk_hthread *thr, duk_hobject *obj, duk_uarri
data = duk_hbufobj_uint8array_get_validated_data_ptr(thr, h, idx);
if (DUK_LIKELY(data != NULL)) {
duk_bool_t rc = duk__prop_get_write_u32_result(thr, idx_out, (duk_uint32_t) *data);
DUK_ASSERT(rc == 1);
DUK_ASSERT(DUK__GETOWN_FOUND == 1);
return duk__prop_get_write_u32_result(thr, idx_out, (duk_uint32_t) *data);
return rc;
} else {
/* Out-of-bounds, detached, uncovered: treat as not found. */
return DUK__GETOWN_DONE_NOTFOUND; /* Short circuit. */
@ -1626,20 +1628,28 @@ go_next:
DUK_INTERNAL duk_bool_t duk_prop_getvalue_strkey_outidx(duk_hthread *thr, duk_idx_t idx_recv, duk_hstring *key, duk_idx_t idx_out) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top = duk_get_top(thr);
duk_idx_t entry_top;
#endif
DUK_ASSERT(thr != NULL);
DUK_ASSERT(duk_is_valid_posidx(thr, idx_recv));
DUK_ASSERT(key != NULL);
DUK_ASSERT(duk_is_valid_posidx(thr, idx_out));
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
if (DUK_UNLIKELY(DUK_HSTRING_HAS_ARRIDX(key))) {
duk_bool_t rc = duk__prop_getvalue_idxkey_outidx(thr, idx_recv, duk_hstring_get_arridx_fast_known(key), idx_out);
duk_bool_t rc;
rc = duk__prop_getvalue_idxkey_outidx(thr, idx_recv, duk_hstring_get_arridx_fast_known(key), idx_out);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
} else {
duk_bool_t rc = duk__prop_getvalue_strkey_outidx(thr, idx_recv, key, idx_out);
duk_bool_t rc;
DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key));
rc = duk__prop_getvalue_strkey_outidx(thr, idx_recv, key, idx_out);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
}
@ -1647,7 +1657,7 @@ DUK_INTERNAL duk_bool_t duk_prop_getvalue_strkey_outidx(duk_hthread *thr, duk_id
DUK_INTERNAL duk_bool_t duk_prop_getvalue_strkey_push(duk_hthread *thr, duk_idx_t idx_recv, duk_hstring *key) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top = duk_get_top(thr);
duk_idx_t entry_top;
#endif
duk_idx_t idx_out;
duk_bool_t rc;
@ -1655,6 +1665,9 @@ DUK_INTERNAL duk_bool_t duk_prop_getvalue_strkey_push(duk_hthread *thr, duk_idx_
DUK_ASSERT(thr != NULL);
DUK_ASSERT(duk_is_valid_posidx(thr, idx_recv));
DUK_ASSERT(key != NULL);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
duk_push_undefined(thr);
idx_out = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom - 1);
@ -1669,11 +1682,16 @@ DUK_INTERNAL duk_bool_t duk_prop_getvalue_idxkey_outidx(duk_hthread *thr,
duk_uarridx_t idx,
duk_idx_t idx_out) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top = duk_get_top(thr);
duk_idx_t entry_top;
#endif
DUK_ASSERT(thr != NULL);
DUK_ASSERT(duk_is_valid_posidx(thr, idx_recv));
DUK_ASSERT_ARRIDX_VALID(idx);
DUK_ASSERT(duk_is_valid_posidx(thr, idx_out));
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
if (DUK_LIKELY(idx <= DUK_ARRIDX_MAX)) {
duk_bool_t rc = duk__prop_getvalue_idxkey_outidx(thr, idx_recv, idx, idx_out);
@ -1700,7 +1718,7 @@ DUK_INTERNAL duk_bool_t duk_prop_getvalue_idxkey_outidx(duk_hthread *thr,
DUK_INTERNAL duk_bool_t duk_prop_getvalue_outidx(duk_hthread *thr, duk_idx_t idx_recv, duk_tval *tv_key, duk_idx_t idx_out) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top = duk_get_top(thr);
duk_idx_t entry_top;
#endif
duk_bool_t rc;
duk_hstring *key;
@ -1715,6 +1733,9 @@ DUK_INTERNAL duk_bool_t duk_prop_getvalue_outidx(duk_hthread *thr, duk_idx_t idx
*/
DUK_ASSERT(duk_is_valid_posidx(thr, idx_out));
/* Output index may overlap with receiver or key. */
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
/* Must check receiver (typically object) and key (typically
* string, symbol, or numeric index) efficiently. Key is coerced
@ -1848,7 +1869,7 @@ use_str:
DUK_INTERNAL duk_bool_t duk_prop_getvalue_push(duk_hthread *thr, duk_idx_t idx_recv, duk_tval *tv_key) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top = duk_get_top(thr);
duk_idx_t entry_top;
#endif
duk_bool_t rc;
duk_idx_t idx_out;
@ -1856,6 +1877,9 @@ DUK_INTERNAL duk_bool_t duk_prop_getvalue_push(duk_hthread *thr, duk_idx_t idx_r
DUK_ASSERT(thr != NULL);
DUK_ASSERT(duk_is_valid_posidx(thr, idx_recv));
DUK_ASSERT(tv_key != NULL);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
duk_push_undefined(thr);
idx_out = duk_get_top_index_unsafe(thr);
@ -1868,7 +1892,7 @@ DUK_INTERNAL duk_bool_t duk_prop_getvalue_stridx_outidx(duk_hthread *thr,
duk_small_uint_t stridx,
duk_idx_t idx_out) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top = duk_get_top(thr);
duk_idx_t entry_top;
#endif
duk_bool_t rc;
@ -1876,6 +1900,9 @@ DUK_INTERNAL duk_bool_t duk_prop_getvalue_stridx_outidx(duk_hthread *thr,
DUK_ASSERT(duk_is_valid_posidx(thr, idx_recv));
DUK_ASSERT_STRIDX_VALID(stridx);
DUK_ASSERT(duk_is_valid_posidx(thr, idx_out));
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
rc = duk_prop_getvalue_strkey_outidx(thr, idx_recv, DUK_HTHREAD_GET_STRING(thr, stridx), idx_out);
DUK_ASSERT(duk_get_top(thr) == entry_top);
@ -1884,13 +1911,16 @@ DUK_INTERNAL duk_bool_t duk_prop_getvalue_stridx_outidx(duk_hthread *thr,
DUK_INTERNAL duk_bool_t duk_prop_getvalue_stridx_push(duk_hthread *thr, duk_idx_t idx_recv, duk_small_uint_t stridx) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top = duk_get_top(thr);
duk_idx_t entry_top;
#endif
duk_bool_t rc;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(duk_is_valid_posidx(thr, idx_recv));
DUK_ASSERT_STRIDX_VALID(stridx);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
rc = duk_prop_getvalue_strkey_push(thr, idx_recv, DUK_HTHREAD_GET_STRING(thr, stridx));
DUK_ASSERT(duk_get_top(thr) == entry_top + 1);

67
src-input/duk_prop_getown.c

@ -467,19 +467,27 @@ DUK_LOCAL duk_small_int_t duk__prop_getowndesc_idxkey_safe(duk_hthread *thr, duk
DUK_INTERNAL duk_small_int_t duk_prop_getowndesc_obj_strkey(duk_hthread *thr, duk_hobject *obj, duk_hstring *key) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top = duk_get_top(thr);
duk_idx_t entry_top;
#endif
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
DUK_ASSERT(key != NULL);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
if (DUK_UNLIKELY(DUK_HSTRING_HAS_ARRIDX(key))) {
duk_small_int_t attrs = duk__prop_getowndesc_idxkey_unsafe(thr, obj, duk_hstring_get_arridx_fast_known(key));
duk_small_int_t attrs;
attrs = duk__prop_getowndesc_idxkey_unsafe(thr, obj, duk_hstring_get_arridx_fast_known(key));
DUK_ASSERT(duk_get_top(thr) == entry_top + duk_prop_propdesc_valcount(attrs));
return attrs;
} else {
duk_small_int_t attrs = duk__prop_getowndesc_strkey_unsafe(thr, obj, key);
duk_small_int_t attrs;
DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key));
attrs = duk__prop_getowndesc_strkey_unsafe(thr, obj, key);
DUK_ASSERT(duk_get_top(thr) == entry_top + duk_prop_propdesc_valcount(attrs));
return attrs;
}
@ -487,12 +495,15 @@ DUK_INTERNAL duk_small_int_t duk_prop_getowndesc_obj_strkey(duk_hthread *thr, du
DUK_INTERNAL duk_small_int_t duk_prop_getowndesc_obj_idxkey(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top = duk_get_top(thr);
duk_idx_t entry_top;
#endif
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
DUK_ASSERT_ARRIDX_VALID(idx);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
if (DUK_LIKELY(idx <= DUK_ARRIDX_MAX)) {
duk_small_int_t attrs = duk__prop_getowndesc_idxkey_unsafe(thr, obj, idx);
@ -513,27 +524,41 @@ DUK_INTERNAL duk_small_int_t duk_prop_getowndesc_obj_idxkey(duk_hthread *thr, du
}
DUK_INTERNAL duk_small_int_t duk_prop_getowndesc_obj_tvkey(duk_hthread *thr, duk_hobject *obj, duk_tval *tv_key) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top;
#endif
duk_idx_t idx_key;
duk_small_int_t attrs;
duk_idx_t entry_top = duk_get_top(thr);
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
DUK_ASSERT(tv_key != NULL);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
idx_key = duk_get_top(thr);
duk_push_tval(thr, tv_key);
attrs = duk_prop_getowndesc_obj_strkey(thr, obj, duk_to_property_key_hstring(thr, -1));
duk_remove(thr, entry_top); /* Remove stabilized 'tv_key', keep property value or get/set. */
DUK_ASSERT(duk_get_top(thr) >= entry_top);
duk_remove(thr, idx_key); /* Remove stabilized 'tv_key', keep property value or get/set. */
DUK_ASSERT(duk_get_top(thr) == entry_top + duk_prop_propdesc_valcount(attrs));
return attrs;
}
DUK_INTERNAL duk_small_int_t duk_prop_getownattr_obj_strkey(duk_hthread *thr, duk_hobject *obj, duk_hstring *key) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top = duk_get_top(thr);
duk_idx_t entry_top;
#endif
duk_small_int_t attrs;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
DUK_ASSERT(key != NULL);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
duk_small_int_t attrs = duk_prop_getowndesc_obj_strkey(thr, obj, key);
attrs = duk_prop_getowndesc_obj_strkey(thr, obj, key);
duk_prop_pop_propdesc(thr, attrs);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return attrs;
@ -541,10 +566,18 @@ DUK_INTERNAL duk_small_int_t duk_prop_getownattr_obj_strkey(duk_hthread *thr, du
DUK_INTERNAL duk_small_int_t duk_prop_getownattr_obj_idxkey(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t idx) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top = duk_get_top(thr);
duk_idx_t entry_top;
#endif
duk_small_int_t attrs;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
DUK_ASSERT_ARRIDX_VALID(idx);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
duk_small_int_t attrs = duk_prop_getowndesc_obj_idxkey(thr, obj, idx);
attrs = duk_prop_getowndesc_obj_idxkey(thr, obj, idx);
duk_prop_pop_propdesc(thr, attrs);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return attrs;
@ -552,10 +585,18 @@ DUK_INTERNAL duk_small_int_t duk_prop_getownattr_obj_idxkey(duk_hthread *thr, du
DUK_INTERNAL duk_small_int_t duk_prop_getownattr_obj_tvkey(duk_hthread *thr, duk_hobject *obj, duk_tval *tv_key) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top = duk_get_top(thr);
duk_idx_t entry_top;
#endif
duk_small_int_t attrs;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
DUK_ASSERT(tv_key != NULL);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
duk_small_int_t attrs = duk_prop_getowndesc_obj_tvkey(thr, obj, tv_key);
attrs = duk_prop_getowndesc_obj_tvkey(thr, obj, tv_key);
duk_prop_pop_propdesc(thr, attrs);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return attrs;

50
src-input/duk_prop_has.c

@ -447,25 +447,51 @@ DUK_LOCAL duk_bool_t duk__prop_has_idxkey(duk_hthread *thr, duk_tval *tv_obj, du
}
DUK_INTERNAL duk_bool_t duk_prop_has_strkey(duk_hthread *thr, duk_tval *tv_obj, duk_hstring *key) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top;
#endif
DUK_ASSERT(thr != NULL);
DUK_ASSERT(tv_obj != NULL);
DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_obj) || DUK_TVAL_IS_BUFFER(tv_obj) || DUK_TVAL_IS_LIGHTFUNC(tv_obj));
DUK_ASSERT(key != NULL);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
if (DUK_UNLIKELY(DUK_HSTRING_HAS_ARRIDX(key))) {
return duk__prop_has_idxkey(thr, tv_obj, duk_hstring_get_arridx_fast_known(key));
duk_bool_t rc;
rc = duk__prop_has_idxkey(thr, tv_obj, duk_hstring_get_arridx_fast_known(key));
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
} else {
return duk__prop_has_strkey(thr, tv_obj, key);
duk_bool_t rc;
DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key));
rc = duk__prop_has_strkey(thr, tv_obj, key);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
}
}
DUK_INTERNAL duk_bool_t duk_prop_has_idxkey(duk_hthread *thr, duk_tval *tv_obj, duk_uarridx_t idx) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top;
#endif
DUK_ASSERT(thr != NULL);
DUK_ASSERT(tv_obj != NULL);
DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_obj) || DUK_TVAL_IS_BUFFER(tv_obj) || DUK_TVAL_IS_LIGHTFUNC(tv_obj));
DUK_ASSERT_ARRIDX_VALID(idx);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
if (DUK_LIKELY(idx <= DUK_ARRIDX_MAX)) {
return duk__prop_has_idxkey(thr, tv_obj, idx);
duk_bool_t rc = duk__prop_has_idxkey(thr, tv_obj, idx);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
} else {
duk_bool_t rc;
duk_hstring *key;
@ -474,11 +500,15 @@ DUK_INTERNAL duk_bool_t duk_prop_has_idxkey(duk_hthread *thr, duk_tval *tv_obj,
key = duk_push_u32_tohstring(thr, idx);
rc = duk__prop_has_strkey(thr, tv_obj, key);
duk_pop_unsafe(thr);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
}
}
DUK_INTERNAL duk_bool_t duk_prop_has(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top;
#endif
duk_bool_t rc;
duk_hstring *key;
duk_uarridx_t idx;
@ -486,6 +516,9 @@ DUK_INTERNAL duk_bool_t duk_prop_has(duk_hthread *thr, duk_tval *tv_obj, duk_tva
DUK_ASSERT(thr != NULL);
DUK_ASSERT(tv_obj != NULL);
DUK_ASSERT(tv_key != NULL);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
/* Behavior for non-object rvalue differs from other property
* operations: primitive values are rejected, and rvalue (base)
@ -566,11 +599,18 @@ DUK_INTERNAL duk_bool_t duk_prop_has(duk_hthread *thr, duk_tval *tv_obj, duk_tva
(void) duk_to_property_key_hstring(thr, -1);
rc = duk_prop_has(thr, DUK_GET_TVAL_NEGIDX(thr, -2), DUK_GET_TVAL_NEGIDX(thr, -1));
duk_pop_2_unsafe(thr);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
use_idx:
return duk__prop_has_idxkey(thr, tv_obj, idx);
DUK_ASSERT_ARRIDX_VALID(idx);
rc = duk__prop_has_idxkey(thr, tv_obj, idx);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
use_str:
return duk__prop_has_strkey(thr, tv_obj, key);
DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key));
rc = duk__prop_has_strkey(thr, tv_obj, key);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
}

8
src-input/duk_prop_ownpropkeys.c

@ -442,12 +442,18 @@ DUK_LOCAL duk_small_int_t duk__prop_ownpropkeys_proxy(duk_hthread *thr, duk_hobj
}
DUK_INTERNAL void duk_prop_ownpropkeys(duk_hthread *thr, duk_hobject *obj, duk_uint_t ownpropkeys_flags) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top;
#endif
duk_small_uint_t htype;
duk_uarridx_t idx_out = 0;
duk_harray *arr_out;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
/* Stabilize 'obj' in case we need to traverse Proxy targets. Keep
* the current object in this value stack slot (link to previous
@ -625,4 +631,6 @@ success:
DUK_HARRAY_ASSERT_VALID(thr->heap, (duk_harray *) duk_require_hobject(thr, -1));
duk_remove_m2(thr); /* Remove stabilized 'obj': [ ... obj arr_out ] -> [ ... arr_out ] */
DUK_ASSERT(duk_get_top(thr) == entry_top + 1);
}

48
src-input/duk_prop_set.c

@ -3058,13 +3058,23 @@ fail_not_writable:
DUK_INTERNAL duk_bool_t
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) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top;
#endif
DUK_ASSERT(thr != NULL);
DUK_ASSERT(duk_is_valid_posidx(thr, idx_recv));
DUK_ASSERT_ARRIDX_VALID(idx);
DUK_ASSERT(duk_is_valid_posidx(thr, idx_val));
DUK_ASSERT(throw_flag == 0 || throw_flag == 1);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
if (DUK_LIKELY(idx <= DUK_ARRIDX_MAX)) {
return duk__prop_putvalue_idxkey_inidx(thr, idx_recv, idx, idx_val, throw_flag);
duk_bool_t rc = duk__prop_putvalue_idxkey_inidx(thr, idx_recv, idx, idx_val, throw_flag);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
} else {
duk_bool_t rc;
duk_hstring *key;
@ -3073,27 +3083,47 @@ duk_prop_putvalue_idxkey_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_uarridx
key = duk_push_u32_tohstring(thr, idx);
rc = duk__prop_putvalue_strkey_inidx(thr, idx_recv, key, idx_val, throw_flag);
duk_pop_unsafe(thr);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
}
}
DUK_INTERNAL duk_bool_t
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) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top;
#endif
DUK_ASSERT(thr != NULL);
DUK_ASSERT(duk_is_valid_posidx(thr, idx_recv));
DUK_ASSERT(key != NULL);
DUK_ASSERT(duk_is_valid_posidx(thr, idx_val));
DUK_ASSERT(throw_flag == 0 || throw_flag == 1);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
if (DUK_UNLIKELY(DUK_HSTRING_HAS_ARRIDX(key))) {
return duk__prop_putvalue_idxkey_inidx(thr, idx_recv, duk_hstring_get_arridx_fast_known(key), idx_val, throw_flag);
duk_bool_t rc;
rc = duk__prop_putvalue_idxkey_inidx(thr, idx_recv, duk_hstring_get_arridx_fast_known(key), idx_val, throw_flag);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
} else {
return duk__prop_putvalue_strkey_inidx(thr, idx_recv, key, idx_val, throw_flag);
duk_bool_t rc;
DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key));
rc = duk__prop_putvalue_strkey_inidx(thr, idx_recv, key, idx_val, throw_flag);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
}
}
DUK_INTERNAL duk_bool_t
duk_prop_putvalue_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_tval *tv_key, duk_idx_t idx_val, duk_bool_t throw_flag) {
#if defined(DUK_USE_ASSERTIONS)
duk_idx_t entry_top;
#endif
duk_bool_t rc;
duk_hstring *key;
duk_uarridx_t idx;
@ -3110,6 +3140,9 @@ duk_prop_putvalue_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_tval *tv_key,
* because it might be a VM register source.
*/
DUK_ASSERT(throw_flag == 0 || throw_flag == 1);
#if defined(DUK_USE_ASSERTIONS)
entry_top = duk_get_top(thr);
#endif
switch (DUK_TVAL_GET_TAG(tv_key)) {
case DUK_TAG_STRING:
@ -3176,13 +3209,18 @@ duk_prop_putvalue_inidx(duk_hthread *thr, duk_idx_t idx_recv, duk_tval *tv_key,
key = duk_to_property_key_hstring(thr, -1);
rc = duk_prop_putvalue_strkey_inidx(thr, idx_recv, key, idx_val, throw_flag);
duk_pop_unsafe(thr);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
use_idx:
DUK_ASSERT_ARRIDX_VALID(idx);
return duk__prop_putvalue_idxkey_inidx(thr, idx_recv, idx, idx_val, throw_flag);
rc = duk__prop_putvalue_idxkey_inidx(thr, idx_recv, idx, idx_val, throw_flag);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
use_str:
DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(key));
return duk__prop_putvalue_strkey_inidx(thr, idx_recv, key, idx_val, throw_flag);
rc = duk__prop_putvalue_strkey_inidx(thr, idx_recv, key, idx_val, throw_flag);
DUK_ASSERT(duk_get_top(thr) == entry_top);
return rc;
}

19
src-tools/lib/builtins/initdata/property_table_initializers.js

@ -87,7 +87,7 @@ function emitPropertyTableDefinitions(genc, meta, objs, biStrMap, biObjMap) {
return res;
}
function emitInitializer(idx, o, layout) {
function emitInitializer(idx, o) {
var initVals = [];
var initKeys = [];
var initFlags = [];
@ -102,20 +102,7 @@ function emitPropertyTableDefinitions(genc, meta, objs, biStrMap, biObjMap) {
for (let p of o.properties) {
initFlags.push(prepAttrs(p));
}
switch (layout) {
case 1:
initList = initKeys.concat(initVals).concat(initFlags);
break;
case 2:
initList = initVals.concat(initKeys).concat(initFlags);
break;
case 3:
// Same as layout 2 now, no hash/array.
initList = initVals.concat(initKeys).concat(initFlags);
break;
default:
throw new TypeError('internal error, invalid layout: ' + layout);
}
initList = initVals.concat(initKeys).concat(initFlags);
if (initList.length > 0) {
genc.emitLine('DUK_EXTERNAL const duk_romprops_' + idx + ' duk_prop_' + idx +
@ -124,7 +111,7 @@ function emitPropertyTableDefinitions(genc, meta, objs, biStrMap, biObjMap) {
}
objs.forEach((o, idx) => {
emitInitializer(idx, o, 2);
emitInitializer(idx, o);
});
}
exports.emitPropertyTableDefinitions = emitPropertyTableDefinitions;

40
tests/ecmascript/test-bi-proxy-revocable-creation.js

@ -0,0 +1,40 @@
/*===
[object Object]
true
[object Object]
true true true
[object Function]
true true true
undefined undefined undefined undefined
undefined undefined undefined undefined
===*/
var target = { foo: 123 };
var P = Proxy.revocable(target, {});
print(Object.prototype.toString.call(P));
print(Object.getPrototypeOf(P) === Object.prototype);
print(Object.prototype.toString.call(P.proxy));
var pd = Object.getOwnPropertyDescriptor(P, 'proxy');
print(pd.writable, pd.enumerable, pd.writable);
print(Object.prototype.toString.call(P.revoke));
var pd = Object.getOwnPropertyDescriptor(P, 'revoke');
print(pd.writable, pd.enumerable, pd.writable);
Object.getOwnPropertyNames(P).forEach(function (k) {
if (k === 'proxy' || k === 'revoke') {
return;
}
print('extra key:', k);
});
Object.getOwnPropertySymbols(P).forEach(function (s) {
print('extra symbol:', String(s));
});
var pd = Object.getOwnPropertyDescriptor(P.revoke, 'name') || {};
print(pd.value, pd.writable, pd.enumerable, pd.writable);
var pd = Object.getOwnPropertyDescriptor(P.revoke, 'length') || {};
print(pd.value, pd.writable, pd.enumerable, pd.writable);

49
tests/ecmascript/test-bi-proxy-revoked-func-call.js

@ -0,0 +1,49 @@
/*===
call
NaN
revoke
revoked
call
TypeError
revoke again
revoked
call
TypeError
done
===*/
var P = Proxy.revocable(Math.cos, {});
var F = P.proxy;
try {
print('call');
print(F());
} catch (e) {
print(e.name);
}
print('revoke');
P.revoke();
print('revoked');
try {
print('call');
print(F());
} catch (e) {
print(e.name);
//print(e.stack);
}
print('revoke again'); // nop
P.revoke();
print('revoked');
try {
print('call');
print(F());
} catch (e) {
print(e.name);
//print(e.stack);
}
print('done');

23
tests/ecmascript/test-bi-proxy-revoked-instanceof.js

@ -0,0 +1,23 @@
/*===
false
undefined
TypeError
done
===*/
var target = function test() {};
var P = Proxy.revocable(target, {});
try {
print({} instanceof P.proxy);
} catch (e) {
print(e.name);
//print(e.stack);
}
print(P.revoke());
try {
print({} instanceof P.proxy);
} catch (e) {
print(e.name);
//print(e.stack);
}
print('done');

23
tests/ecmascript/test-bi-proxy-revoked-isarray.js

@ -0,0 +1,23 @@
/*===
true
undefined
TypeError
done
===*/
var target = [];
var P = Proxy.revocable(target, {});
try {
print(Array.isArray(P.proxy));
} catch (e) {
print(e.name);
//print(e.stack);
}
print(P.revoke());
try {
print(Array.isArray(P.proxy));
} catch (e) {
print(e.name);
//print(e.stack);
}
print('done');
Loading…
Cancel
Save