From 3527fa7a81e894cb071a7a4535f82b8d356efafb Mon Sep 17 00:00:00 2001 From: Sami Vaarala Date: Wed, 14 May 2014 00:51:55 +0300 Subject: [PATCH] proxy 'has' trap implementation --- src/duk_hobject_props.c | 78 ++++++++++++++++++++++++++++++++++------- src/duk_js_ops.c | 8 +++++ src/genstrings.py | 1 + 3 files changed, 74 insertions(+), 13 deletions(-) diff --git a/src/duk_hobject_props.c b/src/duk_hobject_props.c index 630b754c..ec5279dd 100644 --- a/src/duk_hobject_props.c +++ b/src/duk_hobject_props.c @@ -2006,7 +2006,7 @@ int duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key) { duk_hobject *h_target; if (duk__proxy_check_prop(thr, curr, DUK_STRIDX_GET, tv_key, &h_target)) { - /* -> [ ... func handler ] */ + /* -> [ ... trap handler ] */ DUK_DDD(DUK_DDDPRINT("-> proxy object 'get' for key %!T", tv_key)); duk_push_hobject(ctx, h_target); /* target */ duk_push_tval(ctx, tv_key); /* P */ @@ -2040,7 +2040,7 @@ int duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key) { (desc.get == NULL) && !DUK_TVAL_IS_UNDEFINED(tv_hook); if (datadesc_reject || accdesc_reject) { - DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "proxy get rejected"); + DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "proxy 'get' rejected"); } duk_pop_2(ctx); @@ -2283,8 +2283,9 @@ int duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key) { duk_context *ctx = (duk_context *) thr; duk_hobject *obj; duk_hstring *key; + duk_uint32_t arr_idx; int rc; - duk_propdesc dummy; + duk_propdesc desc; DUK_DDD(DUK_DDDPRINT("hasprop: thr=%p, obj=%p, key=%p (obj -> %!T, key -> %!T)", (void *) thr, (void *) tv_obj, (void *) tv_key, tv_obj, tv_key)); @@ -2293,9 +2294,10 @@ int duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key) { DUK_ASSERT(thr->heap != NULL); DUK_ASSERT(tv_obj != NULL); DUK_ASSERT(tv_key != NULL); - DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); + DUK_UNREF(arr_idx); + /* No need to make a copy of the input duk_tvals here. */ if (!DUK_TVAL_IS_OBJECT(tv_obj)) { @@ -2306,6 +2308,55 @@ int duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key) { obj = DUK_TVAL_GET_OBJECT(tv_obj); DUK_ASSERT(obj != NULL); +#if defined(DUK_USE_ES6_PROXY) + if (DUK_UNLIKELY(DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj))) { + duk_hobject *h_target; + duk_small_int_t tmp_bool; + + /* XXX: the key in 'key in obj' is string coerced before we're called + * (which is the required behavior in E5/E5.1/E6) so the key is a string + * here already. + */ + + if (duk__proxy_check_prop(thr, obj, DUK_STRIDX_HAS, tv_key, &h_target)) { + /* [ ... trap handler ] */ + DUK_DDD(DUK_DDDPRINT("-> proxy object 'has' for key %!T", tv_key)); + duk_push_hobject(ctx, h_target); /* target */ + duk_push_tval(ctx, tv_key); /* P */ + duk_call_method(ctx, 2 /*nargs*/); + tmp_bool = duk_to_boolean(ctx, -1); + duk_pop(ctx); + if (!tmp_bool) { + /* Target object must be checked for a conflicting + * non-configurable property. + */ + arr_idx = duk__push_tval_to_hstring_arr_idx(ctx, tv_key, &key); + DUK_ASSERT(key != NULL); + + if (duk__get_own_property_desc_raw(thr, h_target, key, arr_idx, &desc, 0 /*push_value*/)) { + DUK_DDD(DUK_DDDPRINT("proxy 'has': target has matching property %!O, check for " + "conflicting property; desc.flags=0x%08x, " + "desc.get=%p, desc.set=%p", + key, (int) desc.flags, + (void *) desc.get, (void *) desc.set)); + /* XXX: Extensibility check for target uses IsExtensible(). If we + * implemented the isExtensible trap and didn't reject proxies as + * proxy targets, it should be respected here. + */ + if (!((desc.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && /* property is configurable and */ + DUK_HOBJECT_HAS_EXTENSIBLE(h_target))) { /* ... target is extensible */ + DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "proxy 'has' rejected"); + } + } + } + + return tmp_bool; + } + + obj = h_target; /* resume check from proxy target */ + } +#endif /* DUK_USE_ES6_PROXY */ + duk_push_tval(ctx, tv_key); duk_to_string(ctx, -1); key = duk_get_hstring(ctx, -1); @@ -2313,7 +2364,7 @@ int duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key) { /* XXX: inline into a prototype walking loop? */ - rc = duk__get_property_desc(thr, obj, key, &dummy, 0); /* push_value = 0 */ + rc = duk__get_property_desc(thr, obj, key, &desc, 0); /* push_value = 0 */ duk_pop(ctx); /* [key] -> [] */ return rc; @@ -2322,7 +2373,9 @@ int duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key) { /* * HASPROP variant used internally. * - * This primitive must never throw an error, caller's rely on this. + * This primitive must never throw an error, callers rely on this. + * Does not implement proxy behavior: if applied to a proxy object, + * returns key existence on the proxy object itself. */ int duk_hobject_hasprop_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key) { @@ -2338,7 +2391,6 @@ int duk_hobject_hasprop_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key return duk__get_property_desc(thr, obj, key, &dummy, 0); /* push_value = 0 */ } - /* * Helper: handle Array object 'length' write which automatically * deletes properties, see E5 Section 15.4.5.1, step 3. This is @@ -2785,10 +2837,10 @@ int duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, du #if defined(DUK_USE_ES6_PROXY) if (DUK_UNLIKELY(DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(orig))) { duk_hobject *h_target; - int tmp_bool; + duk_small_int_t tmp_bool; if (duk__proxy_check_prop(thr, orig, DUK_STRIDX_SET, tv_key, &h_target)) { - /* -> [ ... func handler ] */ + /* -> [ ... trap handler ] */ DUK_DDD(DUK_DDDPRINT("-> proxy object 'set' for key %!T", tv_key)); duk_push_hobject(ctx, h_target); /* target */ duk_push_tval(ctx, tv_key); /* P */ @@ -2826,7 +2878,7 @@ int duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, du !(desc.flags & DUK_PROPDESC_FLAG_CONFIGURABLE) && (desc.set == NULL); if (datadesc_reject || accdesc_reject) { - DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "proxy set rejected"); + DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "proxy 'set' rejected"); } duk_pop_2(ctx); @@ -3625,12 +3677,12 @@ int duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, in #if defined(DUK_USE_ES6_PROXY) if (DUK_UNLIKELY(DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj))) { duk_hobject *h_target; - int tmp_bool; + duk_small_int_t tmp_bool; /* Note: proxy handling must happen before key is string coerced. */ if (duk__proxy_check_prop(thr, obj, DUK_STRIDX_DELETE_PROPERTY, tv_key, &h_target)) { - /* -> [ ... func handler ] */ + /* -> [ ... trap handler ] */ DUK_DDD(DUK_DDDPRINT("-> proxy object 'deleteProperty' for key %!T", tv_key)); duk_push_hobject(ctx, h_target); /* target */ duk_push_tval(ctx, tv_key); /* P */ @@ -3658,7 +3710,7 @@ int duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, in desc_reject = !(desc.flags & DUK_PROPDESC_FLAG_CONFIGURABLE); if (desc_reject) { /* unconditional */ - DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "proxy deleteProperty rejected"); + DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "proxy 'deleteProperty' rejected"); } } rc = 1; /* success */ diff --git a/src/duk_js_ops.c b/src/duk_js_ops.c index 3bca51f5..af936061 100644 --- a/src/duk_js_ops.c +++ b/src/duk_js_ops.c @@ -1067,6 +1067,14 @@ int duk_js_in(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y) { * lval is already a string). */ + /* XXX: The ES5/5.1/6 specifications require that the key in 'key in obj' + * must be string coerced before the internal HasProperty() algorithm is + * invoked. A fast path skipping coercion could be safely implemented for + * numbers (as number-to-string coercion has no side effects). For ES6 + * proxy behavior, the trap 'key' argument must be in a string coerced + * form (which is a shame). + */ + duk_push_tval(ctx, tv_x); duk_push_tval(ctx, tv_y); (void) duk_require_hobject(ctx, -1); /* TypeError if rval not object */ diff --git a/src/genstrings.py b/src/genstrings.py index bda61528..2f1f9d8a 100644 --- a/src/genstrings.py +++ b/src/genstrings.py @@ -483,6 +483,7 @@ es6_string_list = [ #mkstr("revocable", es6=True), # Proxy trap names + mkstr("has", es6=True), mkstr("set", es6=True), mkstr("get", es6=True), mkstr("deleteProperty", es6=True),