/* 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; }
DUK_INTERNAL duk_ret_t duk_bi_object_prototype_to_string(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_push_this(ctx); duk_push_string(ctx, "[object "); if (duk_is_undefined(ctx, -2)) { duk_push_hstring_stridx(ctx, DUK_STRIDX_UC_UNDEFINED); } else if (duk_is_null(ctx, -2)) { duk_push_hstring_stridx(ctx, DUK_STRIDX_UC_NULL); } else { duk_hobject *h_this; duk_hstring *h_classname; duk_to_object(ctx, -2); h_this = duk_get_hobject(ctx, -2); DUK_ASSERT(h_this != NULL); h_classname = DUK_HOBJECT_GET_CLASS_STRING(thr->heap, h_this); DUK_ASSERT(h_classname != NULL); duk_push_hstring(ctx, h_classname); } duk_push_string(ctx, "]"); duk_concat(ctx, 3); return 1; }
static duk_double_t duk__push_this_number_plain(duk_context *ctx) { duk_hobject *h; /* Number built-in accepts a plain number or a Number object (whose * internal value is operated on). Other types cause TypeError. */ duk_push_this(ctx); if (duk_is_number(ctx, -1)) { DUK_DDD(DUK_DDDPRINT("plain number value: %!T", (duk_tval *) duk_get_tval(ctx, -1))); goto done; } h = duk_get_hobject(ctx, -1); if (!h || (DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_NUMBER)) { DUK_DDD(DUK_DDDPRINT("unacceptable this value: %!T", (duk_tval *) duk_get_tval(ctx, -1))); DUK_ERROR((duk_hthread *) ctx, DUK_ERR_TYPE_ERROR, "expected a number"); } duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_VALUE); DUK_ASSERT(duk_is_number(ctx, -1)); DUK_DDD(DUK_DDDPRINT("number object: %!T, internal value: %!T", (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1))); duk_remove(ctx, -2); done: return duk_get_number(ctx, -1); }
int wrapped_compile_execute(duk_context *ctx) { int comp_flags; comp_flags = 0; duk_compile(ctx, comp_flags); #if 0 /* FIXME: something similar with public API */ if (interactive_mode) { duk_hcompiledfunction *f = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1); if (f && DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) f)) { fprintf(stdout, "[bytecode length %d opcodes, registers %d, constants %d, inner functions %d]\n", (int) DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(f), (int) f->nregs, (int) DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(f), (int) DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(f)); fflush(stdout); } else { fprintf(stdout, "[invalid compile result]\n"); fflush(stdout); } } #endif duk_push_global_object(ctx); /* 'this' binding */ duk_call_method(ctx, 0); if (interactive_mode) { /* * In interactive mode, write to stdout so output won't interleave as easily. * * NOTE: the ToString() coercion may fail in some cases; for instance, * if you evaluate: * * ( {valueOf: function() {return {}}, toString: function() {return {}}}); * * The error is: * * TypeError: failed to coerce with [[DefaultValue]] * duk_api.c:1420 * * These errors are caught and printed out as errors although * the errors are not generated by user code as such. Changing * duk_to_string() to duk_safe_to_string() would avoid these * errors. */ fprintf(stdout, "= %s\n", duk_to_string(ctx, -1)); fflush(stdout); } else { /* In non-interactive mode, success results are not written at all. * It is important that the result value is not string coerced, * as the string coercion may cause an error in some cases. */ } duk_pop(ctx); return 0; }
int duk_builtin_object_prototype_to_string(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_push_this(ctx); duk_push_string(ctx, "[object "); if (duk_is_undefined(ctx, -2)) { duk_push_string(ctx, "Undefined"); } else if (duk_is_null(ctx, -2)) { duk_push_string(ctx, "Null"); } else { duk_hobject *h_this; duk_hstring *h_classname; duk_to_object(ctx, -2); h_this = duk_get_hobject(ctx, -2); DUK_ASSERT(h_this != NULL); h_classname = DUK_HOBJECT_GET_CLASS_STRING(thr->heap, h_this); DUK_ASSERT(h_classname != NULL); duk_push_hstring(ctx, h_classname); } duk_push_string(ctx, "]"); duk_concat(ctx, 3); return 1; }
static int duk__protected_compact_object(duk_context *ctx) { /* XXX: for threads, compact value stack, call stack, catch stack? */ duk_hobject *obj = duk_get_hobject(ctx, -1); DUK_ASSERT(obj != NULL); duk_hobject_compact_props((duk_hthread *) ctx, obj); return 0; }
DUK_LOCAL int duk__protected_compact_object(duk_context *ctx, void *udata) { duk_hobject *obj; /* XXX: for threads, compact value stack, call stack, catch stack? */ DUK_UNREF(udata); obj = duk_get_hobject(ctx, -1); DUK_ASSERT(obj != NULL); duk_hobject_compact_props((duk_hthread *) ctx, obj); return 0; }
DUK_INTERNAL void duk_err_setup_heap_ljstate(duk_hthread *thr, duk_small_int_t lj_type) { duk_tval tv_tmp; #if defined(DUK_USE_DEBUGGER_SUPPORT) /* If something is thrown with the debugger attached and nobody will * catch it, execution is paused before the longjmp, turning over * control to the debug client. This allows local state to be examined * before the stack is unwound. */ /* TODO: Allow customizing the pause and notify behavior */ if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap) && lj_type == DUK_LJ_TYPE_THROW) { duk_context *ctx = (duk_context *) thr; duk_bool_t fatal; duk_hobject *h_obj; /* Don't intercept a DoubleError, we may have caused the initial double * fault and attempting to intercept it will cause us to be called * recursively and exhaust the C stack. */ h_obj = duk_get_hobject(ctx, -1); if (h_obj == thr->builtins[DUK_BIDX_DOUBLE_ERROR]) { DUK_D(DUK_DPRINT("built-in DoubleError instance thrown, not intercepting")); goto skip_throw_intercept; } DUK_D(DUK_DPRINT("throw with debugger attached, report to client")); fatal = !duk__have_active_catcher(thr); /* Report it to the debug client */ duk_debug_send_throw(thr, fatal); if (fatal) { DUK_D(DUK_DPRINT("throw will be fatal, halt before longjmp")); duk_debug_halt_execution(thr, 1 /*use_prev_pc*/); } } skip_throw_intercept: #endif thr->heap->lj.type = lj_type; DUK_ASSERT(thr->valstack_top > thr->valstack); DUK_TVAL_SET_TVAL(&tv_tmp, &thr->heap->lj.value1); DUK_TVAL_SET_TVAL(&thr->heap->lj.value1, thr->valstack_top - 1); DUK_TVAL_INCREF(thr, &thr->heap->lj.value1); DUK_TVAL_DECREF(thr, &tv_tmp); duk_pop((duk_context *) thr); }
DUK_INTERNAL duk_ret_t duk_bi_object_constructor_seal_freeze_shared(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_hobject *h; duk_bool_t is_freeze; DUK_ASSERT_TOP(ctx, 1); is_freeze = (duk_bool_t) duk_get_current_magic(ctx); if (duk_is_buffer(ctx, 0)) { /* Plain buffer: already sealed, but not frozen (and can't be frozen * because index properties can't be made non-writable. */ if (is_freeze) { goto fail_cannot_freeze; } return 1; } else if (duk_is_lightfunc(ctx, 0)) { /* Lightfunc: already sealed and frozen, success. */ return 1; } #if 0 /* Seal/freeze are quite rare in practice so it'd be nice to get the * correct behavior simply via automatic promotion (at the cost of some * memory churn). However, the promoted objects don't behave the same, * e.g. promoted lightfuncs are extensible. */ h = duk_require_hobject_promote_mask(ctx, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER); #endif h = duk_get_hobject(ctx, 0); if (h == NULL) { /* ES2015 Sections 19.1.2.5, 19.1.2.17 */ return 1; } if (is_freeze && DUK_HOBJECT_IS_BUFOBJ(h)) { /* Buffer objects cannot be frozen because there's no internal * support for making virtual array indices non-writable. */ DUK_DD(DUK_DDPRINT("cannot freeze a buffer object")); goto fail_cannot_freeze; } duk_hobject_object_seal_freeze_helper(thr, h, is_freeze); /* Sealed and frozen objects cannot gain any more properties, * so this is a good time to compact them. */ duk_hobject_compact_props(thr, h); return 1; fail_cannot_freeze: DUK_DCERROR_TYPE_INVALID_ARGS(thr); /* XXX: proper error message */ }
DUK_EXTERNAL void duk_compact(duk_context *ctx, duk_idx_t obj_index) { duk_hthread *thr = (duk_hthread *) ctx; duk_hobject *obj; DUK_ASSERT_CTX_VALID(ctx); obj = duk_get_hobject(ctx, obj_index); if (obj) { /* Note: this may fail, caller should protect the call if necessary */ duk_hobject_compact_props(thr, obj); } }
int duk_builtin_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); /* FIXME: tail call: return duk_push_false(ctx) */ return 1; } duk_push_this_coercible_to_object(ctx); h_obj = duk_get_hobject(ctx, 1); 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_ret_t duk_bi_thread_constructor(duk_context *ctx) { duk_hthread *new_thr; duk_hobject *func; if (!duk_is_callable(ctx, 0)) { return DUK_RET_TYPE_ERROR; } func = duk_get_hobject(ctx, 0); DUK_ASSERT(func != NULL); duk_push_thread(ctx); new_thr = (duk_hthread *) duk_get_hobject(ctx, -1); DUK_ASSERT(new_thr != NULL); new_thr->state = DUK_HTHREAD_STATE_INACTIVE; /* push initial function call to new thread stack; this is * picked up by resume(). */ duk_push_hobject((duk_context *) new_thr, func); return 1; /* return thread */ }
duk_ret_t duk_bi_number_constructor(duk_context *ctx) { duk_idx_t nargs; duk_hobject *h_this; /* * The Number constructor uses ToNumber(arg) for number coercion * (coercing an undefined argument to NaN). However, if the * argument is not given at all, +0 must be used instead. To do * this, a vararg function is used. */ nargs = duk_get_top(ctx); if (nargs == 0) { duk_push_int(ctx, 0); } duk_to_number(ctx, 0); duk_set_top(ctx, 1); DUK_ASSERT_TOP(ctx, 1); if (!duk_is_constructor_call(ctx)) { return 1; } /* * E5 Section 15.7.2.1 requires that the constructed object * must have the original Number.prototype as its internal * prototype. However, since Number.prototype is non-writable * and non-configurable, this doesn't have to be enforced here: * The default object (bound to 'this') is OK, though we have * to change its class. * * Internal value set to ToNumber(arg) or +0; if no arg given, * ToNumber(undefined) = NaN, so special treatment is needed * (above). String internal value is immutable. */ /* XXX: helper */ duk_push_this(ctx); h_this = duk_get_hobject(ctx, -1); DUK_ASSERT(h_this != NULL); DUK_HOBJECT_SET_CLASS_NUMBER(h_this, DUK_HOBJECT_CLASS_NUMBER); DUK_ASSERT(h_this->prototype == ((duk_hthread *) ctx)->builtins[DUK_BIDX_NUMBER_PROTOTYPE]); DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(h_this) == DUK_HOBJECT_CLASS_NUMBER); DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h_this)); duk_dup(ctx, 0); /* -> [ val obj val ] */ duk_def_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); return 0; /* no return value -> don't replace created value */ }
DUK_INTERNAL void duk_regexp_create_instance(duk_hthread *thr) { duk_context *ctx = (duk_context *) thr; duk_hobject *h; duk_hstring *h_bc; duk_small_int_t re_flags; /* [ ... escape_source bytecode ] */ h_bc = duk_get_hstring(ctx, -1); DUK_ASSERT(h_bc != NULL); DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(h_bc) >= 1); /* always at least the header */ DUK_ASSERT(DUK_HSTRING_GET_CHARLEN(h_bc) >= 1); DUK_ASSERT((duk_small_int_t) DUK_HSTRING_GET_DATA(h_bc)[0] < 0x80); /* flags always encodes to 1 byte */ re_flags = (duk_small_int_t) DUK_HSTRING_GET_DATA(h_bc)[0]; /* [ ... escaped_source bytecode ] */ duk_push_object(ctx); h = duk_get_hobject(ctx, -1); DUK_ASSERT(h != NULL); duk_insert(ctx, -3); /* [ ... regexp_object escaped_source bytecode ] */ DUK_HOBJECT_SET_CLASS_NUMBER(h, DUK_HOBJECT_CLASS_REGEXP); DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[DUK_BIDX_REGEXP_PROTOTYPE]); duk_xdef_prop_stridx(ctx, -3, DUK_STRIDX_INT_BYTECODE, DUK_PROPDESC_FLAGS_NONE); /* [ ... regexp_object escaped_source ] */ duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_SOURCE, DUK_PROPDESC_FLAGS_NONE); /* [ ... regexp_object ] */ duk_push_boolean(ctx, (re_flags & DUK_RE_FLAG_GLOBAL)); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_GLOBAL, DUK_PROPDESC_FLAGS_NONE); duk_push_boolean(ctx, (re_flags & DUK_RE_FLAG_IGNORE_CASE)); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_IGNORE_CASE, DUK_PROPDESC_FLAGS_NONE); duk_push_boolean(ctx, (re_flags & DUK_RE_FLAG_MULTILINE)); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_MULTILINE, DUK_PROPDESC_FLAGS_NONE); duk_push_int(ctx, 0); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LAST_INDEX, DUK_PROPDESC_FLAGS_W); /* [ ... regexp_object ] */ }
DUK_INTERNAL void duk_err_augment_error_create(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_bool_t noblame_fileline) { duk_context *ctx = (duk_context *) thr; duk_hobject *obj; DUK_ASSERT(thr != NULL); DUK_ASSERT(thr_callstack != NULL); DUK_ASSERT(ctx != NULL); /* [ ... error ] */ /* * Criteria for augmenting: * * - augmentation enabled in build (naturally) * - error value internal prototype chain contains the built-in * Error prototype object (i.e. 'val instanceof Error') * * Additional criteria for built-in augmenting: * * - error value is an extensible object */ obj = duk_get_hobject(ctx, -1); if (!obj) { DUK_DDD(DUK_DDDPRINT("value is not an object, skip both built-in and user augment")); return; } if (!duk_hobject_prototype_chain_contains(thr, obj, thr->builtins[DUK_BIDX_ERROR_PROTOTYPE], 1 /*ignore_loop*/)) { /* If the value has a prototype loop, it's critical not to * throw here. Instead, assume the value is not to be * augmented. */ DUK_DDD(DUK_DDDPRINT("value is not an error instance, skip both built-in and user augment")); return; } if (DUK_HOBJECT_HAS_EXTENSIBLE(obj)) { DUK_DDD(DUK_DDDPRINT("error meets criteria, built-in augment")); duk__err_augment_builtin_create(thr, thr_callstack, c_filename, c_line, noblame_fileline, obj); } else { DUK_DDD(DUK_DDDPRINT("error does not meet criteria, no built-in augment")); } /* [ ... error ] */ #if defined(DUK_USE_ERRCREATE) duk__err_augment_user(thr, DUK_STRIDX_ERR_CREATE); #endif }
int duk_builtin_object_constructor_create(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_tval *tv; duk_hobject *proto = NULL; duk_hobject *h; DUK_ASSERT_TOP(ctx, 2); tv = duk_get_tval(ctx, 0); DUK_ASSERT(tv != NULL); if (DUK_TVAL_IS_NULL(tv)) { ; } else if (DUK_TVAL_IS_OBJECT(tv)) { proto = DUK_TVAL_GET_OBJECT(tv); DUK_ASSERT(proto != NULL); } else { return DUK_RET_TYPE_ERROR; } /* FIXME: direct helper to create with specific prototype */ (void) duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT), -1); h = duk_get_hobject(ctx, -1); DUK_ASSERT(h != NULL); DUK_ASSERT(h->prototype == NULL); DUK_HOBJECT_SET_PROTOTYPE(thr, h, proto); if (!duk_is_undefined(ctx, 1)) { /* [ O Properties obj ] */ /* Use original function. No need to get it explicitly, * just call the helper. */ duk_replace(ctx, 0); /* [ obj Properties ] */ return duk_hobject_object_define_properties(ctx); } /* [ O Properties obj ] */ return 1; }
DUK_EXTERNAL void duk_set_prototype(duk_context *ctx, duk_idx_t index) { duk_hthread *thr = (duk_hthread *) ctx; duk_hobject *obj; duk_hobject *proto; DUK_ASSERT_CTX_VALID(ctx); obj = duk_require_hobject(ctx, index); DUK_ASSERT(obj != NULL); duk_require_type_mask(ctx, -1, DUK_TYPE_MASK_UNDEFINED | DUK_TYPE_MASK_OBJECT); proto = duk_get_hobject(ctx, -1); /* proto can also be NULL here (allowed explicitly) */ DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, obj, proto); duk_pop(ctx); }
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; }
duk_ret_t duk_bi_boolean_constructor(duk_context *ctx) { duk_hobject *h_this; duk_to_boolean(ctx, 0); if (duk_is_constructor_call(ctx)) { /* FIXME: helper; rely on Boolean.prototype as being non-writable, non-configurable */ duk_push_this(ctx); h_this = duk_get_hobject(ctx, -1); DUK_ASSERT(h_this != NULL); DUK_ASSERT(h_this->prototype == ((duk_hthread *) ctx)->builtins[DUK_BIDX_BOOLEAN_PROTOTYPE]); DUK_HOBJECT_SET_CLASS_NUMBER(h_this, DUK_HOBJECT_CLASS_BOOLEAN); duk_dup(ctx, 0); /* -> [ val obj val ] */ duk_def_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); /* FIXME: proper flags? */ } /* unbalanced stack */ return 1; }
duk_ret_t duk_hobject_get_enumerated_keys(duk_context *ctx, duk_small_uint_t enum_flags) { duk_hobject *e; duk_uint_fast32_t i; duk_uint_fast32_t idx; DUK_ASSERT(ctx != NULL); DUK_ASSERT(duk_get_hobject(ctx, -1) != NULL); /* Create a temporary enumerator to get the (non-duplicated) key list; * the enumerator state is initialized without being needed, but that * has little impact. */ duk_hobject_enumerator_create(ctx, enum_flags); duk_push_array(ctx); /* [enum_target enum res] */ e = duk_require_hobject(ctx, -2); DUK_ASSERT(e != NULL); idx = 0; for (i = DUK__ENUM_START_INDEX; i < (duk_uint_fast32_t) e->e_next; i++) { duk_hstring *k; k = DUK_HOBJECT_E_GET_KEY(e, i); DUK_ASSERT(k); /* enumerator must have no keys deleted */ /* [enum_target enum res] */ duk_push_hstring(ctx, k); duk_put_prop_index(ctx, -2, idx); idx++; } /* [enum_target enum res] */ duk_remove(ctx, -2); /* [enum_target res] */ return 1; /* return 1 to allow callers to tail call */ }
/* Push 'this' binding, check that it is a Date object; then push the * internal time value. At the end, stack is: [ ... this timeval ]. * Returns the time value. Local time adjustment is done if requested. */ static double push_this_and_get_timeval_tzoffset(duk_context *ctx, int flags, int *out_tzoffset) { duk_hthread *thr = (duk_hthread *) ctx; duk_hobject *h; double d; int tzoffset = 0; duk_push_this(ctx); h = duk_get_hobject(ctx, -1); /* FIXME: getter with class check, useful in built-ins */ if (h == NULL || DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_DATE) { DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "expected Date"); } duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_VALUE); d = duk_to_number(ctx, -1); duk_pop(ctx); if (DUK_ISNAN(d)) { if (flags & FLAG_NAN_TO_ZERO) { d = 0.0; } if (flags & FLAG_NAN_TO_RANGE_ERROR) { DUK_ERROR(thr, DUK_ERR_RANGE_ERROR, "Invalid Date"); } } /* if no NaN handling flag, may still be NaN here, but not Inf */ DUK_ASSERT(!DUK_ISINF(d)); if (flags & FLAG_LOCALTIME) { /* Note: DST adjustment is determined using UTC time. * If 'd' is NaN, tzoffset will be 0. */ tzoffset = GET_LOCAL_TZOFFSET(d); /* seconds */ d += tzoffset * 1000; } if (out_tzoffset) { *out_tzoffset = tzoffset; } /* [ ... this ] */ return d; }
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_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) */ } 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*/)); } return 1; }
DUK_INTERNAL 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. * Prototype loops should cause an error to be thrown. */ duk_push_boolean(ctx, duk_hobject_prototype_chain_contains(thr, h_v->prototype, h_obj, 0 /*ignore_loop*/)); return 1; }
DUK_INTERNAL duk_ret_t duk_bi_function_constructor(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_hstring *h_sourcecode; duk_idx_t nargs; duk_idx_t i; duk_small_uint_t comp_flags; duk_hcompiledfunction *func; duk_hobject *outer_lex_env; duk_hobject *outer_var_env; /* normal and constructor calls have identical semantics */ nargs = duk_get_top(ctx); for (i = 0; i < nargs; i++) { duk_to_string(ctx, i); } if (nargs == 0) { duk_push_string(ctx, ""); duk_push_string(ctx, ""); } else if (nargs == 1) { /* XXX: cover this with the generic >1 case? */ duk_push_string(ctx, ""); } else { duk_insert(ctx, 0); /* [ arg1 ... argN-1 body] -> [body arg1 ... argN-1] */ duk_push_string(ctx, ","); duk_insert(ctx, 1); duk_join(ctx, nargs - 1); } /* [ body formals ], formals is comma separated list that needs to be parsed */ DUK_ASSERT_TOP(ctx, 2); /* XXX: this placeholder is not always correct, but use for now. * It will fail in corner cases; see test-dev-func-cons-args.js. */ duk_push_string(ctx, "function("); duk_dup(ctx, 1); duk_push_string(ctx, "){"); duk_dup(ctx, 0); duk_push_string(ctx, "}"); duk_concat(ctx, 5); /* [ body formals source ] */ DUK_ASSERT_TOP(ctx, 3); /* strictness is not inherited, intentional */ comp_flags = DUK_JS_COMPILE_FLAG_FUNCEXPR; duk_push_hstring_stridx(ctx, DUK_STRIDX_COMPILE); /* XXX: copy from caller? */ /* XXX: ignored now */ h_sourcecode = duk_require_hstring(ctx, -2); duk_js_compile(thr, (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_sourcecode), (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sourcecode), comp_flags); func = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1); DUK_ASSERT(func != NULL); DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) func)); /* [ body formals source template ] */ /* only outer_lex_env matters, as functions always get a new * variable declaration environment. */ outer_lex_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; outer_var_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; duk_js_push_closure(thr, func, outer_var_env, outer_lex_env, 1 /*add_auto_proto*/); /* [ body formals source template closure ] */ return 1; }
/* XXX: the implementation now assumes "chained" bound functions, * whereas "collapsed" bound functions (where there is ever only * one bound function which directly points to a non-bound, final * function) would require a "collapsing" implementation which * merges argument lists etc here. */ DUK_INTERNAL duk_ret_t duk_bi_function_prototype_bind(duk_context *ctx) { duk_hobject *h_bound; duk_hobject *h_target; duk_idx_t nargs; duk_idx_t i; /* vararg function, careful arg handling (e.g. thisArg may not be present) */ nargs = duk_get_top(ctx); /* = 1 + arg count */ if (nargs == 0) { duk_push_undefined(ctx); nargs++; } DUK_ASSERT(nargs >= 1); duk_push_this(ctx); if (!duk_is_callable(ctx, -1)) { DUK_DDD(DUK_DDDPRINT("func is not callable")); goto type_error; } /* [ thisArg arg1 ... argN func ] (thisArg+args == nargs total) */ DUK_ASSERT_TOP(ctx, nargs + 1); /* create bound function object */ duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_BOUND | DUK_HOBJECT_FLAG_CONSTRUCTABLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION), DUK_BIDX_FUNCTION_PROTOTYPE); h_bound = duk_get_hobject(ctx, -1); DUK_ASSERT(h_bound != NULL); /* [ thisArg arg1 ... argN func boundFunc ] */ duk_dup(ctx, -2); /* func */ duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE); duk_dup(ctx, 0); /* thisArg */ duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_THIS, DUK_PROPDESC_FLAGS_NONE); duk_push_array(ctx); /* [ thisArg arg1 ... argN func boundFunc argArray ] */ for (i = 0; i < nargs - 1; i++) { duk_dup(ctx, 1 + i); duk_put_prop_index(ctx, -2, i); } duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_ARGS, DUK_PROPDESC_FLAGS_NONE); /* [ thisArg arg1 ... argN func boundFunc ] */ /* bound function 'length' property is interesting */ h_target = duk_get_hobject(ctx, -2); if (h_target == NULL || /* lightfunc */ DUK_HOBJECT_GET_CLASS_NUMBER(h_target) == DUK_HOBJECT_CLASS_FUNCTION) { /* For lightfuncs, simply read the virtual property. */ duk_int_t tmp; duk_get_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH); tmp = duk_to_int(ctx, -1) - (nargs - 1); /* step 15.a */ duk_pop(ctx); duk_push_int(ctx, (tmp < 0 ? 0 : tmp)); } else { duk_push_int(ctx, 0); } duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); /* attrs in E5 Section 15.3.5.1 */ /* caller and arguments must use the same thrower, [[ThrowTypeError]] */ duk_xdef_prop_stridx_thrower(ctx, -1, DUK_STRIDX_CALLER, DUK_PROPDESC_FLAGS_NONE); duk_xdef_prop_stridx_thrower(ctx, -1, DUK_STRIDX_LC_ARGUMENTS, DUK_PROPDESC_FLAGS_NONE); /* these non-standard properties are copied for convenience */ /* XXX: 'copy properties' API call? */ duk_get_prop_stridx(ctx, -2, DUK_STRIDX_NAME); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_WC); duk_get_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC); /* The 'strict' flag is copied to get the special [[Get]] of E5.1 * Section 15.3.5.4 to apply when a 'caller' value is a strict bound * function. Not sure if this is correct, because the specification * is a bit ambiguous on this point but it would make sense. */ if (h_target == NULL) { /* Lightfuncs are always strict. */ DUK_HOBJECT_SET_STRICT(h_bound); } else if (DUK_HOBJECT_HAS_STRICT(h_target)) { DUK_HOBJECT_SET_STRICT(h_bound); } DUK_DDD(DUK_DDDPRINT("created bound function: %!iT", (duk_tval *) duk_get_tval(ctx, -1))); return 1; type_error: return DUK_RET_TYPE_ERROR; }
DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t output_type) { duk_hthread *thr = (duk_hthread *) ctx; duk_idx_t idx_td; duk_small_int_t i; /* traceback depth fits into 16 bits */ duk_small_int_t t; /* stack type fits into 16 bits */ duk_small_int_t count_func = 0; /* traceback depth ensures fits into 16 bits */ const char *str_tailcall = " tailcall"; const char *str_strict = " strict"; const char *str_construct = " construct"; const char *str_prevyield = " preventsyield"; const char *str_directeval = " directeval"; const char *str_empty = ""; DUK_ASSERT_TOP(ctx, 0); /* fixed arg count */ DUK_UNREF(thr); duk_push_this(ctx); duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_TRACEDATA); idx_td = duk_get_top_index(ctx); duk_push_hstring_stridx(ctx, DUK_STRIDX_NEWLINE_4SPACE); duk_push_this(ctx); /* [ ... this tracedata sep this ] */ /* XXX: skip null filename? */ if (duk_check_type(ctx, idx_td, DUK_TYPE_OBJECT)) { /* Current tracedata contains 2 entries per callstack entry. */ for (i = 0; ; i += 2) { duk_int_t pc; duk_int_t line; duk_int_t flags; duk_double_t d; const char *funcname; const char *filename; duk_hobject *h_func; duk_hstring *h_name; duk_require_stack(ctx, 5); duk_get_prop_index(ctx, idx_td, i); duk_get_prop_index(ctx, idx_td, i + 1); d = duk_to_number(ctx, -1); pc = (duk_int_t) DUK_FMOD(d, DUK_DOUBLE_2TO32); flags = (duk_int_t) DUK_FLOOR(d / DUK_DOUBLE_2TO32); t = (duk_small_int_t) duk_get_type(ctx, -2); if (t == DUK_TYPE_OBJECT || t == DUK_TYPE_LIGHTFUNC) { /* * Ecmascript/native function call or lightfunc call */ count_func++; /* [ ... v1(func) v2(pc+flags) ] */ h_func = duk_get_hobject(ctx, -2); /* NULL for lightfunc */ duk_get_prop_stridx(ctx, -2, DUK_STRIDX_NAME); duk_get_prop_stridx(ctx, -3, DUK_STRIDX_FILE_NAME); #if defined(DUK_USE_PC2LINE) line = duk_hobject_pc2line_query(ctx, -4, (duk_uint_fast32_t) pc); #else line = 0; #endif /* [ ... v1 v2 name filename ] */ /* When looking for .fileName/.lineNumber, blame first * function which has a .fileName. */ if (duk_is_string(ctx, -1)) { if (output_type == DUK__OUTPUT_TYPE_FILENAME) { return 1; } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) { duk_push_int(ctx, line); return 1; } } /* XXX: Change 'anon' handling here too, to use empty string for anonymous functions? */ /* XXX: Could be improved by coercing to a readable duk_tval (especially string escaping) */ h_name = duk_get_hstring(ctx, -2); /* may be NULL */ funcname = (h_name == NULL || h_name == DUK_HTHREAD_STRING_EMPTY_STRING(thr)) ? "[anon]" : (const char *) DUK_HSTRING_GET_DATA(h_name); filename = duk_get_string(ctx, -1); filename = filename ? filename : ""; DUK_ASSERT(funcname != NULL); DUK_ASSERT(filename != NULL); if (h_func == NULL) { duk_push_sprintf(ctx, "at %s light%s%s%s%s%s", (const char *) funcname, (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty), (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty), (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty), (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty), (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty)); } else if (DUK_HOBJECT_HAS_NATFUNC(h_func)) { duk_push_sprintf(ctx, "at %s (%s) native%s%s%s%s%s", (const char *) funcname, (const char *) filename, (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty), (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty), (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty), (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty), (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty)); } else { duk_push_sprintf(ctx, "at %s (%s:%ld)%s%s%s%s%s", (const char *) funcname, (const char *) filename, (long) line, (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty), (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty), (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty), (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty), (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty)); } duk_replace(ctx, -5); /* [ ... v1 v2 name filename str ] -> [ ... str v2 name filename ] */ duk_pop_n(ctx, 3); /* -> [ ... str ] */ } else if (t == DUK_TYPE_STRING) { /* * __FILE__ / __LINE__ entry, here 'pc' is line number directly. * Sometimes __FILE__ / __LINE__ is reported as the source for * the error (fileName, lineNumber), sometimes not. */ /* [ ... v1(filename) v2(line+flags) ] */ /* When looking for .fileName/.lineNumber, blame compilation * or C call site unless flagged not to do so. */ if (!(flags & DUK_TB_FLAG_NOBLAME_FILELINE)) { if (output_type == DUK__OUTPUT_TYPE_FILENAME) { duk_pop(ctx); return 1; } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) { duk_push_int(ctx, pc); return 1; } } duk_push_sprintf(ctx, "at [anon] (%s:%ld) internal", (const char *) duk_get_string(ctx, -2), (long) pc); duk_replace(ctx, -3); /* [ ... v1 v2 str ] -> [ ... str v2 ] */ duk_pop(ctx); /* -> [ ... str ] */ } else { /* unknown, ignore */ duk_pop_2(ctx); break; } } if (count_func >= DUK_USE_TRACEBACK_DEPTH) { /* Possibly truncated; there is no explicit truncation * marker so this is the best we can do. */ duk_push_hstring_stridx(ctx, DUK_STRIDX_BRACKETED_ELLIPSIS); } } /* [ ... this tracedata sep this str1 ... strN ] */ if (output_type != DUK__OUTPUT_TYPE_TRACEBACK) { return 0; } else { /* The 'this' after 'sep' will get ToString() coerced by * duk_join() automatically. We don't want to do that * coercion when providing .fileName or .lineNumber (GH-254). */ duk_join(ctx, duk_get_top(ctx) - (idx_td + 2) /*count, not including sep*/); return 1; } }
/* Helper which can be called both directly and with duk_safe_call(). */ DUK_LOCAL duk_ret_t duk__do_compile(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk__compile_raw_args *comp_args; duk_uint_t flags; duk_small_uint_t comp_flags; duk_hcompiledfunction *h_templ; /* Note: strictness is not inherited from the current Duktape/C * context. Otherwise it would not be possible to compile * non-strict code inside a Duktape/C activation (which is * always strict now). See api-testcases/test-eval-strictness.c * for discussion. */ /* [ ... source? filename &comp_args ] (depends on flags) */ comp_args = (duk__compile_raw_args *) duk_require_pointer(ctx, -1); flags = comp_args->flags; duk_pop(ctx); /* [ ... source? filename ] */ if (!comp_args->src_buffer) { duk_hstring *h_sourcecode; h_sourcecode = duk_get_hstring(ctx, -2); if ((flags & DUK_COMPILE_NOSOURCE) || /* args incorrect */ (h_sourcecode == NULL)) { /* e.g. duk_push_file_string_raw() pushed undefined */ /* XXX: when this error is caused by a nonexistent * file given to duk_peval_file() or similar, the * error message is not the best possible. */ DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_NO_SOURCECODE); } DUK_ASSERT(h_sourcecode != NULL); comp_args->src_buffer = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_sourcecode); comp_args->src_length = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sourcecode); } DUK_ASSERT(comp_args->src_buffer != NULL); /* XXX: unnecessary translation of flags */ comp_flags = 0; if (flags & DUK_COMPILE_EVAL) { comp_flags |= DUK_JS_COMPILE_FLAG_EVAL; } if (flags & DUK_COMPILE_FUNCTION) { comp_flags |= DUK_JS_COMPILE_FLAG_EVAL | DUK_JS_COMPILE_FLAG_FUNCEXPR; } if (flags & DUK_COMPILE_STRICT) { comp_flags |= DUK_JS_COMPILE_FLAG_STRICT; } /* [ ... source? filename ] */ duk_js_compile(thr, comp_args->src_buffer, comp_args->src_length, comp_flags); /* [ ... source? func_template ] */ if (flags & DUK_COMPILE_NOSOURCE) { ; } else { duk_remove(ctx, -2); } /* [ ... func_template ] */ h_templ = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1); DUK_ASSERT(h_templ != NULL); duk_js_push_closure(thr, h_templ, thr->builtins[DUK_BIDX_GLOBAL_ENV], thr->builtins[DUK_BIDX_GLOBAL_ENV]); duk_remove(ctx, -2); /* -> [ ... closure ] */ /* [ ... closure ] */ return 1; }
/* XXX: better place for this */ DUK_EXTERNAL void duk_set_global_object(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_hobject *h_glob; duk_hobject *h_prev_glob; duk_hobject *h_env; duk_hobject *h_prev_env; DUK_D(DUK_DPRINT("replace global object with: %!T", duk_get_tval(ctx, -1))); h_glob = duk_require_hobject(ctx, -1); DUK_ASSERT(h_glob != NULL); /* * Replace global object. */ h_prev_glob = thr->builtins[DUK_BIDX_GLOBAL]; DUK_UNREF(h_prev_glob); thr->builtins[DUK_BIDX_GLOBAL] = h_glob; DUK_HOBJECT_INCREF(thr, h_glob); DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_prev_glob); /* side effects, in theory (referenced by global env) */ /* * Replace lexical environment for global scope * * Create a new object environment for the global lexical scope. * We can't just reset the _Target property of the current one, * because the lexical scope is shared by other threads with the * same (initial) built-ins. */ (void) duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV), -1); /* no prototype, updated below */ duk_dup(ctx, -2); duk_dup(ctx, -3); /* [ ... new_glob new_env new_glob new_glob ] */ duk_xdef_prop_stridx(thr, -3, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE); duk_xdef_prop_stridx(thr, -2, DUK_STRIDX_INT_THIS, DUK_PROPDESC_FLAGS_NONE); /* [ ... new_glob new_env ] */ h_env = duk_get_hobject(ctx, -1); DUK_ASSERT(h_env != NULL); h_prev_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; thr->builtins[DUK_BIDX_GLOBAL_ENV] = h_env; DUK_HOBJECT_INCREF(thr, h_env); DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_prev_env); /* side effects */ DUK_UNREF(h_env); /* without refcounts */ DUK_UNREF(h_prev_env); /* [ ... new_glob new_env ] */ duk_pop_2(ctx); /* [ ... ] */ }
/* XXX: much to improve (code size) */ DUK_INTERNAL duk_ret_t duk_bi_regexp_constructor(duk_hthread *thr) { duk_hobject *h_pattern; DUK_ASSERT_TOP(thr, 2); h_pattern = duk_get_hobject(thr, 0); if (!duk_is_constructor_call(thr) && h_pattern != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h_pattern) == DUK_HOBJECT_CLASS_REGEXP && duk_is_undefined(thr, 1)) { /* Called as a function, pattern has [[Class]] "RegExp" and * flags is undefined -> return object as is. */ /* XXX: ES2015 has a NewTarget SameValue() check which is not * yet implemented. */ duk_dup_0(thr); return 1; } /* Else functionality is identical for function call and constructor * call. */ if (h_pattern != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h_pattern) == DUK_HOBJECT_CLASS_REGEXP) { duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_SOURCE); if (duk_is_undefined(thr, 1)) { /* In ES5 one would need to read the flags individually; * in ES2015 just read .flags. */ duk_get_prop_stridx(thr, 0, DUK_STRIDX_FLAGS); } else { /* In ES2015 allowed; overrides argument RegExp flags. */ duk_dup_1(thr); } } else { if (duk_is_undefined(thr, 0)) { duk_push_hstring_empty(thr); } else { duk_dup_0(thr); duk_to_string(thr, -1); /* Rejects Symbols. */ } if (duk_is_undefined(thr, 1)) { duk_push_hstring_empty(thr); } else { duk_dup_1(thr); duk_to_string(thr, -1); /* Rejects Symbols. */ } /* [ ... pattern flags ] */ } DUK_DDD(DUK_DDDPRINT("RegExp constructor/function call, pattern=%!T, flags=%!T", (duk_tval *) duk_get_tval(thr, -2), (duk_tval *) duk_get_tval(thr, -1))); /* [ ... pattern flags ] (both uncoerced) */ duk_to_string(thr, -2); duk_to_string(thr, -1); duk_regexp_compile(thr); /* [ ... bytecode escaped_source ] */ duk_regexp_create_instance(thr); /* [ ... RegExp ] */ return 1; }