2 * Custom formatter for debug printing, allowing Duktape specific data
3 * structures (such as tagged values and heap objects) to be printed with
4 * a nice format string. Because debug printing should not affect execution
5 * state, formatting here must be independent of execution (see implications
6 * below) and must not allocate memory.
8 * Custom format tags begin with a '%!' to safely distinguish them from
9 * standard format tags. The following conversions are supported:
11 * %!T tagged value (duk_tval *)
12 * %!O heap object (duk_heaphdr *)
13 * %!I decoded bytecode instruction
14 * %!C bytecode instruction opcode name (arg is long)
16 * Everything is serialized in a JSON-like manner. The default depth is one
17 * level, internal prototype is not followed, and internal properties are not
18 * serialized. The following modifiers change this behavior:
21 * # print binary representations (where applicable)
22 * d deep traversal of own properties (not prototype)
23 * p follow prototype chain (useless without 'd')
24 * i include internal properties (other than prototype)
28 * For instance, the following serializes objects recursively, but does not
29 * follow the prototype chain nor print internal properties: "%!dO".
33 * * Standard snprintf return value semantics seem to vary. This
34 * implementation returns the number of bytes it actually wrote
35 * (excluding the null terminator). If retval == buffer size,
36 * output was truncated (except for corner cases).
38 * * Output format is intentionally different from Ecmascript
39 * formatting requirements, as formatting here serves debugging
42 * * Depth checking (and updating) is done in each type printer
43 * separately, to allow them to call each other freely.
45 * * Some pathological structures might take ages to print (e.g.
46 * self recursion with 100 properties pointing to the object
47 * itself). To guard against these, each printer also checks
48 * whether the output buffer is full; if so, early exit.
50 * * Reference loops are detected using a loop stack.
53 #include "duk_internal.h"
61 /* list of conversion specifiers that terminate a format tag;
62 * this is unfortunately guesswork.
64 #define DUK__ALLOWED_STANDARD_SPECIFIERS "diouxXeEfFgGaAcsCSpnm"
66 /* maximum length of standard format tag that we support */
67 #define DUK__MAX_FORMAT_TAG_LENGTH 32
69 /* heapobj recursion depth when deep printing is selected */
70 #define DUK__DEEP_DEPTH_LIMIT 8
72 /* maximum recursion depth for loop detection stacks */
73 #define DUK__LOOP_STACK_DEPTH 256
75 /* must match bytecode defines now; build autogenerate? */
76 DUK_LOCAL
const char *duk__bc_optab
[64] = {
77 "LDREG", "STREG", "LDCONST", "LDINT", "LDINTX", "MPUTOBJ", "MPUTOBJI", "MPUTARR", "MPUTARRI", "NEW",
78 "NEWI", "REGEXP", "CSREG", "CSREGI", "GETVAR", "PUTVAR", "DECLVAR", "DELVAR", "CSVAR", "CSVARI",
79 "CLOSURE", "GETPROP", "PUTPROP", "DELPROP", "CSPROP", "CSPROPI", "ADD", "SUB", "MUL", "DIV",
80 "MOD", "BAND", "BOR", "BXOR", "BASL", "BLSR", "BASR", "EQ", "NEQ", "SEQ",
81 "SNEQ", "GT", "GE", "LT", "LE", "IF", "JUMP", "RETURN", "CALL", "CALLI",
82 "TRYCATCH", "EXTRA", "PREINCR", "PREDECR", "POSTINCR", "POSTDECR", "PREINCV", "PREDECV", "POSTINCV", "POSTDECV",
83 "PREINCP", "PREDECP", "POSTINCP", "POSTDECP"
86 DUK_LOCAL
const char *duk__bc_extraoptab
[256] = {
87 "NOP", "INVALID", "LDTHIS", "LDUNDEF", "LDNULL", "LDTRUE", "LDFALSE", "NEWOBJ", "NEWARR", "SETALEN",
88 "TYPEOF", "TYPEOFID", "INITENUM", "NEXTENUM", "INITSET", "INITSETI", "INITGET", "INITGETI", "ENDTRY", "ENDCATCH",
89 "ENDFIN", "THROW", "INVLHS", "UNM", "UNP", "DEBUGGER", "BREAK", "CONTINUE", "BNOT", "LNOT",
90 "INSTOF", "IN", "LABEL", "ENDLABEL", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
91 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
93 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
94 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
95 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
96 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
97 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
99 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
100 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
101 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
102 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
103 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
105 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
106 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
107 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
108 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
109 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
111 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
112 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
113 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
114 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
115 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX", "XXX",
117 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX"
120 typedef struct duk__dprint_state duk__dprint_state
;
121 struct duk__dprint_state
{
124 /* loop_stack_index could be perhaps be replaced by 'depth', but it's nice
125 * to not couple these two mechanisms unnecessarily.
127 duk_hobject
*loop_stack
[DUK__LOOP_STACK_DEPTH
];
128 duk_int_t loop_stack_index
;
129 duk_int_t loop_stack_limit
;
132 duk_int_t depth_limit
;
137 duk_bool_t follow_proto
;
143 DUK_LOCAL_DECL
void duk__print_hstring(duk__dprint_state
*st
, duk_hstring
*k
, duk_bool_t quotes
);
144 DUK_LOCAL_DECL
void duk__print_hobject(duk__dprint_state
*st
, duk_hobject
*h
);
145 DUK_LOCAL_DECL
void duk__print_hbuffer(duk__dprint_state
*st
, duk_hbuffer
*h
);
146 DUK_LOCAL_DECL
void duk__print_tval(duk__dprint_state
*st
, duk_tval
*tv
);
147 DUK_LOCAL_DECL
void duk__print_instr(duk__dprint_state
*st
, duk_instr_t ins
);
148 DUK_LOCAL_DECL
void duk__print_heaphdr(duk__dprint_state
*st
, duk_heaphdr
*h
);
149 DUK_LOCAL_DECL
void duk__print_shared_heaphdr(duk__dprint_state
*st
, duk_heaphdr
*h
);
150 DUK_LOCAL_DECL
void duk__print_shared_heaphdr_string(duk__dprint_state
*st
, duk_heaphdr_string
*h
);
152 DUK_LOCAL
void duk__print_shared_heaphdr(duk__dprint_state
*st
, duk_heaphdr
*h
) {
153 duk_fixedbuffer
*fb
= st
->fb
;
156 duk_fb_sprintf(fb
, "(%p)", (void *) h
);
165 duk_fb_put_byte(fb
, (duk_uint8_t
) DUK_ASC_LBRACKET
);
166 for (i
= 0; i
< (duk_size_t
) sizeof(*h
); i
++) {
167 duk_fb_sprintf(fb
, "%02lx", (unsigned long) ((duk_uint8_t
*)h
)[i
]);
169 duk_fb_put_byte(fb
, (duk_uint8_t
) DUK_ASC_RBRACKET
);
172 #ifdef DUK_USE_REFERENCE_COUNTING /* currently implicitly also DUK_USE_DOUBLE_LINKED_HEAP */
174 duk_fb_sprintf(fb
, "[h_next=%p,h_prev=%p,h_refcount=%lu,h_flags=%08lx,type=%ld,"
175 "reachable=%ld,temproot=%ld,finalizable=%ld,finalized=%ld]",
176 (void *) DUK_HEAPHDR_GET_NEXT(NULL
, h
),
177 (void *) DUK_HEAPHDR_GET_PREV(NULL
, h
),
178 (unsigned long) DUK_HEAPHDR_GET_REFCOUNT(h
),
179 (unsigned long) DUK_HEAPHDR_GET_FLAGS(h
),
180 (long) DUK_HEAPHDR_GET_TYPE(h
),
181 (long) (DUK_HEAPHDR_HAS_REACHABLE(h
) ? 1 : 0),
182 (long) (DUK_HEAPHDR_HAS_TEMPROOT(h
) ? 1 : 0),
183 (long) (DUK_HEAPHDR_HAS_FINALIZABLE(h
) ? 1 : 0),
184 (long) (DUK_HEAPHDR_HAS_FINALIZED(h
) ? 1 : 0));
188 duk_fb_sprintf(fb
, "[h_next=%p,h_flags=%08lx,type=%ld,reachable=%ld,temproot=%ld,finalizable=%ld,finalized=%ld]",
189 (void *) DUK_HEAPHDR_GET_NEXT(NULL
, h
),
190 (unsigned long) DUK_HEAPHDR_GET_FLAGS(h
),
191 (long) DUK_HEAPHDR_GET_TYPE(h
),
192 (long) (DUK_HEAPHDR_HAS_REACHABLE(h
) ? 1 : 0),
193 (long) (DUK_HEAPHDR_HAS_TEMPROOT(h
) ? 1 : 0),
194 (long) (DUK_HEAPHDR_HAS_FINALIZABLE(h
) ? 1 : 0),
195 (long) (DUK_HEAPHDR_HAS_FINALIZED(h
) ? 1 : 0));
200 DUK_LOCAL
void duk__print_shared_heaphdr_string(duk__dprint_state
*st
, duk_heaphdr_string
*h
) {
201 duk_fixedbuffer
*fb
= st
->fb
;
204 duk_fb_sprintf(fb
, "(%p)", (void *) h
);
213 duk_fb_put_byte(fb
, (duk_uint8_t
) DUK_ASC_LBRACKET
);
214 for (i
= 0; i
< (duk_size_t
) sizeof(*h
); i
++) {
215 duk_fb_sprintf(fb
, "%02lx", (unsigned long) ((duk_uint8_t
*)h
)[i
]);
217 duk_fb_put_byte(fb
, (duk_uint8_t
) DUK_ASC_RBRACKET
);
220 #ifdef DUK_USE_REFERENCE_COUNTING
222 duk_fb_sprintf(fb
, "[h_refcount=%lu,h_flags=%08lx,type=%ld,reachable=%ld,temproot=%ld,finalizable=%ld,finalized=%ld]",
223 (unsigned long) DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr
*) h
),
224 (unsigned long) DUK_HEAPHDR_GET_FLAGS((duk_heaphdr
*) h
),
225 (long) DUK_HEAPHDR_GET_TYPE((duk_heaphdr
*) h
),
226 (long) (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr
*) h
) ? 1 : 0),
227 (long) (DUK_HEAPHDR_HAS_TEMPROOT((duk_heaphdr
*) h
) ? 1 : 0),
228 (long) (DUK_HEAPHDR_HAS_FINALIZABLE((duk_heaphdr
*) h
) ? 1 : 0),
229 (long) (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr
*) h
) ? 1 : 0));
233 duk_fb_sprintf(fb
, "[h_flags=%08lx,type=%ld,reachable=%ld,temproot=%ld,finalizable=%ld,finalized=%ld]",
234 (unsigned long) DUK_HEAPHDR_GET_FLAGS((duk_heaphdr
*) h
),
235 (long) DUK_HEAPHDR_GET_TYPE((duk_heaphdr
*) h
),
236 (long) (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr
*) h
) ? 1 : 0),
237 (long) (DUK_HEAPHDR_HAS_TEMPROOT((duk_heaphdr
*) h
) ? 1 : 0),
238 (long) (DUK_HEAPHDR_HAS_FINALIZABLE((duk_heaphdr
*) h
) ? 1 : 0),
239 (long) (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr
*) h
) ? 1 : 0));
244 DUK_LOCAL
void duk__print_hstring(duk__dprint_state
*st
, duk_hstring
*h
, duk_bool_t quotes
) {
245 duk_fixedbuffer
*fb
= st
->fb
;
246 const duk_uint8_t
*p
;
247 const duk_uint8_t
*p_end
;
249 /* terminal type: no depth check */
251 if (duk_fb_is_full(fb
)) {
255 duk__print_shared_heaphdr_string(st
, &h
->hdr
);
258 duk_fb_put_cstring(fb
, "NULL");
262 p
= DUK_HSTRING_GET_DATA(h
);
263 p_end
= p
+ DUK_HSTRING_GET_BYTELEN(h
);
265 if (p_end
> p
&& p
[0] == DUK_ASC_UNDERSCORE
) {
266 /* if property key begins with underscore, encode it with
267 * forced quotes (e.g. "_Foo") to distinguish it from encoded
268 * internal properties (e.g. \xffBar -> _Bar).
274 duk_fb_put_byte(fb
, (duk_uint8_t
) DUK_ASC_DOUBLEQUOTE
);
277 duk_uint8_t ch
= *p
++;
279 /* two special escapes: '\' and '"', other printables as is */
281 duk_fb_sprintf(fb
, "\\\\");
282 } else if (ch
== '"') {
283 duk_fb_sprintf(fb
, "\\\"");
284 } else if (ch
>= 0x20 && ch
<= 0x7e) {
285 duk_fb_put_byte(fb
, ch
);
286 } else if (ch
== 0xff && !quotes
) {
287 /* encode \xffBar as _Bar if no quotes are applied, this is for
288 * readable internal keys.
290 duk_fb_put_byte(fb
, (duk_uint8_t
) DUK_ASC_UNDERSCORE
);
292 duk_fb_sprintf(fb
, "\\x%02lx", (unsigned long) ch
);
296 duk_fb_put_byte(fb
, (duk_uint8_t
) DUK_ASC_DOUBLEQUOTE
);
298 #ifdef DUK_USE_REFERENCE_COUNTING
299 /* XXX: limit to quoted strings only, to save keys from being cluttered? */
300 duk_fb_sprintf(fb
, "/%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT(&h
->hdr
));
307 #define DUK__COMMA() do { \
311 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_COMMA); \
315 DUK_LOCAL
void duk__print_hobject(duk__dprint_state
*st
, duk_hobject
*h
) {
316 duk_fixedbuffer
*fb
= st
->fb
;
320 duk_bool_t first
= 1;
321 const char *brace1
= "{";
322 const char *brace2
= "}";
323 duk_bool_t pushed_loopstack
= 0;
325 if (duk_fb_is_full(fb
)) {
329 duk__print_shared_heaphdr(st
, &h
->hdr
);
331 if (h
&& DUK_HOBJECT_HAS_ARRAY_PART(h
)) {
337 duk_fb_put_cstring(fb
, "NULL");
341 if (st
->depth
>= st
->depth_limit
) {
342 if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h
)) {
343 duk_fb_sprintf(fb
, "%sobject/compiledfunction %p%s", (const char *) brace1
, (void *) h
, (const char *) brace2
);
344 } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h
)) {
345 duk_fb_sprintf(fb
, "%sobject/nativefunction %p%s", (const char *) brace1
, (void *) h
, (const char *) brace2
);
346 } else if (DUK_HOBJECT_IS_THREAD(h
)) {
347 duk_fb_sprintf(fb
, "%sobject/thread %p%s", (const char *) brace1
, (void *) h
, (const char *) brace2
);
349 duk_fb_sprintf(fb
, "%sobject %p%s", (const char *) brace1
, (void *) h
, (const char *) brace2
); /* may be NULL */
354 for (i
= 0; i
< (duk_uint_fast32_t
) st
->loop_stack_index
; i
++) {
355 if (st
->loop_stack
[i
] == h
) {
356 duk_fb_sprintf(fb
, "%sLOOP:%p%s", (const char *) brace1
, (void *) h
, (const char *) brace2
);
361 /* after this, return paths should 'goto finished' for decrement */
364 if (st
->loop_stack_index
>= st
->loop_stack_limit
) {
365 duk_fb_sprintf(fb
, "%sOUT-OF-LOOP-STACK%s", (const char *) brace1
, (const char *) brace2
);
368 st
->loop_stack
[st
->loop_stack_index
++] = h
;
369 pushed_loopstack
= 1;
372 * Notation: double underscore used for internal properties which are not
373 * stored in the property allocation (e.g. '__valstack').
376 duk_fb_put_cstring(fb
, brace1
);
378 if (DUK_HOBJECT_GET_PROPS(NULL
, h
)) {
379 duk_uint32_t a_limit
;
381 a_limit
= DUK_HOBJECT_GET_ASIZE(h
);
383 /* dump all allocated entries, unused entries print as 'unused',
384 * note that these may extend beyond current 'length' and look
388 /* leave out trailing 'unused' elements */
389 while (a_limit
> 0) {
390 tv
= DUK_HOBJECT_A_GET_VALUE_PTR(NULL
, h
, a_limit
- 1);
391 if (!DUK_TVAL_IS_UNDEFINED_UNUSED(tv
)) {
398 for (i
= 0; i
< a_limit
; i
++) {
399 tv
= DUK_HOBJECT_A_GET_VALUE_PTR(NULL
, h
, i
);
401 duk__print_tval(st
, tv
);
403 for (i
= 0; i
< DUK_HOBJECT_GET_ENEXT(h
); i
++) {
404 key
= DUK_HOBJECT_E_GET_KEY(NULL
, h
, i
);
409 DUK_HSTRING_GET_BYTELEN(key
) > 0 &&
410 DUK_HSTRING_GET_DATA(key
)[0] == 0xff) {
411 /* XXX: use DUK_HSTRING_FLAG_INTERNAL? */
415 duk__print_hstring(st
, key
, 0);
416 duk_fb_put_byte(fb
, (duk_uint8_t
) DUK_ASC_COLON
);
417 if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(NULL
, h
, i
)) {
418 duk_fb_sprintf(fb
, "[get:%p,set:%p]",
419 (void *) DUK_HOBJECT_E_GET_VALUE(NULL
, h
, i
).a
.get
,
420 (void *) DUK_HOBJECT_E_GET_VALUE(NULL
, h
, i
).a
.set
);
422 tv
= &DUK_HOBJECT_E_GET_VALUE(NULL
, h
, i
).v
;
423 duk__print_tval(st
, tv
);
426 duk_fb_sprintf(fb
, "<%02lx>", (unsigned long) DUK_HOBJECT_E_GET_FLAGS(NULL
, h
, i
));
431 if (DUK_HOBJECT_HAS_EXTENSIBLE(h
)) {
432 DUK__COMMA(); duk_fb_sprintf(fb
, "__extensible:true");
436 if (DUK_HOBJECT_HAS_CONSTRUCTABLE(h
)) {
437 DUK__COMMA(); duk_fb_sprintf(fb
, "__constructable:true");
441 if (DUK_HOBJECT_HAS_BOUND(h
)) {
442 DUK__COMMA(); duk_fb_sprintf(fb
, "__bound:true");
446 if (DUK_HOBJECT_HAS_COMPILEDFUNCTION(h
)) {
447 DUK__COMMA(); duk_fb_sprintf(fb
, "__compiledfunction:true");
451 if (DUK_HOBJECT_HAS_NATIVEFUNCTION(h
)) {
452 DUK__COMMA(); duk_fb_sprintf(fb
, "__nativefunction:true");
456 if (DUK_HOBJECT_HAS_THREAD(h
)) {
457 DUK__COMMA(); duk_fb_sprintf(fb
, "__thread:true");
461 if (DUK_HOBJECT_HAS_ARRAY_PART(h
)) {
462 DUK__COMMA(); duk_fb_sprintf(fb
, "__array_part:true");
466 if (DUK_HOBJECT_HAS_STRICT(h
)) {
467 DUK__COMMA(); duk_fb_sprintf(fb
, "__strict:true");
471 if (DUK_HOBJECT_HAS_NEWENV(h
)) {
472 DUK__COMMA(); duk_fb_sprintf(fb
, "__newenv:true");
476 if (DUK_HOBJECT_HAS_NAMEBINDING(h
)) {
477 DUK__COMMA(); duk_fb_sprintf(fb
, "__namebinding:true");
481 if (DUK_HOBJECT_HAS_CREATEARGS(h
)) {
482 DUK__COMMA(); duk_fb_sprintf(fb
, "__createargs:true");
486 if (DUK_HOBJECT_HAS_ENVRECCLOSED(h
)) {
487 DUK__COMMA(); duk_fb_sprintf(fb
, "__envrecclosed:true");
491 if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(h
)) {
492 DUK__COMMA(); duk_fb_sprintf(fb
, "__special_array:true");
496 if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(h
)) {
497 DUK__COMMA(); duk_fb_sprintf(fb
, "__special_stringobj:true");
501 if (DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h
)) {
502 DUK__COMMA(); duk_fb_sprintf(fb
, "__special_arguments:true");
506 if (DUK_HOBJECT_HAS_EXOTIC_DUKFUNC(h
)) {
507 DUK__COMMA(); duk_fb_sprintf(fb
, "__special_dukfunc:true");
511 if (DUK_HOBJECT_IS_BUFFEROBJECT(h
)) {
512 DUK__COMMA(); duk_fb_sprintf(fb
, "__special_bufferobj:true");
516 if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h
)) {
517 DUK__COMMA(); duk_fb_sprintf(fb
, "__special_proxyobj:true");
522 if (st
->internal
&& DUK_HOBJECT_IS_COMPILEDFUNCTION(h
)) {
523 duk_hcompiledfunction
*f
= (duk_hcompiledfunction
*) h
;
524 DUK__COMMA(); duk_fb_put_cstring(fb
, "__data:");
525 duk__print_hbuffer(st
, (duk_hbuffer
*) DUK_HCOMPILEDFUNCTION_GET_DATA(NULL
, f
));
526 DUK__COMMA(); duk_fb_sprintf(fb
, "__nregs:%ld", (long) f
->nregs
);
527 DUK__COMMA(); duk_fb_sprintf(fb
, "__nargs:%ld", (long) f
->nargs
);
528 #if defined(DUK_USE_DEBUGGER_SUPPORT)
529 DUK__COMMA(); duk_fb_sprintf(fb
, "__start_line:%ld", (long) f
->start_line
);
530 DUK__COMMA(); duk_fb_sprintf(fb
, "__end_line:%ld", (long) f
->end_line
);
532 DUK__COMMA(); duk_fb_put_cstring(fb
, "__data:");
533 duk__print_hbuffer(st
, (duk_hbuffer
*) DUK_HCOMPILEDFUNCTION_GET_DATA(NULL
, f
));
534 } else if (st
->internal
&& DUK_HOBJECT_IS_NATIVEFUNCTION(h
)) {
535 duk_hnativefunction
*f
= (duk_hnativefunction
*) h
;
536 DUK__COMMA(); duk_fb_sprintf(fb
, "__func:");
537 duk_fb_put_funcptr(fb
, (duk_uint8_t
*) &f
->func
, sizeof(f
->func
));
538 DUK__COMMA(); duk_fb_sprintf(fb
, "__nargs:%ld", (long) f
->nargs
);
539 } else if (st
->internal
&& DUK_HOBJECT_IS_BUFFEROBJECT(h
)) {
540 duk_hbufferobject
*b
= (duk_hbufferobject
*) h
;
541 DUK__COMMA(); duk_fb_sprintf(fb
, "__buf:");
542 duk__print_hbuffer(st
, (duk_hbuffer
*) b
->buf
);
543 DUK__COMMA(); duk_fb_sprintf(fb
, "__offset:%ld", (long) b
->offset
);
544 DUK__COMMA(); duk_fb_sprintf(fb
, "__length:%ld", (long) b
->length
);
545 DUK__COMMA(); duk_fb_sprintf(fb
, "__shift:%ld", (long) b
->shift
);
546 DUK__COMMA(); duk_fb_sprintf(fb
, "__elemtype:%ld", (long) b
->elem_type
);
547 } else if (st
->internal
&& DUK_HOBJECT_IS_THREAD(h
)) {
548 duk_hthread
*t
= (duk_hthread
*) h
;
549 DUK__COMMA(); duk_fb_sprintf(fb
, "__strict:%ld", (long) t
->strict
);
550 DUK__COMMA(); duk_fb_sprintf(fb
, "__state:%ld", (long) t
->state
);
551 DUK__COMMA(); duk_fb_sprintf(fb
, "__unused1:%ld", (long) t
->unused1
);
552 DUK__COMMA(); duk_fb_sprintf(fb
, "__unused2:%ld", (long) t
->unused2
);
553 DUK__COMMA(); duk_fb_sprintf(fb
, "__valstack_max:%ld", (long) t
->valstack_max
);
554 DUK__COMMA(); duk_fb_sprintf(fb
, "__callstack_max:%ld", (long) t
->callstack_max
);
555 DUK__COMMA(); duk_fb_sprintf(fb
, "__catchstack_max:%ld", (long) t
->catchstack_max
);
556 DUK__COMMA(); duk_fb_sprintf(fb
, "__valstack:%p", (void *) t
->valstack
);
557 DUK__COMMA(); duk_fb_sprintf(fb
, "__valstack_end:%p/%ld", (void *) t
->valstack_end
, (long) (t
->valstack_end
- t
->valstack
));
558 DUK__COMMA(); duk_fb_sprintf(fb
, "__valstack_bottom:%p/%ld", (void *) t
->valstack_bottom
, (long) (t
->valstack_bottom
- t
->valstack
));
559 DUK__COMMA(); duk_fb_sprintf(fb
, "__valstack_top:%p/%ld", (void *) t
->valstack_top
, (long) (t
->valstack_top
- t
->valstack
));
560 DUK__COMMA(); duk_fb_sprintf(fb
, "__catchstack:%p", (void *) t
->catchstack
);
561 DUK__COMMA(); duk_fb_sprintf(fb
, "__catchstack_size:%ld", (long) t
->catchstack_size
);
562 DUK__COMMA(); duk_fb_sprintf(fb
, "__catchstack_top:%ld", (long) t
->catchstack_top
);
563 DUK__COMMA(); duk_fb_sprintf(fb
, "__resumer:"); duk__print_hobject(st
, (duk_hobject
*) t
->resumer
);
564 /* XXX: print built-ins array? */
567 #ifdef DUK_USE_REFERENCE_COUNTING
569 DUK__COMMA(); duk_fb_sprintf(fb
, "__refcount:%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr
*) h
));
573 DUK__COMMA(); duk_fb_sprintf(fb
, "__class:%ld", (long) DUK_HOBJECT_GET_CLASS_NUMBER(h
));
576 /* prototype should be last, for readability */
577 if (st
->follow_proto
&& DUK_HOBJECT_GET_PROTOTYPE(NULL
, h
)) {
578 DUK__COMMA(); duk_fb_put_cstring(fb
, "__prototype:"); duk__print_hobject(st
, DUK_HOBJECT_GET_PROTOTYPE(NULL
, h
));
581 duk_fb_put_cstring(fb
, brace2
);
583 #if defined(DUK_USE_HOBJECT_HASH_PART)
584 if (st
->heavy
&& DUK_HOBJECT_GET_HSIZE(h
) > 0) {
585 duk_fb_put_byte(fb
, (duk_uint8_t
) DUK_ASC_LANGLE
);
586 for (i
= 0; i
< DUK_HOBJECT_GET_HSIZE(h
); i
++) {
587 duk_uint_t h_idx
= DUK_HOBJECT_H_GET_INDEX(NULL
, h
, i
);
589 duk_fb_put_byte(fb
, (duk_uint8_t
) DUK_ASC_COMMA
);
591 if (h_idx
== DUK_HOBJECT_HASHIDX_UNUSED
) {
592 duk_fb_sprintf(fb
, "u");
593 } else if (h_idx
== DUK_HOBJECT_HASHIDX_DELETED
) {
594 duk_fb_sprintf(fb
, "d");
596 duk_fb_sprintf(fb
, "%ld", (long) h_idx
);
599 duk_fb_put_byte(fb
, (duk_uint8_t
) DUK_ASC_RANGLE
);
605 if (pushed_loopstack
) {
606 st
->loop_stack_index
--;
607 st
->loop_stack
[st
->loop_stack_index
] = NULL
;
613 DUK_LOCAL
void duk__print_hbuffer(duk__dprint_state
*st
, duk_hbuffer
*h
) {
614 duk_fixedbuffer
*fb
= st
->fb
;
618 if (duk_fb_is_full(fb
)) {
622 /* terminal type: no depth check */
625 duk_fb_put_cstring(fb
, "NULL");
629 if (DUK_HBUFFER_HAS_DYNAMIC(h
)) {
630 if (DUK_HBUFFER_HAS_EXTERNAL(h
)) {
631 duk_hbuffer_external
*g
= (duk_hbuffer_external
*) h
;
632 duk_fb_sprintf(fb
, "buffer:external:%p:%ld",
633 (void *) DUK_HBUFFER_EXTERNAL_GET_DATA_PTR(NULL
, g
),
634 (long) DUK_HBUFFER_EXTERNAL_GET_SIZE(g
));
636 duk_hbuffer_dynamic
*g
= (duk_hbuffer_dynamic
*) h
;
637 duk_fb_sprintf(fb
, "buffer:dynamic:%p:%ld",
638 (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(NULL
, g
),
639 (long) DUK_HBUFFER_DYNAMIC_GET_SIZE(g
));
642 duk_fb_sprintf(fb
, "buffer:fixed:%ld", (long) DUK_HBUFFER_GET_SIZE(h
));
645 #ifdef DUK_USE_REFERENCE_COUNTING
646 duk_fb_sprintf(fb
, "/%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT(&h
->hdr
));
650 duk_fb_sprintf(fb
, "=[");
651 n
= DUK_HBUFFER_GET_SIZE(h
);
652 p
= (duk_uint8_t
*) DUK_HBUFFER_GET_DATA_PTR(NULL
, h
);
653 for (i
= 0; i
< n
; i
++) {
654 duk_fb_sprintf(fb
, "%02lx", (unsigned long) p
[i
]);
656 duk_fb_sprintf(fb
, "]");
660 DUK_LOCAL
void duk__print_heaphdr(duk__dprint_state
*st
, duk_heaphdr
*h
) {
661 duk_fixedbuffer
*fb
= st
->fb
;
663 if (duk_fb_is_full(fb
)) {
668 duk_fb_put_cstring(fb
, "NULL");
672 switch (DUK_HEAPHDR_GET_TYPE(h
)) {
673 case DUK_HTYPE_STRING
:
674 duk__print_hstring(st
, (duk_hstring
*) h
, 1);
676 case DUK_HTYPE_OBJECT
:
677 duk__print_hobject(st
, (duk_hobject
*) h
);
679 case DUK_HTYPE_BUFFER
:
680 duk__print_hbuffer(st
, (duk_hbuffer
*) h
);
683 duk_fb_sprintf(fb
, "[unknown htype %ld]", (long) DUK_HEAPHDR_GET_TYPE(h
));
688 DUK_LOCAL
void duk__print_tval(duk__dprint_state
*st
, duk_tval
*tv
) {
689 duk_fixedbuffer
*fb
= st
->fb
;
691 if (duk_fb_is_full(fb
)) {
695 /* depth check is done when printing an actual type */
698 duk_fb_sprintf(fb
, "(%p)", (void *) tv
);
702 duk_fb_put_cstring(fb
, "NULL");
708 duk_fb_put_byte(fb
, (duk_uint8_t
) DUK_ASC_LBRACKET
);
709 for (i
= 0; i
< (duk_size_t
) sizeof(*tv
); i
++) {
710 duk_fb_sprintf(fb
, "%02lx", (unsigned long) ((duk_uint8_t
*)tv
)[i
]);
712 duk_fb_put_byte(fb
, (duk_uint8_t
) DUK_ASC_RBRACKET
);
716 duk_fb_put_byte(fb
, (duk_uint8_t
) DUK_ASC_LANGLE
);
718 switch (DUK_TVAL_GET_TAG(tv
)) {
719 case DUK_TAG_UNDEFINED
: {
720 if (DUK_TVAL_IS_UNDEFINED_UNUSED(tv
)) {
721 duk_fb_put_cstring(fb
, "unused");
723 duk_fb_put_cstring(fb
, "undefined");
728 duk_fb_put_cstring(fb
, "null");
731 case DUK_TAG_BOOLEAN
: {
732 duk_fb_put_cstring(fb
, DUK_TVAL_GET_BOOLEAN(tv
) ? "true" : "false");
735 case DUK_TAG_STRING
: {
736 /* Note: string is a terminal heap object, so no depth check here */
737 duk__print_hstring(st
, DUK_TVAL_GET_STRING(tv
), 1);
740 case DUK_TAG_OBJECT
: {
741 duk__print_hobject(st
, DUK_TVAL_GET_OBJECT(tv
));
744 case DUK_TAG_BUFFER
: {
745 duk__print_hbuffer(st
, DUK_TVAL_GET_BUFFER(tv
));
748 case DUK_TAG_POINTER
: {
749 duk_fb_sprintf(fb
, "pointer:%p", (void *) DUK_TVAL_GET_POINTER(tv
));
752 case DUK_TAG_LIGHTFUNC
: {
754 duk_small_uint_t lf_flags
;
756 DUK_TVAL_GET_LIGHTFUNC(tv
, func
, lf_flags
);
757 duk_fb_sprintf(fb
, "lightfunc:");
758 duk_fb_put_funcptr(fb
, (duk_uint8_t
*) &func
, sizeof(func
));
759 duk_fb_sprintf(fb
, ":%04lx", (long) lf_flags
);
762 #if defined(DUK_USE_FASTINT)
763 case DUK_TAG_FASTINT
:
766 /* IEEE double is approximately 16 decimal digits; print a couple extra */
767 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv
));
768 duk_fb_sprintf(fb
, "%.18g", (double) DUK_TVAL_GET_NUMBER(tv
));
773 duk_fb_put_byte(fb
, (duk_uint8_t
) DUK_ASC_RANGLE
);
777 DUK_LOCAL
void duk__print_instr(duk__dprint_state
*st
, duk_instr_t ins
) {
778 duk_fixedbuffer
*fb
= st
->fb
;
781 const char *extraop_name
;
783 op
= (duk_small_int_t
) DUK_DEC_OP(ins
);
784 op_name
= duk__bc_optab
[op
];
786 /* XXX: option to fix opcode length so it lines up nicely */
788 if (op
== DUK_OP_EXTRA
) {
789 extraop_name
= duk__bc_extraoptab
[DUK_DEC_A(ins
)];
791 duk_fb_sprintf(fb
, "%s %ld, %ld",
792 (const char *) extraop_name
, (long) DUK_DEC_B(ins
), (long) DUK_DEC_C(ins
));
793 } else if (op
== DUK_OP_JUMP
) {
794 duk_int_t diff1
= DUK_DEC_ABC(ins
) - DUK_BC_JUMP_BIAS
; /* from next pc */
795 duk_int_t diff2
= diff1
+ 1; /* from curr pc */
797 duk_fb_sprintf(fb
, "%s %ld (to pc%c%ld)",
798 (const char *) op_name
, (long) diff1
,
799 (int) (diff2
>= 0 ? '+' : '-'), /* char format: use int */
800 (long) (diff2
>= 0 ? diff2
: -diff2
));
802 duk_fb_sprintf(fb
, "%s %ld, %ld, %ld",
803 (const char *) op_name
, (long) DUK_DEC_A(ins
),
804 (long) DUK_DEC_B(ins
), (long) DUK_DEC_C(ins
));
808 DUK_LOCAL
void duk__print_opcode(duk__dprint_state
*st
, duk_small_int_t opcode
) {
809 duk_fixedbuffer
*fb
= st
->fb
;
811 if (opcode
< DUK_BC_OP_MIN
|| opcode
> DUK_BC_OP_MAX
) {
812 duk_fb_sprintf(fb
, "?(%ld)", (long) opcode
);
814 duk_fb_sprintf(fb
, "%s", (const char *) duk__bc_optab
[opcode
]);
818 DUK_INTERNAL duk_int_t
duk_debug_vsnprintf(char *str
, duk_size_t size
, const char *format
, va_list ap
) {
820 const char *p
= format
;
821 const char *p_end
= p
+ DUK_STRLEN(format
);
824 DUK_MEMZERO(&fb
, sizeof(fb
));
825 fb
.buffer
= (duk_uint8_t
*) str
;
832 const char *p_begfmt
= NULL
;
833 duk_bool_t got_exclamation
= 0;
834 duk_bool_t got_long
= 0; /* %lf, %ld etc */
835 duk__dprint_state st
;
837 if (ch
!= DUK_ASC_PERCENT
) {
838 duk_fb_put_byte(&fb
, (duk_uint8_t
) ch
);
843 * Format tag parsing. Since we don't understand all the
844 * possible format tags allowed, we just scan for a terminating
845 * specifier and keep track of relevant modifiers that we do
846 * understand. See man 3 printf.
849 DUK_MEMZERO(&st
, sizeof(st
));
853 st
.loop_stack_index
= 0;
854 st
.loop_stack_limit
= DUK__LOOP_STACK_DEPTH
;
860 if (ch
== DUK_ASC_STAR
) {
861 /* unsupported: would consume multiple args */
863 } else if (ch
== DUK_ASC_PERCENT
) {
864 duk_fb_put_byte(&fb
, (duk_uint8_t
) DUK_ASC_PERCENT
);
866 } else if (ch
== DUK_ASC_EXCLAMATION
) {
868 } else if (!got_exclamation
&& ch
== DUK_ASC_LC_L
) {
870 } else if (got_exclamation
&& ch
== DUK_ASC_LC_D
) {
871 st
.depth_limit
= DUK__DEEP_DEPTH_LIMIT
;
872 } else if (got_exclamation
&& ch
== DUK_ASC_LC_P
) {
874 } else if (got_exclamation
&& ch
== DUK_ASC_LC_I
) {
876 } else if (got_exclamation
&& ch
== DUK_ASC_LC_X
) {
878 } else if (got_exclamation
&& ch
== DUK_ASC_LC_H
) {
880 } else if (got_exclamation
&& ch
== DUK_ASC_ATSIGN
) {
882 } else if (got_exclamation
&& ch
== DUK_ASC_HASH
) {
884 } else if (got_exclamation
&& ch
== DUK_ASC_UC_T
) {
885 duk_tval
*t
= va_arg(ap
, duk_tval
*);
886 if (st
.pointer
&& !st
.heavy
) {
887 duk_fb_sprintf(&fb
, "(%p)", (void *) t
);
889 duk__print_tval(&st
, t
);
891 } else if (got_exclamation
&& ch
== DUK_ASC_UC_O
) {
892 duk_heaphdr
*t
= va_arg(ap
, duk_heaphdr
*);
893 if (st
.pointer
&& !st
.heavy
) {
894 duk_fb_sprintf(&fb
, "(%p)", (void *) t
);
896 duk__print_heaphdr(&st
, t
);
898 } else if (got_exclamation
&& ch
== DUK_ASC_UC_I
) {
899 duk_instr_t t
= va_arg(ap
, duk_instr_t
);
900 duk__print_instr(&st
, t
);
902 } else if (got_exclamation
&& ch
== DUK_ASC_UC_C
) {
903 long t
= va_arg(ap
, long);
904 duk__print_opcode(&st
, (duk_small_int_t
) t
);
906 } else if (!got_exclamation
&& strchr(DUK__ALLOWED_STANDARD_SPECIFIERS
, (int) ch
)) {
907 char fmtbuf
[DUK__MAX_FORMAT_TAG_LENGTH
];
910 DUK_ASSERT(p
>= p_begfmt
);
911 fmtlen
= (duk_size_t
) (p
- p_begfmt
);
912 if (fmtlen
>= sizeof(fmtbuf
)) {
913 /* format is too large, abort */
916 DUK_MEMZERO(fmtbuf
, sizeof(fmtbuf
));
917 DUK_MEMCPY(fmtbuf
, p_begfmt
, fmtlen
);
919 /* assume exactly 1 arg, which is why '*' is forbidden; arg size still
920 * depends on type though.
923 if (ch
== DUK_ASC_LC_F
|| ch
== DUK_ASC_LC_G
|| ch
== DUK_ASC_LC_E
) {
924 /* %f and %lf both consume a 'long' */
925 double arg
= va_arg(ap
, double);
926 duk_fb_sprintf(&fb
, fmtbuf
, arg
);
927 } else if (ch
== DUK_ASC_LC_D
&& got_long
) {
929 long arg
= va_arg(ap
, long);
930 duk_fb_sprintf(&fb
, fmtbuf
, arg
);
931 } else if (ch
== DUK_ASC_LC_D
) {
932 /* %d; only 16 bits are guaranteed */
933 int arg
= va_arg(ap
, int);
934 duk_fb_sprintf(&fb
, fmtbuf
, arg
);
935 } else if (ch
== DUK_ASC_LC_U
&& got_long
) {
937 unsigned long arg
= va_arg(ap
, unsigned long);
938 duk_fb_sprintf(&fb
, fmtbuf
, arg
);
939 } else if (ch
== DUK_ASC_LC_U
) {
940 /* %u; only 16 bits are guaranteed */
941 unsigned int arg
= va_arg(ap
, unsigned int);
942 duk_fb_sprintf(&fb
, fmtbuf
, arg
);
943 } else if (ch
== DUK_ASC_LC_X
&& got_long
) {
945 unsigned long arg
= va_arg(ap
, unsigned long);
946 duk_fb_sprintf(&fb
, fmtbuf
, arg
);
947 } else if (ch
== DUK_ASC_LC_X
) {
948 /* %x; only 16 bits are guaranteed */
949 unsigned int arg
= va_arg(ap
, unsigned int);
950 duk_fb_sprintf(&fb
, fmtbuf
, arg
);
951 } else if (ch
== DUK_ASC_LC_S
) {
953 const char *arg
= va_arg(ap
, const char *);
955 /* '%s' and NULL is not portable, so special case
956 * it for debug printing.
958 duk_fb_sprintf(&fb
, "NULL");
960 duk_fb_sprintf(&fb
, fmtbuf
, arg
);
962 } else if (ch
== DUK_ASC_LC_P
) {
964 void *arg
= va_arg(ap
, void *);
966 /* '%p' and NULL is portable, but special case it
967 * anyway to get a standard NULL marker in logs.
969 duk_fb_sprintf(&fb
, "NULL");
971 duk_fb_sprintf(&fb
, fmtbuf
, arg
);
973 } else if (ch
== DUK_ASC_LC_C
) {
974 /* '%c', passed concretely as int */
975 int arg
= va_arg(ap
, int);
976 duk_fb_sprintf(&fb
, fmtbuf
, arg
);
978 /* Should not happen. */
979 duk_fb_sprintf(&fb
, "INVALID-FORMAT(%s)", (const char *) fmtbuf
);
990 duk_fb_put_cstring(&fb
, "FMTERR");
994 retval
= (duk_int_t
) fb
.offset
;
995 duk_fb_put_byte(&fb
, (duk_uint8_t
) 0);
997 /* return total chars written excluding terminator */
1002 DUK_INTERNAL duk_int_t
duk_debug_snprintf(char *str
, duk_size_t size
, const char *format
, ...) {
1005 va_start(ap
, format
);
1006 retval
= duk_debug_vsnprintf(str
, size
, format
, ap
);
1012 /* Formatting function pointers is tricky: there is no standard pointer for
1013 * function pointers and the size of a function pointer may depend on the
1014 * specific pointer type. This helper formats a function pointer based on
1015 * its memory layout to get something useful on most platforms.
1017 DUK_INTERNAL
void duk_debug_format_funcptr(char *buf
, duk_size_t buf_size
, duk_uint8_t
*fptr
, duk_size_t fptr_size
) {
1019 duk_uint8_t
*p
= (duk_uint8_t
*) buf
;
1020 duk_uint8_t
*p_end
= (duk_uint8_t
*) (buf
+ buf_size
- 1);
1022 DUK_MEMZERO(buf
, buf_size
);
1024 for (i
= 0; i
< fptr_size
; i
++) {
1025 duk_int_t left
= (duk_int_t
) (p_end
- p
);
1031 /* Quite approximate but should be useful for little and big endian. */
1032 #ifdef DUK_USE_INTEGER_BE
1035 ch
= fptr
[fptr_size
- 1 - i
];
1037 p
+= DUK_SNPRINTF((char *) p
, left
, "%02lx", (unsigned long) ch
);
1041 #endif /* DUK_USE_DEBUG */