static void duk__free_hobject_inner(duk_heap *heap, duk_hobject *h) { DUK_ASSERT(heap != NULL); DUK_ASSERT(h != NULL); DUK_FREE(heap, h->p); if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) { duk_hcompiledfunction *f = (duk_hcompiledfunction *) h; DUK_UNREF(f); /* Currently nothing to free; 'data' is a heap object */ } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h)) { duk_hnativefunction *f = (duk_hnativefunction *) h; DUK_UNREF(f); /* Currently nothing to free */ } else if (DUK_HOBJECT_IS_THREAD(h)) { duk_hthread *t = (duk_hthread *) h; DUK_FREE(heap, t->valstack); DUK_FREE(heap, t->callstack); DUK_FREE(heap, t->catchstack); /* Don't free h->resumer because it exists in the heap. * Callstack entries also contain function pointers which * are not freed for the same reason. */ /* XXX: with 'caller' property the callstack would need * to be unwound to update the 'caller' properties of * functions in the callstack. */ } }
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; }
/* for thread dumping */ static char duk__get_act_summary_char(duk_activation *act) { if (act->func) { if (DUK_HOBJECT_IS_COMPILEDFUNCTION(act->func)) { return 'c'; } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(act->func)) { return 'n'; } else { /* should not happen */ return '?'; } } else { /* should not happen */ return '?'; } }
/* for thread dumping */ static char duk__get_tval_summary_char(duk_tval *tv) { switch (DUK_TVAL_GET_TAG(tv)) { case DUK_TAG_UNDEFINED: if (DUK_TVAL_IS_UNDEFINED_UNUSED(tv)) { return '.'; } return 'u'; case DUK_TAG_NULL: return 'n'; case DUK_TAG_BOOLEAN: return 'b'; case DUK_TAG_STRING: return 's'; case DUK_TAG_OBJECT: { duk_hobject *h = DUK_TVAL_GET_OBJECT(tv); if (DUK_HOBJECT_IS_ARRAY(h)) { return 'A'; } else if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) { return 'C'; } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h)) { return 'N'; } else if (DUK_HOBJECT_IS_THREAD(h)) { return 'T'; } return 'O'; } case DUK_TAG_BUFFER: { return 'B'; } case DUK_TAG_POINTER: { return 'P'; } default: DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); return 'd'; } DUK_UNREACHABLE(); }
static void free_hobject_inner(duk_heap *heap, duk_hobject *h) { DUK_ASSERT(heap != NULL); DUK_ASSERT(h != NULL); DUK_FREE(heap, h->p); if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) { duk_hcompiledfunction *f = (duk_hcompiledfunction *) h; DUK_UNREF(f); /* Currently nothing to free; 'data' is a heap object */ } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h)) { duk_hnativefunction *f = (duk_hnativefunction *) h; DUK_UNREF(f); /* Currently nothing to free */ } else if (DUK_HOBJECT_IS_THREAD(h)) { duk_hthread *t = (duk_hthread *) h; DUK_FREE(heap, t->valstack); DUK_FREE(heap, t->callstack); DUK_FREE(heap, t->catchstack); /* don't free h->resumer, because it exists in the heap */ } }
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; }
static void duk__mark_hobject(duk_heap *heap, duk_hobject *h) { duk_uint_fast32_t i; DUK_DDD(DUK_DDDPRINT("duk__mark_hobject: %p", (void *) h)); DUK_ASSERT(h); /* XXX: use advancing pointers instead of index macros -> faster and smaller? */ for (i = 0; i < h->e_used; i++) { duk_hstring *key = DUK_HOBJECT_E_GET_KEY(h, i); if (!key) { continue; } duk__mark_heaphdr(heap, (duk_heaphdr *) key); if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(h, i)) { duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(h, i)->a.get); duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(h, i)->a.set); } else { duk__mark_tval(heap, &DUK_HOBJECT_E_GET_VALUE_PTR(h, i)->v); } } for (i = 0; i < h->a_size; i++) { duk__mark_tval(heap, DUK_HOBJECT_A_GET_VALUE_PTR(h, i)); } /* hash part is a 'weak reference' and does not contribute */ duk__mark_heaphdr(heap, (duk_heaphdr *) h->prototype); if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) { duk_hcompiledfunction *f = (duk_hcompiledfunction *) h; duk_tval *tv, *tv_end; duk_hobject **funcs, **funcs_end; /* 'data' is reachable through every compiled function which * contains a reference. */ duk__mark_heaphdr(heap, (duk_heaphdr *) f->data); tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(f); tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(f); while (tv < tv_end) { duk__mark_tval(heap, tv); tv++; } funcs = DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(f); funcs_end = DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(f); while (funcs < funcs_end) { duk__mark_heaphdr(heap, (duk_heaphdr *) *funcs); funcs++; } } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h)) { duk_hnativefunction *f = (duk_hnativefunction *) h; DUK_UNREF(f); /* nothing to mark */ } else if (DUK_HOBJECT_IS_THREAD(h)) { duk_hthread *t = (duk_hthread *) h; duk_tval *tv; tv = t->valstack; while (tv < t->valstack_end) { duk__mark_tval(heap, tv); tv++; } for (i = 0; i < t->callstack_top; i++) { duk_activation *act = &t->callstack[i]; duk__mark_heaphdr(heap, (duk_heaphdr *) act->func); duk__mark_heaphdr(heap, (duk_heaphdr *) act->var_env); duk__mark_heaphdr(heap, (duk_heaphdr *) act->lex_env); #ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY duk__mark_heaphdr(heap, (duk_heaphdr *) act->prev_caller); #endif } #if 0 /* nothing now */ for (i = 0; i < t->catchstack_top; i++) { duk_catcher *cat = &t->catchstack[i]; } #endif duk__mark_heaphdr(heap, (duk_heaphdr *) t->resumer); for (i = 0; i < DUK_NUM_BUILTINS; i++) { duk__mark_heaphdr(heap, (duk_heaphdr *) t->builtins[i]); } } }
/* Raw helper to extract internal information / statistics about a value. * The return values are version specific and must not expose anything * that would lead to security issues (e.g. exposing compiled function * 'data' buffer might be an issue). Currently only counts and sizes and * such are given so there should not be a security impact. */ duk_ret_t duk_bi_duktape_object_info(duk_context *ctx) { duk_tval *tv; duk_heaphdr *h; duk_int_t i, n; tv = duk_get_tval(ctx, 0); DUK_ASSERT(tv != NULL); /* because arg count is 1 */ duk_push_array(ctx); /* -> [ val arr ] */ /* type tag (public) */ duk_push_int(ctx, duk_get_type(ctx, 0)); /* address */ if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { h = DUK_TVAL_GET_HEAPHDR(tv); duk_push_pointer(ctx, (void *) h); } else { goto done; } DUK_ASSERT(h != NULL); /* refcount */ #ifdef DUK_USE_REFERENCE_COUNTING duk_push_int(ctx, DUK_HEAPHDR_GET_REFCOUNT(h)); #else duk_push_undefined(ctx); #endif /* heaphdr size and additional allocation size, followed by * type specific stuff (with varying value count) */ switch ((duk_small_int_t) DUK_HEAPHDR_GET_TYPE(h)) { case DUK_HTYPE_STRING: { duk_hstring *h_str = (duk_hstring *) h; duk_push_int(ctx, (int) (sizeof(duk_hstring) + DUK_HSTRING_GET_BYTELEN(h_str) + 1)); break; } case DUK_HTYPE_OBJECT: { duk_hobject *h_obj = (duk_hobject *) h; duk_int_t hdr_size; if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) { hdr_size = (duk_int_t) sizeof(duk_hcompiledfunction); } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h_obj)) { hdr_size = (duk_int_t) sizeof(duk_hnativefunction); } else if (DUK_HOBJECT_IS_THREAD(h_obj)) { hdr_size = (duk_int_t) sizeof(duk_hthread); } else { hdr_size = (duk_int_t) sizeof(duk_hobject); } duk_push_int(ctx, (int) hdr_size); duk_push_int(ctx, (int) DUK_HOBJECT_E_ALLOC_SIZE(h_obj)); duk_push_int(ctx, (int) h_obj->e_size); duk_push_int(ctx, (int) h_obj->e_used); duk_push_int(ctx, (int) h_obj->a_size); duk_push_int(ctx, (int) h_obj->h_size); if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) { duk_hbuffer *h_data = ((duk_hcompiledfunction *) h_obj)->data; if (h_data) { duk_push_int(ctx, DUK_HBUFFER_GET_SIZE(h_data)); } else { duk_push_int(ctx, 0); } } break; } case DUK_HTYPE_BUFFER: { duk_hbuffer *h_buf = (duk_hbuffer *) h; if (DUK_HBUFFER_HAS_DYNAMIC(h_buf)) { /* XXX: when usable_size == 0, dynamic buf ptr may now be NULL, in which case * the second allocation does not exist. */ duk_hbuffer_dynamic *h_dyn = (duk_hbuffer_dynamic *) h; duk_push_int(ctx, (int) (sizeof(duk_hbuffer_dynamic))); duk_push_int(ctx, (int) (DUK_HBUFFER_DYNAMIC_GET_ALLOC_SIZE(h_dyn))); } else { duk_push_int(ctx, (int) (sizeof(duk_hbuffer_fixed) + DUK_HBUFFER_GET_SIZE(h_buf) + 1)); } break; } } done: /* set values into ret array */ /* FIXME: primitive to make array from valstack slice */ n = duk_get_top(ctx); for (i = 2; i < n; i++) { duk_dup(ctx, i); duk_put_prop_index(ctx, 1, i - 2); } duk_dup(ctx, 1); return 1; }
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)); } } }
static void duk__err_augment_builtin_throw(duk_hthread *thr, duk_hthread *thr_callstack, const char *filename, duk_int_t line, duk_small_int_t noblame_fileline, duk_hobject *obj) { duk_context *ctx = (duk_context *) thr; #ifdef DUK_USE_ASSERTIONS duk_int_t entry_top; #endif #ifdef DUK_USE_ASSERTIONS entry_top = duk_get_top(ctx); #endif DUK_ASSERT(obj != NULL); DUK_UNREF(obj); /* unreferenced w/o tracebacks */ DUK_UNREF(ctx); /* unreferenced w/ tracebacks */ #ifdef DUK_USE_TRACEBACKS /* * If tracebacks are enabled, the 'tracedata' property is the only * thing we need: 'fileName' and 'lineNumber' are virtual properties * which use 'tracedata'. */ if (duk_hobject_hasprop_raw(thr, obj, DUK_HTHREAD_STRING_TRACEDATA(thr))) { DUK_DDD(DUK_DDDPRINT("error value already has a 'tracedata' property, not modifying it")); } else { duk__add_traceback(thr, thr_callstack, filename, line, noblame_fileline); } #else /* * If tracebacks are disabled, 'fileName' and 'lineNumber' are added * as plain own properties. Since Error.prototype has accessors of * the same name, we need to define own properties directly (cannot * just use e.g. duk_put_prop_stridx). Existing properties are not * overwritten in case they already exist. */ if (filename && !noblame_fileline) { /* XXX: file/line is disabled in minimal builds, so disable this too * when appropriate. */ duk_push_string(ctx, filename); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); duk_push_int(ctx, line); duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); } else if (thr_callstack->callstack_top > 0) { duk_activation *act; duk_hobject *func; act = thr_callstack->callstack + thr_callstack->callstack_top - 1; DUK_ASSERT(act >= thr_callstack->callstack && act < thr_callstack->callstack + thr_callstack->callstack_size); func = act->func; if (func) { duk_uint32_t pc; duk_uint32_t line; /* PC points to next instruction, find offending PC. Note that * PC == 0 for native code. */ pc = act->pc; if (pc > 0) { pc--; } DUK_ASSERT_DISABLE(pc >= 0); /* unsigned */ DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32); /* assume PC is at most 32 bits and non-negative */ act = NULL; /* invalidated by pushes, so get out of the way */ duk_push_hobject(ctx, func); /* [ ... error func ] */ duk_get_prop_stridx(ctx, -1, DUK_STRIDX_FILE_NAME); duk_def_prop_stridx(ctx, -3, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) { #if 0 duk_push_number(ctx, pc); duk_def_prop_stridx(ctx, -3, DUK_STRIDX_PC, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAGS_NO_OVERWRITE); #endif line = duk_hobject_pc2line_query(ctx, -1, (duk_uint_fast32_t) pc); if (line > 0) { duk_push_u32(ctx, (duk_uint32_t) line); /* -> [ ... error func line ] */ duk_def_prop_stridx(ctx, -3, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); } } else { /* Native function, no relevant lineNumber. */ } duk_pop(ctx); } } #endif /* DUK_USE_TRACEBACKS */ #ifdef DUK_USE_ASSERTIONS DUK_ASSERT(duk_get_top(ctx) == entry_top); #endif }
DUK_LOCAL void duk__add_fileline(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_bool_t noblame_fileline) { duk_context *ctx; #if defined(DUK_USE_ASSERTIONS) duk_int_t entry_top; #endif ctx = (duk_context *) thr; #if defined(DUK_USE_ASSERTIONS) entry_top = duk_get_top(ctx); #endif /* * If tracebacks are disabled, 'fileName' and 'lineNumber' are added * as plain own properties. Since Error.prototype has accessors of * the same name, we need to define own properties directly (cannot * just use e.g. duk_put_prop_stridx). Existing properties are not * overwritten in case they already exist. */ if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) { /* Compiler SyntaxError (or other error) gets the primary blame. * Currently no flag to prevent blaming. */ duk_push_uint(ctx, (duk_uint_t) thr->compile_ctx->curr_token.start_line); duk_push_hstring(ctx, thr->compile_ctx->h_filename); } else if (c_filename && !noblame_fileline) { /* C call site gets blamed next, unless flagged not to do so. * XXX: file/line is disabled in minimal builds, so disable this * too when appropriate. */ duk_push_int(ctx, c_line); duk_push_string(ctx, c_filename); } else { /* Finally, blame the innermost callstack entry which has a * .fileName property. */ duk_small_uint_t depth; duk_int_t i, i_min; duk_uint32_t ecma_line; depth = DUK_USE_TRACEBACK_DEPTH; i_min = (thr_callstack->callstack_top > (duk_size_t) depth ? (duk_int_t) (thr_callstack->callstack_top - depth) : 0); DUK_ASSERT(i_min >= 0); DUK_ASSERT(thr_callstack->callstack_top <= DUK_INT_MAX); /* callstack limits */ for (i = (duk_int_t) (thr_callstack->callstack_top - 1); i >= i_min; i--) { duk_activation *act; duk_hobject *func; duk_uint32_t pc; act = thr_callstack->callstack + i; DUK_ASSERT(act >= thr_callstack->callstack && act < thr_callstack->callstack + thr_callstack->callstack_size); func = DUK_ACT_GET_FUNC(act); if (func == NULL) { /* Lightfunc, not blamed now. */ continue; } /* PC points to next instruction, find offending PC, * PC == 0 for native code. */ pc = duk_hthread_get_act_prev_pc(thr, act); /* thr argument only used for thr->heap, so specific thread doesn't matter */ DUK_ASSERT_DISABLE(pc >= 0); /* unsigned */ DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32); /* assume PC is at most 32 bits and non-negative */ act = NULL; /* invalidated by pushes, so get out of the way */ duk_push_hobject(ctx, func); /* [ ... error func ] */ duk_get_prop_stridx(ctx, -1, DUK_STRIDX_FILE_NAME); if (!duk_is_string(ctx, -1)) { duk_pop_2(ctx); continue; } /* [ ... error func fileName ] */ ecma_line = 0; #if defined(DUK_USE_PC2LINE) if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) { ecma_line = duk_hobject_pc2line_query(ctx, -2, (duk_uint_fast32_t) pc); } else { /* Native function, no relevant lineNumber. */ } #endif /* DUK_USE_PC2LINE */ duk_push_u32(ctx, ecma_line); /* [ ... error func fileName lineNumber ] */ duk_replace(ctx, -3); /* [ ... error lineNumber fileName ] */ goto define_props; } /* No activation matches, use undefined for both .fileName and * .lineNumber (matches what we do with a _Tracedata based * no-match lookup. */ duk_push_undefined(ctx); duk_push_undefined(ctx); } define_props: /* [ ... error lineNumber fileName ] */ #if defined(DUK_USE_ASSERTIONS) DUK_ASSERT(duk_get_top(ctx) == entry_top + 2); #endif duk_xdef_prop_stridx(ctx, -3, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); }
DUK_LOCAL void duk__mark_hobject(duk_heap *heap, duk_hobject *h) { duk_uint_fast32_t i; DUK_DDD(DUK_DDDPRINT("duk__mark_hobject: %p", (void *) h)); DUK_ASSERT(h); /* XXX: use advancing pointers instead of index macros -> faster and smaller? */ for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(h); i++) { duk_hstring *key = DUK_HOBJECT_E_GET_KEY(heap, h, i); if (!key) { continue; } duk__mark_heaphdr(heap, (duk_heaphdr *) key); if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, h, i)) { duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.get); duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.set); } else { duk__mark_tval(heap, &DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->v); } } for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) { duk__mark_tval(heap, DUK_HOBJECT_A_GET_VALUE_PTR(heap, h, i)); } /* hash part is a 'weak reference' and does not contribute */ duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_GET_PROTOTYPE(heap, h)); if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) { duk_hcompiledfunction *f = (duk_hcompiledfunction *) h; duk_tval *tv, *tv_end; duk_hobject **fn, **fn_end; /* 'data' is reachable through every compiled function which * contains a reference. */ duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HCOMPILEDFUNCTION_GET_DATA(heap, f)); tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(heap, f); tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(heap, f); while (tv < tv_end) { duk__mark_tval(heap, tv); tv++; } fn = DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(heap, f); fn_end = DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(heap, f); while (fn < fn_end) { duk__mark_heaphdr(heap, (duk_heaphdr *) *fn); fn++; } } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h)) { duk_hnativefunction *f = (duk_hnativefunction *) h; DUK_UNREF(f); /* nothing to mark */ } else if (DUK_HOBJECT_IS_BUFFEROBJECT(h)) { duk_hbufferobject *b = (duk_hbufferobject *) h; duk__mark_heaphdr(heap, (duk_heaphdr *) b->buf); } else if (DUK_HOBJECT_IS_THREAD(h)) { duk_hthread *t = (duk_hthread *) h; duk_tval *tv; tv = t->valstack; while (tv < t->valstack_top) { duk__mark_tval(heap, tv); tv++; } for (i = 0; i < (duk_uint_fast32_t) t->callstack_top; i++) { duk_activation *act = t->callstack + i; duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_ACT_GET_FUNC(act)); duk__mark_heaphdr(heap, (duk_heaphdr *) act->var_env); duk__mark_heaphdr(heap, (duk_heaphdr *) act->lex_env); #ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY duk__mark_heaphdr(heap, (duk_heaphdr *) act->prev_caller); #endif } #if 0 /* nothing now */ for (i = 0; i < (duk_uint_fast32_t) t->catchstack_top; i++) { duk_catcher *cat = t->catchstack + i; } #endif duk__mark_heaphdr(heap, (duk_heaphdr *) t->resumer); /* XXX: duk_small_uint_t would be enough for this loop */ for (i = 0; i < DUK_NUM_BUILTINS; i++) { duk__mark_heaphdr(heap, (duk_heaphdr *) t->builtins[i]); } } }
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; }
static duk_uint8_t *duk__dump_func(duk_context *ctx, duk_hcompiledfunction *func, duk_bufwriter_ctx *bw_ctx, duk_uint8_t *p) { duk_hthread *thr; duk_tval *tv, *tv_end; duk_instr_t *ins, *ins_end; duk_hobject **fn, **fn_end; duk_hstring *h_str; duk_uint32_t count_instr; duk_uint32_t tmp32; duk_uint16_t tmp16; duk_double_t d; thr = (duk_hthread *) ctx; DUK_UNREF(ctx); DUK_UNREF(thr); DUK_DD(DUK_DDPRINT("dumping function %p to %p: " "consts=[%p,%p[ (%ld bytes, %ld items), " "funcs=[%p,%p[ (%ld bytes, %ld items), " "code=[%p,%p[ (%ld bytes, %ld items)", (void *) func, (void *) p, (void *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, func), (void *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(thr->heap, func), (long) DUK_HCOMPILEDFUNCTION_GET_CONSTS_SIZE(thr->heap, func), (long) DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(thr->heap, func), (void *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr->heap, func), (void *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(thr->heap, func), (long) DUK_HCOMPILEDFUNCTION_GET_FUNCS_SIZE(thr->heap, func), (long) DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(thr->heap, func), (void *) DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, func), (void *) DUK_HCOMPILEDFUNCTION_GET_CODE_END(thr->heap, func), (long) DUK_HCOMPILEDFUNCTION_GET_CODE_SIZE(thr->heap, func), (long) DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(thr->heap, func))); DUK_ASSERT(DUK_USE_ESBC_MAX_BYTES <= 0x7fffffffUL); /* ensures no overflow */ count_instr = (duk_uint32_t) DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(thr->heap, func); p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 3 * 4 + 2 * 2 + 3 * 4 + count_instr * 4, p); /* Fixed header info. */ tmp32 = count_instr; DUK_RAW_WRITE_U32_BE(p, tmp32); tmp32 = (duk_uint32_t) DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(thr->heap, func); DUK_RAW_WRITE_U32_BE(p, tmp32); tmp32 = (duk_uint32_t) DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(thr->heap, func); DUK_RAW_WRITE_U32_BE(p, tmp32); tmp16 = func->nregs; DUK_RAW_WRITE_U16_BE(p, tmp16); tmp16 = func->nargs; DUK_RAW_WRITE_U16_BE(p, tmp16); #if defined(DUK_USE_DEBUGGER_SUPPORT) tmp32 = func->start_line; DUK_RAW_WRITE_U32_BE(p, tmp32); tmp32 = func->end_line; DUK_RAW_WRITE_U32_BE(p, tmp32); #else DUK_RAW_WRITE_U32_BE(p, 0); DUK_RAW_WRITE_U32_BE(p, 0); #endif tmp32 = ((duk_heaphdr *) func)->h_flags & DUK_HEAPHDR_FLAGS_FLAG_MASK; DUK_RAW_WRITE_U32_BE(p, tmp32); /* Bytecode instructions: endian conversion needed unless * platform is big endian. */ ins = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, func); ins_end = DUK_HCOMPILEDFUNCTION_GET_CODE_END(thr->heap, func); DUK_ASSERT((duk_size_t) (ins_end - ins) == (duk_size_t) count_instr); #if defined(DUK_USE_INTEGER_BE) DUK_MEMCPY((void *) p, (const void *) ins, (size_t) (ins_end - ins)); p += (size_t) (ins_end - ins); #else while (ins != ins_end) { tmp32 = (duk_uint32_t) (*ins); DUK_RAW_WRITE_U32_BE(p, tmp32); ins++; } #endif /* Constants: variable size encoding. */ tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, func); tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(thr->heap, func); while (tv != tv_end) { /* constants are strings or numbers now */ DUK_ASSERT(DUK_TVAL_IS_STRING(tv) || DUK_TVAL_IS_NUMBER(tv)); if (DUK_TVAL_IS_STRING(tv)) { h_str = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h_str != NULL); DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1 + 4 + DUK_HSTRING_GET_BYTELEN(h_str), p), *p++ = DUK__SER_STRING; p = duk__dump_hstring_raw(p, h_str); } else { DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1 + 8, p); *p++ = DUK__SER_NUMBER; d = DUK_TVAL_GET_NUMBER(tv); DUK_RAW_WRITE_DOUBLE_BE(p, d); } tv++; } /* Inner functions recursively. */ fn = (duk_hobject **) DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr->heap, func); fn_end = (duk_hobject **) DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(thr->heap, func); while (fn != fn_end) { /* XXX: This causes recursion up to inner function depth * which is normally not an issue, e.g. mark-and-sweep uses * a recursion limiter to avoid C stack issues. Avoiding * this would mean some sort of a work list or just refusing * to serialize deep functions. */ DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(*fn)); p = duk__dump_func(ctx, (duk_hcompiledfunction *) *fn, bw_ctx, p); fn++; } /* Object extra properties. * * There are some difference between function templates and functions. * For example, function templates don't have .length and nargs is * normally used to instantiate the functions. */ p = duk__dump_uint32_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_LENGTH, (duk_uint32_t) func->nargs); p = duk__dump_string_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_NAME); p = duk__dump_string_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_FILE_NAME); p = duk__dump_buffer_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_INT_PC2LINE); p = duk__dump_varmap(thr, p, bw_ctx, (duk_hobject *) func); p = duk__dump_formals(thr, p, bw_ctx, (duk_hobject *) func); DUK_DD(DUK_DDPRINT("serialized function %p -> final pointer %p", (void *) func, (void *) p)); return p; }
static void duk__refcount_finalize_hobject(duk_hthread *thr, duk_hobject *h) { duk_uint_fast32_t i; DUK_ASSERT(h); DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h) == DUK_HTYPE_OBJECT); /* XXX: better to get base and walk forwards? */ for (i = 0; i < (duk_uint_fast32_t) h->e_next; i++) { duk_hstring *key = DUK_HOBJECT_E_GET_KEY(h, i); if (!key) { continue; } duk_heap_heaphdr_decref(thr, (duk_heaphdr *) key); if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(h, i)) { duk_heap_heaphdr_decref(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_GETTER(h, i)); duk_heap_heaphdr_decref(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_SETTER(h, i)); } else { duk_heap_tval_decref(thr, DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(h, i)); } } for (i = 0; i < (duk_uint_fast32_t) h->a_size; i++) { duk_heap_tval_decref(thr, DUK_HOBJECT_A_GET_VALUE_PTR(h, i)); } /* hash part is a 'weak reference' and does not contribute */ duk_heap_heaphdr_decref(thr, (duk_heaphdr *) h->prototype); if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) { duk_hcompiledfunction *f = (duk_hcompiledfunction *) h; duk_tval *tv, *tv_end; duk_hobject **funcs, **funcs_end; DUK_ASSERT(f->data != NULL); /* compiled functions must be created 'atomically' */ tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(f); tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(f); while (tv < tv_end) { duk_heap_tval_decref(thr, tv); tv++; } funcs = DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(f); funcs_end = DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(f); while (funcs < funcs_end) { duk_heap_heaphdr_decref(thr, (duk_heaphdr *) *funcs); funcs++; } duk_heap_heaphdr_decref(thr, (duk_heaphdr *) f->data); } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h)) { duk_hnativefunction *f = (duk_hnativefunction *) h; DUK_UNREF(f); /* nothing to finalize */ } else if (DUK_HOBJECT_IS_THREAD(h)) { duk_hthread *t = (duk_hthread *) h; duk_tval *tv; tv = t->valstack; while (tv < t->valstack_end) { duk_heap_tval_decref(thr, tv); tv++; } for (i = 0; i < (duk_uint_fast32_t) t->callstack_top; i++) { duk_activation *act = t->callstack + i; duk_heap_heaphdr_decref(thr, (duk_heaphdr *) act->func); duk_heap_heaphdr_decref(thr, (duk_heaphdr *) act->var_env); duk_heap_heaphdr_decref(thr, (duk_heaphdr *) act->lex_env); #ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY duk_heap_heaphdr_decref(thr, (duk_heaphdr *) act->prev_caller); #endif } #if 0 /* nothing now */ for (i = 0; i < (duk_uint_fast32_t) t->catchstack_top; i++) { duk_catcher *cat = t->catchstack + i; } #endif for (i = 0; i < DUK_NUM_BUILTINS; i++) { duk_heap_heaphdr_decref(thr, (duk_heaphdr *) t->builtins[i]); } duk_heap_heaphdr_decref(thr, (duk_heaphdr *) t->resumer); } }
void duk_err_augment_error(duk_hthread *thr, duk_hthread *thr_callstack, int err_index, const char *filename, int line, int noblame_fileline) { duk_context *ctx = (duk_context *) thr; duk_hobject *obj; DUK_ASSERT(thr != NULL); DUK_ASSERT(thr_callstack != NULL); DUK_ASSERT(ctx != NULL); err_index = duk_require_normalize_index(ctx, err_index); /* * Criteria for augmenting: * * - augmentation enabled in build (naturally) * - error value is an extensible object * - error value internal prototype chain contains the built-in * Error prototype object (i.e. 'val instanceof Error') */ obj = duk_require_hobject(ctx, err_index); if (!obj) { DUK_DDDPRINT("error value not an object, not augmented"); return; } if (!DUK_HOBJECT_HAS_EXTENSIBLE(obj)) { DUK_DDDPRINT("error value not extensible, not augmented"); return; } if (!duk_hobject_prototype_chain_contains(thr, obj, thr->builtins[DUK_BIDX_ERROR_PROTOTYPE])) { DUK_DDDPRINT("error value not inherited from Error, not augmented"); return; } /* Yes, augment error. */ #ifdef DUK_USE_TRACEBACKS /* * If tracebacks are enabled, the 'tracedata' property is the only * thing we need: 'fileName' and 'lineNumber' are virtual properties * which use 'tracedata'. */ if (duk_hobject_hasprop_raw(thr, obj, DUK_HTHREAD_STRING_TRACEDATA(thr))) { DUK_DDDPRINT("error value already has a 'traceback' property, not modifying it"); } else { add_traceback(thr, thr_callstack, obj, err_index, filename, line, noblame_fileline); } #else /* * If tracebacks are disabled, 'fileName' and 'lineNumber' are added * as plain own properties. Since Error.prototype has accessors of * the same name, we need to define own properties directly (cannot * just use e.g. duk_put_prop_stridx). Existing properties are not * overwritten in case they already exist. */ if (filename && !noblame_fileline) { /* FIXME: file/line is disabled in minimal builds, so disable this too * when appropriate. */ duk_push_string(ctx, filename); duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); duk_push_int(ctx, line); duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); } else if (thr_callstack->callstack_top > 0) { duk_activation *act; duk_hobject *func; duk_hbuffer *pc2line; act = thr_callstack->callstack + thr_callstack->callstack_top - 1; DUK_ASSERT(act >= thr_callstack->callstack && act < thr_callstack->callstack + thr_callstack->callstack_size); func = act->func; if (func) { int pc; duk_uint32_t line; /* PC points to next instruction, find offending PC. Note that * PC == 0 for native code. */ pc = act->pc; if (pc > 0) { pc--; } DUK_ASSERT(pc >= 0 && (double) pc < DUK_DOUBLE_2TO32); /* assume PC is at most 32 bits and non-negative */ act = NULL; /* invalidated by pushes, so get out of the way */ duk_push_hobject(ctx, func); duk_get_prop_stridx(ctx, -1, DUK_STRIDX_FILE_NAME); duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) { #if 0 duk_push_number(ctx, pc); duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_PC, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAGS_NO_OVERWRITE); #endif duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_PC2LINE); if (duk_is_buffer(ctx, -1)) { pc2line = duk_get_hbuffer(ctx, -1); DUK_ASSERT(pc2line != NULL); DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC(pc2line)); line = duk_hobject_pc2line_query((duk_hbuffer_fixed *) pc2line, (duk_uint_fast32_t) pc); duk_push_number(ctx, (double) line); /* FIXME: u32 */ duk_def_prop_stridx(ctx, err_index, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_WC | DUK_PROPDESC_FLAG_NO_OVERWRITE); } duk_pop(ctx); } else { /* Native function, no relevant lineNumber. */ } duk_pop(ctx); } } #endif /* DUK_USE_TRACEBACKS */ }
int duk_bi_function_constructor(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; int num_args; int i; int comp_flags; duk_hcompiledfunction *func; duk_hobject *outer_lex_env; duk_hobject *outer_var_env; /* normal and constructor calls have identical semantics */ num_args = duk_get_top(ctx); for (i = 0; i < num_args; i++) { duk_to_string(ctx, i); } if (num_args == 0) { duk_push_string(ctx, ""); duk_push_string(ctx, ""); } else if (num_args == 1) { 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, num_args - 1); } /* [ body formals ], formals is comma separated list that needs to be parsed */ DUK_ASSERT_TOP(ctx, 2); /* FIXME: 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); DUK_ASSERT_TOP(ctx, 3); /* FIXME: uses internal API */ /* strictness is not inherited, intentional */ comp_flags = DUK_JS_COMPILE_FLAG_FUNCEXPR; duk_push_hstring_stridx(ctx, DUK_STRIDX_COMPILE); /* XXX: copy from caller? */ duk_js_compile(thr, comp_flags); func = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1); DUK_ASSERT(func != NULL); DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) func)); /* 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); return 1; }
/* Raw helper to extract internal information / statistics about a value. * The return values are version specific and must not expose anything * that would lead to security issues (e.g. exposing compiled function * 'data' buffer might be an issue). Currently only counts and sizes and * such are given so there should not be a security impact. */ DUK_INTERNAL duk_ret_t duk_bi_duktape_object_info(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_tval *tv; duk_heaphdr *h; duk_int_t i, n; DUK_UNREF(thr); /* result array */ duk_push_array(ctx); /* -> [ val arr ] */ /* type tag (public) */ duk_push_int(ctx, duk_get_type(ctx, 0)); /* address */ tv = duk_get_tval(ctx, 0); DUK_ASSERT(tv != NULL); /* because arg count is 1 */ if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { h = DUK_TVAL_GET_HEAPHDR(tv); duk_push_pointer(ctx, (void *) h); } else { /* internal type tag */ duk_push_int(ctx, (duk_int_t) DUK_TVAL_GET_TAG(tv)); goto done; } DUK_ASSERT(h != NULL); /* refcount */ #ifdef DUK_USE_REFERENCE_COUNTING duk_push_size_t(ctx, DUK_HEAPHDR_GET_REFCOUNT(h)); #else duk_push_undefined(ctx); #endif /* heaphdr size and additional allocation size, followed by * type specific stuff (with varying value count) */ switch ((duk_small_int_t) DUK_HEAPHDR_GET_TYPE(h)) { case DUK_HTYPE_STRING: { duk_hstring *h_str = (duk_hstring *) h; duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hstring) + DUK_HSTRING_GET_BYTELEN(h_str) + 1)); break; } case DUK_HTYPE_OBJECT: { duk_hobject *h_obj = (duk_hobject *) h; duk_small_uint_t hdr_size; if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) { hdr_size = (duk_small_uint_t) sizeof(duk_hcompiledfunction); } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h_obj)) { hdr_size = (duk_small_uint_t) sizeof(duk_hnativefunction); } else if (DUK_HOBJECT_IS_THREAD(h_obj)) { hdr_size = (duk_small_uint_t) sizeof(duk_hthread); #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) } else if (DUK_HOBJECT_IS_BUFFEROBJECT(h_obj)) { hdr_size = (duk_small_uint_t) sizeof(duk_hbufferobject); #endif } else { hdr_size = (duk_small_uint_t) sizeof(duk_hobject); } duk_push_uint(ctx, (duk_uint_t) hdr_size); duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_P_ALLOC_SIZE(h_obj)); duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_ESIZE(h_obj)); /* Note: e_next indicates the number of gc-reachable entries * in the entry part, and also indicates the index where the * next new property would be inserted. It does *not* indicate * the number of non-NULL keys present in the object. That * value could be counted separately but requires a pass through * the key list. */ duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_ENEXT(h_obj)); duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_ASIZE(h_obj)); duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_HSIZE(h_obj)); if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) { duk_hbuffer *h_data = (duk_hbuffer *) DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, (duk_hcompiledfunction *) h_obj); if (h_data) { duk_push_uint(ctx, (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_data)); } else { duk_push_uint(ctx, 0); } } break; } case DUK_HTYPE_BUFFER: { duk_hbuffer *h_buf = (duk_hbuffer *) h; if (DUK_HBUFFER_HAS_DYNAMIC(h_buf)) { if (DUK_HBUFFER_HAS_EXTERNAL(h_buf)) { duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hbuffer_external))); } else { /* When alloc_size == 0 the second allocation may not * actually exist. */ duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hbuffer_dynamic))); } duk_push_uint(ctx, (duk_uint_t) (DUK_HBUFFER_GET_SIZE(h_buf))); } else { duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hbuffer_fixed) + DUK_HBUFFER_GET_SIZE(h_buf) + 1)); } break; } } done: /* set values into ret array */ /* XXX: primitive to make array from valstack slice */ n = duk_get_top(ctx); for (i = 2; i < n; i++) { duk_dup(ctx, i); duk_put_prop_index(ctx, 1, i - 2); } duk_dup(ctx, 1); return 1; }
duk_ret_t duk_bi_thread_resume(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_hthread *thr_resume; duk_tval tv_tmp; duk_tval *tv; duk_hobject *func; duk_small_int_t is_error; DUK_DDDPRINT("Duktape.Thread.resume(): thread=%!T, value=%!T, is_error=%!T", duk_get_tval(ctx, 0), duk_get_tval(ctx, 1), duk_get_tval(ctx, 2)); DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING); DUK_ASSERT(thr->heap->curr_thread == thr); thr_resume = duk_require_hthread(ctx, 0); is_error = (duk_small_int_t) duk_to_boolean(ctx, 2); duk_set_top(ctx, 2); /* [ thread value ] */ /* * Thread state and calling context checks */ if (thr->callstack_top < 2) { DUK_DDPRINT("resume state invalid: callstack should contain at least 2 entries (caller and Duktape.Thread.resume)"); goto state_error; } DUK_ASSERT((thr->callstack + thr->callstack_top - 1)->func != NULL); /* us */ DUK_ASSERT(DUK_HOBJECT_IS_NATIVEFUNCTION((thr->callstack + thr->callstack_top - 1)->func)); DUK_ASSERT((thr->callstack + thr->callstack_top - 2)->func != NULL); /* caller */ if (!DUK_HOBJECT_IS_COMPILEDFUNCTION((thr->callstack + thr->callstack_top - 2)->func)) { DUK_DDPRINT("resume state invalid: caller must be Ecmascript code"); goto state_error; } /* Note: there is no requirement that: 'thr->callstack_preventcount == 1' * like for yield. */ if (thr_resume->state != DUK_HTHREAD_STATE_INACTIVE && thr_resume->state != DUK_HTHREAD_STATE_YIELDED) { DUK_DDPRINT("resume state invalid: target thread must be INACTIVE or YIELDED"); goto state_error; } DUK_ASSERT(thr_resume->state == DUK_HTHREAD_STATE_INACTIVE || thr_resume->state == DUK_HTHREAD_STATE_YIELDED); /* Further state-dependent pre-checks */ if (thr_resume->state == DUK_HTHREAD_STATE_YIELDED) { /* no pre-checks now, assume a previous yield() has left things in * tip-top shape (longjmp handler will assert for these). */ } else { DUK_ASSERT(thr_resume->state == DUK_HTHREAD_STATE_INACTIVE); if ((thr_resume->callstack_top != 0) || (thr_resume->valstack_top - thr_resume->valstack != 1)) { goto state_invalid_initial; } tv = &thr_resume->valstack_top[-1]; DUK_ASSERT(tv >= thr_resume->valstack && tv < thr_resume->valstack_top); if (!DUK_TVAL_IS_OBJECT(tv)) { goto state_invalid_initial; } func = DUK_TVAL_GET_OBJECT(tv); DUK_ASSERT(func != NULL); if (!DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) { /* Note: cannot be a bound function either right now, * this would be easy to relax though. */ goto state_invalid_initial; } } /* * The error object has been augmented with a traceback and other * info from its creation point -- usually another thread. The * error handler is called here right before throwing, but it also * runs in the resumer's thread. It might be nice to get a traceback * from the resumee but this is not the case now. */ #if defined(DUK_USE_AUGMENT_ERROR_THROW) if (is_error) { DUK_ASSERT_TOP(ctx, 2); /* value (error) is at stack top */ duk_err_augment_error_throw(thr); /* in resumer's context */ } #endif #ifdef DUK_USE_DEBUG /* debug logging */ if (is_error) { DUK_DDDPRINT("RESUME ERROR: thread=%!T, value=%!T", duk_get_tval(ctx, 0), duk_get_tval(ctx, 1)); } else if (thr_resume->state == DUK_HTHREAD_STATE_YIELDED) { DUK_DDDPRINT("RESUME NORMAL: thread=%!T, value=%!T", duk_get_tval(ctx, 0), duk_get_tval(ctx, 1)); } else { DUK_DDDPRINT("RESUME INITIAL: thread=%!T, value=%!T", duk_get_tval(ctx, 0), duk_get_tval(ctx, 1)); } #endif thr->heap->lj.type = DUK_LJ_TYPE_RESUME; /* lj value2: thread */ DUK_ASSERT(thr->valstack_bottom < thr->valstack_top); DUK_TVAL_SET_TVAL(&tv_tmp, &thr->heap->lj.value2); DUK_TVAL_SET_TVAL(&thr->heap->lj.value2, &thr->valstack_bottom[0]); DUK_TVAL_INCREF(thr, &thr->heap->lj.value2); DUK_TVAL_DECREF(thr, &tv_tmp); /* lj value1: value */ DUK_ASSERT(thr->valstack_bottom + 1 < thr->valstack_top); DUK_TVAL_SET_TVAL(&tv_tmp, &thr->heap->lj.value1); DUK_TVAL_SET_TVAL(&thr->heap->lj.value1, &thr->valstack_bottom[1]); DUK_TVAL_INCREF(thr, &thr->heap->lj.value1); DUK_TVAL_DECREF(thr, &tv_tmp); thr->heap->lj.iserror = is_error; DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* call is from executor, so we know we have a jmpbuf */ duk_err_longjmp(thr); /* execution resumes in bytecode executor */ return 0; /* never here */ state_invalid_initial: DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid initial thread state/stack"); return 0; /* never here */ state_error: DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid state for resume"); return 0; /* never here */ }
duk_ret_t duk_bi_thread_yield(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_tval tv_tmp; duk_small_int_t is_error; DUK_DDDPRINT("Duktape.Thread.yield(): value=%!T, is_error=%!T", duk_get_tval(ctx, 0), duk_get_tval(ctx, 1)); DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING); DUK_ASSERT(thr->heap->curr_thread == thr); is_error = (duk_small_int_t) duk_to_boolean(ctx, 1); duk_set_top(ctx, 1); /* [ value ] */ /* * Thread state and calling context checks */ if (!thr->resumer) { DUK_DDPRINT("yield state invalid: current thread must have a resumer"); goto state_error; } DUK_ASSERT(thr->resumer->state == DUK_HTHREAD_STATE_RESUMED); if (thr->callstack_top < 2) { DUK_DDPRINT("yield state invalid: callstack should contain at least 2 entries (caller and Duktape.Thread.yield)"); goto state_error; } DUK_ASSERT((thr->callstack + thr->callstack_top - 1)->func != NULL); /* us */ DUK_ASSERT(DUK_HOBJECT_IS_NATIVEFUNCTION((thr->callstack + thr->callstack_top - 1)->func)); DUK_ASSERT((thr->callstack + thr->callstack_top - 2)->func != NULL); /* caller */ if (!DUK_HOBJECT_IS_COMPILEDFUNCTION((thr->callstack + thr->callstack_top - 2)->func)) { DUK_DDPRINT("yield state invalid: caller must be Ecmascript code"); goto state_error; } DUK_ASSERT(thr->callstack_preventcount >= 1); /* should never be zero, because we (Duktape.Thread.yield) are on the stack */ if (thr->callstack_preventcount != 1) { /* Note: the only yield-preventing call is Duktape.Thread.yield(), hence check for 1, not 0 */ DUK_DDPRINT("yield state invalid: there must be no yield-preventing calls in current thread callstack (preventcount is %d)", (int) thr->callstack_preventcount); goto state_error; } /* * The error object has been augmented with a traceback and other * info from its creation point -- usually the current thread. * The error handler, however, is called right before throwing * and runs in the yielder's thread. */ #if defined(DUK_USE_AUGMENT_ERROR_THROW) if (is_error) { DUK_ASSERT_TOP(ctx, 1); /* value (error) is at stack top */ duk_err_augment_error_throw(thr); /* in yielder's context */ } #endif #ifdef DUK_USE_DEBUG if (is_error) { DUK_DDDPRINT("YIELD ERROR: value=%!T", duk_get_tval(ctx, 0)); } else { DUK_DDDPRINT("YIELD NORMAL: value=%!T", duk_get_tval(ctx, 0)); } #endif /* * Process yield * * After longjmp(), processing continues in bytecode executor longjmp * handler, which will e.g. update thr->resumer to NULL. */ thr->heap->lj.type = DUK_LJ_TYPE_YIELD; /* lj value1: value */ DUK_ASSERT(thr->valstack_bottom < thr->valstack_top); DUK_TVAL_SET_TVAL(&tv_tmp, &thr->heap->lj.value1); DUK_TVAL_SET_TVAL(&thr->heap->lj.value1, &thr->valstack_bottom[0]); DUK_TVAL_INCREF(thr, &thr->heap->lj.value1); DUK_TVAL_DECREF(thr, &tv_tmp); thr->heap->lj.iserror = is_error; DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* call is from executor, so we know we have a jmpbuf */ duk_err_longjmp(thr); /* execution resumes in bytecode executor */ return 0; /* never here */ state_error: DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "invalid state for yield"); return 0; /* never here */ }