DUK_INTERNAL void duk_free_hobject_inner(duk_heap *heap, duk_hobject *h) { DUK_ASSERT(heap != NULL); DUK_ASSERT(h != NULL); DUK_FREE(heap, DUK_HOBJECT_GET_PROPS(heap, h)); if (DUK_HOBJECT_IS_COMPFUNC(h)) { duk_hcompfunc *f = (duk_hcompfunc *) h; DUK_UNREF(f); /* Currently nothing to free; 'data' is a heap object */ } else if (DUK_HOBJECT_IS_NATFUNC(h)) { duk_hnatfunc *f = (duk_hnatfunc *) 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. */ } }
DUK_INTERNAL duk_uint_fast32_t duk_hthread_get_act_curr_pc(duk_hthread *thr, duk_activation *act) { duk_instr_t *bcode; DUK_ASSERT(thr != NULL); DUK_ASSERT(act != NULL); DUK_UNREF(thr); /* XXX: store 'bcode' pointer to activation for faster lookup? */ if (act->func && DUK_HOBJECT_IS_COMPFUNC(act->func)) { bcode = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, (duk_hcompfunc *) (act->func)); return (duk_uint_fast32_t) (act->curr_pc - bcode); } return 0; }
DUK_INTERNAL duk_uint_fast32_t duk_hthread_get_act_prev_pc(duk_hthread *thr, duk_activation *act) { duk_instr_t *bcode; duk_uint_fast32_t ret; DUK_ASSERT(thr != NULL); DUK_ASSERT(act != NULL); DUK_UNREF(thr); if (act->func && DUK_HOBJECT_IS_COMPFUNC(act->func)) { bcode = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, (duk_hcompfunc *) (act->func)); ret = (duk_uint_fast32_t) (act->curr_pc - bcode); if (ret > 0) { ret--; } return ret; } return 0; }
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)); /* XXX: rearrange bits to allow a switch case to be used here? */ /* XXX: add a fast path for objects (and arrays)? */ /* DUK_HOBJECT_IS_ARRAY(h): needs no special handling now as there are * no extra fields in need of marking. */ if (DUK_HOBJECT_IS_COMPFUNC(h)) { duk_hcompfunc *f = (duk_hcompfunc *) 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_HCOMPFUNC_GET_DATA(heap, f)); duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HCOMPFUNC_GET_LEXENV(heap, f)); duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HCOMPFUNC_GET_VARENV(heap, f)); if (DUK_HCOMPFUNC_GET_DATA(heap, f) != NULL) { tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(heap, f); tv_end = DUK_HCOMPFUNC_GET_CONSTS_END(heap, f); while (tv < tv_end) { duk__mark_tval(heap, tv); tv++; } fn = DUK_HCOMPFUNC_GET_FUNCS_BASE(heap, f); fn_end = DUK_HCOMPFUNC_GET_FUNCS_END(heap, f); while (fn < fn_end) { duk__mark_heaphdr(heap, (duk_heaphdr *) *fn); fn++; } } else { /* May happen in some out-of-memory corner cases. */ DUK_D(DUK_DPRINT("duk_hcompfunc 'data' is NULL, skipping marking")); } } else if (DUK_HOBJECT_IS_NATFUNC(h)) { duk_hnatfunc *f = (duk_hnatfunc *) h; DUK_UNREF(f); /* nothing to mark */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) } else if (DUK_HOBJECT_IS_BUFOBJ(h)) { duk_hbufobj *b = (duk_hbufobj *) h; duk__mark_heaphdr(heap, (duk_heaphdr *) b->buf); duk__mark_heaphdr(heap, (duk_heaphdr *) b->buf_prop); #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ } 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); #if defined(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_LOCAL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h) { duk_fixedbuffer *fb = st->fb; duk_uint_fast32_t i; duk_tval *tv; duk_hstring *key; duk_bool_t first = 1; const char *brace1 = "{"; const char *brace2 = "}"; duk_bool_t pushed_loopstack = 0; if (duk_fb_is_full(fb)) { return; } duk__print_shared_heaphdr(st, &h->hdr); if (h && DUK_HOBJECT_HAS_ARRAY_PART(h)) { brace1 = "["; brace2 = "]"; } if (!h) { duk_fb_put_cstring(fb, "NULL"); goto finished; } if (st->depth >= st->depth_limit) { const char *subtype = "generic"; if (DUK_HOBJECT_IS_COMPFUNC(h)) { subtype = "compfunc"; } else if (DUK_HOBJECT_IS_NATFUNC(h)) { subtype = "natfunc"; } else if (DUK_HOBJECT_IS_THREAD(h)) { subtype = "thread"; } else if (DUK_HOBJECT_IS_BUFOBJ(h)) { subtype = "bufobj"; } else if (DUK_HOBJECT_IS_ARRAY(h)) { subtype = "array"; } duk_fb_sprintf(fb, "%sobject/%s %p%s", (const char *) brace1, subtype, (void *) h, (const char *) brace2); return; } for (i = 0; i < (duk_uint_fast32_t) st->loop_stack_index; i++) { if (st->loop_stack[i] == h) { duk_fb_sprintf(fb, "%sLOOP:%p%s", (const char *) brace1, (void *) h, (const char *) brace2); return; } } /* after this, return paths should 'goto finished' for decrement */ st->depth++; if (st->loop_stack_index >= st->loop_stack_limit) { duk_fb_sprintf(fb, "%sOUT-OF-LOOP-STACK%s", (const char *) brace1, (const char *) brace2); goto finished; } st->loop_stack[st->loop_stack_index++] = h; pushed_loopstack = 1; /* * Notation: double underscore used for internal properties which are not * stored in the property allocation (e.g. '__valstack'). */ duk_fb_put_cstring(fb, brace1); if (DUK_HOBJECT_GET_PROPS(NULL, h)) { duk_uint32_t a_limit; a_limit = DUK_HOBJECT_GET_ASIZE(h); if (st->internal) { /* dump all allocated entries, unused entries print as 'unused', * note that these may extend beyond current 'length' and look * a bit funny. */ } else { /* leave out trailing 'unused' elements */ while (a_limit > 0) { tv = DUK_HOBJECT_A_GET_VALUE_PTR(NULL, h, a_limit - 1); if (!DUK_TVAL_IS_UNUSED(tv)) { break; } a_limit--; } } for (i = 0; i < a_limit; i++) { tv = DUK_HOBJECT_A_GET_VALUE_PTR(NULL, h, i); DUK__COMMA(); duk__print_tval(st, tv); } for (i = 0; i < DUK_HOBJECT_GET_ENEXT(h); i++) { key = DUK_HOBJECT_E_GET_KEY(NULL, h, i); if (!key) { continue; } if (!st->internal && DUK_HSTRING_HAS_HIDDEN(key)) { continue; } DUK__COMMA(); duk__print_hstring(st, key, 0); duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_COLON); if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(NULL, h, i)) { duk_fb_sprintf(fb, "[get:%p,set:%p]", (void *) DUK_HOBJECT_E_GET_VALUE(NULL, h, i).a.get, (void *) DUK_HOBJECT_E_GET_VALUE(NULL, h, i).a.set); } else { tv = &DUK_HOBJECT_E_GET_VALUE(NULL, h, i).v; duk__print_tval(st, tv); } if (st->heavy) { duk_fb_sprintf(fb, "<%02lx>", (unsigned long) DUK_HOBJECT_E_GET_FLAGS(NULL, h, i)); } } } if (st->internal) { if (DUK_HOBJECT_HAS_EXTENSIBLE(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__extensible:true"); } if (DUK_HOBJECT_HAS_CONSTRUCTABLE(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__constructable:true"); } if (DUK_HOBJECT_HAS_BOUNDFUNC(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__boundfunc:true"); } if (DUK_HOBJECT_HAS_COMPFUNC(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__compfunc:true"); } if (DUK_HOBJECT_HAS_NATFUNC(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__natfunc:true"); } if (DUK_HOBJECT_HAS_BUFOBJ(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__bufobj:true"); } if (DUK_HOBJECT_HAS_THREAD(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__thread:true"); } if (DUK_HOBJECT_HAS_ARRAY_PART(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__array_part:true"); } if (DUK_HOBJECT_HAS_STRICT(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__strict:true"); } if (DUK_HOBJECT_HAS_NOTAIL(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__notail:true"); } if (DUK_HOBJECT_HAS_NEWENV(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__newenv:true"); } if (DUK_HOBJECT_HAS_NAMEBINDING(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__namebinding:true"); } if (DUK_HOBJECT_HAS_CREATEARGS(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__createargs:true"); } if (DUK_HOBJECT_HAS_ENVRECCLOSED(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__envrecclosed:true"); } if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_array:true"); } if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_stringobj:true"); } if (DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_arguments:true"); } if (DUK_HOBJECT_HAS_EXOTIC_DUKFUNC(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_dukfunc:true"); } if (DUK_HOBJECT_IS_BUFOBJ(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_bufobj:true"); } if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_proxyobj:true"); } } if (st->internal && DUK_HOBJECT_IS_ARRAY(h)) { duk_harray *a = (duk_harray *) h; DUK__COMMA(); duk_fb_sprintf(fb, "__length:%ld", (long) a->length); DUK__COMMA(); duk_fb_sprintf(fb, "__length_nonwritable:%ld", (long) a->length_nonwritable); } else if (st->internal && DUK_HOBJECT_IS_COMPFUNC(h)) { duk_hcompfunc *f = (duk_hcompfunc *) h; DUK__COMMA(); duk_fb_put_cstring(fb, "__data:"); duk__print_hbuffer(st, (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(NULL, f)); DUK__COMMA(); duk_fb_put_cstring(fb, "__lexenv:"); duk__print_hobject(st, DUK_HCOMPFUNC_GET_LEXENV(NULL, f)); DUK__COMMA(); duk_fb_put_cstring(fb, "__varenv:"); duk__print_hobject(st, DUK_HCOMPFUNC_GET_VARENV(NULL, f)); DUK__COMMA(); duk_fb_sprintf(fb, "__nregs:%ld", (long) f->nregs); DUK__COMMA(); duk_fb_sprintf(fb, "__nargs:%ld", (long) f->nargs); #if defined(DUK_USE_DEBUGGER_SUPPORT) DUK__COMMA(); duk_fb_sprintf(fb, "__start_line:%ld", (long) f->start_line); DUK__COMMA(); duk_fb_sprintf(fb, "__end_line:%ld", (long) f->end_line); #endif DUK__COMMA(); duk_fb_put_cstring(fb, "__data:"); duk__print_hbuffer(st, (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(NULL, f)); } else if (st->internal && DUK_HOBJECT_IS_NATFUNC(h)) { duk_hnatfunc *f = (duk_hnatfunc *) h; DUK__COMMA(); duk_fb_sprintf(fb, "__func:"); duk_fb_put_funcptr(fb, (duk_uint8_t *) &f->func, sizeof(f->func)); DUK__COMMA(); duk_fb_sprintf(fb, "__nargs:%ld", (long) f->nargs); DUK__COMMA(); duk_fb_sprintf(fb, "__magic:%ld", (long) f->magic); #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) } else if (st->internal && DUK_HOBJECT_IS_BUFOBJ(h)) { duk_hbufobj *b = (duk_hbufobj *) h; DUK__COMMA(); duk_fb_sprintf(fb, "__buf:"); duk__print_hbuffer(st, (duk_hbuffer *) b->buf); DUK__COMMA(); duk_fb_sprintf(fb, "__buf_prop:"); duk__print_hobject(st, (duk_hobject *) b->buf_prop); DUK__COMMA(); duk_fb_sprintf(fb, "__offset:%ld", (long) b->offset); DUK__COMMA(); duk_fb_sprintf(fb, "__length:%ld", (long) b->length); DUK__COMMA(); duk_fb_sprintf(fb, "__shift:%ld", (long) b->shift); DUK__COMMA(); duk_fb_sprintf(fb, "__elemtype:%ld", (long) b->elem_type); #endif } else if (st->internal && DUK_HOBJECT_IS_THREAD(h)) { duk_hthread *t = (duk_hthread *) h; DUK__COMMA(); duk_fb_sprintf(fb, "__strict:%ld", (long) t->strict); DUK__COMMA(); duk_fb_sprintf(fb, "__state:%ld", (long) t->state); DUK__COMMA(); duk_fb_sprintf(fb, "__unused1:%ld", (long) t->unused1); DUK__COMMA(); duk_fb_sprintf(fb, "__unused2:%ld", (long) t->unused2); DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_max:%ld", (long) t->valstack_max); DUK__COMMA(); duk_fb_sprintf(fb, "__callstack_max:%ld", (long) t->callstack_max); DUK__COMMA(); duk_fb_sprintf(fb, "__catchstack_max:%ld", (long) t->catchstack_max); DUK__COMMA(); duk_fb_sprintf(fb, "__valstack:%p", (void *) t->valstack); DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_end:%p/%ld", (void *) t->valstack_end, (long) (t->valstack_end - t->valstack)); DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_bottom:%p/%ld", (void *) t->valstack_bottom, (long) (t->valstack_bottom - t->valstack)); DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_top:%p/%ld", (void *) t->valstack_top, (long) (t->valstack_top - t->valstack)); DUK__COMMA(); duk_fb_sprintf(fb, "__catchstack:%p", (void *) t->catchstack); DUK__COMMA(); duk_fb_sprintf(fb, "__catchstack_size:%ld", (long) t->catchstack_size); DUK__COMMA(); duk_fb_sprintf(fb, "__catchstack_top:%ld", (long) t->catchstack_top); DUK__COMMA(); duk_fb_sprintf(fb, "__resumer:"); duk__print_hobject(st, (duk_hobject *) t->resumer); /* XXX: print built-ins array? */ } #if defined(DUK_USE_REFERENCE_COUNTING) if (st->internal) { DUK__COMMA(); duk_fb_sprintf(fb, "__refcount:%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h)); } #endif if (st->internal) { DUK__COMMA(); duk_fb_sprintf(fb, "__class:%ld", (long) DUK_HOBJECT_GET_CLASS_NUMBER(h)); } DUK__COMMA(); duk_fb_sprintf(fb, "__heapptr:%p", (void *) h); /* own pointer */ /* prototype should be last, for readability */ if (DUK_HOBJECT_GET_PROTOTYPE(NULL, h)) { if (st->follow_proto) { DUK__COMMA(); duk_fb_put_cstring(fb, "__prototype:"); duk__print_hobject(st, DUK_HOBJECT_GET_PROTOTYPE(NULL, h)); } else { DUK__COMMA(); duk_fb_sprintf(fb, "__prototype:%p", (void *) DUK_HOBJECT_GET_PROTOTYPE(NULL, h)); } } duk_fb_put_cstring(fb, brace2); #if defined(DUK_USE_HOBJECT_HASH_PART) if (st->heavy && DUK_HOBJECT_GET_HSIZE(h) > 0) { duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LANGLE); for (i = 0; i < DUK_HOBJECT_GET_HSIZE(h); i++) { duk_uint_t h_idx = DUK_HOBJECT_H_GET_INDEX(NULL, h, i); if (i > 0) { duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_COMMA); } if (h_idx == DUK_HOBJECT_HASHIDX_UNUSED) { duk_fb_sprintf(fb, "u"); } else if (h_idx == DUK_HOBJECT_HASHIDX_DELETED) { duk_fb_sprintf(fb, "d"); } else { duk_fb_sprintf(fb, "%ld", (long) h_idx); } } duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RANGLE); } #endif finished: st->depth--; if (pushed_loopstack) { st->loop_stack_index--; st->loop_stack[st->loop_stack_index] = NULL; } }
DUK_LOCAL 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) DUK_HOBJECT_GET_ENEXT(h); i++) { duk_hstring *key = DUK_HOBJECT_E_GET_KEY(thr->heap, h, i); if (!key) { continue; } duk_heaphdr_decref(thr, (duk_heaphdr *) key); if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, h, i)) { duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, h, i)); duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, h, i)); } else { duk_tval_decref(thr, DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, h, i)); } } for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) { duk_tval_decref(thr, DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, h, i)); } /* hash part is a 'weak reference' and does not contribute */ duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h)); /* XXX: rearrange bits to allow a switch case to be used here? */ /* XXX: add a fast path for objects (and arrays)? */ /* DUK_HOBJECT_IS_ARRAY(h): needs no special handling now as there are * no extra fields in need of decref. */ if (DUK_HOBJECT_IS_COMPFUNC(h)) { duk_hcompfunc *f = (duk_hcompfunc *) h; duk_tval *tv, *tv_end; duk_hobject **funcs, **funcs_end; DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, f) != NULL); /* compiled functions must be created 'atomically' */ tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, f); tv_end = DUK_HCOMPFUNC_GET_CONSTS_END(thr->heap, f); while (tv < tv_end) { duk_tval_decref(thr, tv); tv++; } funcs = DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, f); funcs_end = DUK_HCOMPFUNC_GET_FUNCS_END(thr->heap, f); while (funcs < funcs_end) { duk_heaphdr_decref(thr, (duk_heaphdr *) *funcs); funcs++; } duk_heaphdr_decref(thr, (duk_heaphdr *) DUK_HCOMPFUNC_GET_DATA(thr->heap, f)); } else if (DUK_HOBJECT_IS_NATFUNC(h)) { duk_hnatfunc *f = (duk_hnatfunc *) h; DUK_UNREF(f); /* nothing to finalize */ } else if (DUK_HOBJECT_IS_BUFOBJ(h)) { duk_hbufobj *b = (duk_hbufobj *) h; if (b->buf) { duk_heaphdr_decref(thr, (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_tval_decref(thr, tv); tv++; } for (i = 0; i < (duk_uint_fast32_t) t->callstack_top; i++) { duk_activation *act = t->callstack + i; duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) DUK_ACT_GET_FUNC(act)); duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) act->var_env); duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) act->lex_env); #ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY duk_heaphdr_decref_allownull(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_heaphdr_decref_allownull(thr, (duk_heaphdr *) t->builtins[i]); } duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) t->resumer); } }
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; DUK_UNREF(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_COMPFUNC(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); }