forked from svaarala/duktape
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathduk_api_inspect.c
More file actions
229 lines (200 loc) · 7.26 KB
/
duk_api_inspect.c
File metadata and controls
229 lines (200 loc) · 7.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/*
* 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_literal(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_literal(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.
*/
}