/* Shared helper to implement ES6 Object.setPrototypeOf and * Object.prototype.__proto__ setter. * * https://people.mozilla.org/~jorendorff/es6-draft.html#sec-get-object.prototype.__proto__ * https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.setprototypeof */ duk_ret_t duk_bi_object_setprototype_shared(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_hobject *h_obj; duk_hobject *h_new_proto; duk_hobject *h_curr; duk_ret_t ret_success = 1; /* retval for success path */ /* Preliminaries for __proto__ and setPrototypeOf (E6 19.1.2.18 steps 1-4); * magic: 0=setter call, 1=Object.setPrototypeOf */ if (duk_get_current_magic(ctx) == 0) { duk_push_this_check_object_coercible(ctx); duk_insert(ctx, 0); if (!duk_check_type_mask(ctx, 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 { duk_require_object_coercible(ctx, 0); duk_require_type_mask(ctx, 1, DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_OBJECT); } h_obj = duk_get_hobject(ctx, 0); if (!h_obj) { goto skip; } h_new_proto = duk_get_hobject(ctx, 1); DUK_ASSERT(h_obj != NULL); /* h_new_proto may be NULL */ /* [[SetPrototypeOf]] standard behavior, E6 9.1.2 */ /* NOTE: steps 7-8 seem to be a cut-paste bug in the E6 draft */ /* TODO: implement Proxy object support here */ if (h_new_proto == h_obj->prototype) { goto skip; } if (!DUK_HOBJECT_HAS_EXTENSIBLE(h_obj)) { goto fail_nonextensible; } for (h_curr = h_new_proto; h_curr != NULL; h_curr = h_curr->prototype) { /* 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(ctx, 1); return ret_success; fail_nonextensible: fail_loop: return DUK_RET_TYPE_ERROR; }
/* 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; } }