mirror of https://github.com/svaarala/duktape.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
466 lines
14 KiB
466 lines
14 KiB
/*
|
|
* Debug dumping of duk_hobject.
|
|
*/
|
|
|
|
#include "duk_internal.h"
|
|
|
|
#ifdef DUK_USE_DEBUG
|
|
|
|
/* must match duk_hobject.h */
|
|
static const char *class_names[32] = {
|
|
"unused",
|
|
"Arguments",
|
|
"Array",
|
|
"Boolean",
|
|
"Date",
|
|
"Error",
|
|
"Function",
|
|
"JSON",
|
|
"Math",
|
|
"Number",
|
|
"Object",
|
|
"RegExp",
|
|
"String",
|
|
"global",
|
|
"ObjEnv",
|
|
"DecEnv",
|
|
"Buffer",
|
|
"Pointer",
|
|
"unused",
|
|
"unused",
|
|
"unused",
|
|
"unused",
|
|
"unused",
|
|
"unused",
|
|
"unused",
|
|
"unused",
|
|
"unused",
|
|
"unused",
|
|
"unused",
|
|
"unused",
|
|
"unused",
|
|
"unused",
|
|
};
|
|
|
|
/* for thread dumping */
|
|
static char get_activation_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 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';
|
|
}
|
|
return '?';
|
|
}
|
|
|
|
/* for thread dumping */
|
|
static char get_catcher_summary_char(duk_catcher *catcher) {
|
|
switch (DUK_CAT_GET_TYPE(catcher)) {
|
|
case DUK_CAT_TYPE_TCF:
|
|
if (DUK_CAT_HAS_CATCH_ENABLED(catcher)) {
|
|
if (DUK_CAT_HAS_FINALLY_ENABLED(catcher)) {
|
|
return 'C'; /* catch and finally active */
|
|
} else {
|
|
return 'c'; /* only catch active */
|
|
}
|
|
} else {
|
|
if (DUK_CAT_HAS_FINALLY_ENABLED(catcher)) {
|
|
return 'f'; /* only finally active */
|
|
} else {
|
|
return 'w'; /* neither active (usually 'with') */
|
|
}
|
|
}
|
|
case DUK_CAT_TYPE_LABEL:
|
|
return 'l';
|
|
case DUK_CAT_TYPE_UNKNOWN:
|
|
default:
|
|
return '?';
|
|
}
|
|
return '?';
|
|
}
|
|
|
|
void duk_debug_dump_hobject(duk_hobject *obj) {
|
|
int i;
|
|
const char *str_empty = "";
|
|
const char *str_excl = "!";
|
|
|
|
DUK_DPRINT("=== hobject %p ===", (void *) obj);
|
|
if (!obj) {
|
|
return;
|
|
}
|
|
|
|
DUK_DPRINT(" %sextensible", DUK_HOBJECT_HAS_EXTENSIBLE(obj) ? str_empty : str_excl);
|
|
DUK_DPRINT(" %sconstructable", DUK_HOBJECT_HAS_CONSTRUCTABLE(obj) ? str_empty : str_excl);
|
|
DUK_DPRINT(" %sbound", DUK_HOBJECT_HAS_BOUND(obj) ? str_empty : str_excl);
|
|
DUK_DPRINT(" %scompiledfunction", DUK_HOBJECT_HAS_COMPILEDFUNCTION(obj) ? str_empty : str_excl);
|
|
DUK_DPRINT(" %snativefunction", DUK_HOBJECT_HAS_NATIVEFUNCTION(obj) ? str_empty : str_excl);
|
|
DUK_DPRINT(" %sthread", DUK_HOBJECT_HAS_THREAD(obj) ? str_empty : str_excl);
|
|
DUK_DPRINT(" %sarray_part", DUK_HOBJECT_HAS_ARRAY_PART(obj) ? str_empty : str_excl);
|
|
DUK_DPRINT(" %sstrict", DUK_HOBJECT_HAS_STRICT(obj) ? str_empty : str_excl);
|
|
DUK_DPRINT(" %snewenv", DUK_HOBJECT_HAS_NEWENV(obj) ? str_empty : str_excl);
|
|
DUK_DPRINT(" %snamebinding", DUK_HOBJECT_HAS_NAMEBINDING(obj) ? str_empty : str_excl);
|
|
DUK_DPRINT(" %screateargs", DUK_HOBJECT_HAS_CREATEARGS(obj) ? str_empty : str_excl);
|
|
DUK_DPRINT(" %senvrecclosed", DUK_HOBJECT_HAS_ENVRECCLOSED(obj) ? str_empty : str_excl);
|
|
DUK_DPRINT(" %sspecial_array", DUK_HOBJECT_HAS_SPECIAL_ARRAY(obj) ? str_empty : str_excl);
|
|
DUK_DPRINT(" %sspecial_stringobj", DUK_HOBJECT_HAS_SPECIAL_STRINGOBJ(obj) ? str_empty : str_excl);
|
|
DUK_DPRINT(" %sspecial_arguments", DUK_HOBJECT_HAS_SPECIAL_ARGUMENTS(obj) ? str_empty : str_excl);
|
|
|
|
DUK_DPRINT(" class: number %d -> %s",
|
|
(int) DUK_HOBJECT_GET_CLASS_NUMBER(obj),
|
|
class_names[(DUK_HOBJECT_GET_CLASS_NUMBER(obj)) & ((1 << DUK_HOBJECT_FLAG_CLASS_BITS) - 1)]);
|
|
|
|
DUK_DPRINT(" prototype: %p -> %!O",
|
|
(void *) obj->prototype,
|
|
(duk_heaphdr *) obj->prototype);
|
|
|
|
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_DPRINT(" hcompiledfunction");
|
|
DUK_DPRINT(" data: %!O", h->data);
|
|
DUK_DPRINT(" nregs: %d", (int) h->nregs);
|
|
DUK_DPRINT(" nargs: %d", (int) h->nargs);
|
|
|
|
if (h->data && DUK_HBUFFER_HAS_DYNAMIC(h->data) && DUK_HBUFFER_GET_DATA_PTR(h->data)) {
|
|
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_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_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_DPRINT(" consts: ???");
|
|
DUK_DPRINT(" funcs: ???");
|
|
DUK_DPRINT(" bytecode: ???");
|
|
}
|
|
} else if (DUK_HOBJECT_IS_NATIVEFUNCTION(obj)) {
|
|
duk_hnativefunction *h = (duk_hnativefunction *) obj;
|
|
|
|
DUK_DPRINT(" hnativefunction");
|
|
/* XXX: h->func, cannot print function pointers portably */
|
|
DUK_DPRINT(" nargs: %d", (int) h->nargs);
|
|
} else if (DUK_HOBJECT_IS_THREAD(obj)) {
|
|
duk_hthread *thr = (duk_hthread *) obj;
|
|
duk_tval *p;
|
|
|
|
DUK_DPRINT(" hthread");
|
|
DUK_DPRINT(" strict: %d", (int) thr->strict);
|
|
DUK_DPRINT(" state: %d", (int) thr->state);
|
|
|
|
DUK_DPRINT(" valstack_max: %d, callstack_max:%d, catchstack_max: %d",
|
|
thr->valstack_max, thr->callstack_max, thr->catchstack_max);
|
|
|
|
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(get_activation_summary_char(&thr->callstack[i]));
|
|
} else {
|
|
DUK_DEBUG_SUMMARY_CHAR('.');
|
|
}
|
|
}
|
|
}
|
|
DUK_DEBUG_SUMMARY_CHAR(']');
|
|
DUK_DEBUG_SUMMARY_FINISH();
|
|
|
|
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 = (int) (p - thr->valstack);
|
|
if (thr->callstack &&
|
|
thr->callstack_top > 0 &&
|
|
i == (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(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_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(get_catcher_summary_char(&thr->catchstack[i]));
|
|
} else {
|
|
DUK_DEBUG_SUMMARY_CHAR('.');
|
|
}
|
|
}
|
|
}
|
|
DUK_DEBUG_SUMMARY_CHAR(']');
|
|
DUK_DEBUG_SUMMARY_FINISH();
|
|
|
|
DUK_DPRINT(" resumer: ptr %p",
|
|
(void *) thr->resumer);
|
|
|
|
#if 0 /* worth dumping? */
|
|
for (i = 0; i < DUK_NUM_BUILTINS; i++) {
|
|
DUK_DPRINT(" builtins[%d] -> %!@O", i, thr->builtins[i]);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (obj->p) {
|
|
DUK_DPRINT(" props alloc size: %d",
|
|
(int) DUK_HOBJECT_P_COMPUTE_SIZE(obj->e_size, obj->a_size, obj->h_size));
|
|
} else {
|
|
DUK_DPRINT(" props alloc size: n/a");
|
|
}
|
|
|
|
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_DPRINT(" [%d]: UNUSED", i);
|
|
continue;
|
|
}
|
|
|
|
if (!k) {
|
|
DUK_DPRINT(" [%d]: NULL", i);
|
|
continue;
|
|
}
|
|
|
|
if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(obj, i)) {
|
|
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_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_DPRINT(" array entries:");
|
|
for (i = 0; i < obj->a_size; i++) {
|
|
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_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_DPRINT(" [%d]: unused", i);
|
|
} else if (t == DUK_HOBJECT_HASHIDX_DELETED) {
|
|
DUK_DPRINT(" [%d]: deleted", i);
|
|
} else {
|
|
DUK_DPRINT(" [%d]: %d",
|
|
i,
|
|
(int) t);
|
|
}
|
|
}
|
|
}
|
|
|
|
void duk_debug_dump_callstack(duk_hthread *thr) {
|
|
int i;
|
|
|
|
DUK_DPRINT("=== hthread %p callstack: %d entries ===",
|
|
(void *) thr,
|
|
(thr == NULL ? 0 : thr->callstack_top));
|
|
if (!thr) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < thr->callstack_top; i++) {
|
|
duk_activation *act = &thr->callstack[i];
|
|
duk_tval *this_binding = NULL;
|
|
|
|
this_binding = thr->valstack + act->idx_bottom - 1;
|
|
if (this_binding < thr->valstack || this_binding >= thr->valstack_top) {
|
|
this_binding = NULL;
|
|
}
|
|
|
|
DUK_DPRINT(" [%d] -> flags=0x%08x, func=%!O, var_env=%!iO, lex_env=%!iO, pc=%d, idx_bottom=%d, idx_retval=%d, this_binding=%!T",
|
|
i,
|
|
act->flags,
|
|
(duk_heaphdr *) act->func,
|
|
(duk_heaphdr *) act->var_env,
|
|
(duk_heaphdr *) act->lex_env,
|
|
act->pc,
|
|
act->idx_bottom,
|
|
act->idx_retval,
|
|
this_binding);
|
|
}
|
|
}
|
|
|
|
void duk_debug_dump_activation(duk_hthread *thr, duk_activation *act) {
|
|
if (!act) {
|
|
DUK_DPRINT("duk_activation: NULL");
|
|
} else {
|
|
duk_tval *this_binding = NULL;
|
|
|
|
this_binding = thr->valstack + act->idx_bottom - 1;
|
|
if (this_binding < thr->valstack || this_binding >= thr->valstack_top) {
|
|
this_binding = NULL;
|
|
}
|
|
|
|
DUK_DPRINT("duk_activation: %p -> flags=0x%08x, func=%!O, var_env=%!O, lex_env=%!O, pc=%d, idx_bottom=%d, idx_retval=%d, this_binding=%!T",
|
|
(void *) act,
|
|
act->flags,
|
|
(duk_heaphdr *) act->func,
|
|
(duk_heaphdr *) act->var_env,
|
|
(duk_heaphdr *) act->lex_env,
|
|
act->pc,
|
|
act->idx_bottom,
|
|
act->idx_retval,
|
|
this_binding);
|
|
}
|
|
}
|
|
|
|
#endif /* DUK_USE_DEBUG */
|
|
|
|
|