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