DUK_EXTERNAL void duk_dump_function(duk_context *ctx) { duk_hthread *thr; duk_hcompiledfunction *func; duk_bufwriter_ctx bw_ctx_alloc; duk_bufwriter_ctx *bw_ctx = &bw_ctx_alloc; duk_uint8_t *p; DUK_ASSERT(ctx != NULL); thr = (duk_hthread *) ctx; /* Bound functions don't have all properties so we'd either need to * lookup the non-bound target function or reject bound functions. * For now, bound functions are rejected. */ func = duk_require_hcompiledfunction(ctx, -1); DUK_ASSERT(func != NULL); DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(&func->obj)); /* Estimating the result size beforehand would be costly, so * start with a reasonable size and extend as needed. */ DUK_BW_INIT_PUSHBUF(thr, bw_ctx, DUK__BYTECODE_INITIAL_ALLOC); p = DUK_BW_GET_PTR(thr, bw_ctx); *p++ = DUK__SER_MARKER; *p++ = DUK__SER_VERSION; p = duk__dump_func(ctx, func, bw_ctx, p); DUK_BW_SET_PTR(thr, bw_ctx, p); DUK_BW_COMPACT(thr, bw_ctx); DUK_DD(DUK_DDPRINT("serialized result: %!T", duk_get_tval(ctx, -1))); duk_remove(ctx, -2); /* [ ... func buf ] -> [ ... buf ] */ }
void duk_debug_dump_hobject(duk_hobject *obj) { duk_uint_fast32_t i; const char *str_empty = ""; const char *str_excl = "!"; DUK_D(DUK_DPRINT("=== hobject %p ===", (void *) obj)); if (!obj) { return; } DUK_D(DUK_DPRINT(" %sextensible", DUK_HOBJECT_HAS_EXTENSIBLE(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sconstructable", DUK_HOBJECT_HAS_CONSTRUCTABLE(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sbound", DUK_HOBJECT_HAS_BOUND(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %scompiledfunction", DUK_HOBJECT_HAS_COMPILEDFUNCTION(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %snativefunction", DUK_HOBJECT_HAS_NATIVEFUNCTION(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sthread", DUK_HOBJECT_HAS_THREAD(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sarray_part", DUK_HOBJECT_HAS_ARRAY_PART(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sstrict", DUK_HOBJECT_HAS_STRICT(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %snewenv", DUK_HOBJECT_HAS_NEWENV(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %snamebinding", DUK_HOBJECT_HAS_NAMEBINDING(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %screateargs", DUK_HOBJECT_HAS_CREATEARGS(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %senvrecclosed", DUK_HOBJECT_HAS_ENVRECCLOSED(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sexotic_array", DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sexotic_stringobj", DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sexotic_arguments", DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sexotic_dukfunc", DUK_HOBJECT_HAS_EXOTIC_DUKFUNC(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sexotic_bufferobj", DUK_HOBJECT_HAS_EXOTIC_BUFFEROBJ(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" %sexotic_proxyobj", DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj) ? str_empty : str_excl)); DUK_D(DUK_DPRINT(" class: number %d -> %s", (int) DUK_HOBJECT_GET_CLASS_NUMBER(obj), duk__class_names[(DUK_HOBJECT_GET_CLASS_NUMBER(obj)) & ((1 << DUK_HOBJECT_FLAG_CLASS_BITS) - 1)])); DUK_D(DUK_DPRINT(" prototype: %p -> %!O", (void *) obj->prototype, (duk_heaphdr *) obj->prototype)); DUK_D(DUK_DPRINT(" props: p=%p, e_size=%d, e_used=%d, a_size=%d, h_size=%d", (void *) obj->p, (int) obj->e_size, (int) obj->e_used, (int) obj->a_size, (int) obj->h_size)); /* * Object (struct layout) specific dumping. Inline code here * instead of helpers, to ensure debug line prefix is identical. */ if (DUK_HOBJECT_IS_COMPILEDFUNCTION(obj)) { duk_hcompiledfunction *h = (duk_hcompiledfunction *) obj; DUK_D(DUK_DPRINT(" hcompiledfunction")); DUK_D(DUK_DPRINT(" data: %!O", h->data)); DUK_D(DUK_DPRINT(" nregs: %d", (int) h->nregs)); DUK_D(DUK_DPRINT(" nargs: %d", (int) h->nargs)); if (h->data && DUK_HBUFFER_HAS_DYNAMIC(h->data) && DUK_HBUFFER_GET_DATA_PTR(h->data)) { DUK_D(DUK_DPRINT(" consts: %p (%d, %d bytes)", (void *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(h), (int) DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(h), (int) DUK_HCOMPILEDFUNCTION_GET_CONSTS_SIZE(h))); DUK_D(DUK_DPRINT(" funcs: %p (%d, %d bytes)", (void *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(h), (int) DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(h), (int) DUK_HCOMPILEDFUNCTION_GET_FUNCS_SIZE(h))); DUK_D(DUK_DPRINT(" bytecode: %p (%d, %d bytes)", (void *) DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(h), (int) DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(h), (int) DUK_HCOMPILEDFUNCTION_GET_CODE_SIZE(h))); } else { DUK_D(DUK_DPRINT(" consts: ???")); DUK_D(DUK_DPRINT(" funcs: ???")); DUK_D(DUK_DPRINT(" bytecode: ???")); } } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(obj)) { duk_hnativefunction *h = (duk_hnativefunction *) obj; DUK_D(DUK_DPRINT(" hnativefunction")); /* XXX: h->func, cannot print function pointers portably */ DUK_D(DUK_DPRINT(" nargs: %d", (int) h->nargs)); } else if (DUK_HOBJECT_IS_THREAD(obj)) { duk_hthread *thr = (duk_hthread *) obj; duk_tval *p; DUK_D(DUK_DPRINT(" hthread")); DUK_D(DUK_DPRINT(" strict: %d", (int) thr->strict)); DUK_D(DUK_DPRINT(" state: %d", (int) thr->state)); DUK_D(DUK_DPRINT(" valstack_max: %d, callstack_max:%d, catchstack_max: %d", thr->valstack_max, thr->callstack_max, thr->catchstack_max)); DUK_D(DUK_DPRINT(" callstack: ptr %p, size %d, top %d, preventcount %d, used size %d entries (%d bytes), alloc size %d entries (%d bytes)", (void *) thr->callstack, thr->callstack_size, thr->callstack_top, thr->callstack_preventcount, thr->callstack_top, thr->callstack_top * sizeof(duk_activation), thr->callstack_size, thr->callstack_size * sizeof(duk_activation))); DUK_DEBUG_SUMMARY_INIT(); DUK_DEBUG_SUMMARY_CHAR('['); for (i = 0; i <= thr->callstack_size; i++) { if (i == thr->callstack_top) { DUK_DEBUG_SUMMARY_CHAR('|'); } if (!thr->callstack) { DUK_DEBUG_SUMMARY_CHAR('@'); } else if (i < thr->callstack_size) { if (i < thr->callstack_top) { /* tailcalling is nice to see immediately; other flags (e.g. strict) * not that important. */ if (thr->callstack[i].flags & DUK_ACT_FLAG_TAILCALLED) { DUK_DEBUG_SUMMARY_CHAR('/'); } DUK_DEBUG_SUMMARY_CHAR(duk__get_act_summary_char(&thr->callstack[i])); } else { DUK_DEBUG_SUMMARY_CHAR('.'); } } } DUK_DEBUG_SUMMARY_CHAR(']'); DUK_DEBUG_SUMMARY_FINISH(); DUK_D(DUK_DPRINT(" valstack: ptr %p, end %p (%d), bottom %p (%d), top %p (%d), used size %d entries (%d bytes), alloc size %d entries (%d bytes)", (void *) thr->valstack, (void *) thr->valstack_end, (int) (thr->valstack_end - thr->valstack), (void *) thr->valstack_bottom, (int) (thr->valstack_bottom - thr->valstack), (void *) thr->valstack_top, (int) (thr->valstack_top - thr->valstack), (int) (thr->valstack_top - thr->valstack), (int) (thr->valstack_top - thr->valstack) * sizeof(duk_tval), (int) (thr->valstack_end - thr->valstack), (int) (thr->valstack_end - thr->valstack) * sizeof(duk_tval))); DUK_DEBUG_SUMMARY_INIT(); DUK_DEBUG_SUMMARY_CHAR('['); p = thr->valstack; while (p <= thr->valstack_end) { i = (duk_uint_fast32_t) (p - thr->valstack); if (thr->callstack && thr->callstack_top > 0 && i == (duk_size_t) (thr->callstack + thr->callstack_top - 1)->idx_bottom) { DUK_DEBUG_SUMMARY_CHAR('>'); } if (p == thr->valstack_top) { DUK_DEBUG_SUMMARY_CHAR('|'); } if (p < thr->valstack_end) { if (p < thr->valstack_top) { DUK_DEBUG_SUMMARY_CHAR(duk__get_tval_summary_char(p)); } else { /* XXX: safe printer for these? would be nice, because * we could visualize whether the values are in proper * state. */ DUK_DEBUG_SUMMARY_CHAR('.'); } } p++; } DUK_DEBUG_SUMMARY_CHAR(']'); DUK_DEBUG_SUMMARY_FINISH(); DUK_D(DUK_DPRINT(" catchstack: ptr %p, size %d, top %d, used size %d entries (%d bytes), alloc size %d entries (%d bytes)", (void *) thr->catchstack, thr->catchstack_size, thr->catchstack_top, thr->catchstack_top, thr->catchstack_top * sizeof(duk_catcher), thr->catchstack_size, thr->catchstack_size * sizeof(duk_catcher))); DUK_DEBUG_SUMMARY_INIT(); DUK_DEBUG_SUMMARY_CHAR('['); for (i = 0; i <= thr->catchstack_size; i++) { if (i == thr->catchstack_top) { DUK_DEBUG_SUMMARY_CHAR('|'); } if (!thr->catchstack) { DUK_DEBUG_SUMMARY_CHAR('@'); } else if (i < thr->catchstack_size) { if (i < thr->catchstack_top) { DUK_DEBUG_SUMMARY_CHAR(duk__get_cat_summary_char(&thr->catchstack[i])); } else { DUK_DEBUG_SUMMARY_CHAR('.'); } } } DUK_DEBUG_SUMMARY_CHAR(']'); DUK_DEBUG_SUMMARY_FINISH(); DUK_D(DUK_DPRINT(" resumer: ptr %p", (void *) thr->resumer)); #if 0 /* worth dumping? */ for (i = 0; i < DUK_NUM_BUILTINS; i++) { DUK_D(DUK_DPRINT(" builtins[%d] -> %!@O", i, thr->builtins[i])); } #endif } if (obj->p) { DUK_D(DUK_DPRINT(" props alloc size: %d", (int) DUK_HOBJECT_P_COMPUTE_SIZE(obj->e_size, obj->a_size, obj->h_size))); } else { DUK_D(DUK_DPRINT(" props alloc size: n/a")); } DUK_D(DUK_DPRINT(" prop entries:")); for (i = 0; i < obj->e_size; i++) { duk_hstring *k; duk_propvalue *v; k = DUK_HOBJECT_E_GET_KEY(obj, i); v = DUK_HOBJECT_E_GET_VALUE_PTR(obj, i); if (i >= obj->e_used) { DUK_D(DUK_DPRINT(" [%d]: UNUSED", i)); continue; } if (!k) { DUK_D(DUK_DPRINT(" [%d]: NULL", i)); continue; } if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(obj, i)) { DUK_D(DUK_DPRINT(" [%d]: [w=%d e=%d c=%d a=%d] %!O -> get:%p set:%p; get %!O; set %!O", i, DUK_HOBJECT_E_SLOT_IS_WRITABLE(obj, i), DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(obj, i), DUK_HOBJECT_E_SLOT_IS_CONFIGURABLE(obj, i), DUK_HOBJECT_E_SLOT_IS_ACCESSOR(obj, i), k, (void *) v->a.get, (void *) v->a.set, (duk_heaphdr *) v->a.get, (duk_heaphdr *) v->a.set)); } else { DUK_D(DUK_DPRINT(" [%d]: [w=%d e=%d c=%d a=%d] %!O -> %!T", i, DUK_HOBJECT_E_SLOT_IS_WRITABLE(obj, i), DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(obj, i), DUK_HOBJECT_E_SLOT_IS_CONFIGURABLE(obj, i), DUK_HOBJECT_E_SLOT_IS_ACCESSOR(obj, i), k, &v->v)); } } DUK_D(DUK_DPRINT(" array entries:")); for (i = 0; i < obj->a_size; i++) { DUK_D(DUK_DPRINT(" [%d]: [w=%d e=%d c=%d a=%d] %d -> %!T", i, 1, /* implicit attributes */ 1, 1, 0, i, DUK_HOBJECT_A_GET_VALUE_PTR(obj, i))); } DUK_D(DUK_DPRINT(" hash entries:")); for (i = 0; i < obj->h_size; i++) { duk_uint32_t t = DUK_HOBJECT_H_GET_INDEX(obj, i); if (t == DUK_HOBJECT_HASHIDX_UNUSED) { DUK_D(DUK_DPRINT(" [%d]: unused", i)); } else if (t == DUK_HOBJECT_HASHIDX_DELETED) { DUK_D(DUK_DPRINT(" [%d]: deleted", i)); } else { DUK_D(DUK_DPRINT(" [%d]: %d", i, (int) t)); } } }
DUK_INTERNAL duk_ret_t duk_bi_function_prototype_to_string(duk_context *ctx) { duk_tval *tv; /* * E5 Section 15.3.4.2 places few requirements on the output of * this function: * * - The result is an implementation dependent representation * of the function; in particular * * - The result must follow the syntax of a FunctionDeclaration. * In particular, the function must have a name (even in the * case of an anonymous function or a function with an empty * name). * * - Note in particular that the output does NOT need to compile * into anything useful. */ /* XXX: faster internal way to get this */ duk_push_this(ctx); tv = duk_get_tval(ctx, -1); DUK_ASSERT(tv != NULL); if (DUK_TVAL_IS_OBJECT(tv)) { duk_hobject *obj = DUK_TVAL_GET_OBJECT(tv); const char *func_name; /* Function name: missing/undefined is mapped to empty string, * otherwise coerce to string. */ /* XXX: currently no handling for non-allowed identifier characters, * e.g. a '{' in the function name. */ duk_get_prop_stridx(ctx, -1, DUK_STRIDX_NAME); if (duk_is_undefined(ctx, -1)) { func_name = ""; } else { func_name = duk_to_string(ctx, -1); DUK_ASSERT(func_name != NULL); } /* Indicate function type in the function body using a dummy * directive. */ if (DUK_HOBJECT_HAS_COMPILEDFUNCTION(obj)) { duk_push_sprintf(ctx, "function %s() {\"ecmascript\"}", (const char *) func_name); } else if (DUK_HOBJECT_HAS_NATIVEFUNCTION(obj)) { duk_push_sprintf(ctx, "function %s() {\"native\"}", (const char *) func_name); } else if (DUK_HOBJECT_HAS_BOUND(obj)) { duk_push_sprintf(ctx, "function %s() {\"bound\"}", (const char *) func_name); } else { goto type_error; } } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) { duk_push_lightfunc_tostring(ctx, tv); } else { goto type_error; } return 1; type_error: return DUK_RET_TYPE_ERROR; }
DUK_EXTERNAL void duk_new(duk_context *ctx, duk_idx_t nargs) { /* * There are two [[Construct]] operations in the specification: * * - E5 Section 13.2.2: for Function objects * - E5 Section 15.3.4.5.2: for "bound" Function objects * * The chain of bound functions is resolved in Section 15.3.4.5.2, * with arguments "piling up" until the [[Construct]] internal * method is called on the final, actual Function object. Note * that the "prototype" property is looked up *only* from the * final object, *before* calling the constructor. * * Currently we follow the bound function chain here to get the * "prototype" property value from the final, non-bound function. * However, we let duk_handle_call() handle the argument "piling" * when the constructor is called. The bound function chain is * thus now processed twice. * * When constructing new Array instances, an unnecessary object is * created and discarded now: the standard [[Construct]] creates an * object, and calls the Array constructor. The Array constructor * returns an Array instance, which is used as the result value for * the "new" operation; the object created before the Array constructor * call is discarded. * * This would be easy to fix, e.g. by knowing that the Array constructor * will always create a replacement object and skip creating the fallback * object in that case. * * Note: functions called via "new" need to know they are called as a * constructor. For instance, built-in constructors behave differently * depending on how they are called. */ /* XXX: merge this with duk_js_call.c, as this function implements * core semantics (or perhaps merge the two files altogether). */ duk_hthread *thr = (duk_hthread *) ctx; duk_hobject *proto; duk_hobject *cons; duk_hobject *fallback; duk_idx_t idx_cons; duk_small_uint_t call_flags; DUK_ASSERT_CTX_VALID(ctx); /* [... constructor arg1 ... argN] */ idx_cons = duk_require_normalize_index(ctx, -nargs - 1); DUK_DDD(DUK_DDDPRINT("top=%ld, nargs=%ld, idx_cons=%ld", (long) duk_get_top(ctx), (long) nargs, (long) idx_cons)); /* XXX: code duplication */ /* * Figure out the final, non-bound constructor, to get "prototype" * property. */ duk_dup(ctx, idx_cons); for (;;) { cons = duk_get_hobject(ctx, -1); if (cons == NULL || !DUK_HOBJECT_HAS_CONSTRUCTABLE(cons)) { /* Checking constructability from anything else than the * initial constructor is not strictly necessary, but a * nice sanity check. */ goto not_constructable; } if (!DUK_HOBJECT_HAS_BOUND(cons)) { break; } duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_TARGET); /* -> [... cons target] */ duk_remove(ctx, -2); /* -> [... target] */ } DUK_ASSERT(cons != NULL && !DUK_HOBJECT_HAS_BOUND(cons)); /* [... constructor arg1 ... argN final_cons] */ /* * Create "fallback" object to be used as the object instance, * unless the constructor returns a replacement value. * Its internal prototype needs to be set based on "prototype" * property of the constructor. */ duk_push_object(ctx); /* class Object, extensible */ /* [... constructor arg1 ... argN final_cons fallback] */ duk_get_prop_stridx(ctx, -2, DUK_STRIDX_PROTOTYPE); proto = duk_get_hobject(ctx, -1); if (!proto) { DUK_DDD(DUK_DDDPRINT("constructor has no 'prototype' property, or value not an object " "-> leave standard Object prototype as fallback prototype")); } else { DUK_DDD(DUK_DDDPRINT("constructor has 'prototype' property with object value " "-> set fallback prototype to that value: %!iO", (duk_heaphdr *) proto)); fallback = duk_get_hobject(ctx, -2); DUK_ASSERT(fallback != NULL); DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, fallback, proto); } duk_pop(ctx); /* [... constructor arg1 ... argN final_cons fallback] */ /* * Manipulate callstack for the call. */ duk_dup_top(ctx); duk_insert(ctx, idx_cons + 1); /* use fallback as 'this' value */ duk_insert(ctx, idx_cons); /* also stash it before constructor, * in case we need it (as the fallback value) */ duk_pop(ctx); /* pop final_cons */ /* [... fallback constructor fallback(this) arg1 ... argN]; * Note: idx_cons points to first 'fallback', not 'constructor'. */ DUK_DDD(DUK_DDDPRINT("before call, idx_cons+1 (constructor) -> %!T, idx_cons+2 (fallback/this) -> %!T, " "nargs=%ld, top=%ld", (duk_tval *) duk_get_tval(ctx, idx_cons + 1), (duk_tval *) duk_get_tval(ctx, idx_cons + 2), (long) nargs, (long) duk_get_top(ctx))); /* * Call the constructor function (called in "constructor mode"). */ call_flags = DUK_CALL_FLAG_CONSTRUCTOR_CALL; /* not protected, respect reclimit, is a constructor call */ duk_handle_call_unprotected(thr, /* thread */ nargs, /* num_stack_args */ call_flags); /* call_flags */ /* [... fallback retval] */ DUK_DDD(DUK_DDDPRINT("constructor call finished, fallback=%!iT, retval=%!iT", (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1))); /* * Determine whether to use the constructor return value as the created * object instance or not. */ if (duk_is_object(ctx, -1)) { duk_remove(ctx, -2); } else { duk_pop(ctx); } /* * Augment created errors upon creation (not when they are thrown or * rethrown). __FILE__ and __LINE__ are not desirable here; the call * stack reflects the caller which is correct. */ #ifdef DUK_USE_AUGMENT_ERROR_CREATE duk_hthread_sync_currpc(thr); duk_err_augment_error_create(thr, thr, NULL, 0, 1 /*noblame_fileline*/); #endif /* [... retval] */ return; not_constructable: DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_CONSTRUCTABLE); }
DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y) { duk_context *ctx = (duk_context *) thr; duk_hobject *func; duk_hobject *val; duk_hobject *proto; duk_uint_t sanity; /* * Get the values onto the stack first. It would be possible to cover * some normal cases without resorting to the value stack. * * The right hand side could be a light function (as they generally * behave like objects). Light functions never have a 'prototype' * property so E5.1 Section 15.3.5.3 step 3 always throws a TypeError. * Using duk_require_hobject() is thus correct (except for error msg). */ duk_push_tval(ctx, tv_x); duk_push_tval(ctx, tv_y); func = duk_require_hobject(ctx, -1); /* * For bound objects, [[HasInstance]] just calls the target function * [[HasInstance]]. If that is again a bound object, repeat until * we find a non-bound Function object. */ /* XXX: this bound function resolution also happens elsewhere, * move into a shared helper. */ sanity = DUK_HOBJECT_BOUND_CHAIN_SANITY; do { /* check func supports [[HasInstance]] (this is checked for every function * in the bound chain, including the final one) */ if (!DUK_HOBJECT_IS_CALLABLE(func)) { /* * Note: of native Ecmascript objects, only Function instances * have a [[HasInstance]] internal property. Custom objects might * also have it, but not in current implementation. * * XXX: add a separate flag, DUK_HOBJECT_FLAG_ALLOW_INSTANCEOF? */ DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid instanceof rval"); } if (!DUK_HOBJECT_HAS_BOUND(func)) { break; } /* [ ... lval rval ] */ duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_TARGET); /* -> [ ... lval rval new_rval ] */ duk_replace(ctx, -1); /* -> [ ... lval new_rval ] */ func = duk_require_hobject(ctx, -1); /* func support for [[HasInstance]] checked in the beginning of the loop */ } while (--sanity > 0); if (sanity == 0) { DUK_ERROR(thr, DUK_ERR_INTERNAL_ERROR, DUK_STR_BOUND_CHAIN_LIMIT); } /* * 'func' is now a non-bound object which supports [[HasInstance]] * (which here just means DUK_HOBJECT_FLAG_CALLABLE). Move on * to execute E5 Section 15.3.5.3. */ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func)); DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(func)); /* [ ... lval rval(func) ] */ /* Handle lightfuncs through object coercion for now. */ /* XXX: direct implementation */ val = duk_get_hobject_or_lfunc_coerce(ctx, -2); if (!val) { goto pop_and_false; } duk_get_prop_stridx(ctx, -1, DUK_STRIDX_PROTOTYPE); /* -> [ ... lval rval rval.prototype ] */ proto = duk_require_hobject(ctx, -1); duk_pop(ctx); /* -> [ ... lval rval ] */ sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY; do { /* * Note: prototype chain is followed BEFORE first comparison. This * means that the instanceof lval is never itself compared to the * rval.prototype property. This is apparently intentional, see E5 * Section 15.3.5.3, step 4.a. * * Also note: * * js> (function() {}) instanceof Function * true * js> Function instanceof Function * true * * For the latter, h_proto will be Function.prototype, which is the * built-in Function prototype. Because Function.[[Prototype]] is * also the built-in Function prototype, the result is true. */ val = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, val); if (!val) { goto pop_and_false; } else if (val == proto) { goto pop_and_true; } /* follow prototype chain */ } while (--sanity > 0); if (sanity == 0) { DUK_ERROR(thr, DUK_ERR_INTERNAL_ERROR, DUK_STR_PROTOTYPE_CHAIN_LIMIT); } DUK_UNREACHABLE(); pop_and_false: duk_pop_2(ctx); return 0; pop_and_true: duk_pop_2(ctx); return 1; }
int duk_bi_function_prototype_to_string(duk_context *ctx) { duk_tval *tv; /* * E5 Section 15.3.4.2 places few requirements on the output of * this function: * * - The result is an implementation dependent representation * of the function; in particular * * - The result must follow the syntax of a FunctionDeclaration. * In particular, the function must have a name (even in the * case of an anonymous function or a function with an empty * name). * * - Note in particular that the output does NOT need to compile * into anything useful. */ /* FIXME: faster internal way to get this */ duk_push_this(ctx); tv = duk_get_tval(ctx, -1); DUK_ASSERT(tv != NULL); if (DUK_TVAL_IS_OBJECT(tv)) { duk_hobject *obj = DUK_TVAL_GET_OBJECT(tv); const char *func_name = "anonymous"; /* FIXME: rework, it would be nice to avoid C formatting functions to * ensure there are no Unicode issues. */ duk_get_prop_stridx(ctx, -1, DUK_STRIDX_NAME); if (!duk_is_undefined(ctx, -1)) { func_name = duk_to_string(ctx, -1); DUK_ASSERT(func_name != NULL); if (func_name[0] == (char) 0) { func_name = "empty"; } } if (DUK_HOBJECT_HAS_COMPILEDFUNCTION(obj)) { /* FIXME: actual source, if available */ duk_push_sprintf(ctx, "function %s() {/* source code */}", func_name); } else if (DUK_HOBJECT_HAS_NATIVEFUNCTION(obj)) { duk_push_sprintf(ctx, "function %s() {/* native code */}", func_name); } else if (DUK_HOBJECT_HAS_BOUND(obj)) { duk_push_sprintf(ctx, "function %s() {/* bound */}", func_name); } else { goto type_error; } } else { goto type_error; } return 1; type_error: return DUK_RET_TYPE_ERROR; }
DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { duk_context *ctx = (duk_context *) thr; duk_bitdecoder_ctx bd_ctx; duk_bitdecoder_ctx *bd = &bd_ctx; /* convenience */ duk_hobject *h; duk_small_uint_t i, j; DUK_D(DUK_DPRINT("INITBUILTINS BEGIN")); DUK_MEMZERO(&bd_ctx, sizeof(bd_ctx)); bd->data = (const duk_uint8_t *) duk_builtins_data; bd->length = (duk_size_t) DUK_BUILTINS_DATA_LENGTH; /* * First create all built-in bare objects on the empty valstack. * During init, their indices will correspond to built-in indices. * * Built-ins will be reachable from both valstack and thr->builtins. */ /* XXX: there is no need to resize valstack because builtin count * is much less than the default space; assert for it. */ DUK_DD(DUK_DDPRINT("create empty built-ins")); DUK_ASSERT_TOP(ctx, 0); for (i = 0; i < DUK_NUM_BUILTINS; i++) { duk_small_uint_t class_num; duk_small_int_t len = -1; /* must be signed */ class_num = (duk_small_uint_t) duk_bd_decode(bd, DUK__CLASS_BITS); len = (duk_small_int_t) duk_bd_decode_flagged(bd, DUK__LENGTH_PROP_BITS, (duk_int32_t) -1 /*def_value*/); if (class_num == DUK_HOBJECT_CLASS_FUNCTION) { duk_small_uint_t natidx; duk_small_uint_t stridx; duk_int_t c_nargs; /* must hold DUK_VARARGS */ duk_c_function c_func; duk_int16_t magic; DUK_DDD(DUK_DDDPRINT("len=%ld", (long) len)); DUK_ASSERT(len >= 0); natidx = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS); stridx = (duk_small_uint_t) duk_bd_decode(bd, DUK__STRIDX_BITS); c_func = duk_bi_native_functions[natidx]; c_nargs = (duk_small_uint_t) duk_bd_decode_flagged(bd, DUK__NARGS_BITS, len /*def_value*/); if (c_nargs == DUK__NARGS_VARARGS_MARKER) { c_nargs = DUK_VARARGS; } /* XXX: set magic directly here? (it could share the c_nargs arg) */ duk_push_c_function_noexotic(ctx, c_func, c_nargs); h = duk_require_hobject(ctx, -1); DUK_ASSERT(h != NULL); /* Currently all built-in native functions are strict. * duk_push_c_function() now sets strict flag, so * assert for it. */ DUK_ASSERT(DUK_HOBJECT_HAS_STRICT(h)); /* XXX: function properties */ duk_push_hstring_stridx(ctx, stridx); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE); /* Almost all global level Function objects are constructable * but not all: Function.prototype is a non-constructable, * callable Function. */ if (duk_bd_decode_flag(bd)) { DUK_ASSERT(DUK_HOBJECT_HAS_CONSTRUCTABLE(h)); } else { DUK_HOBJECT_CLEAR_CONSTRUCTABLE(h); } /* Cast converts magic to 16-bit signed value */ magic = (duk_int16_t) duk_bd_decode_flagged(bd, DUK__MAGIC_BITS, 0 /*def_value*/); ((duk_hnativefunction *) h)->magic = magic; } else { /* XXX: ARRAY_PART for Array prototype? */ duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE, -1); /* no prototype or class yet */ h = duk_require_hobject(ctx, -1); DUK_ASSERT(h != NULL); } DUK_HOBJECT_SET_CLASS_NUMBER(h, class_num); thr->builtins[i] = h; DUK_HOBJECT_INCREF(thr, &h->hdr); if (len >= 0) { /* * For top-level objects, 'length' property has the following * default attributes: non-writable, non-enumerable, non-configurable * (E5 Section 15). * * However, 'length' property for Array.prototype has attributes * expected of an Array instance which are different: writable, * non-enumerable, non-configurable (E5 Section 15.4.5.2). * * This is currently determined implicitly based on class; there are * no attribute flags in the init data. */ duk_push_int(ctx, len); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, (class_num == DUK_HOBJECT_CLASS_ARRAY ? /* only Array.prototype matches */ DUK_PROPDESC_FLAGS_W : DUK_PROPDESC_FLAGS_NONE)); } /* enable exotic behaviors last */ if (class_num == DUK_HOBJECT_CLASS_ARRAY) { DUK_HOBJECT_SET_EXOTIC_ARRAY(h); } if (class_num == DUK_HOBJECT_CLASS_STRING) { DUK_HOBJECT_SET_EXOTIC_STRINGOBJ(h); } /* some assertions */ DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h)); /* DUK_HOBJECT_FLAG_CONSTRUCTABLE varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_COMPILEDFUNCTION(h)); /* DUK_HOBJECT_FLAG_NATIVEFUNCTION varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(h)); /* currently, even for Array.prototype */ /* DUK_HOBJECT_FLAG_STRICT varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_NATIVEFUNCTION(h) || /* all native functions have NEWENV */ DUK_HOBJECT_HAS_NEWENV(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_NAMEBINDING(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_ENVRECCLOSED(h)); /* DUK_HOBJECT_FLAG_EXOTIC_ARRAY varies */ /* DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h)); DUK_DDD(DUK_DDDPRINT("created built-in %ld, class=%ld, length=%ld", (long) i, (long) class_num, (long) len)); } /* * Then decode the builtins init data (see genbuiltins.py) to * init objects */ DUK_DD(DUK_DDPRINT("initialize built-in object properties")); for (i = 0; i < DUK_NUM_BUILTINS; i++) { duk_small_uint_t t; duk_small_uint_t num; DUK_DDD(DUK_DDDPRINT("initializing built-in object at index %ld", (long) i)); h = thr->builtins[i]; t = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS); if (t != DUK__NO_BIDX_MARKER) { DUK_DDD(DUK_DDDPRINT("set internal prototype: built-in %ld", (long) t)); DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[t]); } t = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS); if (t != DUK__NO_BIDX_MARKER) { /* 'prototype' property for all built-in objects (which have it) has attributes: * [[Writable]] = false, * [[Enumerable]] = false, * [[Configurable]] = false */ DUK_DDD(DUK_DDDPRINT("set external prototype: built-in %ld", (long) t)); duk_xdef_prop_stridx_builtin(ctx, i, DUK_STRIDX_PROTOTYPE, t, DUK_PROPDESC_FLAGS_NONE); } t = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS); if (t != DUK__NO_BIDX_MARKER) { /* 'constructor' property for all built-in objects (which have it) has attributes: * [[Writable]] = true, * [[Enumerable]] = false, * [[Configurable]] = true */ DUK_DDD(DUK_DDDPRINT("set external constructor: built-in %ld", (long) t)); duk_xdef_prop_stridx_builtin(ctx, i, DUK_STRIDX_CONSTRUCTOR, t, DUK_PROPDESC_FLAGS_WC); } /* normal valued properties */ num = (duk_small_uint_t) duk_bd_decode(bd, DUK__NUM_NORMAL_PROPS_BITS); DUK_DDD(DUK_DDDPRINT("built-in object %ld, %ld normal valued properties", (long) i, (long) num)); for (j = 0; j < num; j++) { duk_small_uint_t stridx; duk_small_uint_t prop_flags; stridx = (duk_small_uint_t) duk_bd_decode(bd, DUK__STRIDX_BITS); /* * Property attribute defaults are defined in E5 Section 15 (first * few pages); there is a default for all properties and a special * default for 'length' properties. Variation from the defaults is * signaled using a single flag bit in the bitstream. */ if (duk_bd_decode_flag(bd)) { prop_flags = (duk_small_uint_t) duk_bd_decode(bd, DUK__PROP_FLAGS_BITS); } else { if (stridx == DUK_STRIDX_LENGTH) { prop_flags = DUK_PROPDESC_FLAGS_NONE; } else { prop_flags = DUK_PROPDESC_FLAGS_WC; } } t = (duk_small_uint_t) duk_bd_decode(bd, DUK__PROP_TYPE_BITS); DUK_DDD(DUK_DDDPRINT("built-in %ld, normal-valued property %ld, stridx %ld, flags 0x%02lx, type %ld", (long) i, (long) j, (long) stridx, (unsigned long) prop_flags, (long) t)); switch (t) { case DUK__PROP_TYPE_DOUBLE: { duk_double_union du; duk_small_uint_t k; for (k = 0; k < 8; k++) { /* Encoding endianness must match target memory layout, * build scripts and genbuiltins.py must ensure this. */ du.uc[k] = (duk_uint8_t) duk_bd_decode(bd, 8); } duk_push_number(ctx, du.d); /* push operation normalizes NaNs */ break; } case DUK__PROP_TYPE_STRING: { duk_small_uint_t n; duk_small_uint_t k; duk_uint8_t *p; n = (duk_small_uint_t) duk_bd_decode(bd, DUK__STRING_LENGTH_BITS); p = (duk_uint8_t *) duk_push_fixed_buffer(ctx, n); for (k = 0; k < n; k++) { *p++ = (duk_uint8_t) duk_bd_decode(bd, DUK__STRING_CHAR_BITS); } duk_to_string(ctx, -1); break; } case DUK__PROP_TYPE_STRIDX: { duk_small_uint_t n; n = (duk_small_uint_t) duk_bd_decode(bd, DUK__STRIDX_BITS); DUK_ASSERT_DISABLE(n >= 0); /* unsigned */ DUK_ASSERT(n < DUK_HEAP_NUM_STRINGS); duk_push_hstring_stridx(ctx, n); break; } case DUK__PROP_TYPE_BUILTIN: { duk_small_uint_t bidx; bidx = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS); DUK_ASSERT(bidx != DUK__NO_BIDX_MARKER); duk_dup(ctx, (duk_idx_t) bidx); break; } case DUK__PROP_TYPE_UNDEFINED: { duk_push_undefined(ctx); break; } case DUK__PROP_TYPE_BOOLEAN_TRUE: { duk_push_true(ctx); break; } case DUK__PROP_TYPE_BOOLEAN_FALSE: { duk_push_false(ctx); break; } case DUK__PROP_TYPE_ACCESSOR: { duk_small_uint_t natidx_getter = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS); duk_small_uint_t natidx_setter = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS); duk_c_function c_func_getter; duk_c_function c_func_setter; /* XXX: this is a bit awkward because there is no exposed helper * in the API style, only this internal helper. */ DUK_DDD(DUK_DDDPRINT("built-in accessor property: objidx=%ld, stridx=%ld, getteridx=%ld, setteridx=%ld, flags=0x%04lx", (long) i, (long) stridx, (long) natidx_getter, (long) natidx_setter, (unsigned long) prop_flags)); c_func_getter = duk_bi_native_functions[natidx_getter]; c_func_setter = duk_bi_native_functions[natidx_setter]; duk_push_c_function_noconstruct_noexotic(ctx, c_func_getter, 0); /* always 0 args */ duk_push_c_function_noconstruct_noexotic(ctx, c_func_setter, 1); /* always 1 arg */ /* XXX: magic for getter/setter? */ prop_flags |= DUK_PROPDESC_FLAG_ACCESSOR; /* accessor flag not encoded explicitly */ duk_hobject_define_accessor_internal(thr, duk_require_hobject(ctx, i), DUK_HTHREAD_GET_STRING(thr, stridx), duk_require_hobject(ctx, -2), duk_require_hobject(ctx, -1), prop_flags); duk_pop_2(ctx); /* getter and setter, now reachable through object */ goto skip_value; } default: { /* exhaustive */ DUK_UNREACHABLE(); } } DUK_ASSERT((prop_flags & DUK_PROPDESC_FLAG_ACCESSOR) == 0); duk_xdef_prop_stridx(ctx, i, stridx, prop_flags); skip_value: continue; /* avoid empty label at the end of a compound statement */ } /* native function properties */ num = (duk_small_uint_t) duk_bd_decode(bd, DUK__NUM_FUNC_PROPS_BITS); DUK_DDD(DUK_DDDPRINT("built-in object %ld, %ld function valued properties", (long) i, (long) num)); for (j = 0; j < num; j++) { duk_small_uint_t stridx; duk_small_uint_t natidx; duk_int_t c_nargs; /* must hold DUK_VARARGS */ duk_small_uint_t c_length; duk_int16_t magic; duk_c_function c_func; duk_hnativefunction *h_func; #if defined(DUK_USE_LIGHTFUNC_BUILTINS) duk_small_int_t lightfunc_eligible; #endif stridx = (duk_small_uint_t) duk_bd_decode(bd, DUK__STRIDX_BITS); natidx = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS); c_length = (duk_small_uint_t) duk_bd_decode(bd, DUK__LENGTH_PROP_BITS); c_nargs = (duk_int_t) duk_bd_decode_flagged(bd, DUK__NARGS_BITS, (duk_int32_t) c_length /*def_value*/); if (c_nargs == DUK__NARGS_VARARGS_MARKER) { c_nargs = DUK_VARARGS; } c_func = duk_bi_native_functions[natidx]; DUK_DDD(DUK_DDDPRINT("built-in %ld, function-valued property %ld, stridx %ld, natidx %ld, length %ld, nargs %ld", (long) i, (long) j, (long) stridx, (long) natidx, (long) c_length, (c_nargs == DUK_VARARGS ? (long) -1 : (long) c_nargs))); /* Cast converts magic to 16-bit signed value */ magic = (duk_int16_t) duk_bd_decode_flagged(bd, DUK__MAGIC_BITS, 0); #if defined(DUK_USE_LIGHTFUNC_BUILTINS) lightfunc_eligible = ((c_nargs >= DUK_LFUNC_NARGS_MIN && c_nargs <= DUK_LFUNC_NARGS_MAX) || (c_nargs == DUK_VARARGS)) && (c_length <= DUK_LFUNC_LENGTH_MAX) && (magic >= DUK_LFUNC_MAGIC_MIN && magic <= DUK_LFUNC_MAGIC_MAX); if (stridx == DUK_STRIDX_EVAL || stridx == DUK_STRIDX_YIELD || stridx == DUK_STRIDX_RESUME || stridx == DUK_STRIDX_REQUIRE) { /* These functions have trouble working as lightfuncs. * Some of them have specific asserts and some may have * additional properties (e.g. 'require.id' may be written). */ DUK_D(DUK_DPRINT("reject as lightfunc: stridx=%d, i=%d, j=%d", (int) stridx, (int) i, (int) j)); lightfunc_eligible = 0; } if (lightfunc_eligible) { duk_tval tv_lfunc; duk_small_uint_t lf_nargs = (c_nargs == DUK_VARARGS ? DUK_LFUNC_NARGS_VARARGS : c_nargs); duk_small_uint_t lf_flags = DUK_LFUNC_FLAGS_PACK(magic, c_length, lf_nargs); DUK_TVAL_SET_LIGHTFUNC(&tv_lfunc, c_func, lf_flags); duk_push_tval(ctx, &tv_lfunc); DUK_D(DUK_DPRINT("built-in function eligible as light function: i=%d, j=%d c_length=%ld, c_nargs=%ld, magic=%ld -> %!iT", (int) i, (int) j, (long) c_length, (long) c_nargs, (long) magic, duk_get_tval(ctx, -1))); goto lightfunc_skip; } DUK_D(DUK_DPRINT("built-in function NOT ELIGIBLE as light function: i=%d, j=%d c_length=%ld, c_nargs=%ld, magic=%ld", (int) i, (int) j, (long) c_length, (long) c_nargs, (long) magic)); #endif /* DUK_USE_LIGHTFUNC_BUILTINS */ /* [ (builtin objects) ] */ duk_push_c_function_noconstruct_noexotic(ctx, c_func, c_nargs); h_func = duk_require_hnativefunction(ctx, -1); DUK_UNREF(h_func); /* Currently all built-in native functions are strict. * This doesn't matter for many functions, but e.g. * String.prototype.charAt (and other string functions) * rely on being strict so that their 'this' binding is * not automatically coerced. */ DUK_HOBJECT_SET_STRICT((duk_hobject *) h_func); /* No built-in functions are constructable except the top * level ones (Number, etc). */ DUK_ASSERT(!DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) h_func)); /* XXX: any way to avoid decoding magic bit; there are quite * many function properties and relatively few with magic values. */ h_func->magic = magic; /* [ (builtin objects) func ] */ duk_push_int(ctx, c_length); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); duk_push_hstring_stridx(ctx, stridx); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE); /* XXX: other properties of function instances; 'arguments', 'caller'. */ DUK_DD(DUK_DDPRINT("built-in object %ld, function property %ld -> %!T", (long) i, (long) j, (duk_tval *) duk_get_tval(ctx, -1))); /* [ (builtin objects) func ] */ /* * The default property attributes are correct for all * function valued properties of built-in objects now. */ #if defined(DUK_USE_LIGHTFUNC_BUILTINS) lightfunc_skip: #endif duk_xdef_prop_stridx(ctx, i, stridx, DUK_PROPDESC_FLAGS_WC); /* [ (builtin objects) ] */ } } /* * Special post-tweaks, for cases not covered by the init data format. * * - Set Date.prototype.toGMTString to Date.prototype.toUTCString. * toGMTString is required to have the same Function object as * toUTCString in E5 Section B.2.6. Note that while Smjs respects * this, V8 does not (the Function objects are distinct). * * - Make DoubleError non-extensible. * * - Add info about most important effective compile options to Duktape. * * - Possibly remove some properties (values or methods) which are not * desirable with current feature options but are not currently * conditional in init data. */ duk_get_prop_stridx(ctx, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_UTC_STRING); duk_xdef_prop_stridx(ctx, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_GMT_STRING, DUK_PROPDESC_FLAGS_WC); h = duk_require_hobject(ctx, DUK_BIDX_DOUBLE_ERROR); DUK_ASSERT(h != NULL); DUK_HOBJECT_CLEAR_EXTENSIBLE(h); #if !defined(DUK_USE_ES6_OBJECT_PROTO_PROPERTY) DUK_DD(DUK_DDPRINT("delete Object.prototype.__proto__ built-in which is not enabled in features")); (void) duk_hobject_delprop_raw(thr, thr->builtins[DUK_BIDX_OBJECT_PROTOTYPE], DUK_HTHREAD_STRING___PROTO__(thr), DUK_DELPROP_FLAG_THROW); #endif #if !defined(DUK_USE_ES6_OBJECT_SETPROTOTYPEOF) DUK_DD(DUK_DDPRINT("delete Object.setPrototypeOf built-in which is not enabled in features")); (void) duk_hobject_delprop_raw(thr, thr->builtins[DUK_BIDX_OBJECT_CONSTRUCTOR], DUK_HTHREAD_STRING_SET_PROTOTYPE_OF(thr), DUK_DELPROP_FLAG_THROW); #endif duk_push_string(ctx, /* Endianness indicator */ #if defined(DUK_USE_INTEGER_LE) "l" #elif defined(DUK_USE_INTEGER_BE) "b" #elif defined(DUK_USE_INTEGER_ME) /* integer mixed endian not really used now */ "m" #else "?" #endif #if defined(DUK_USE_DOUBLE_LE) "l" #elif defined(DUK_USE_DOUBLE_BE) "b" #elif defined(DUK_USE_DOUBLE_ME) "m" #else "?" #endif #if defined(DUK_USE_BYTEORDER_FORCED) "f" #endif " " /* Packed or unpacked tval */ #if defined(DUK_USE_PACKED_TVAL) "p" #else "u" #endif #if defined(DUK_USE_FASTINT) "f" #endif " " /* Low memory options */ #if defined(DUK_USE_STRTAB_CHAIN) "c" /* chain */ #elif defined(DUK_USE_STRTAB_PROBE) "p" /* probe */ #else "?" #endif #if !defined(DUK_USE_HEAPPTR16) && !defined(DUK_DATAPTR16) && !defined(DUK_FUNCPTR16) "n" #endif #if defined(DUK_USE_HEAPPTR16) "h" #endif #if defined(DUK_USE_DATAPTR16) "d" #endif #if defined(DUK_USE_FUNCPTR16) "f" #endif #if defined(DUK_USE_REFCOUNT16) "R" #endif #if defined(DUK_USE_STRHASH16) "H" #endif #if defined(DUK_USE_STRLEN16) "S" #endif #if defined(DUK_USE_BUFLEN16) "B" #endif #if defined(DUK_USE_OBJSIZES16) "O" #endif #if defined(DUK_USE_LIGHTFUNC_BUILTINS) "L" #endif " " /* Object property allocation layout */ #if defined(DUK_USE_HOBJECT_LAYOUT_1) "p1" #elif defined(DUK_USE_HOBJECT_LAYOUT_2) "p2" #elif defined(DUK_USE_HOBJECT_LAYOUT_3) "p3" #else "p?" #endif " " /* Alignment guarantee */ #if defined(DUK_USE_ALIGN_4) "a4" #elif defined(DUK_USE_ALIGN_8) "a8" #else "a1" #endif " " /* Architecture, OS, and compiler strings */ DUK_USE_ARCH_STRING " " DUK_USE_OS_STRING " " DUK_USE_COMPILER_STRING); duk_xdef_prop_stridx(ctx, DUK_BIDX_DUKTAPE, DUK_STRIDX_ENV, DUK_PROPDESC_FLAGS_WC); /* * InitJS code - Ecmascript code evaluated from a built-in source * which provides e.g. backward compatibility. User can also provide * JS code to be evaluated at startup. */ #ifdef DUK_USE_BUILTIN_INITJS /* XXX: compression */ DUK_DD(DUK_DDPRINT("running built-in initjs")); duk_eval_string(ctx, (const char *) duk_initjs_data); /* initjs data is NUL terminated */ duk_pop(ctx); #endif /* DUK_USE_BUILTIN_INITJS */ #ifdef DUK_USE_USER_INITJS /* XXX: compression (as an option) */ DUK_DD(DUK_DDPRINT("running user initjs")); duk_eval_string_noresult(ctx, (const char *) DUK_USE_USER_INITJS); #endif /* DUK_USE_USER_INITJS */ /* * Since built-ins are not often extended, compact them. */ DUK_DD(DUK_DDPRINT("compact built-ins")); for (i = 0; i < DUK_NUM_BUILTINS; i++) { duk_hobject_compact_props(thr, thr->builtins[i]); } DUK_D(DUK_DPRINT("INITBUILTINS END")); #ifdef DUK_USE_DDPRINT for (i = 0; i < DUK_NUM_BUILTINS; i++) { DUK_DD(DUK_DDPRINT("built-in object %ld after initialization and compacting: %!@iO", (long) i, (duk_heaphdr *) thr->builtins[i])); } #endif /* * Pop built-ins from stack: they are now INCREF'd and * reachable from the builtins[] array. */ duk_pop_n(ctx, DUK_NUM_BUILTINS); DUK_ASSERT_TOP(ctx, 0); }
void duk_hthread_create_builtin_objects(duk_hthread *thr) { duk_context *ctx = (duk_context *) thr; duk_bitdecoder_ctx bd_ctx; duk_bitdecoder_ctx *bd = &bd_ctx; /* convenience */ duk_hobject *h; int i, j; DUK_DPRINT("INITBUILTINS BEGIN"); DUK_MEMZERO(&bd_ctx, sizeof(bd_ctx)); bd->data = (const duk_uint8_t *) duk_builtins_data; bd->length = (duk_size_t) DUK_BUILTINS_DATA_LENGTH; /* * First create all built-in bare objects on the empty valstack. * During init, their indices will correspond to built-in indices. * * Built-ins will be reachable from both valstack and thr->builtins. */ /* XXX: there is no need to resize valstack because builtin count * is much less than the default space; assert for it. */ DUK_DDPRINT("create empty built-ins"); DUK_ASSERT_TOP(ctx, 0); for (i = 0; i < DUK_NUM_BUILTINS; i++) { int class_num; int len = -1; class_num = duk_bd_decode(bd, DUK__CLASS_BITS); len = duk_bd_decode_flagged(bd, DUK__LENGTH_PROP_BITS, (duk_int32_t) -1 /*def_value*/); if (class_num == DUK_HOBJECT_CLASS_FUNCTION) { int natidx; int stridx; int c_nargs; duk_c_function c_func; duk_int16_t magic; DUK_DDDPRINT("len=%d", len); DUK_ASSERT(len >= 0); natidx = duk_bd_decode(bd, DUK__NATIDX_BITS); stridx = duk_bd_decode(bd, DUK__STRIDX_BITS); c_func = duk_bi_native_functions[natidx]; c_nargs = duk_bd_decode_flagged(bd, DUK__NARGS_BITS, len /*def_value*/); if (c_nargs == DUK__NARGS_VARARGS_MARKER) { c_nargs = DUK_VARARGS; } /* FIXME: set magic directly here? (it could share the c_nargs arg) */ duk_push_c_function_nospecial(ctx, c_func, c_nargs); h = duk_require_hobject(ctx, -1); DUK_ASSERT(h != NULL); /* Currently all built-in native functions are strict. * duk_push_c_function() now sets strict flag, so * assert for it. */ DUK_ASSERT(DUK_HOBJECT_HAS_STRICT(h)); /* FIXME: function properties */ duk_push_hstring_stridx(ctx, stridx); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE); /* Almost all global level Function objects are constructable * but not all: Function.prototype is a non-constructable, * callable Function. */ if (duk_bd_decode_flag(bd)) { DUK_ASSERT(DUK_HOBJECT_HAS_CONSTRUCTABLE(h)); } else { DUK_HOBJECT_CLEAR_CONSTRUCTABLE(h); } /* Cast converts magic to 16-bit signed value */ magic = (duk_int16_t) duk_bd_decode_flagged(bd, DUK__MAGIC_BITS, 0 /*def_value*/); ((duk_hnativefunction *) h)->magic = magic; } else { /* FIXME: ARRAY_PART for Array prototype? */ duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE, -1); /* no prototype or class yet */ h = duk_require_hobject(ctx, -1); DUK_ASSERT(h != NULL); } DUK_HOBJECT_SET_CLASS_NUMBER(h, class_num); thr->builtins[i] = h; DUK_HOBJECT_INCREF(thr, &h->hdr); if (len >= 0) { /* * For top-level objects, 'length' property has the following * default attributes: non-writable, non-enumerable, non-configurable * (E5 Section 15). * * However, 'length' property for Array.prototype has attributes * expected of an Array instance which are different: writable, * non-enumerable, non-configurable (E5 Section 15.4.5.2). * * This is currently determined implicitly based on class; there are * no attribute flags in the init data. */ duk_push_int(ctx, len); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, (class_num == DUK_HOBJECT_CLASS_ARRAY ? /* only Array.prototype matches */ DUK_PROPDESC_FLAGS_W : DUK_PROPDESC_FLAGS_NONE)); } /* enable special behaviors last */ if (class_num == DUK_HOBJECT_CLASS_ARRAY) { DUK_HOBJECT_SET_SPECIAL_ARRAY(h); } if (class_num == DUK_HOBJECT_CLASS_STRING) { DUK_HOBJECT_SET_SPECIAL_STRINGOBJ(h); } /* some assertions */ DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h)); /* DUK_HOBJECT_FLAG_CONSTRUCTABLE varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_COMPILEDFUNCTION(h)); /* DUK_HOBJECT_FLAG_NATIVEFUNCTION varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(h)); /* currently, even for Array.prototype */ /* DUK_HOBJECT_FLAG_STRICT varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_NATIVEFUNCTION(h) || /* all native functions have NEWENV */ DUK_HOBJECT_HAS_NEWENV(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_NAMEBINDING(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_ENVRECCLOSED(h)); /* DUK_HOBJECT_FLAG_SPECIAL_ARRAY varies */ /* DUK_HOBJECT_FLAG_SPECIAL_STRINGOBJ varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_SPECIAL_ARGUMENTS(h)); DUK_DDDPRINT("created built-in %d, class=%d, length=%d", i, class_num, len); } /* * Then decode the builtins init data (see genbuiltins.py) to * init objects */ DUK_DDPRINT("initialize built-in object properties"); for (i = 0; i < DUK_NUM_BUILTINS; i++) { unsigned char t; int num; DUK_DDDPRINT("initializing built-in object at index %d", i); h = thr->builtins[i]; t = duk_bd_decode(bd, DUK__BIDX_BITS); if (t != DUK__NO_BIDX_MARKER) { DUK_DDDPRINT("set internal prototype: built-in %d", (int) t); DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[t]); } t = duk_bd_decode(bd, DUK__BIDX_BITS); if (t != DUK__NO_BIDX_MARKER) { /* 'prototype' property for all built-in objects (which have it) has attributes: * [[Writable]] = false, * [[Enumerable]] = false, * [[Configurable]] = false */ DUK_DDDPRINT("set external prototype: built-in %d", (int) t); duk_def_prop_stridx_builtin(ctx, i, DUK_STRIDX_PROTOTYPE, t, DUK_PROPDESC_FLAGS_NONE); } t = duk_bd_decode(bd, DUK__BIDX_BITS); if (t != DUK__NO_BIDX_MARKER) { /* 'constructor' property for all built-in objects (which have it) has attributes: * [[Writable]] = true, * [[Enumerable]] = false, * [[Configurable]] = true */ DUK_DDDPRINT("set external constructor: built-in %d", (int) t); duk_def_prop_stridx_builtin(ctx, i, DUK_STRIDX_CONSTRUCTOR, t, DUK_PROPDESC_FLAGS_WC); } /* normal valued properties */ num = duk_bd_decode(bd, DUK__NUM_NORMAL_PROPS_BITS); DUK_DDDPRINT("built-in object %d, %d normal valued properties", i, num); for (j = 0; j < num; j++) { int stridx; int prop_flags; stridx = duk_bd_decode(bd, DUK__STRIDX_BITS); /* * Property attribute defaults are defined in E5 Section 15 (first * few pages); there is a default for all properties and a special * default for 'length' properties. Variation from the defaults is * signaled using a single flag bit in the bitstream. */ if (duk_bd_decode_flag(bd)) { prop_flags = duk_bd_decode(bd, DUK__PROP_FLAGS_BITS); } else { if (stridx == DUK_STRIDX_LENGTH) { prop_flags = DUK_PROPDESC_FLAGS_NONE; } else { prop_flags = DUK_PROPDESC_FLAGS_WC; } } t = duk_bd_decode(bd, DUK__PROP_TYPE_BITS); DUK_DDDPRINT("built-in %d, normal-valued property %d, stridx %d, flags 0x%02x, type %d", i, j, stridx, prop_flags, (int) t); switch (t) { case DUK__PROP_TYPE_DOUBLE: { duk_double_union du; int k; for (k = 0; k < 8; k++) { /* Encoding endianness must match target memory layout, * build scripts and genbuiltins.py must ensure this. */ du.uc[k] = (duk_uint8_t) duk_bd_decode(bd, 8); } duk_push_number(ctx, du.d); /* push operation normalizes NaNs */ break; } case DUK__PROP_TYPE_STRING: { int n; int k; char *p; n = duk_bd_decode(bd, DUK__STRING_LENGTH_BITS); p = (char *) duk_push_fixed_buffer(ctx, n); for (k = 0; k < n; k++) { *p++ = duk_bd_decode(bd, DUK__STRING_CHAR_BITS); } duk_to_string(ctx, -1); break; } case DUK__PROP_TYPE_STRIDX: { int n; n = duk_bd_decode(bd, DUK__STRIDX_BITS); DUK_ASSERT(n >= 0 && n < DUK_HEAP_NUM_STRINGS); duk_push_hstring_stridx(ctx, n); break; } case DUK__PROP_TYPE_BUILTIN: { int bidx; bidx = duk_bd_decode(bd, DUK__BIDX_BITS); DUK_ASSERT(bidx != DUK__NO_BIDX_MARKER); duk_dup(ctx, bidx); break; } case DUK__PROP_TYPE_UNDEFINED: { duk_push_undefined(ctx); break; } case DUK__PROP_TYPE_BOOLEAN_TRUE: { duk_push_true(ctx); break; } case DUK__PROP_TYPE_BOOLEAN_FALSE: { duk_push_false(ctx); break; } case DUK__PROP_TYPE_ACCESSOR: { int natidx_getter = duk_bd_decode(bd, DUK__NATIDX_BITS); int natidx_setter = duk_bd_decode(bd, DUK__NATIDX_BITS); duk_c_function c_func_getter; duk_c_function c_func_setter; /* XXX: this is a bit awkward because there is no exposed helper * in the API style, only this internal helper. */ DUK_DDDPRINT("built-in accessor property: objidx=%d, stridx=%d, getteridx=%d, setteridx=%d, flags=0x%04x", i, stridx, natidx_getter, natidx_setter, prop_flags); c_func_getter = duk_bi_native_functions[natidx_getter]; c_func_setter = duk_bi_native_functions[natidx_setter]; duk_push_c_function_noconstruct_nospecial(ctx, c_func_getter, 0); /* always 0 args */ duk_push_c_function_noconstruct_nospecial(ctx, c_func_setter, 1); /* always 1 arg */ /* FIXME: magic for getter/setter? */ prop_flags |= DUK_PROPDESC_FLAG_ACCESSOR; /* accessor flag not encoded explicitly */ duk_hobject_define_accessor_internal(thr, duk_require_hobject(ctx, i), DUK_HTHREAD_GET_STRING(thr, stridx), duk_require_hobject(ctx, -2), duk_require_hobject(ctx, -1), prop_flags); duk_pop_2(ctx); /* getter and setter, now reachable through object */ goto skip_value; } default: { /* exhaustive */ DUK_UNREACHABLE(); } } DUK_ASSERT((prop_flags & DUK_PROPDESC_FLAG_ACCESSOR) == 0); duk_def_prop_stridx(ctx, i, stridx, prop_flags); skip_value: continue; /* avoid empty label at the end of a compound statement */ } /* native function properties */ num = duk_bd_decode(bd, DUK__NUM_FUNC_PROPS_BITS); DUK_DDDPRINT("built-in object %d, %d function valued properties", i, num); for (j = 0; j < num; j++) { int stridx; int natidx; int c_nargs; int c_length; duk_int16_t magic; duk_c_function c_func; duk_hnativefunction *h_func; stridx = duk_bd_decode(bd, DUK__STRIDX_BITS); natidx = duk_bd_decode(bd, DUK__NATIDX_BITS); c_length = duk_bd_decode(bd, DUK__LENGTH_PROP_BITS); c_nargs = duk_bd_decode_flagged(bd, DUK__NARGS_BITS, (duk_int32_t) c_length /*def_value*/); if (c_nargs == DUK__NARGS_VARARGS_MARKER) { c_nargs = DUK_VARARGS; } c_func = duk_bi_native_functions[natidx]; DUK_DDDPRINT("built-in %d, function-valued property %d, stridx %d, natidx %d, length %d, nargs %d", i, j, stridx, natidx, c_length, (c_nargs == DUK_VARARGS ? -1 : c_nargs)); /* [ (builtin objects) ] */ duk_push_c_function_noconstruct_nospecial(ctx, c_func, c_nargs); h_func = duk_require_hnativefunction(ctx, -1); DUK_UNREF(h_func); /* Currently all built-in native functions are strict. * This doesn't matter for many functions, but e.g. * String.prototype.charAt (and other string functions) * rely on being strict so that their 'this' binding is * not automatically coerced. */ DUK_HOBJECT_SET_STRICT((duk_hobject *) h_func); /* No built-in functions are constructable except the top * level ones (Number, etc). */ DUK_ASSERT(!DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) h_func)); /* FIXME: any way to avoid decoding magic bit; there are quite * many function properties and relatively few with magic values. */ /* Cast converts magic to 16-bit signed value */ magic = (duk_int16_t) duk_bd_decode_flagged(bd, DUK__MAGIC_BITS, 0); h_func->magic = magic; /* [ (builtin objects) func ] */ duk_push_int(ctx, c_length); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); duk_push_hstring_stridx(ctx, stridx); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE); /* FIXME: other properties of function instances; 'arguments', 'caller'. */ DUK_DDPRINT("built-in object %d, function property %d -> %!T", i, j, duk_get_tval(ctx, -1)); /* [ (builtin objects) func ] */ /* * The default property attributes are correct for all * function valued properties of built-in objects now. */ duk_def_prop_stridx(ctx, i, stridx, DUK_PROPDESC_FLAGS_WC); /* [ (builtin objects) ] */ } } /* * Special post-tweaks, for cases not covered by the init data format. * * - Set Date.prototype.toGMTString to Date.prototype.toUTCString. * toGMTString is required to have the same Function object as * toUTCString in E5 Section B.2.6. Note that while Smjs respects * this, V8 does not (the Function objects are distinct). * * - Make DoubleError non-extensible. * * - Add info about most important effective compile options to Duktape. */ duk_get_prop_stridx(ctx, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_UTC_STRING); duk_def_prop_stridx(ctx, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_GMT_STRING, DUK_PROPDESC_FLAGS_WC); h = duk_require_hobject(ctx, DUK_BIDX_DOUBLE_ERROR); DUK_ASSERT(h != NULL); DUK_HOBJECT_CLEAR_EXTENSIBLE(h); duk_push_string(ctx, #if defined(DUK_USE_INTEGER_LE) "l" #elif defined(DUK_USE_INTEGER_BE) "b" #elif defined(DUK_USE_INTEGER_ME) /* integer mixed endian not really used now */ "m" #else "?" #endif #if defined(DUK_USE_DOUBLE_LE) "l" #elif defined(DUK_USE_DOUBLE_BE) "b" #elif defined(DUK_USE_DOUBLE_ME) "m" #else "?" #endif #if defined(DUK_USE_BYTEORDER_FORCED) "f" #endif " " #if defined(DUK_USE_PACKED_TVAL) "p" #else "u" #endif " " #if defined(DUK_USE_HOBJECT_LAYOUT_1) "p1" #elif defined(DUK_USE_HOBJECT_LAYOUT_2) "p2" #elif defined(DUK_USE_HOBJECT_LAYOUT_3) "p3" #else "p?" #endif " " #if defined(DUK_USE_ALIGN_4) "a4" #elif defined(DUK_USE_ALIGN_8) "a8" #else "a1" #endif " " DUK_USE_ARCH_STRING); duk_def_prop_stridx(ctx, DUK_BIDX_DUKTAPE, DUK_STRIDX_ENV, DUK_PROPDESC_FLAGS_WC); /* * InitJS code - Ecmascript code evaluated from a built-in source * which provides e.g. backward compatibility. User can also provide * JS code to be evaluated at startup. */ #ifdef DUK_USE_INITJS /* FIXME: compression */ duk_eval_string(ctx, (const char *) duk_initjs_data); /* initjs data is NUL terminated */ duk_pop(ctx); #endif /* DUK_USE_INITJS */ #ifdef DUK_USE_USER_INITJS /* FIXME: compression, at least as an option? */ /* FIXME: unused now */ duk_eval_string(ctx, (const char *) DUK_USE_USER_INITJS); duk_pop(ctx); #endif /* DUK_USE_USER_INITJS */ /* * Since built-ins are not often extended, compact them. */ DUK_DDPRINT("compact built-ins"); for (i = 0; i < DUK_NUM_BUILTINS; i++) { duk_hobject_compact_props(thr, thr->builtins[i]); } DUK_DPRINT("INITBUILTINS END"); #ifdef DUK_USE_DDEBUG for (i = 0; i < DUK_NUM_BUILTINS; i++) { DUK_DDPRINT("built-in object %d after initialization and compacting: %!@iO", i, thr->builtins[i]); } #endif #ifdef DUK_USE_DDDEBUG for (i = 0; i < DUK_NUM_BUILTINS; i++) { DUK_DDDPRINT("built-in object %d after initialization and compacting", i); DUK_DEBUG_DUMP_HOBJECT(thr->builtins[i]); } #endif /* * Pop built-ins from stack: they are now INCREF'd and * reachable from the builtins[] array. */ duk_pop_n(ctx, DUK_NUM_BUILTINS); DUK_ASSERT_TOP(ctx, 0); }
static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t *p_end) { duk_hthread *thr; duk_hcompiledfunction *h_fun; duk_hbuffer *h_data; duk_size_t data_size; duk_uint32_t count_instr, count_const, count_funcs; duk_uint32_t n; duk_uint32_t tmp32; duk_small_uint_t const_type; duk_uint8_t *fun_data; duk_uint8_t *q; duk_idx_t idx_base; duk_tval *tv; duk_uarridx_t arr_idx; /* XXX: There's some overlap with duk_js_closure() here, but * seems difficult to share code. Ensure that the final function * looks the same as created by duk_js_closure(). */ DUK_ASSERT(ctx != NULL); thr = (duk_hthread *) ctx; DUK_DD(DUK_DDPRINT("loading function, p=%p, p_end=%p", (void *) p, (void *) p_end)); DUK__ASSERT_LEFT(3 * 4); count_instr = DUK_RAW_READ_U32_BE(p); count_const = DUK_RAW_READ_U32_BE(p); count_funcs = DUK_RAW_READ_U32_BE(p); data_size = sizeof(duk_tval) * count_const + sizeof(duk_hobject *) * count_funcs + sizeof(duk_instr_t) * count_instr; DUK_DD(DUK_DDPRINT("instr=%ld, const=%ld, funcs=%ld, data_size=%ld", (long) count_instr, (long) count_const, (long) count_const, (long) data_size)); /* Value stack is used to ensure reachability of constants and * inner functions being loaded. Require enough space to handle * large functions correctly. */ duk_require_stack(ctx, 2 + count_const + count_funcs); idx_base = duk_get_top(ctx); /* Push function object, init flags etc. This must match * duk_js_push_closure() quite carefully. */ duk_push_compiledfunction(ctx); h_fun = duk_get_hcompiledfunction(ctx, -1); DUK_ASSERT(h_fun != NULL); DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) h_fun)); DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, h_fun) == NULL); DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_FUNCS(thr->heap, h_fun) == NULL); DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_BYTECODE(thr->heap, h_fun) == NULL); h_fun->nregs = DUK_RAW_READ_U16_BE(p); h_fun->nargs = DUK_RAW_READ_U16_BE(p); #if defined(DUK_USE_DEBUGGER_SUPPORT) h_fun->start_line = DUK_RAW_READ_U32_BE(p); h_fun->end_line = DUK_RAW_READ_U32_BE(p); #else p += 8; /* skip line info */ #endif /* duk_hcompiledfunction flags; quite version specific */ tmp32 = DUK_RAW_READ_U32_BE(p); DUK_HEAPHDR_SET_FLAGS((duk_heaphdr *) h_fun, tmp32); /* standard prototype */ DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, &h_fun->obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]); /* assert just a few critical flags */ DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h_fun) == DUK_HTYPE_OBJECT); DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(&h_fun->obj)); DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(&h_fun->obj)); DUK_ASSERT(!DUK_HOBJECT_HAS_NATIVEFUNCTION(&h_fun->obj)); DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(&h_fun->obj)); DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(&h_fun->obj)); DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(&h_fun->obj)); DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(&h_fun->obj)); /* Create function 'data' buffer but don't attach it yet. */ fun_data = (duk_uint8_t *) duk_push_fixed_buffer(ctx, data_size); DUK_ASSERT(fun_data != NULL); /* Load bytecode instructions. */ DUK_ASSERT(sizeof(duk_instr_t) == 4); DUK__ASSERT_LEFT(count_instr * sizeof(duk_instr_t)); #if defined(DUK_USE_INTEGER_BE) q = fun_data + sizeof(duk_tval) * count_const + sizeof(duk_hobject *) * count_funcs; DUK_MEMCPY((void *) q, (const void *) p, sizeof(duk_instr_t) * count_instr); p += sizeof(duk_instr_t) * count_instr; #else q = fun_data + sizeof(duk_tval) * count_const + sizeof(duk_hobject *) * count_funcs; for (n = count_instr; n > 0; n--) { *((duk_instr_t *) (void *) q) = DUK_RAW_READ_U32_BE(p); q += sizeof(duk_instr_t); } #endif /* Load constants onto value stack but don't yet copy to buffer. */ for (n = count_const; n > 0; n--) { DUK__ASSERT_LEFT(1); const_type = DUK_RAW_READ_U8(p); switch (const_type) { case DUK__SER_STRING: { p = duk__load_string_raw(ctx, p); break; } case DUK__SER_NUMBER: { /* Important to do a fastint check so that constants are * properly read back as fastints. */ duk_tval tv_tmp; duk_double_t val; DUK__ASSERT_LEFT(8); val = DUK_RAW_READ_DOUBLE_BE(p); DUK_TVAL_SET_NUMBER_CHKFAST(&tv_tmp, val); duk_push_tval(ctx, &tv_tmp); break; } default: { goto format_error; } } } /* Load inner functions to value stack, but don't yet copy to buffer. */ for (n = count_funcs; n > 0; n--) { p = duk__load_func(ctx, p, p_end); if (p == NULL) { goto format_error; } } /* With constants and inner functions on value stack, we can now * atomically finish the function 'data' buffer, bump refcounts, * etc. * * Here we take advantage of the value stack being just a duk_tval * array: we can just memcpy() the constants as long as we incref * them afterwards. */ h_data = (duk_hbuffer *) duk_get_hbuffer(ctx, idx_base + 1); DUK_ASSERT(h_data != NULL); DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC(h_data)); DUK_HCOMPILEDFUNCTION_SET_DATA(thr->heap, h_fun, h_data); DUK_HBUFFER_INCREF(thr, h_data); tv = duk_get_tval(ctx, idx_base + 2); /* may be NULL if no constants or inner funcs */ DUK_ASSERT((count_const == 0 && count_funcs == 0) || tv != NULL); q = fun_data; if (count_const > 0) { /* Explicit zero size check to avoid NULL 'tv'. */ DUK_MEMCPY((void *) q, (const void *) tv, sizeof(duk_tval) * count_const); for (n = count_const; n > 0; n--) { DUK_TVAL_INCREF_FAST(thr, (duk_tval *) (void *) q); /* no side effects */ q += sizeof(duk_tval); } tv += count_const; } DUK_HCOMPILEDFUNCTION_SET_FUNCS(thr->heap, h_fun, (duk_hobject **) (void *) q); for (n = count_funcs; n > 0; n--) { duk_hobject *h_obj; DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv)); h_obj = DUK_TVAL_GET_OBJECT(tv); DUK_ASSERT(h_obj != NULL); tv++; DUK_HOBJECT_INCREF(thr, h_obj); *((duk_hobject **) (void *) q) = h_obj; q += sizeof(duk_hobject *); } DUK_HCOMPILEDFUNCTION_SET_BYTECODE(thr->heap, h_fun, (duk_instr_t *) (void *) q); /* The function object is now reachable and refcounts are fine, * so we can pop off all the temporaries. */ DUK_DDD(DUK_DDDPRINT("function is reachable, reset top; func: %!iT", duk_get_tval(ctx, idx_base))); duk_set_top(ctx, idx_base + 1); /* Setup function properties. */ tmp32 = DUK_RAW_READ_U32_BE(p); duk_push_u32(ctx, tmp32); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); p = duk__load_string_raw(ctx, p); if (DUK_HOBJECT_HAS_NAMEBINDING((duk_hobject *) h_fun)) { /* Original function instance/template had NAMEBINDING. * Must create a lexical environment on loading to allow * recursive functions like 'function foo() { foo(); }'. */ duk_hobject *proto; proto = thr->builtins[DUK_BIDX_GLOBAL_ENV]; (void) duk_push_object_helper_proto(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV), proto); duk_dup(ctx, -2); /* -> [ func funcname env funcname ] */ duk_dup(ctx, idx_base); /* -> [ func funcname env funcname func ] */ duk_xdef_prop(ctx, -3, DUK_PROPDESC_FLAGS_NONE); /* -> [ func funcname env ] */ duk_xdef_prop_stridx(ctx, idx_base, DUK_STRIDX_INT_LEXENV, DUK_PROPDESC_FLAGS_WC); /* since closure has NEWENV, never define DUK_STRIDX_INT_VARENV, as it * will be ignored anyway */ } duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE); p = duk__load_string_raw(ctx, p); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC); duk_push_object(ctx); duk_dup(ctx, -2); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC); /* func.prototype.constructor = func */ duk_compact(ctx, -1); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_W); p = duk__load_buffer_raw(ctx, p); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_PC2LINE, DUK_PROPDESC_FLAGS_WC); duk_push_object(ctx); /* _Varmap */ for (;;) { /* XXX: awkward */ p = duk__load_string_raw(ctx, p); if (duk_get_length(ctx, -1) == 0) { duk_pop(ctx); break; } tmp32 = DUK_RAW_READ_U32_BE(p); duk_push_u32(ctx, tmp32); duk_put_prop(ctx, -3); } duk_compact(ctx, -1); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_VARMAP, DUK_PROPDESC_FLAGS_NONE); duk_push_array(ctx); /* _Formals */ for (arr_idx = 0; ; arr_idx++) { /* XXX: awkward */ p = duk__load_string_raw(ctx, p); if (duk_get_length(ctx, -1) == 0) { duk_pop(ctx); break; } duk_put_prop_index(ctx, -2, arr_idx); } duk_compact(ctx, -1); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE); /* Return with final function pushed on stack top. */ DUK_DD(DUK_DDPRINT("final loaded function: %!iT", duk_get_tval(ctx, -1))); DUK_ASSERT_TOP(ctx, idx_base + 1); return p; format_error: return NULL; }