Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add getOwnPropertyDescriptor Proxy trap #1567

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src-input/duk_bi_object.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion src-input/duk_bi_protos.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
16 changes: 9 additions & 7 deletions src-input/duk_bi_proxy.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src-input/duk_hobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
3 changes: 2 additions & 1 deletion src-input/duk_hobject_enum.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
83 changes: 83 additions & 0 deletions src-input/duk_hobject_props.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
75 changes: 27 additions & 48 deletions tests/ecmascript/test-bi-array-proto-reverse.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,6 @@
// XXX: shared test utils

function formatValue(v) {
if (typeof v === 'function') {
return 'function';
}
return typeof(v) + ':' + String(v);
}

function printDescriptor(obj, key) {
var pd = Object.getOwnPropertyDescriptor(obj, key);
if (!pd) {
print('key=' + key, 'nonexistent');
return;
}

print('key=' + key,
'value=' + formatValue(pd.value),
'writable=' + formatValue(pd.writable),
'enumerable=' + formatValue(pd.enumerable),
'configurable=' + formatValue(pd.configurable),
'get=' + formatValue(pd.get),
'set=' + formatValue(pd.set));
}
/*@include util-object.js@*/

function dumpValue(x) {
var i, n, clipped;
Expand Down Expand Up @@ -157,33 +136,33 @@ try {
/*===
attributes
true true false false false true false true
key=0 value=undefined:undefined writable=undefined:undefined enumerable=boolean:false configurable=boolean:true get=function set=function
key=1 value=undefined:undefined writable=undefined:undefined enumerable=boolean:false configurable=boolean:true get=function set=function
key=2 nonexistent
key=3 nonexistent
key=4 nonexistent
key=5 value=undefined:undefined writable=undefined:undefined enumerable=boolean:true configurable=boolean:true get=function set=function
key=6 nonexistent
key=7 value=undefined:undefined writable=undefined:undefined enumerable=boolean:true configurable=boolean:true get=function set=function
key: 0, desc: get=function,set=function,enumerable=false,configurable=true
key: 1, desc: get=function,set=function,enumerable=false,configurable=true
key: 2, desc: none
key: 3, desc: none
key: 4, desc: none
key: 5, desc: get=function,set=function,enumerable=true,configurable=true
key: 6, desc: none
key: 7, desc: get=function,set=function,enumerable=true,configurable=true
get 0
get 7
set 0 val7
set 7 val0
get 1
get 5
true false true false false false true true
key=0 value=undefined:undefined writable=undefined:undefined enumerable=boolean:false configurable=boolean:true get=function set=function
key=1 nonexistent
key=2 value=string:val5 writable=boolean:true enumerable=boolean:true configurable=boolean:true get=undefined:undefined set=undefined:undefined
key=3 nonexistent
key=4 nonexistent
key=5 nonexistent
key=6 value=string:val1 writable=boolean:true enumerable=boolean:true configurable=boolean:true get=undefined:undefined set=undefined:undefined
key=7 value=undefined:undefined writable=undefined:undefined enumerable=boolean:true configurable=boolean:true get=function set=function
key=0 value=string:foo writable=boolean:true enumerable=boolean:false configurable=boolean:true get=undefined:undefined set=undefined:undefined
key=1 nonexistent
key=0 value=string:foo writable=boolean:true enumerable=boolean:false configurable=boolean:true get=undefined:undefined set=undefined:undefined
key=1 nonexistent
key: 0, desc: get=function,set=function,enumerable=false,configurable=true
key: 1, desc: none
key: 2, desc: value="val5",writable=true,enumerable=true,configurable=true
key: 3, desc: none
key: 4, desc: none
key: 5, desc: none
key: 6, desc: value="val1",writable=true,enumerable=true,configurable=true
key: 7, desc: get=function,set=function,enumerable=true,configurable=true
key: 0, desc: value="foo",writable=true,enumerable=false,configurable=true
key: 1, desc: none
key: 0, desc: value="foo",writable=true,enumerable=false,configurable=true
key: 1, desc: none
===*/

print('attributes');
Expand Down Expand Up @@ -245,7 +224,7 @@ function attributesTest() {
t.hasOwnProperty(6), t.hasOwnProperty(7));

for (i = 0; i < 8; i++) {
printDescriptor(t, String(i));
printKeyPropDesc(t, String(i));
}

t.reverse();
Expand All @@ -256,7 +235,7 @@ function attributesTest() {
t.hasOwnProperty(6), t.hasOwnProperty(7));

for (i = 0; i < 8; i++) {
printDescriptor(t, String(i));
printKeyPropDesc(t, String(i));
}

// Here, '0' is not enumerable, but when it gets swapped to position 1,
Expand All @@ -269,11 +248,11 @@ function attributesTest() {
'0': { value: 'foo', writable: true, enumerable: false, configurable: true },
'length': { value: 2 }
});
printDescriptor(t, '0');
printDescriptor(t, '1');
printKeyPropDesc(t, '0');
printKeyPropDesc(t, '1');
[].reverse();
printDescriptor(t, '0');
printDescriptor(t, '1');
printKeyPropDesc(t, '0');
printKeyPropDesc(t, '1');
}

try {
Expand Down
40 changes: 40 additions & 0 deletions tests/ecmascript/test-bi-proxy-defineproperty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Proxy (ES2015) 'defineProperty'.
*/

/*@include util-object.js@*/

/*===
value=123,writable=false,enumerable=false,configurable=true
get=function,set=function,enumerable=false,configurable=true
===*/

function passThroughTest() {
var T = {};
var P = new Proxy(T, {});
var pd;

Object.defineProperty(P, 'foo', {
value: 123,
writable: false,
enumerable: false,
configurable: true
});
Object.defineProperty(P, 'bar', {
get: function getter() {},
set: function setter() {},
enumerable: false,
configurable: true
});

pd = Object.getOwnPropertyDescriptor(T, 'foo');
printPropDesc(pd);
pd = Object.getOwnPropertyDescriptor(T, 'bar');
printPropDesc(pd);
}

try {
passThroughTest();
} catch (e) {
print(e.stack || e);
}
Loading