1 /*
2 * Inspection
3 */
4
5 #include "duk_internal.h"
6
7 /* For footprint efficient multiple value setting: arrays are much better than
8 * varargs, format string with parsing is often better than string pointer arrays.
9 */
duk__inspect_multiple_uint(duk_hthread * thr,const char * fmt,duk_int_t * vals)10 DUK_LOCAL void duk__inspect_multiple_uint(duk_hthread *thr, const char *fmt, duk_int_t *vals) {
11 duk_int_t val;
12 const char *p;
13 const char *p_curr;
14 duk_size_t len;
15
16 for (p = fmt;;) {
17 len = DUK_STRLEN(p);
18 p_curr = p;
19 p += len + 1;
20 if (len == 0) {
21 /* Double NUL (= empty key) terminates. */
22 break;
23 }
24 val = *vals++;
25 if (val >= 0) {
26 /* Negative values are markers to skip key. */
27 duk_push_string(thr, p_curr);
28 duk_push_int(thr, val);
29 duk_put_prop(thr, -3);
30 }
31 }
32 }
33
34 /* Raw helper to extract internal information / statistics about a value.
35 * The return value is an object with properties that are version specific.
36 * The properties must not expose anything that would lead to security
37 * issues (e.g. exposing compiled function 'data' buffer might be an issue).
38 * Currently only counts and sizes and such are given so there shouldn't
39 * be security implications.
40 */
41
42 #define DUK__IDX_TYPE 0
43 #define DUK__IDX_ITAG 1
44 #define DUK__IDX_REFC 2
45 #define DUK__IDX_HBYTES 3
46 #define DUK__IDX_CLASS 4
47 #define DUK__IDX_PBYTES 5
48 #define DUK__IDX_ESIZE 6
49 #define DUK__IDX_ENEXT 7
50 #define DUK__IDX_ASIZE 8
51 #define DUK__IDX_HSIZE 9
52 #define DUK__IDX_BCBYTES 10
53 #define DUK__IDX_DBYTES 11
54 #define DUK__IDX_TSTATE 12
55 #define DUK__IDX_VARIANT 13
56
duk_inspect_value(duk_hthread * thr,duk_idx_t idx)57 DUK_EXTERNAL void duk_inspect_value(duk_hthread *thr, duk_idx_t idx) {
58 duk_tval *tv;
59 duk_heaphdr *h;
60 /* The temporary values should be in an array rather than individual
61 * variables which (in practice) ensures that the compiler won't map
62 * them to registers and emit a lot of unnecessary shuffling code.
63 */
64 duk_int_t vals[14];
65
66 DUK_ASSERT_API_ENTRY(thr);
67
68 /* Assume two's complement and set everything to -1. */
69 duk_memset((void *) &vals, (int) 0xff, sizeof(vals));
70 DUK_ASSERT(vals[DUK__IDX_TYPE] == -1); /* spot check one */
71
72 tv = duk_get_tval_or_unused(thr, idx);
73 h = (DUK_TVAL_IS_HEAP_ALLOCATED(tv) ? DUK_TVAL_GET_HEAPHDR(tv) : NULL);
74
75 vals[DUK__IDX_TYPE] = duk_get_type_tval(tv);
76 vals[DUK__IDX_ITAG] = (duk_int_t) DUK_TVAL_GET_TAG(tv);
77
78 duk_push_bare_object(thr); /* Invalidates 'tv'. */
79 tv = NULL;
80
81 if (h == NULL) {
82 goto finish;
83 }
84 duk_push_pointer(thr, (void *) h);
85 duk_put_prop_literal(thr, -2, "hptr");
86
87 #if 0
88 /* Covers a lot of information, e.g. buffer and string variants. */
89 duk_push_uint(thr, (duk_uint_t) DUK_HEAPHDR_GET_FLAGS(h));
90 duk_put_prop_literal(thr, -2, "hflags");
91 #endif
92
93 #if defined(DUK_USE_REFERENCE_COUNTING)
94 vals[DUK__IDX_REFC] = (duk_int_t) DUK_HEAPHDR_GET_REFCOUNT(h);
95 #endif
96 vals[DUK__IDX_VARIANT] = 0;
97
98 /* Heaphdr size and additional allocation size, followed by
99 * type specific stuff (with varying value count).
100 */
101 switch ((duk_small_int_t) DUK_HEAPHDR_GET_TYPE(h)) {
102 case DUK_HTYPE_STRING: {
103 duk_hstring *h_str = (duk_hstring *) h;
104 vals[DUK__IDX_HBYTES] = (duk_int_t) (sizeof(duk_hstring) + DUK_HSTRING_GET_BYTELEN(h_str) + 1);
105 #if defined(DUK_USE_HSTRING_EXTDATA)
106 if (DUK_HSTRING_HAS_EXTDATA(h_str)) {
107 vals[DUK__IDX_VARIANT] = 1;
108 }
109 #endif
110 break;
111 }
112 case DUK_HTYPE_OBJECT: {
113 duk_hobject *h_obj = (duk_hobject *) h;
114
115 /* XXX: variants here are maybe pointless; class is enough? */
116 if (DUK_HOBJECT_IS_ARRAY(h_obj)) {
117 vals[DUK__IDX_HBYTES] = sizeof(duk_harray);
118 } else if (DUK_HOBJECT_IS_COMPFUNC(h_obj)) {
119 vals[DUK__IDX_HBYTES] = sizeof(duk_hcompfunc);
120 } else if (DUK_HOBJECT_IS_NATFUNC(h_obj)) {
121 vals[DUK__IDX_HBYTES] = sizeof(duk_hnatfunc);
122 } else if (DUK_HOBJECT_IS_THREAD(h_obj)) {
123 vals[DUK__IDX_HBYTES] = sizeof(duk_hthread);
124 vals[DUK__IDX_TSTATE] = ((duk_hthread *) h_obj)->state;
125 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
126 } else if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) {
127 vals[DUK__IDX_HBYTES] = sizeof(duk_hbufobj);
128 /* XXX: some size information */
129 #endif
130 } else {
131 vals[DUK__IDX_HBYTES] = (duk_small_uint_t) sizeof(duk_hobject);
132 }
133
134 vals[DUK__IDX_CLASS] = (duk_int_t) DUK_HOBJECT_GET_CLASS_NUMBER(h_obj);
135 vals[DUK__IDX_PBYTES] = (duk_int_t) DUK_HOBJECT_P_ALLOC_SIZE(h_obj);
136 vals[DUK__IDX_ESIZE] = (duk_int_t) DUK_HOBJECT_GET_ESIZE(h_obj);
137 vals[DUK__IDX_ENEXT] = (duk_int_t) DUK_HOBJECT_GET_ENEXT(h_obj);
138 vals[DUK__IDX_ASIZE] = (duk_int_t) DUK_HOBJECT_GET_ASIZE(h_obj);
139 vals[DUK__IDX_HSIZE] = (duk_int_t) DUK_HOBJECT_GET_HSIZE(h_obj);
140
141 /* Note: e_next indicates the number of gc-reachable entries
142 * in the entry part, and also indicates the index where the
143 * next new property would be inserted. It does *not* indicate
144 * the number of non-NULL keys present in the object. That
145 * value could be counted separately but requires a pass through
146 * the key list.
147 */
148
149 if (DUK_HOBJECT_IS_COMPFUNC(h_obj)) {
150 duk_hbuffer *h_data = (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(thr->heap, (duk_hcompfunc *) h_obj);
151 vals[DUK__IDX_BCBYTES] = (duk_int_t) (h_data ? DUK_HBUFFER_GET_SIZE(h_data) : 0);
152 }
153 break;
154 }
155 case DUK_HTYPE_BUFFER: {
156 duk_hbuffer *h_buf = (duk_hbuffer *) h;
157
158 if (DUK_HBUFFER_HAS_DYNAMIC(h_buf)) {
159 if (DUK_HBUFFER_HAS_EXTERNAL(h_buf)) {
160 vals[DUK__IDX_VARIANT] = 2; /* buffer variant 2: external */
161 vals[DUK__IDX_HBYTES] = (duk_uint_t) (sizeof(duk_hbuffer_external));
162 } else {
163 /* When alloc_size == 0 the second allocation may not
164 * actually exist.
165 */
166 vals[DUK__IDX_VARIANT] = 1; /* buffer variant 1: dynamic */
167 vals[DUK__IDX_HBYTES] = (duk_uint_t) (sizeof(duk_hbuffer_dynamic));
168 }
169 vals[DUK__IDX_DBYTES] = (duk_int_t) (DUK_HBUFFER_GET_SIZE(h_buf));
170 } else {
171 DUK_ASSERT(vals[DUK__IDX_VARIANT] == 0); /* buffer variant 0: fixed */
172 vals[DUK__IDX_HBYTES] = (duk_int_t) (sizeof(duk_hbuffer_fixed) + DUK_HBUFFER_GET_SIZE(h_buf));
173 }
174 break;
175 }
176 }
177
178 finish:
179 duk__inspect_multiple_uint(thr,
180 "type" "\x00" "itag" "\x00" "refc" "\x00" "hbytes" "\x00" "class" "\x00"
181 "pbytes" "\x00" "esize" "\x00" "enext" "\x00" "asize" "\x00" "hsize" "\x00"
182 "bcbytes" "\x00" "dbytes" "\x00" "tstate" "\x00" "variant" "\x00" "\x00",
183 (duk_int_t *) &vals);
184 }
185
duk_inspect_callstack_entry(duk_hthread * thr,duk_int_t level)186 DUK_EXTERNAL void duk_inspect_callstack_entry(duk_hthread *thr, duk_int_t level) {
187 duk_activation *act;
188 duk_uint_fast32_t pc;
189 duk_uint_fast32_t line;
190
191 DUK_ASSERT_API_ENTRY(thr);
192
193 /* -1 = top callstack entry
194 * -2 = caller of level -1
195 * etc
196 */
197 act = duk_hthread_get_activation_for_level(thr, level);
198 if (act == NULL) {
199 duk_push_undefined(thr);
200 return;
201 }
202 duk_push_bare_object(thr);
203
204 /* Relevant PC is just before current one because PC is
205 * post-incremented. This should match what error augment
206 * code does.
207 */
208 pc = duk_hthread_get_act_prev_pc(thr, act);
209
210 duk_push_tval(thr, &act->tv_func);
211
212 duk_push_uint(thr, (duk_uint_t) pc);
213 duk_put_prop_stridx_short(thr, -3, DUK_STRIDX_PC);
214
215 #if defined(DUK_USE_PC2LINE)
216 line = duk_hobject_pc2line_query(thr, -1, pc);
217 #else
218 line = 0;
219 #endif
220 duk_push_uint(thr, (duk_uint_t) line);
221 duk_put_prop_stridx_short(thr, -3, DUK_STRIDX_LINE_NUMBER);
222
223 duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_LC_FUNCTION);
224 /* Providing access to e.g. act->lex_env would be dangerous: these
225 * internal structures must never be accessible to the application.
226 * Duktape relies on them having consistent data, and this consistency
227 * is only asserted for, not checked for.
228 */
229 }
230