/* * Inspection */ #include "duk_internal.h" /* For footprint efficient multiple value setting: arrays are much better than * varargs, format string with parsing is often better than string pointer arrays. */ DUK_LOCAL void duk__inspect_multiple_uint(duk_hthread *thr, const char *fmt, duk_int_t *vals) { duk_int_t val; const char *p; const char *p_curr; duk_size_t len; for (p = fmt;;) { len = DUK_STRLEN(p); p_curr = p; p += len + 1; if (len == 0) { /* Double NUL (= empty key) terminates. */ break; } val = *vals++; if (val >= 0) { /* Negative values are markers to skip key. */ duk_push_string(thr, p_curr); duk_push_int(thr, val); duk_put_prop(thr, -3); } } } /* Raw helper to extract internal information / statistics about a value. * The return value is an object with properties that are version specific. * The properties 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 shouldn't * be security implications. */ #define DUK__IDX_TYPE 0 #define DUK__IDX_ITAG 1 #define DUK__IDX_REFC 2 #define DUK__IDX_HBYTES 3 #define DUK__IDX_CLASS 4 #define DUK__IDX_PBYTES 5 #define DUK__IDX_ESIZE 6 #define DUK__IDX_ENEXT 7 #define DUK__IDX_ASIZE 8 #define DUK__IDX_HSIZE 9 #define DUK__IDX_BCBYTES 10 #define DUK__IDX_DBYTES 11 #define DUK__IDX_TSTATE 12 #define DUK__IDX_VARIANT 13 DUK_EXTERNAL void duk_inspect_value(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; duk_heaphdr *h; /* The temporary values should be in an array rather than individual * variables which (in practice) ensures that the compiler won't map * them to registers and emit a lot of unnecessary shuffling code. */ duk_int_t vals[14]; DUK_ASSERT_API_ENTRY(thr); /* Assume two's complement and set everything to -1. */ DUK_MEMSET((void *) &vals, (int) 0xff, sizeof(vals)); DUK_ASSERT(vals[DUK__IDX_TYPE] == -1); /* spot check one */ tv = duk_get_tval_or_unused(thr, idx); h = (DUK_TVAL_IS_HEAP_ALLOCATED(tv) ? DUK_TVAL_GET_HEAPHDR(tv) : NULL); vals[DUK__IDX_TYPE] = duk_get_type_tval(tv); vals[DUK__IDX_ITAG] = (duk_int_t) DUK_TVAL_GET_TAG(tv); duk_push_bare_object(thr); /* Invalidates 'tv'. */ tv = NULL; if (h == NULL) { goto finish; } duk_push_pointer(thr, (void *) h); duk_put_prop_string(thr, -2, "hptr"); #if 0 /* Covers a lot of information, e.g. buffer and string variants. */ duk_push_uint(thr, (duk_uint_t) DUK_HEAPHDR_GET_FLAGS(h)); duk_put_prop_string(thr, -2, "hflags"); #endif #if defined(DUK_USE_REFERENCE_COUNTING) vals[DUK__IDX_REFC] = (duk_int_t) DUK_HEAPHDR_GET_REFCOUNT(h); #endif vals[DUK__IDX_VARIANT] = 0; /* 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; vals[DUK__IDX_HBYTES] = (duk_int_t) (sizeof(duk_hstring) + DUK_HSTRING_GET_BYTELEN(h_str) + 1); #if defined(DUK_USE_HSTRING_EXTDATA) if (DUK_HSTRING_HAS_EXTDATA(h_str)) { vals[DUK__IDX_VARIANT] = 1; } #endif break; } case DUK_HTYPE_OBJECT: { duk_hobject *h_obj = (duk_hobject *) h; /* XXX: variants here are maybe pointless; class is enough? */ if (DUK_HOBJECT_IS_ARRAY(h_obj)) { vals[DUK__IDX_HBYTES] = sizeof(duk_harray); } else if (DUK_HOBJECT_IS_COMPFUNC(h_obj)) { vals[DUK__IDX_HBYTES] = sizeof(duk_hcompfunc); } else if (DUK_HOBJECT_IS_NATFUNC(h_obj)) { vals[DUK__IDX_HBYTES] = sizeof(duk_hnatfunc); } else if (DUK_HOBJECT_IS_THREAD(h_obj)) { vals[DUK__IDX_HBYTES] = sizeof(duk_hthread); vals[DUK__IDX_TSTATE] = ((duk_hthread *) h_obj)->state; #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) } else if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) { vals[DUK__IDX_HBYTES] = sizeof(duk_hbufobj); /* XXX: some size information */ #endif } else { vals[DUK__IDX_HBYTES] = (duk_small_uint_t) sizeof(duk_hobject); } vals[DUK__IDX_CLASS] = (duk_int_t) DUK_HOBJECT_GET_CLASS_NUMBER(h_obj); vals[DUK__IDX_PBYTES] = (duk_int_t) DUK_HOBJECT_P_ALLOC_SIZE(h_obj), vals[DUK__IDX_ESIZE] = (duk_int_t) DUK_HOBJECT_GET_ESIZE(h_obj); vals[DUK__IDX_ENEXT] = (duk_int_t) DUK_HOBJECT_GET_ENEXT(h_obj); vals[DUK__IDX_ASIZE] = (duk_int_t) DUK_HOBJECT_GET_ASIZE(h_obj); vals[DUK__IDX_HSIZE] = (duk_int_t) DUK_HOBJECT_GET_HSIZE(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. */ if (DUK_HOBJECT_IS_COMPFUNC(h_obj)) { duk_hbuffer *h_data = (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(thr->heap, (duk_hcompfunc *) h_obj); vals[DUK__IDX_BCBYTES] = (duk_int_t) (h_data ? DUK_HBUFFER_GET_SIZE(h_data) : 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)) { vals[DUK__IDX_VARIANT] = 2; /* buffer variant 2: external */ vals[DUK__IDX_HBYTES] = (duk_uint_t) (sizeof(duk_hbuffer_external)); } else { /* When alloc_size == 0 the second allocation may not * actually exist. */ vals[DUK__IDX_VARIANT] = 1; /* buffer variant 1: dynamic */ vals[DUK__IDX_HBYTES] = (duk_uint_t) (sizeof(duk_hbuffer_dynamic)); } vals[DUK__IDX_DBYTES] = (duk_int_t) (DUK_HBUFFER_GET_SIZE(h_buf)); } else { DUK_ASSERT(vals[DUK__IDX_VARIANT] == 0); /* buffer variant 0: fixed */ vals[DUK__IDX_HBYTES] = (duk_int_t) (sizeof(duk_hbuffer_fixed) + DUK_HBUFFER_GET_SIZE(h_buf)); } break; } } finish: duk__inspect_multiple_uint(thr, "type" "\x00" "itag" "\x00" "refc" "\x00" "hbytes" "\x00" "class" "\x00" "pbytes" "\x00" "esize" "\x00" "enext" "\x00" "asize" "\x00" "hsize" "\x00" "bcbytes" "\x00" "dbytes" "\x00" "tstate" "\x00" "variant" "\x00" "\x00", (duk_int_t *) &vals); } DUK_EXTERNAL void duk_inspect_callstack_entry(duk_hthread *thr, duk_int_t level) { duk_activation *act; duk_uint_fast32_t pc; duk_uint_fast32_t line; DUK_ASSERT_API_ENTRY(thr); /* -1 = top callstack entry * -2 = caller of level -1 * etc */ act = duk_hthread_get_activation_for_level(thr, level); if (act == NULL) { duk_push_undefined(thr); return; } duk_push_bare_object(thr); /* Relevant PC is just before current one because PC is * post-incremented. This should match what error augment * code does. */ pc = duk_hthread_get_act_prev_pc(thr, act); duk_push_tval(thr, &act->tv_func); duk_push_uint(thr, (duk_uint_t) pc); duk_put_prop_stridx_short(thr, -3, DUK_STRIDX_PC); #if defined(DUK_USE_PC2LINE) line = duk_hobject_pc2line_query(thr, -1, pc); #else line = 0; #endif duk_push_uint(thr, (duk_uint_t) line); duk_put_prop_stridx_short(thr, -3, DUK_STRIDX_LINE_NUMBER); duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_LC_FUNCTION); /* Providing access to e.g. act->lex_env would be dangerous: these * internal structures must never be accessible to the application. * Duktape relies on them having consistent data, and this consistency * is only asserted for, not checked for. */ }