DUK_INTERNAL duk_ret_t duk_bi_proxy_constructor(duk_context *ctx) { duk_hobject *h_target; duk_hobject *h_handler; if (!duk_is_constructor_call(ctx)) { return DUK_RET_TYPE_ERROR; } /* Reject a proxy object as the target because it would need * special handler in property lookups. (ES6 has no such restriction) */ h_target = duk_require_hobject_or_lfunc_coerce(ctx, 0); DUK_ASSERT(h_target != NULL); if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h_target)) { return DUK_RET_TYPE_ERROR; } /* Reject a proxy object as the handler because it would cause * potentially unbounded recursion. (ES6 has no such restriction) */ h_handler = duk_require_hobject_or_lfunc_coerce(ctx, 1); DUK_ASSERT(h_handler != NULL); if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h_handler)) { return DUK_RET_TYPE_ERROR; } /* XXX: the returned value is exotic in ES6, but we use a * simple object here with no prototype. Without a prototype, * [[DefaultValue]] coercion fails which is abit confusing. * No callable check/handling in the current Proxy subset. */ (void) duk_push_object_helper_proto(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT), NULL); DUK_ASSERT_TOP(ctx, 3); /* Make _Target and _Handler non-configurable and non-writable. * They can still be forcibly changed by C code (both user and * Duktape internal), but not by Ecmascript code. */ /* Proxy target */ duk_dup(ctx, 0); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE); /* Proxy handler */ duk_dup(ctx, 1); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_HANDLER, DUK_PROPDESC_FLAGS_NONE); return 1; /* replacement handler */ }
DUK_EXTERNAL void duk_enum(duk_context *ctx, duk_idx_t obj_index, duk_uint_t enum_flags) { DUK_ASSERT_CTX_VALID(ctx); duk_dup(ctx, obj_index); duk_require_hobject_or_lfunc_coerce(ctx, -1); duk_hobject_enumerator_create(ctx, enum_flags); /* [target] -> [enum] */ }
DUK_INTERNAL duk_ret_t duk_bi_error_prototype_to_string(duk_context *ctx) { /* XXX: optimize with more direct internal access */ duk_push_this(ctx); (void) duk_require_hobject_or_lfunc_coerce(ctx, -1); /* [ ... this ] */ duk_get_prop_stridx(ctx, -1, DUK_STRIDX_NAME); if (duk_is_undefined(ctx, -1)) { duk_pop(ctx); duk_push_string(ctx, "Error"); } else { duk_to_string(ctx, -1); } /* [ ... this name ] */ /* XXX: Are steps 6 and 7 in E5 Section 15.11.4.4 duplicated by * accident or are they actually needed? The first ToString() * could conceivably return 'undefined'. */ duk_get_prop_stridx(ctx, -2, DUK_STRIDX_MESSAGE); if (duk_is_undefined(ctx, -1)) { duk_pop(ctx); duk_push_string(ctx, ""); } else { duk_to_string(ctx, -1); } /* [ ... this name message ] */ if (duk_get_length(ctx, -2) == 0) { /* name is empty -> return message */ return 1; } if (duk_get_length(ctx, -1) == 0) { /* message is empty -> return name */ duk_pop(ctx); return 1; } duk_push_string(ctx, ": "); duk_insert(ctx, -2); /* ... name ': ' message */ duk_concat(ctx, 3); return 1; }
/* Shared helper for Object.getOwnPropertyNames() and Object.keys(). * Magic: 0=getOwnPropertyNames, 1=Object.keys. */ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_keys_shared(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_hobject *obj; #if defined(DUK_USE_ES6_PROXY) duk_hobject *h_proxy_target; duk_hobject *h_proxy_handler; duk_hobject *h_trap_result; duk_uarridx_t i, len, idx; #endif duk_small_uint_t enum_flags; DUK_ASSERT_TOP(ctx, 1); obj = duk_require_hobject_or_lfunc_coerce(ctx, 0); DUK_ASSERT(obj != NULL); DUK_UNREF(obj); #if defined(DUK_USE_ES6_PROXY) if (DUK_LIKELY(!duk_hobject_proxy_check(thr, obj, &h_proxy_target, &h_proxy_handler))) { goto skip_proxy; } duk_push_hobject(ctx, h_proxy_handler); if (!duk_get_prop_stridx(ctx, -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(ctx); duk_push_hobject(ctx, h_proxy_target); duk_replace(ctx, 0); DUK_ASSERT_TOP(ctx, 1); goto skip_proxy; } /* [ obj handler trap ] */ duk_insert(ctx, -2); duk_push_hobject(ctx, h_proxy_target); /* -> [ obj trap handler target ] */ duk_call_method(ctx, 1 /*nargs*/); /* -> [ obj trap_result ] */ h_trap_result = duk_require_hobject(ctx, -1); DUK_UNREF(h_trap_result); len = (duk_uarridx_t) duk_get_length(ctx, -1); idx = 0; duk_push_array(ctx); for (i = 0; i < len; i++) { /* [ obj trap_result res_arr ] */ if (duk_get_prop_index(ctx, -2, i) && duk_is_string(ctx, -1)) { /* XXX: for Object.keys() we should check enumerability of key */ /* [ obj trap_result res_arr propname ] */ duk_put_prop_index(ctx, -2, idx); idx++; } else { duk_pop(ctx); } } /* XXX: for Object.keys() the [[OwnPropertyKeys]] result (trap result) * should be filtered so that only enumerable keys remain. Enumerability * should be checked with [[GetOwnProperty]] on the original object * (i.e., the proxy in this case). If the proxy has a getOwnPropertyDescriptor * trap, it should be triggered for every property. If the proxy doesn't have * the trap, enumerability should be checked against the target object instead. * We don't do any of this now, so Object.keys() and Object.getOwnPropertyNames() * return the same result now for proxy traps. We still do clean up the trap * result, so that Object.keys() and Object.getOwnPropertyNames() will return a * clean array of strings without gaps. */ return 1; skip_proxy: #endif /* DUK_USE_ES6_PROXY */ DUK_ASSERT_TOP(ctx, 1); if (duk_get_current_magic(ctx)) { /* Object.keys */ enum_flags = DUK_ENUM_OWN_PROPERTIES_ONLY | DUK_ENUM_NO_PROXY_BEHAVIOR; } else { /* Object.getOwnPropertyNames */ enum_flags = DUK_ENUM_INCLUDE_NONENUMERABLE | DUK_ENUM_OWN_PROPERTIES_ONLY | DUK_ENUM_NO_PROXY_BEHAVIOR; } return duk_hobject_get_enumerated_keys(ctx, enum_flags); }
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_properties(duk_context *ctx) { 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 handling by ToObject() coercion. */ obj = duk_require_hobject_or_lfunc_coerce(ctx, 0); /* target */ DUK_ASSERT(obj != NULL); duk_to_object(ctx, 1); /* properties object */ DUK_DDD(DUK_DDDPRINT("target=%!iT, properties=%!iT", (duk_tval *) duk_get_tval(ctx, 0), (duk_tval *) duk_get_tval(ctx, 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(ctx, 2); /* -> [ hobject props ] */ duk_enum(ctx, 1, DUK_ENUM_OWN_PROPERTIES_ONLY /*enum_flags*/); for (;;) { duk_hstring *key; /* [ hobject props enum(props) ] */ duk_set_top(ctx, 3); if (!duk_next(ctx, 2, 1 /*get_value*/)) { break; } DUK_DDD(DUK_DDDPRINT("-> key=%!iT, desc=%!iT", (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1))); /* [ hobject props enum(props) key desc ] */ duk_hobject_prepare_property_descriptor(ctx, 4 /*idx_desc*/, &defprop_flags, &idx_value, &get, &set); /* [ hobject props enum(props) key desc value? getter? setter? ] */ if (pass == 0) { continue; } key = duk_get_hstring(ctx, 3); DUK_ASSERT(key != NULL); duk_hobject_define_property_helper(ctx, defprop_flags, obj, key, idx_value, get, set); } } /* * Return target object */ duk_dup(ctx, 0); return 1; }
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_property(duk_context *ctx) { duk_hobject *obj; duk_hstring *key; duk_hobject *get; duk_hobject *set; duk_idx_t idx_value; duk_uint_t defprop_flags; DUK_ASSERT(ctx != NULL); DUK_DDD(DUK_DDDPRINT("Object.defineProperty(): ctx=%p obj=%!T key=%!T desc=%!T", (void *) ctx, (duk_tval *) duk_get_tval(ctx, 0), (duk_tval *) duk_get_tval(ctx, 1), (duk_tval *) duk_get_tval(ctx, 2))); /* [ obj key desc ] */ /* Lightfuncs are currently supported by coercing to a temporary * Function object; changes will be allowed (the coerced value is * extensible) but will be lost. */ obj = duk_require_hobject_or_lfunc_coerce(ctx, 0); (void) duk_to_string(ctx, 1); key = duk_require_hstring(ctx, 1); (void) duk_require_hobject(ctx, 2); DUK_ASSERT(obj != NULL); DUK_ASSERT(key != NULL); DUK_ASSERT(duk_get_hobject(ctx, 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(ctx, 2 /*idx_desc*/, &defprop_flags, &idx_value, &get, &set); /* * Use Object.defineProperty() helper for the actual operation. */ duk_hobject_define_property_helper(ctx, defprop_flags, obj, key, idx_value, get, set); /* Ignore the normalize/validate helper outputs on the value stack, * they're popped automatically. */ /* * Return target object. */ duk_push_hobject(ctx, obj); return 1; }