|
|
|
/*
|
|
|
|
* Object built-ins
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "duk_internal.h"
|
|
|
|
|
|
|
|
/* Needed even when Object built-in disabled. */
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_to_string(duk_hthread *thr) {
|
|
|
|
duk_tval *tv;
|
|
|
|
|
|
|
|
tv = DUK_HTHREAD_THIS_PTR(thr);
|
|
|
|
duk_push_class_string_tval(thr, tv, 0 /*avoid_side_effects*/);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN)
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_constructor(duk_hthread *thr) {
|
|
|
|
duk_uint_t arg_mask;
|
|
|
|
|
|
|
|
arg_mask = duk_get_type_mask(thr, 0);
|
|
|
|
|
|
|
|
if (!duk_is_constructor_call(thr) && /* not a constructor call */
|
|
|
|
((arg_mask & (DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_UNDEFINED)) == 0)) { /* and argument not null or undefined */
|
|
|
|
duk_to_object(thr, 0);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Pointer and buffer primitive values are treated like other
|
|
|
|
* primitives values which have a fully fledged object counterpart:
|
|
|
|
* promote to an object value. Lightfuncs and plain buffers are
|
|
|
|
* coerced with ToObject() even they could also be returned as is.
|
|
|
|
*/
|
|
|
|
if (arg_mask & (DUK_TYPE_MASK_OBJECT |
|
|
|
|
DUK_TYPE_MASK_STRING |
|
|
|
|
DUK_TYPE_MASK_BOOLEAN |
|
|
|
|
DUK_TYPE_MASK_NUMBER |
|
|
|
|
DUK_TYPE_MASK_POINTER |
|
|
|
|
DUK_TYPE_MASK_BUFFER |
|
|
|
|
DUK_TYPE_MASK_LIGHTFUNC)) {
|
|
|
|
/* For DUK_TYPE_OBJECT the coercion is a no-op and could
|
|
|
|
* be checked for explicitly, but Object(obj) calls are
|
|
|
|
* not very common so opt for minimal footprint.
|
|
|
|
*/
|
|
|
|
duk_to_object(thr, 0);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
(void) duk_push_object_helper(thr,
|
|
|
|
DUK_HOBJECT_FLAG_EXTENSIBLE |
|
|
|
|
DUK_HOBJECT_FLAG_FASTREFS |
|
|
|
|
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
|
|
|
|
DUK_BIDX_OBJECT_PROTOTYPE);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_OBJECT_BUILTIN */
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN) && defined(DUK_USE_ES6)
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_assign(duk_hthread *thr) {
|
|
|
|
duk_idx_t nargs;
|
|
|
|
duk_int_t idx;
|
|
|
|
|
|
|
|
nargs = duk_get_top_require_min(thr, 1 /*min_top*/);
|
|
|
|
|
|
|
|
duk_to_object(thr, 0);
|
|
|
|
for (idx = 1; idx < nargs; idx++) {
|
|
|
|
/* E7 19.1.2.1 (step 4a) */
|
|
|
|
if (duk_is_null_or_undefined(thr, idx)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* duk_enum() respects ES2015+ [[OwnPropertyKeys]] ordering, which is
|
|
|
|
* convenient here.
|
|
|
|
*/
|
|
|
|
duk_to_object(thr, idx);
|
|
|
|
duk_enum(thr, idx, DUK_ENUM_OWN_PROPERTIES_ONLY);
|
|
|
|
while (duk_next(thr, -1, 1 /*get_value*/)) {
|
|
|
|
/* [ target ... enum key value ] */
|
|
|
|
duk_put_prop(thr, 0);
|
|
|
|
/* [ target ... enum ] */
|
|
|
|
}
|
|
|
|
/* Could pop enumerator, but unnecessary because of duk_set_top()
|
|
|
|
* below.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
duk_set_top(thr, 1);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN) && defined(DUK_USE_ES6)
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is(duk_hthread *thr) {
|
|
|
|
DUK_ASSERT_TOP(thr, 2);
|
|
|
|
duk_push_boolean(thr, duk_samevalue(thr, 0, 1));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN)
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_create(duk_hthread *thr) {
|
|
|
|
duk_hobject *proto;
|
|
|
|
|
|
|
|
DUK_ASSERT_TOP(thr, 2);
|
|
|
|
|
|
|
|
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
|
|
|
|
duk_hbufobj_promote_plain(thr, 0);
|
|
|
|
#endif
|
|
|
|
proto = duk_require_hobject_accept_mask(thr, 0, DUK_TYPE_MASK_NULL);
|
|
|
|
DUK_ASSERT(proto != NULL || duk_is_null(thr, 0));
|
|
|
|
|
|
|
|
(void) duk_push_object_helper_proto(thr,
|
|
|
|
DUK_HOBJECT_FLAG_EXTENSIBLE |
|
|
|
|
DUK_HOBJECT_FLAG_FASTREFS |
|
|
|
|
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
|
|
|
|
proto);
|
|
|
|
|
|
|
|
if (!duk_is_undefined(thr, 1)) {
|
|
|
|
/* [ O Properties obj ] */
|
|
|
|
|
|
|
|
duk_replace(thr, 0);
|
|
|
|
|
|
|
|
/* [ obj Properties ] */
|
|
|
|
|
|
|
|
/* Just call the "original" Object.defineProperties() to
|
|
|
|
* finish up.
|
|
|
|
*/
|
|
|
|
|
|
|
|
return duk_bi_object_constructor_define_properties(thr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* [ O Properties obj ] */
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_OBJECT_BUILTIN */
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN)
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_properties(duk_hthread *thr) {
|
|
|
|
duk_small_uint_t pass;
|
|
|
|
duk_uint_t defprop_flags;
|
|
|
|
duk_hobject *obj;
|
|
|
|
duk_idx_t idx_value;
|
|
|
|
duk_hobject *get;
|
|
|
|
duk_hobject *set;
|
|
|
|
|
|
|
|
/* Lightfunc and plain buffer handling by ToObject() coercion. */
|
|
|
|
obj = duk_require_hobject_promote_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
|
|
|
|
DUK_ASSERT(obj != NULL);
|
|
|
|
|
|
|
|
duk_to_object(thr, 1); /* properties object */
|
|
|
|
|
|
|
|
DUK_DDD(DUK_DDDPRINT("target=%!iT, properties=%!iT",
|
|
|
|
(duk_tval *) duk_get_tval(thr, 0),
|
|
|
|
(duk_tval *) duk_get_tval(thr, 1)));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Two pass approach to processing the property descriptors.
|
|
|
|
* On first pass validate and normalize all descriptors before
|
|
|
|
* any changes are made to the target object. On second pass
|
|
|
|
* make the actual modifications to the target object.
|
|
|
|
*
|
|
|
|
* Right now we'll just use the same normalize/validate helper
|
|
|
|
* on both passes, ignoring its outputs on the first pass.
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (pass = 0; pass < 2; pass++) {
|
|
|
|
duk_set_top(thr, 2); /* -> [ hobject props ] */
|
|
|
|
duk_enum(thr, 1, DUK_ENUM_OWN_PROPERTIES_ONLY | DUK_ENUM_INCLUDE_SYMBOLS /*enum_flags*/);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
duk_hstring *key;
|
|
|
|
|
|
|
|
/* [ hobject props enum(props) ] */
|
|
|
|
|
|
|
|
duk_set_top(thr, 3);
|
|
|
|
|
|
|
|
if (!duk_next(thr, 2, 1 /*get_value*/)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
DUK_DDD(DUK_DDDPRINT("-> key=%!iT, desc=%!iT",
|
|
|
|
(duk_tval *) duk_get_tval(thr, -2),
|
|
|
|
(duk_tval *) duk_get_tval(thr, -1)));
|
|
|
|
|
|
|
|
/* [ hobject props enum(props) key desc ] */
|
|
|
|
|
|
|
|
duk_hobject_prepare_property_descriptor(thr,
|
|
|
|
4 /*idx_desc*/,
|
|
|
|
&defprop_flags,
|
|
|
|
&idx_value,
|
|
|
|
&get,
|
|
|
|
&set);
|
|
|
|
|
|
|
|
/* [ hobject props enum(props) key desc [multiple values] ] */
|
|
|
|
|
|
|
|
if (pass == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This allows symbols on purpose. */
|
|
|
|
key = duk_known_hstring(thr, 3);
|
|
|
|
DUK_ASSERT(key != NULL);
|
|
|
|
|
|
|
|
duk_hobject_define_property_helper(thr,
|
|
|
|
defprop_flags,
|
|
|
|
obj,
|
|
|
|
key,
|
|
|
|
idx_value,
|
|
|
|
get,
|
|
|
|
set,
|
|
|
|
1 /*throw_flag*/);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return target object
|
|
|
|
*/
|
|
|
|
|
|
|
|
duk_dup_0(thr);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_OBJECT_BUILTIN */
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN)
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_seal_freeze_shared(duk_hthread *thr) {
|
|
|
|
DUK_ASSERT_TOP(thr, 1);
|
|
|
|
|
|
|
|
duk_seal_freeze_raw(thr, 0, (duk_bool_t) duk_get_current_magic(thr) /*is_freeze*/);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_OBJECT_BUILTIN */
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN)
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is_sealed_frozen_shared(duk_hthread *thr) {
|
|
|
|
duk_hobject *h;
|
|
|
|
duk_bool_t is_frozen;
|
|
|
|
duk_uint_t mask;
|
|
|
|
|
|
|
|
is_frozen = (duk_bool_t) duk_get_current_magic(thr);
|
|
|
|
mask = duk_get_type_mask(thr, 0);
|
|
|
|
if (mask & (DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER)) {
|
|
|
|
DUK_ASSERT(is_frozen == 0 || is_frozen == 1);
|
|
|
|
duk_push_boolean(thr, (mask & DUK_TYPE_MASK_LIGHTFUNC) ?
|
|
|
|
1 : /* lightfunc always frozen and sealed */
|
|
|
|
(is_frozen ^ 1)); /* buffer sealed but not frozen (index props writable) */
|
First round of lightfunc changes
A lot of changes to add preliminary lightfunc support:
* Add LIGHTFUNC tagged type to duk_tval.h and API.
* Internal changes for preliminary to support lightfuncs in call handling
and other operations (FIXMEs left in obvious places where support is
still missing after this commit)
* Preliminary Ecmascript and API testcases for lightfuncs
Detailed notes:
* Because magic is signed, reading it back involves sign extension which is
quite verbose to do in C. Use macros for reading the magic value and other
bit fields encoded in the flags.
* Function.prototype.bind(): the 'length' property of a bound function now
comes out wrong. We could simply look up the virtual 'length' property
even if h_target is NULL: no extra code and binding is relatively rare in
hot paths. Rewrite more cleanly in any case.
* The use flag DUK_USE_LIGHTFUNC_BUILTINS controls the forced lightfunc
conversion of built-ins. This results in non-compliant built-ins but
significant memory savings in very memory poor environments.
* Reject eval(), Thread.yield/resume as lightfuncs. These functions have
current assertions that they must be called as fully fledged functions.
* Lightfuncs are serialized like ordinary functions for JSON, JX, and JC
by this diff.
* Add 'magic' to activation for lightfuncs. It will be needed for lightweight
functions: we don't have the duk_tval related to the lightfunc, so we must
copy the magic value to the activation when a call is made.
* When lightfuncs are used as property lookup base values, continue property
lookup from the Function.prototype object. This is necessary to allow e.g.
``func.call()`` and ``func.apply()`` to be used.
* Call handling had to be reworked for lightfuncs, especially how bound
function chains are handled. This is a relatively large change but is
necessary to support lightweight functions properly in bound function
resolution.
The current solution is not ideal. The bytecode executor will first try an
ecma-to-ecma call setup which resolves the bound function chain first. If
the final, unbound function is not viable (a native function) the call setup
returns with an error code. The caller will then perform a normal call.
Although bound function resolution has already been done, the normal call
handling code will re-do it (and detect there is nothing to do).
This situation could be avoided by decoupling bound function handling and
effective this binding computation from the actual call setup. The caller
could then to do this prestep first, and only then decide whether to use an
ecma-to-ecma call or an ordinary heavyweight call.
Remove duk__find_nonbound_function as unused.
* Use indirect magic to allow LIGHTFUNCs for Date. Most of the built-in
functions not directly eligible as lightfuncs are the Date built-in methods,
whose magic values contain too much information to fit into the 8-bit magic
of a LIGHTFUNC value.
To work around this, add an array (duk__date_magics[]) containing the
actual control flags needed by the built-ins, and make the Date built-in
magic value an index into this table. With this change Date built-ins are
successfully converted to lightfuncs.
Testcase fixes:
- Whitespace fixes
- Print error for indirect eval error to make diagnosis easier
- Fix error string to match errmsg updated in this branch
11 years ago
|
|
|
} else {
|
|
|
|
/* ES2015 Sections 19.1.2.12, 19.1.2.13: anything other than an object
|
|
|
|
* is considered to be already sealed and frozen.
|
|
|
|
*/
|
|
|
|
h = duk_get_hobject(thr, 0);
|
|
|
|
duk_push_boolean(thr, (h == NULL) ||
|
|
|
|
duk_hobject_object_is_sealed_frozen_helper(thr, h, is_frozen /*is_frozen*/));
|
First round of lightfunc changes
A lot of changes to add preliminary lightfunc support:
* Add LIGHTFUNC tagged type to duk_tval.h and API.
* Internal changes for preliminary to support lightfuncs in call handling
and other operations (FIXMEs left in obvious places where support is
still missing after this commit)
* Preliminary Ecmascript and API testcases for lightfuncs
Detailed notes:
* Because magic is signed, reading it back involves sign extension which is
quite verbose to do in C. Use macros for reading the magic value and other
bit fields encoded in the flags.
* Function.prototype.bind(): the 'length' property of a bound function now
comes out wrong. We could simply look up the virtual 'length' property
even if h_target is NULL: no extra code and binding is relatively rare in
hot paths. Rewrite more cleanly in any case.
* The use flag DUK_USE_LIGHTFUNC_BUILTINS controls the forced lightfunc
conversion of built-ins. This results in non-compliant built-ins but
significant memory savings in very memory poor environments.
* Reject eval(), Thread.yield/resume as lightfuncs. These functions have
current assertions that they must be called as fully fledged functions.
* Lightfuncs are serialized like ordinary functions for JSON, JX, and JC
by this diff.
* Add 'magic' to activation for lightfuncs. It will be needed for lightweight
functions: we don't have the duk_tval related to the lightfunc, so we must
copy the magic value to the activation when a call is made.
* When lightfuncs are used as property lookup base values, continue property
lookup from the Function.prototype object. This is necessary to allow e.g.
``func.call()`` and ``func.apply()`` to be used.
* Call handling had to be reworked for lightfuncs, especially how bound
function chains are handled. This is a relatively large change but is
necessary to support lightweight functions properly in bound function
resolution.
The current solution is not ideal. The bytecode executor will first try an
ecma-to-ecma call setup which resolves the bound function chain first. If
the final, unbound function is not viable (a native function) the call setup
returns with an error code. The caller will then perform a normal call.
Although bound function resolution has already been done, the normal call
handling code will re-do it (and detect there is nothing to do).
This situation could be avoided by decoupling bound function handling and
effective this binding computation from the actual call setup. The caller
could then to do this prestep first, and only then decide whether to use an
ecma-to-ecma call or an ordinary heavyweight call.
Remove duk__find_nonbound_function as unused.
* Use indirect magic to allow LIGHTFUNCs for Date. Most of the built-in
functions not directly eligible as lightfuncs are the Date built-in methods,
whose magic values contain too much information to fit into the 8-bit magic
of a LIGHTFUNC value.
To work around this, add an array (duk__date_magics[]) containing the
actual control flags needed by the built-ins, and make the Date built-in
magic value an index into this table. With this change Date built-ins are
successfully converted to lightfuncs.
Testcase fixes:
- Whitespace fixes
- Print error for indirect eval error to make diagnosis easier
- Fix error string to match errmsg updated in this branch
11 years ago
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_OBJECT_BUILTIN */
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN)
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_to_locale_string(duk_hthread *thr) {
|
|
|
|
DUK_ASSERT_TOP(thr, 0);
|
|
|
|
(void) duk_push_this_coercible_to_object(thr);
|
|
|
|
duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_TO_STRING);
|
|
|
|
#if 0 /* This is mentioned explicitly in the E5.1 spec, but duk_call_method() checks for it in practice. */
|
|
|
|
duk_require_callable(thr, 1);
|
|
|
|
#endif
|
|
|
|
duk_dup_0(thr); /* -> [ O toString O ] */
|
|
|
|
duk_call_method(thr, 0); /* XXX: call method tail call? */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_OBJECT_BUILTIN */
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN)
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_value_of(duk_hthread *thr) {
|
|
|
|
/* For lightfuncs and plain buffers, returns Object() coerced. */
|
|
|
|
(void) duk_push_this_coercible_to_object(thr);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_OBJECT_BUILTIN */
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN)
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_is_prototype_of(duk_hthread *thr) {
|
|
|
|
duk_hobject *h_v;
|
|
|
|
duk_hobject *h_obj;
|
|
|
|
|
|
|
|
DUK_ASSERT_TOP(thr, 1);
|
|
|
|
|
|
|
|
h_v = duk_get_hobject(thr, 0);
|
|
|
|
if (!h_v) {
|
|
|
|
duk_push_false(thr); /* XXX: tail call: return duk_push_false(thr) */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
h_obj = duk_push_this_coercible_to_object(thr);
|
|
|
|
DUK_ASSERT(h_obj != NULL);
|
|
|
|
|
|
|
|
/* E5.1 Section 15.2.4.6, step 3.a, lookup proto once before compare.
|
|
|
|
* Prototype loops should cause an error to be thrown.
|
|
|
|
*/
|
|
|
|
duk_push_boolean(thr, duk_hobject_prototype_chain_contains(thr, DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_v), h_obj, 0 /*ignore_loop*/));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_OBJECT_BUILTIN */
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN)
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_has_own_property(duk_hthread *thr) {
|
|
|
|
return (duk_ret_t) duk_hobject_object_ownprop_helper(thr, 0 /*required_desc_flags*/);
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_OBJECT_BUILTIN */
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN)
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_property_is_enumerable(duk_hthread *thr) {
|
|
|
|
return (duk_ret_t) duk_hobject_object_ownprop_helper(thr, DUK_PROPDESC_FLAG_ENUMERABLE /*required_desc_flags*/);
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_OBJECT_BUILTIN */
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
|
|
|
|
/* Shared helper to implement Object.getPrototypeOf,
|
|
|
|
* Object.prototype.__proto__ getter, and Reflect.getPrototypeOf.
|
|
|
|
*
|
|
|
|
* http://www.ecma-international.org/ecma-262/6.0/index.html#sec-get-object.prototype.__proto__
|
|
|
|
*/
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_getprototype_shared(duk_hthread *thr) {
|
|
|
|
/*
|
|
|
|
* magic = 0: __proto__ getter
|
|
|
|
* magic = 1: Object.getPrototypeOf()
|
|
|
|
* magic = 2: Reflect.getPrototypeOf()
|
|
|
|
*/
|
|
|
|
|
|
|
|
duk_hobject *h;
|
|
|
|
duk_hobject *proto;
|
|
|
|
duk_tval *tv;
|
|
|
|
duk_int_t magic;
|
|
|
|
|
|
|
|
magic = duk_get_current_magic(thr);
|
|
|
|
|
|
|
|
if (magic == 0) {
|
|
|
|
DUK_ASSERT_TOP(thr, 0);
|
|
|
|
duk_push_this_coercible_to_object(thr);
|
|
|
|
}
|
|
|
|
DUK_ASSERT(duk_get_top(thr) >= 1);
|
|
|
|
if (magic < 2) {
|
|
|
|
/* ES2015 Section 19.1.2.9, step 1 */
|
|
|
|
duk_to_object(thr, 0);
|
|
|
|
}
|
|
|
|
tv = DUK_GET_TVAL_POSIDX(thr, 0);
|
|
|
|
|
|
|
|
switch (DUK_TVAL_GET_TAG(tv)) {
|
|
|
|
case DUK_TAG_BUFFER:
|
|
|
|
proto = thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE];
|
|
|
|
break;
|
|
|
|
case DUK_TAG_LIGHTFUNC:
|
|
|
|
proto = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE];
|
|
|
|
break;
|
|
|
|
case DUK_TAG_OBJECT:
|
|
|
|
h = DUK_TVAL_GET_OBJECT(tv);
|
|
|
|
proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* This implicitly handles CheckObjectCoercible() caused
|
|
|
|
* TypeError.
|
|
|
|
*/
|
|
|
|
DUK_DCERROR_TYPE_INVALID_ARGS(thr);
|
|
|
|
}
|
|
|
|
if (proto != NULL) {
|
|
|
|
duk_push_hobject(thr, proto);
|
|
|
|
} else {
|
|
|
|
duk_push_null(thr);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
|
|
|
|
/* Shared helper to implement ES2015 Object.setPrototypeOf,
|
|
|
|
* Object.prototype.__proto__ setter, and Reflect.setPrototypeOf.
|
|
|
|
*
|
|
|
|
* http://www.ecma-international.org/ecma-262/6.0/index.html#sec-get-object.prototype.__proto__
|
|
|
|
* http://www.ecma-international.org/ecma-262/6.0/index.html#sec-object.setprototypeof
|
|
|
|
*/
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_setprototype_shared(duk_hthread *thr) {
|
|
|
|
/*
|
|
|
|
* magic = 0: __proto__ setter
|
|
|
|
* magic = 1: Object.setPrototypeOf()
|
|
|
|
* magic = 2: Reflect.setPrototypeOf()
|
|
|
|
*/
|
|
|
|
|
|
|
|
duk_hobject *h_obj;
|
|
|
|
duk_hobject *h_new_proto;
|
|
|
|
duk_hobject *h_curr;
|
|
|
|
duk_ret_t ret_success = 1; /* retval for success path */
|
|
|
|
duk_uint_t mask;
|
|
|
|
duk_int_t magic;
|
|
|
|
|
|
|
|
/* Preliminaries for __proto__ and setPrototypeOf (E6 19.1.2.18 steps 1-4). */
|
|
|
|
magic = duk_get_current_magic(thr);
|
|
|
|
if (magic == 0) {
|
|
|
|
duk_push_this_check_object_coercible(thr);
|
|
|
|
duk_insert(thr, 0);
|
|
|
|
if (!duk_check_type_mask(thr, 1, DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_OBJECT)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* __proto__ setter returns 'undefined' on success unlike the
|
|
|
|
* setPrototypeOf() call which returns the target object.
|
|
|
|
*/
|
|
|
|
ret_success = 0;
|
|
|
|
} else {
|
|
|
|
if (magic == 1) {
|
|
|
|
duk_require_object_coercible(thr, 0);
|
|
|
|
} else {
|
|
|
|
duk_require_hobject_accept_mask(thr, 0,
|
|
|
|
DUK_TYPE_MASK_LIGHTFUNC |
|
|
|
|
DUK_TYPE_MASK_BUFFER);
|
|
|
|
}
|
|
|
|
duk_require_type_mask(thr, 1, DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_OBJECT);
|
|
|
|
}
|
|
|
|
|
|
|
|
h_new_proto = duk_get_hobject(thr, 1);
|
|
|
|
/* h_new_proto may be NULL */
|
|
|
|
|
|
|
|
mask = duk_get_type_mask(thr, 0);
|
|
|
|
if (mask & (DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER)) {
|
|
|
|
duk_hobject *curr_proto;
|
|
|
|
curr_proto = thr->builtins[(mask & DUK_TYPE_MASK_LIGHTFUNC) ?
|
|
|
|
DUK_BIDX_FUNCTION_PROTOTYPE :
|
|
|
|
DUK_BIDX_UINT8ARRAY_PROTOTYPE];
|
|
|
|
if (h_new_proto == curr_proto) {
|
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
goto fail_nonextensible;
|
|
|
|
}
|
|
|
|
h_obj = duk_get_hobject(thr, 0);
|
|
|
|
if (h_obj == NULL) {
|
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
DUK_ASSERT(h_obj != NULL);
|
|
|
|
|
|
|
|
/* [[SetPrototypeOf]] standard behavior, E6 9.1.2. */
|
|
|
|
/* TODO: implement Proxy object support here */
|
|
|
|
|
|
|
|
if (h_new_proto == DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_obj)) {
|
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
if (!DUK_HOBJECT_HAS_EXTENSIBLE(h_obj)) {
|
|
|
|
goto fail_nonextensible;
|
|
|
|
}
|
|
|
|
for (h_curr = h_new_proto; h_curr != NULL; h_curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_curr)) {
|
|
|
|
/* Loop prevention. */
|
|
|
|
if (h_curr == h_obj) {
|
|
|
|
goto fail_loop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h_obj, h_new_proto);
|
|
|
|
/* fall thru */
|
|
|
|
|
|
|
|
skip:
|
|
|
|
duk_set_top(thr, 1);
|
|
|
|
if (magic == 2) {
|
|
|
|
duk_push_true(thr);
|
|
|
|
}
|
|
|
|
return ret_success;
|
|
|
|
|
|
|
|
fail_nonextensible:
|
|
|
|
fail_loop:
|
|
|
|
if (magic != 2) {
|
|
|
|
DUK_DCERROR_TYPE_INVALID_ARGS(thr);
|
|
|
|
} else {
|
|
|
|
duk_push_false(thr);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_property(duk_hthread *thr) {
|
|
|
|
/*
|
|
|
|
* magic = 0: Object.defineProperty()
|
|
|
|
* magic = 1: Reflect.defineProperty()
|
|
|
|
*/
|
|
|
|
|
|
|
|
duk_hobject *obj;
|
|
|
|
duk_hstring *key;
|
|
|
|
duk_hobject *get;
|
|
|
|
duk_hobject *set;
|
|
|
|
duk_idx_t idx_value;
|
|
|
|
duk_uint_t defprop_flags;
|
|
|
|
duk_small_uint_t magic;
|
|
|
|
duk_bool_t throw_flag;
|
|
|
|
duk_bool_t ret;
|
|
|
|
|
|
|
|
DUK_ASSERT(thr != NULL);
|
|
|
|
|
|
|
|
DUK_DDD(DUK_DDDPRINT("Object.defineProperty(): ctx=%p obj=%!T key=%!T desc=%!T",
|
|
|
|
(void *) thr,
|
|
|
|
(duk_tval *) duk_get_tval(thr, 0),
|
|
|
|
(duk_tval *) duk_get_tval(thr, 1),
|
|
|
|
(duk_tval *) duk_get_tval(thr, 2)));
|
|
|
|
|
|
|
|
/* [ obj key desc ] */
|
|
|
|
|
|
|
|
magic = (duk_small_uint_t) duk_get_current_magic(thr);
|
|
|
|
|
|
|
|
/* Lightfuncs are currently supported by coercing to a temporary
|
|
|
|
* Function object; changes will be allowed (the coerced value is
|
|
|
|
* extensible) but will be lost. Same for plain buffers.
|
|
|
|
*/
|
|
|
|
obj = duk_require_hobject_promote_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
|
|
|
|
DUK_ASSERT(obj != NULL);
|
|
|
|
key = duk_to_property_key_hstring(thr, 1);
|
|
|
|
(void) duk_require_hobject(thr, 2);
|
|
|
|
|
|
|
|
DUK_ASSERT(obj != NULL);
|
|
|
|
DUK_ASSERT(key != NULL);
|
|
|
|
DUK_ASSERT(duk_get_hobject(thr, 2) != NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Validate and convert argument property descriptor (an ECMAScript
|
|
|
|
* object) into a set of defprop_flags and possibly property value,
|
|
|
|
* getter, and/or setter values on the value stack.
|
|
|
|
*
|
|
|
|
* Lightfunc set/get values are coerced to full Functions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
duk_hobject_prepare_property_descriptor(thr,
|
|
|
|
2 /*idx_desc*/,
|
|
|
|
&defprop_flags,
|
|
|
|
&idx_value,
|
|
|
|
&get,
|
|
|
|
&set);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Use Object.defineProperty() helper for the actual operation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
DUK_ASSERT(magic == 0U || magic == 1U);
|
|
|
|
throw_flag = magic ^ 1U;
|
|
|
|
ret = duk_hobject_define_property_helper(thr,
|
|
|
|
defprop_flags,
|
|
|
|
obj,
|
|
|
|
key,
|
|
|
|
idx_value,
|
|
|
|
get,
|
|
|
|
set,
|
|
|
|
throw_flag);
|
|
|
|
|
|
|
|
/* Ignore the normalize/validate helper outputs on the value stack,
|
|
|
|
* they're popped automatically.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (magic == 0U) {
|
|
|
|
/* Object.defineProperty(): return target object. */
|
|
|
|
duk_push_hobject(thr, obj);
|
|
|
|
} else {
|
|
|
|
/* Reflect.defineProperty(): return success/fail. */
|
|
|
|
duk_push_boolean(thr, ret);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_get_own_property_descriptor(duk_hthread *thr) {
|
|
|
|
DUK_ASSERT_TOP(thr, 2);
|
|
|
|
|
|
|
|
/* ES2015 Section 19.1.2.6, step 1 */
|
|
|
|
if (duk_get_current_magic(thr) == 0) {
|
|
|
|
duk_to_object(thr, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* [ obj key ] */
|
|
|
|
|
|
|
|
duk_hobject_object_get_own_property_descriptor(thr, -2);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is_extensible(duk_hthread *thr) {
|
|
|
|
/*
|
|
|
|
* magic = 0: Object.isExtensible()
|
|
|
|
* magic = 1: Reflect.isExtensible()
|
|
|
|
*/
|
|
|
|
|
|
|
|
duk_hobject *h;
|
|
|
|
|
|
|
|
if (duk_get_current_magic(thr) == 0) {
|
|
|
|
h = duk_get_hobject(thr, 0);
|
|
|
|
} else {
|
|
|
|
/* Reflect.isExtensible(): throw if non-object, but we accept lightfuncs
|
|
|
|
* and plain buffers here because they pretend to be objects.
|
|
|
|
*/
|
|
|
|
h = duk_require_hobject_accept_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
|
|
|
|
}
|
|
|
|
|
|
|
|
duk_push_boolean(thr, (h != NULL) && DUK_HOBJECT_HAS_EXTENSIBLE(h));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
|
|
|
|
/* Shared helper for various key/symbol listings, magic:
|
|
|
|
* 0=Object.keys()
|
|
|
|
* 1=Object.getOwnPropertyNames(),
|
|
|
|
* 2=Object.getOwnPropertySymbols(),
|
|
|
|
* 3=Reflect.ownKeys()
|
|
|
|
*/
|
|
|
|
DUK_LOCAL const duk_small_uint_t duk__object_keys_enum_flags[4] = {
|
|
|
|
/* Object.keys() */
|
|
|
|
DUK_ENUM_OWN_PROPERTIES_ONLY |
|
|
|
|
DUK_ENUM_NO_PROXY_BEHAVIOR,
|
|
|
|
|
|
|
|
/* Object.getOwnPropertyNames() */
|
|
|
|
DUK_ENUM_INCLUDE_NONENUMERABLE |
|
|
|
|
DUK_ENUM_OWN_PROPERTIES_ONLY |
|
|
|
|
DUK_ENUM_NO_PROXY_BEHAVIOR,
|
|
|
|
|
|
|
|
/* Object.getOwnPropertySymbols() */
|
|
|
|
DUK_ENUM_INCLUDE_SYMBOLS |
|
|
|
|
DUK_ENUM_OWN_PROPERTIES_ONLY |
|
|
|
|
DUK_ENUM_EXCLUDE_STRINGS |
|
|
|
|
DUK_ENUM_INCLUDE_NONENUMERABLE |
|
|
|
|
DUK_ENUM_NO_PROXY_BEHAVIOR,
|
|
|
|
|
|
|
|
/* Reflect.ownKeys() */
|
|
|
|
DUK_ENUM_INCLUDE_SYMBOLS |
|
|
|
|
DUK_ENUM_OWN_PROPERTIES_ONLY |
|
|
|
|
DUK_ENUM_INCLUDE_NONENUMERABLE |
|
|
|
|
DUK_ENUM_NO_PROXY_BEHAVIOR
|
|
|
|
};
|
|
|
|
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_keys_shared(duk_hthread *thr) {
|
|
|
|
duk_hobject *obj;
|
|
|
|
#if defined(DUK_USE_ES6_PROXY)
|
|
|
|
duk_hobject *h_proxy_target;
|
|
|
|
duk_hobject *h_proxy_handler;
|
|
|
|
duk_hobject *h_trap_result;
|
|
|
|
#endif
|
|
|
|
duk_small_uint_t enum_flags;
|
|
|
|
duk_int_t magic;
|
|
|
|
|
|
|
|
DUK_ASSERT_TOP(thr, 1);
|
|
|
|
|
|
|
|
magic = duk_get_current_magic(thr);
|
|
|
|
if (magic == 3) {
|
|
|
|
/* ES2015 Section 26.1.11 requires a TypeError for non-objects. Lightfuncs
|
|
|
|
* and plain buffers pretend to be objects, so accept those too.
|
|
|
|
*/
|
|
|
|
obj = duk_require_hobject_promote_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
|
|
|
|
} else {
|
|
|
|
/* ES2015: ToObject coerce. */
|
|
|
|
obj = duk_to_hobject(thr, 0);
|
|
|
|
}
|
|
|
|
DUK_ASSERT(obj != NULL);
|
|
|
|
DUK_UNREF(obj);
|
|
|
|
|
|
|
|
/* XXX: proxy chains */
|
|
|
|
|
|
|
|
#if defined(DUK_USE_ES6_PROXY)
|
|
|
|
/* XXX: better sharing of code between proxy target call sites */
|
|
|
|
if (DUK_LIKELY(!duk_hobject_proxy_check(obj,
|
|
|
|
&h_proxy_target,
|
|
|
|
&h_proxy_handler))) {
|
|
|
|
goto skip_proxy;
|
|
|
|
}
|
|
|
|
|
|
|
|
duk_push_hobject(thr, h_proxy_handler);
|
|
|
|
if (!duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_OWN_KEYS)) {
|
|
|
|
/* Careful with reachability here: don't pop 'obj' before pushing
|
|
|
|
* proxy target.
|
|
|
|
*/
|
|
|
|
DUK_DDD(DUK_DDDPRINT("no ownKeys trap, get keys of target instead"));
|
|
|
|
duk_pop_2(thr);
|
|
|
|
duk_push_hobject(thr, h_proxy_target);
|
|
|
|
duk_replace(thr, 0);
|
|
|
|
DUK_ASSERT_TOP(thr, 1);
|
|
|
|
goto skip_proxy;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* [ obj handler trap ] */
|
|
|
|
duk_insert(thr, -2);
|
|
|
|
duk_push_hobject(thr, h_proxy_target); /* -> [ obj trap handler target ] */
|
|
|
|
duk_call_method(thr, 1 /*nargs*/); /* -> [ obj trap_result ] */
|
|
|
|
h_trap_result = duk_require_hobject(thr, -1);
|
|
|
|
DUK_UNREF(h_trap_result);
|
|
|
|
|
|
|
|
magic = duk_get_current_magic(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);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
skip_proxy:
|
|
|
|
#endif /* DUK_USE_ES6_PROXY */
|
|
|
|
|
|
|
|
DUK_ASSERT_TOP(thr, 1);
|
|
|
|
magic = duk_get_current_magic(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];
|
|
|
|
return duk_hobject_get_enumerated_keys(thr, enum_flags);
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
|
|
|
|
|
|
|
|
#if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_prevent_extensions(duk_hthread *thr) {
|
|
|
|
/*
|
|
|
|
* magic = 0: Object.preventExtensions()
|
|
|
|
* magic = 1: Reflect.preventExtensions()
|
|
|
|
*/
|
|
|
|
|
|
|
|
duk_hobject *h;
|
|
|
|
duk_uint_t mask;
|
|
|
|
duk_int_t magic;
|
|
|
|
|
|
|
|
magic = duk_get_current_magic(thr);
|
|
|
|
|
|
|
|
/* Silent success for lightfuncs and plain buffers always. */
|
|
|
|
mask = DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER;
|
|
|
|
|
|
|
|
/* Object.preventExtensions() silent success for non-object. */
|
|
|
|
if (magic == 0) {
|
|
|
|
mask |= DUK_TYPE_MASK_UNDEFINED |
|
|
|
|
DUK_TYPE_MASK_NULL |
|
|
|
|
DUK_TYPE_MASK_BOOLEAN |
|
|
|
|
DUK_TYPE_MASK_NUMBER |
|
|
|
|
DUK_TYPE_MASK_STRING |
|
|
|
|
DUK_TYPE_MASK_POINTER;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (duk_check_type_mask(thr, 0, mask)) {
|
|
|
|
/* Not an object, already non-extensible so always success. */
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
h = duk_require_hobject(thr, 0);
|
|
|
|
DUK_ASSERT(h != NULL);
|
|
|
|
|
|
|
|
DUK_HOBJECT_CLEAR_EXTENSIBLE(h);
|
|
|
|
|
|
|
|
/* A non-extensible object cannot gain any more properties,
|
|
|
|
* so this is a good time to compact.
|
|
|
|
*/
|
|
|
|
duk_hobject_compact_props(thr, h);
|
|
|
|
|
|
|
|
done:
|
|
|
|
if (magic == 1) {
|
|
|
|
duk_push_true(thr);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __defineGetter__, __defineSetter__, __lookupGetter__, __lookupSetter__
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if defined(DUK_USE_ES8)
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_defineaccessor(duk_hthread *thr) {
|
|
|
|
duk_push_this(thr);
|
|
|
|
duk_insert(thr, 0);
|
|
|
|
duk_to_object(thr, 0);
|
|
|
|
duk_require_callable(thr, 2);
|
|
|
|
|
|
|
|
/* [ ToObject(this) key getter/setter ] */
|
|
|
|
|
|
|
|
/* ToPropertyKey() coercion is not needed, duk_def_prop() does it. */
|
|
|
|
duk_def_prop(thr, 0, DUK_DEFPROP_SET_ENUMERABLE |
|
|
|
|
DUK_DEFPROP_SET_CONFIGURABLE |
|
|
|
|
(duk_get_current_magic(thr) ? DUK_DEFPROP_HAVE_SETTER : DUK_DEFPROP_HAVE_GETTER));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_lookupaccessor(duk_hthread *thr) {
|
|
|
|
duk_uint_t sanity;
|
|
|
|
|
|
|
|
duk_push_this(thr);
|
|
|
|
duk_to_object(thr, -1);
|
|
|
|
|
|
|
|
/* XXX: Prototype walk (with sanity) should be a core property
|
|
|
|
* operation, could add a flag to e.g. duk_get_prop_desc().
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* ToPropertyKey() coercion is not needed, duk_get_prop_desc() does it. */
|
|
|
|
sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
|
|
|
|
while (!duk_is_undefined(thr, -1)) {
|
|
|
|
/* [ key obj ] */
|
|
|
|
duk_dup(thr, 0);
|
|
|
|
duk_get_prop_desc(thr, 1, 0 /*flags*/);
|
|
|
|
if (!duk_is_undefined(thr, -1)) {
|
|
|
|
duk_get_prop_stridx(thr, -1, (duk_get_current_magic(thr) != 0 ? DUK_STRIDX_SET : DUK_STRIDX_GET));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
duk_pop(thr);
|
|
|
|
|
|
|
|
if (DUK_UNLIKELY(sanity-- == 0)) {
|
|
|
|
DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT);
|
|
|
|
DUK_WO_NORETURN(return 0;);
|
|
|
|
}
|
|
|
|
|
|
|
|
duk_get_prototype(thr, -1);
|
|
|
|
duk_remove(thr, -2);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif /* DUK_USE_ES8 */
|