/* Shared helper to implement Object.getPrototypeOf and the ES6 * Object.prototype.__proto__ getter. * * https://people.mozilla.org/~jorendorff/es6-draft.html#sec-get-object.prototype.__proto__ */ DUK_INTERNAL duk_ret_t duk_bi_object_getprototype_shared(duk_context *ctx) { duk_hobject *h; duk_hobject *proto; /* magic: 0=getter call, 1=Object.getPrototypeOf */ if (duk_get_current_magic(ctx) == 0) { duk_push_this_coercible_to_object(ctx); duk_insert(ctx, 0); } h = duk_require_hobject_or_lfunc(ctx, 0); /* h is NULL for lightfunc */ /* XXX: should the API call handle this directly, i.e. attempt * to duk_push_hobject(ctx, null) would push a null instead? * (On the other hand 'undefined' would be just as logical, but * not wanted here.) */ if (h == NULL) { duk_push_hobject_bidx(ctx, DUK_BIDX_FUNCTION_PROTOTYPE); } else { proto = DUK_HOBJECT_GET_PROTOTYPE(h); if (proto) { duk_push_hobject(ctx, proto); } else { duk_push_null(ctx); } } return 1; }
DUK_INTERNAL duk_ret_t duk_bi_array_prototype_to_string(duk_context *ctx) { (void) duk_push_this_coercible_to_object(ctx); duk_get_prop_stridx(ctx, -1, DUK_STRIDX_JOIN); /* [ ... this func ] */ if (!duk_is_callable(ctx, -1)) { /* Fall back to the initial (original) Object.toString(). We don't * currently have pointers to the built-in functions, only the top * level global objects (like "Array") so this is now done in a bit * of a hacky manner. It would be cleaner to push the (original) * function and use duk_call_method(). */ /* XXX: 'this' will be ToObject() coerced twice, which is incorrect * but should have no visible side effects. */ DUK_DDD(DUK_DDDPRINT("this.join is not callable, fall back to (original) Object.toString")); duk_set_top(ctx, 0); return duk_bi_object_prototype_to_string(ctx); /* has access to 'this' binding */ } /* [ ... this func ] */ duk_insert(ctx, -2); /* [ ... func this ] */ DUK_DDD(DUK_DDDPRINT("calling: func=%!iT, this=%!iT", (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1))); duk_call_method(ctx, 0); return 1; }
/* Shared helper to implement Object.getPrototypeOf and the ES6 * Object.prototype.__proto__ getter. * * https://people.mozilla.org/~jorendorff/es6-draft.html#sec-get-object.prototype.__proto__ */ duk_ret_t duk_bi_object_getprototype_shared(duk_context *ctx) { duk_hobject *h; /* magic: 0=getter call, 1=Object.getPrototypeOf */ if (duk_get_current_magic(ctx) == 0) { duk_push_this_coercible_to_object(ctx); duk_insert(ctx, 0); } h = duk_require_hobject(ctx, 0); DUK_ASSERT(h != NULL); /* XXX: should the API call handle this directly, i.e. attempt * to duk_push_hobject(ctx, null) would push a null instead? * (On the other hand 'undefined' would be just as logical, but * not wanted here.) */ if (h->prototype) { duk_push_hobject(ctx, h->prototype); } else { duk_push_null(ctx); } return 1; }
/* Shared entry code for many Array built-ins. Note that length is left * on stack (it could be popped, but that's not necessary). */ DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32(duk_context *ctx) { duk_uint32_t len; (void) duk_push_this_coercible_to_object(ctx); duk_get_prop_stridx(ctx, -1, DUK_STRIDX_LENGTH); len = duk_to_uint32(ctx, -1); /* -> [ ... ToObject(this) ToUint32(length) ] */ return len; }
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_to_locale_string(duk_context *ctx) { DUK_ASSERT_TOP(ctx, 0); (void) duk_push_this_coercible_to_object(ctx); duk_get_prop_stridx(ctx, 0, DUK_STRIDX_TO_STRING); if (!duk_is_callable(ctx, 1)) { return DUK_RET_TYPE_ERROR; } duk_dup(ctx, 0); /* -> [ O toString O ] */ duk_call_method(ctx, 0); /* XXX: call method tailcall? */ return 1; }
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; }
/* 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_context *ctx) { /* * magic = 0: __proto__ getter * magic = 1: Object.getPrototypeOf() * magic = 2: Reflect.getPrototypeOf() */ duk_hthread *thr = (duk_hthread *) ctx; duk_hobject *h; duk_hobject *proto; duk_tval *tv; duk_int_t magic; magic = duk_get_current_magic(ctx); if (magic == 0) { DUK_ASSERT_TOP(ctx, 0); duk_push_this_coercible_to_object(ctx); } DUK_ASSERT(duk_get_top(ctx) >= 1); if (magic < 2) { /* ES2015 Section 19.1.2.9, step 1 */ duk_to_object(ctx, 0); } tv = DUK_GET_TVAL_POSIDX(ctx, 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(ctx, proto); } else { duk_push_null(ctx); } return 1; }
duk_ret_t duk_bi_object_prototype_is_prototype_of(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_hobject *h_v; duk_hobject *h_obj; DUK_ASSERT_TOP(ctx, 1); h_v = duk_get_hobject(ctx, 0); if (!h_v) { duk_push_false(ctx); /* XXX: tail call: return duk_push_false(ctx) */ return 1; } h_obj = duk_push_this_coercible_to_object(ctx); DUK_ASSERT(h_obj != NULL); /* E5.1 Section 15.2.4.6, step 3.a, lookup proto once before compare */ duk_push_boolean(ctx, duk_hobject_prototype_chain_contains(thr, h_v->prototype, h_obj)); return 1; }
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; }
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_value_of(duk_context *ctx) { (void) duk_push_this_coercible_to_object(ctx); return 1; }
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; }
DUK_INTERNAL duk_ret_t duk_bi_array_prototype_concat(duk_context *ctx) { duk_idx_t i, n; duk_uarridx_t idx, idx_last; duk_uarridx_t j, len; duk_hobject *h; /* XXX: the insert here is a bit expensive if there are a lot of items. * It could also be special cased in the outermost for loop quite easily * (as the element is dup()'d anyway). */ (void) duk_push_this_coercible_to_object(ctx); duk_insert(ctx, 0); n = duk_get_top(ctx); duk_push_array(ctx); /* -> [ ToObject(this) item1 ... itemN arr ] */ /* NOTE: The Array special behaviors are NOT invoked by duk_xdef_prop_index() * (which differs from the official algorithm). If no error is thrown, this * doesn't matter as the length is updated at the end. However, if an error * is thrown, the length will be unset. That shouldn't matter because the * caller won't get a reference to the intermediate value. */ idx = 0; idx_last = 0; for (i = 0; i < n; i++) { DUK_ASSERT_TOP(ctx, n + 1); /* [ ToObject(this) item1 ... itemN arr ] */ duk_dup(ctx, i); h = duk_get_hobject_with_class(ctx, -1, DUK_HOBJECT_CLASS_ARRAY); if (!h) { duk_xdef_prop_index_wec(ctx, -2, idx++); idx_last = idx; continue; } /* [ ToObject(this) item1 ... itemN arr item(i) ] */ /* XXX: an array can have length higher than 32 bits; this is not handled * correctly now. */ len = (duk_uarridx_t) duk_get_length(ctx, -1); for (j = 0; j < len; j++) { if (duk_get_prop_index(ctx, -1, j)) { /* [ ToObject(this) item1 ... itemN arr item(i) item(i)[j] ] */ duk_xdef_prop_index_wec(ctx, -3, idx++); idx_last = idx; } else { idx++; duk_pop(ctx); #if defined(DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER) /* According to E5.1 Section 15.4.4.4 nonexistent trailing * elements do not affect 'length' of the result. Test262 * and other engines disagree, so update idx_last here too. */ idx_last = idx; #else /* Strict standard behavior, ignore trailing elements for * result 'length'. */ #endif } } duk_pop(ctx); } /* The E5.1 Section 15.4.4.4 algorithm doesn't set the length explicitly * in the end, but because we're operating with an internal value which * is known to be an array, this should be equivalent. */ duk_push_uarridx(ctx, idx_last); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); DUK_ASSERT_TOP(ctx, n + 1); return 1; }
int duk_bi_array_prototype_concat(duk_context *ctx) { int i, n; int j, len; int idx, idx_last; duk_hobject *h; /* XXX: the insert here is a bit expensive if there are a lot of items. * It could also be special cased in the outermost for loop quite easily * (as the element is dup()'d anyway). */ (void) duk_push_this_coercible_to_object(ctx); duk_insert(ctx, 0); n = duk_get_top(ctx); duk_push_array(ctx); /* -> [ ToObject(this) item1 ... itemN arr ] */ /* NOTE: The Array special behaviors are NOT invoked by duk_def_prop_index() * (which differs from the official algorithm). If no error is thrown, this * doesn't matter as the length is updated at the end. However, if an error * is thrown, the length will be unset. That shouldn't matter because the * caller won't get a reference to the intermediate value. */ idx = 0; idx_last = 0; for (i = 0; i < n; i++) { DUK_ASSERT_TOP(ctx, n + 1); /* [ ToObject(this) item1 ... itemN arr ] */ duk_dup(ctx, i); h = duk_get_hobject_with_class(ctx, -1, DUK_HOBJECT_CLASS_ARRAY); if (!h) { duk_def_prop_index(ctx, -2, idx++, DUK_PROPDESC_FLAGS_WEC); idx_last = idx; continue; } /* [ ToObject(this) item1 ... itemN arr item(i) ] */ /* FIXME: an array can have length higher than 32 bits; this is not handled * correctly now (also len is signed so length above 2**31-1 will have trouble. */ len = duk_get_length(ctx, -1); for (j = 0; j < len; j++) { if (duk_get_prop_index(ctx, -1, j)) { /* [ ToObject(this) item1 ... itemN arr item(i) item(i)[j] ] */ duk_def_prop_index(ctx, -3, idx++, DUK_PROPDESC_FLAGS_WEC); idx_last = idx; } else { /* XXX: according to E5.1 Section 15.4.4.4 nonexistent trailing * elements do not affect 'length' but test262 disagrees. Work * as E5.1 mandates for now and don't touch idx_last. */ idx++; duk_pop(ctx); } } duk_pop(ctx); } duk_push_number(ctx, (double) idx_last); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); DUK_ASSERT_TOP(ctx, n + 1); return 1; }
int duk_builtin_object_prototype_value_of(duk_context *ctx) { duk_push_this_coercible_to_object(ctx); return 1; }