From c115f75b038b12c610c90e076b4f1dc86095fdc3 Mon Sep 17 00:00:00 2001 From: Sami Vaarala Date: Wed, 14 Jun 2017 17:33:44 +0300 Subject: [PATCH] Proxy property descriptor trap work Very minimal implementations: * getOwnPropertyDescriptor trap: works enough to support virtualization of enumeration, but doesn't provide 'value', 'get', or 'set'. * defineProperty: just pass-through so far, no actual trap implementation. --- src-input/duk_bi_object.c | 2 +- src-input/duk_bi_protos.h | 2 +- src-input/duk_bi_proxy.c | 16 ++++--- src-input/duk_hobject.h | 4 +- src-input/duk_hobject_enum.c | 3 +- src-input/duk_hobject_props.c | 83 +++++++++++++++++++++++++++++++++++ 6 files changed, 98 insertions(+), 12 deletions(-) diff --git a/src-input/duk_bi_object.c b/src-input/duk_bi_object.c index 9751fe95..f1b602ef 100644 --- a/src-input/duk_bi_object.c +++ b/src-input/duk_bi_object.c @@ -690,7 +690,7 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_keys_shared(duk_hthread *thr) { DUK_ASSERT(magic >= 0 && magic < (duk_int_t) (sizeof(duk__object_keys_enum_flags) / sizeof(duk_small_uint_t))); enum_flags = duk__object_keys_enum_flags[magic]; - duk_proxy_ownkeys_postprocess(thr, h_proxy_target, enum_flags); + duk_proxy_ownkeys_postprocess(thr, (duk_hproxy *) obj, enum_flags); return 1; skip_proxy: diff --git a/src-input/duk_bi_protos.h b/src-input/duk_bi_protos.h index 7c1488d4..595aeea3 100644 --- a/src-input/duk_bi_protos.h +++ b/src-input/duk_bi_protos.h @@ -75,7 +75,7 @@ void duk_bi_json_stringify_helper(duk_hthread *thr, DUK_INTERNAL_DECL duk_ret_t duk_textdecoder_decode_utf8_nodejs(duk_hthread *thr); #if defined(DUK_USE_ES6_PROXY) -DUK_INTERNAL_DECL void duk_proxy_ownkeys_postprocess(duk_hthread *thr, duk_hobject *h_proxy_target, duk_uint_t flags); +DUK_INTERNAL_DECL void duk_proxy_ownkeys_postprocess(duk_hthread *thr, duk_hproxy *h_proxy, duk_uint_t flags); #endif #endif /* DUK_BUILTIN_PROTOS_H_INCLUDED */ diff --git a/src-input/duk_bi_proxy.c b/src-input/duk_bi_proxy.c index a98a5b49..bb47da41 100644 --- a/src-input/duk_bi_proxy.c +++ b/src-input/duk_bi_proxy.c @@ -9,12 +9,12 @@ * array of valid result keys (strings or symbols). TypeError for invalid * values. Flags are shared with duk_enum(). */ -DUK_INTERNAL void duk_proxy_ownkeys_postprocess(duk_hthread *thr, duk_hobject *h_proxy_target, duk_uint_t flags) { +DUK_INTERNAL void duk_proxy_ownkeys_postprocess(duk_hthread *thr, duk_hproxy *h_proxy, duk_uint_t flags) { duk_uarridx_t i, len, idx; duk_propdesc desc; - DUK_ASSERT_CTX_VALID(thr); - DUK_ASSERT(h_proxy_target != NULL); + DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT(h_proxy != NULL); len = (duk_uarridx_t) duk_get_length(thr, -1); idx = 0; @@ -32,11 +32,13 @@ DUK_INTERNAL void duk_proxy_ownkeys_postprocess(duk_hthread *thr, duk_hobject *h } if (!(flags & DUK_ENUM_INCLUDE_NONENUMERABLE)) { - /* No support for 'getOwnPropertyDescriptor' trap yet, - * so check enumerability always from target object - * descriptor. + /* Check enumerability from Proxy so that a possible + * getOwnPropertyDescriptor trap can be invoked. It + * would be tempting to cache the trap function but + * it may be removed as a side effect of a previous + * call so we must look it up every time. */ - if (duk_hobject_get_own_propdesc(thr, h_proxy_target, duk_known_hstring(thr, -1), &desc, 0 /*flags*/)) { + if (duk_hobject_get_own_propdesc(thr, (duk_hobject *) h_proxy, duk_known_hstring(thr, -1), &desc, 0 /*flags*/)) { if ((desc.flags & DUK_PROPDESC_FLAG_ENUMERABLE) == 0) { DUK_DDD(DUK_DDDPRINT("ignore non-enumerable property: %!T", duk_get_tval(thr, -1))); goto skip_key; diff --git a/src-input/duk_hobject.h b/src-input/duk_hobject.h index 6a8d2551..f9dc3699 100644 --- a/src-input/duk_hobject.h +++ b/src-input/duk_hobject.h @@ -748,8 +748,8 @@ union duk_propvalue { struct duk_propdesc { /* read-only values 'lifted' for ease of use */ duk_small_uint_t flags; - duk_hobject *get; - duk_hobject *set; + duk_hobject *get; /* borrowed */ + duk_hobject *set; /* borrowed */ /* for updating (all are set to < 0 for virtual properties) */ duk_int_t e_idx; /* prop index in 'entry part', < 0 if not there */ diff --git a/src-input/duk_hobject_enum.c b/src-input/duk_hobject_enum.c index 9f9bcb91..9f4ceb62 100644 --- a/src-input/duk_hobject_enum.c +++ b/src-input/duk_hobject_enum.c @@ -268,7 +268,8 @@ DUK_INTERNAL void duk_hobject_enumerator_create(duk_hthread *thr, duk_small_uint h_trap_result = duk_require_hobject(thr, -1); DUK_UNREF(h_trap_result); - duk_proxy_ownkeys_postprocess(thr, h_proxy_target, enum_flags); + /* FIXME: enum_target vs. h_proxy_target? */ + duk_proxy_ownkeys_postprocess(thr, (duk_hproxy *) enum_target, enum_flags); /* -> [ ... enum_target res trap_result keys_array ] */ /* Copy cleaned up trap result keys into the enumerator object. */ diff --git a/src-input/duk_hobject_props.c b/src-input/duk_hobject_props.c index 2883623a..ad03b978 100644 --- a/src-input/duk_hobject_props.c +++ b/src-input/duk_hobject_props.c @@ -1595,6 +1595,8 @@ DUK_LOCAL void duk__check_arguments_map_for_delete(duk_hthread *thr, duk_hobject DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); + /* FIXME: find_existing_entry, removes propdesc usage */ + if (!duk_hobject_get_own_propdesc(thr, obj, DUK_HTHREAD_STRING_INT_MAP(thr), temp_desc, DUK_GETDESC_FLAG_PUSH_VALUE)) { DUK_DDD(DUK_DDDPRINT("arguments: key not mapped, no exotic delete behavior")); return; @@ -1686,6 +1688,66 @@ DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *ob out_desc->a_idx = -1; #endif + /* FIXME: property descriptor helpers would need to be reworked + * so that both object and duk_propdesc outputs are supported. + */ +#if defined(DUK_USE_ES6_PROXY) + if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(obj))) { + duk_hproxy *h_proxy; + duk_hobject *h_target; + duk_tval tv_key; + + DUK_TVAL_SET_STRING(&tv_key, key); + if (duk__proxy_check_prop(thr, obj, DUK_STRIDX_GET_OWN_PROPERTY_DESCRIPTOR, &tv_key, &h_target)) { + duk_push_hobject(thr, h_target); /* target */ + duk_push_hstring(thr, key); /* P */ + duk_call_method(thr, 2 /*nargs*/); + + /* XXX: At present out_desc->{get,set} are borrowed so they're + * provided as NULL even when the descriptor has them. + */ + + /* FIXME: validate descriptor, convert it to internal form. + * At present out_desc->get etc are borrowed so value, get, + * set must be scrubbed, leaving only attributes behind which + * is fine for now. + */ + /* FIXME: arguments special */ + + out_desc->get = NULL; + out_desc->set = NULL; + out_desc->e_idx = -1; + out_desc->h_idx = -1; + out_desc->a_idx = -1; + out_desc->flags = 0; + if (duk_get_prop_stridx_boolean(thr, -1, DUK_STRIDX_WRITABLE, NULL)) { + out_desc->flags |= DUK_PROPDESC_FLAG_WRITABLE; + } + if (duk_get_prop_stridx_boolean(thr, -1, DUK_STRIDX_ENUMERABLE, NULL)) { + out_desc->flags |= DUK_PROPDESC_FLAG_ENUMERABLE; + } + if (duk_get_prop_stridx_boolean(thr, -1, DUK_STRIDX_CONFIGURABLE, NULL)) { + out_desc->flags |= DUK_PROPDESC_FLAG_CONFIGURABLE; + } + out_desc->flags |= DUK_PROPDESC_FLAG_VIRTUAL; + + if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) { + duk_get_prop_stridx(thr, -1, DUK_STRIDX_VALUE); + duk_remove_m2(thr); + } else { + duk_pop(thr); + } + + return 1; + } + + DUK_D(DUK_DPRINT("getting own property descriptor for Proxy")); + h_proxy = (duk_hproxy *) obj; + DUK_ASSERT(h_proxy->target != NULL); + obj = h_proxy->target; + } +#endif /* DUK_USE_ES6_PROXY */ + /* * Try entries part first because it's the common case. * @@ -3870,6 +3932,7 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, goto fail_not_writable; } #endif + /* FIXME: add explicit virtual property safety check */ /* Although there are writable virtual properties (e.g. plain buffer * and buffer object number indices), they are handled before we come @@ -4974,6 +5037,7 @@ void duk_hobject_prepare_property_descriptor(duk_hthread *thr, idx_value = duk_get_top_index(thr); } + /* FIXME: some overlap here */ if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_WRITABLE)) { is_data_desc = 1; if (duk_to_boolean(thr, -1)) { @@ -5116,6 +5180,10 @@ duk_bool_t duk_hobject_define_property_helper(duk_hthread *thr, DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); + /* FIXME: maybe add defineProperty trap at the same time, so that only + * non-Proxy objects come here? + */ + /* All the flags fit in 16 bits, so will fit into duk_bool_t. */ has_writable = (defprop_flags & DUK_DEFPROP_HAVE_WRITABLE); @@ -5151,6 +5219,21 @@ duk_bool_t duk_hobject_define_property_helper(duk_hthread *thr, (long) has_set, (void *) set, (duk_heaphdr *) set, (long) arr_idx, (long) throw_flag)); + /* + * Proxy objects. + */ + +#if defined(DUK_USE_ES6_PROXY) + if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(obj))) { + duk_hproxy *h_proxy; + + h_proxy = (duk_hproxy *) obj; + obj = h_proxy->target; + DUK_ASSERT(obj != NULL); + DUK_ASSERT(!DUK_HOBJECT_IS_PROXY(obj)); + } +#endif /* DUK_USE_ES6_PROXY */ + /* * Array exotic behaviors can be implemented at this point. The local variables * are essentially a 'value copy' of the input descriptor (Desc), which is modified