]> git.proxmox.com Git - ceph.git/blob - ceph/src/civetweb/src/third_party/duktape-1.3.0/src-separate/duk_debug_vsnprintf.c
bump version to 12.2.12-pve1
[ceph.git] / ceph / src / civetweb / src / third_party / duktape-1.3.0 / src-separate / duk_debug_vsnprintf.c
1 /*
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.
7 *
8 * Custom format tags begin with a '%!' to safely distinguish them from
9 * standard format tags. The following conversions are supported:
10 *
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)
15 *
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:
19 *
20 * @ print pointers
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)
25 * x hexdump buffers
26 * h heavy formatting
27 *
28 * For instance, the following serializes objects recursively, but does not
29 * follow the prototype chain nor print internal properties: "%!dO".
30 *
31 * Notes:
32 *
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).
37 *
38 * * Output format is intentionally different from Ecmascript
39 * formatting requirements, as formatting here serves debugging
40 * of internals.
41 *
42 * * Depth checking (and updating) is done in each type printer
43 * separately, to allow them to call each other freely.
44 *
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.
49 *
50 * * Reference loops are detected using a loop stack.
51 */
52
53 #include "duk_internal.h"
54
55 #ifdef DUK_USE_DEBUG
56
57 #include <stdio.h>
58 #include <stdarg.h>
59 #include <string.h>
60
61 /* list of conversion specifiers that terminate a format tag;
62 * this is unfortunately guesswork.
63 */
64 #define DUK__ALLOWED_STANDARD_SPECIFIERS "diouxXeEfFgGaAcsCSpnm"
65
66 /* maximum length of standard format tag that we support */
67 #define DUK__MAX_FORMAT_TAG_LENGTH 32
68
69 /* heapobj recursion depth when deep printing is selected */
70 #define DUK__DEEP_DEPTH_LIMIT 8
71
72 /* maximum recursion depth for loop detection stacks */
73 #define DUK__LOOP_STACK_DEPTH 256
74
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"
84 };
85
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",
92
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",
98
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",
104
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",
110
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",
116
117 "XXX", "XXX", "XXX", "XXX", "XXX", "XXX"
118 };
119
120 typedef struct duk__dprint_state duk__dprint_state;
121 struct duk__dprint_state {
122 duk_fixedbuffer *fb;
123
124 /* loop_stack_index could be perhaps be replaced by 'depth', but it's nice
125 * to not couple these two mechanisms unnecessarily.
126 */
127 duk_hobject *loop_stack[DUK__LOOP_STACK_DEPTH];
128 duk_int_t loop_stack_index;
129 duk_int_t loop_stack_limit;
130
131 duk_int_t depth;
132 duk_int_t depth_limit;
133
134 duk_bool_t pointer;
135 duk_bool_t heavy;
136 duk_bool_t binary;
137 duk_bool_t follow_proto;
138 duk_bool_t internal;
139 duk_bool_t hexdump;
140 };
141
142 /* helpers */
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);
151
152 DUK_LOCAL void duk__print_shared_heaphdr(duk__dprint_state *st, duk_heaphdr *h) {
153 duk_fixedbuffer *fb = st->fb;
154
155 if (st->heavy) {
156 duk_fb_sprintf(fb, "(%p)", (void *) h);
157 }
158
159 if (!h) {
160 return;
161 }
162
163 if (st->binary) {
164 duk_size_t i;
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]);
168 }
169 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RBRACKET);
170 }
171
172 #ifdef DUK_USE_REFERENCE_COUNTING /* currently implicitly also DUK_USE_DOUBLE_LINKED_HEAP */
173 if (st->heavy) {
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));
185 }
186 #else
187 if (st->heavy) {
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));
196 }
197 #endif
198 }
199
200 DUK_LOCAL void duk__print_shared_heaphdr_string(duk__dprint_state *st, duk_heaphdr_string *h) {
201 duk_fixedbuffer *fb = st->fb;
202
203 if (st->heavy) {
204 duk_fb_sprintf(fb, "(%p)", (void *) h);
205 }
206
207 if (!h) {
208 return;
209 }
210
211 if (st->binary) {
212 duk_size_t i;
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]);
216 }
217 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RBRACKET);
218 }
219
220 #ifdef DUK_USE_REFERENCE_COUNTING
221 if (st->heavy) {
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));
230 }
231 #else
232 if (st->heavy) {
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));
240 }
241 #endif
242 }
243
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;
248
249 /* terminal type: no depth check */
250
251 if (duk_fb_is_full(fb)) {
252 return;
253 }
254
255 duk__print_shared_heaphdr_string(st, &h->hdr);
256
257 if (!h) {
258 duk_fb_put_cstring(fb, "NULL");
259 return;
260 }
261
262 p = DUK_HSTRING_GET_DATA(h);
263 p_end = p + DUK_HSTRING_GET_BYTELEN(h);
264
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).
269 */
270 quotes = 1;
271 }
272
273 if (quotes) {
274 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_DOUBLEQUOTE);
275 }
276 while (p < p_end) {
277 duk_uint8_t ch = *p++;
278
279 /* two special escapes: '\' and '"', other printables as is */
280 if (ch == '\\') {
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.
289 */
290 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_UNDERSCORE);
291 } else {
292 duk_fb_sprintf(fb, "\\x%02lx", (unsigned long) ch);
293 }
294 }
295 if (quotes) {
296 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_DOUBLEQUOTE);
297 }
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));
301 #endif
302 }
303
304 #ifdef DUK__COMMA
305 #undef DUK__COMMA
306 #endif
307 #define DUK__COMMA() do { \
308 if (first) { \
309 first = 0; \
310 } else { \
311 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_COMMA); \
312 } \
313 } while (0)
314
315 DUK_LOCAL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h) {
316 duk_fixedbuffer *fb = st->fb;
317 duk_uint_fast32_t i;
318 duk_tval *tv;
319 duk_hstring *key;
320 duk_bool_t first = 1;
321 const char *brace1 = "{";
322 const char *brace2 = "}";
323 duk_bool_t pushed_loopstack = 0;
324
325 if (duk_fb_is_full(fb)) {
326 return;
327 }
328
329 duk__print_shared_heaphdr(st, &h->hdr);
330
331 if (h && DUK_HOBJECT_HAS_ARRAY_PART(h)) {
332 brace1 = "[";
333 brace2 = "]";
334 }
335
336 if (!h) {
337 duk_fb_put_cstring(fb, "NULL");
338 goto finished;
339 }
340
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);
348 } else {
349 duk_fb_sprintf(fb, "%sobject %p%s", (const char *) brace1, (void *) h, (const char *) brace2); /* may be NULL */
350 }
351 return;
352 }
353
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);
357 return;
358 }
359 }
360
361 /* after this, return paths should 'goto finished' for decrement */
362 st->depth++;
363
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);
366 goto finished;
367 }
368 st->loop_stack[st->loop_stack_index++] = h;
369 pushed_loopstack = 1;
370
371 /*
372 * Notation: double underscore used for internal properties which are not
373 * stored in the property allocation (e.g. '__valstack').
374 */
375
376 duk_fb_put_cstring(fb, brace1);
377
378 if (DUK_HOBJECT_GET_PROPS(NULL, h)) {
379 duk_uint32_t a_limit;
380
381 a_limit = DUK_HOBJECT_GET_ASIZE(h);
382 if (st->internal) {
383 /* dump all allocated entries, unused entries print as 'unused',
384 * note that these may extend beyond current 'length' and look
385 * a bit funny.
386 */
387 } else {
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)) {
392 break;
393 }
394 a_limit--;
395 }
396 }
397
398 for (i = 0; i < a_limit; i++) {
399 tv = DUK_HOBJECT_A_GET_VALUE_PTR(NULL, h, i);
400 DUK__COMMA();
401 duk__print_tval(st, tv);
402 }
403 for (i = 0; i < DUK_HOBJECT_GET_ENEXT(h); i++) {
404 key = DUK_HOBJECT_E_GET_KEY(NULL, h, i);
405 if (!key) {
406 continue;
407 }
408 if (!st->internal &&
409 DUK_HSTRING_GET_BYTELEN(key) > 0 &&
410 DUK_HSTRING_GET_DATA(key)[0] == 0xff) {
411 /* XXX: use DUK_HSTRING_FLAG_INTERNAL? */
412 continue;
413 }
414 DUK__COMMA();
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);
421 } else {
422 tv = &DUK_HOBJECT_E_GET_VALUE(NULL, h, i).v;
423 duk__print_tval(st, tv);
424 }
425 if (st->heavy) {
426 duk_fb_sprintf(fb, "<%02lx>", (unsigned long) DUK_HOBJECT_E_GET_FLAGS(NULL, h, i));
427 }
428 }
429 }
430 if (st->internal) {
431 if (DUK_HOBJECT_HAS_EXTENSIBLE(h)) {
432 DUK__COMMA(); duk_fb_sprintf(fb, "__extensible:true");
433 } else {
434 ;
435 }
436 if (DUK_HOBJECT_HAS_CONSTRUCTABLE(h)) {
437 DUK__COMMA(); duk_fb_sprintf(fb, "__constructable:true");
438 } else {
439 ;
440 }
441 if (DUK_HOBJECT_HAS_BOUND(h)) {
442 DUK__COMMA(); duk_fb_sprintf(fb, "__bound:true");
443 } else {
444 ;
445 }
446 if (DUK_HOBJECT_HAS_COMPILEDFUNCTION(h)) {
447 DUK__COMMA(); duk_fb_sprintf(fb, "__compiledfunction:true");
448 } else {
449 ;
450 }
451 if (DUK_HOBJECT_HAS_NATIVEFUNCTION(h)) {
452 DUK__COMMA(); duk_fb_sprintf(fb, "__nativefunction:true");
453 } else {
454 ;
455 }
456 if (DUK_HOBJECT_HAS_THREAD(h)) {
457 DUK__COMMA(); duk_fb_sprintf(fb, "__thread:true");
458 } else {
459 ;
460 }
461 if (DUK_HOBJECT_HAS_ARRAY_PART(h)) {
462 DUK__COMMA(); duk_fb_sprintf(fb, "__array_part:true");
463 } else {
464 ;
465 }
466 if (DUK_HOBJECT_HAS_STRICT(h)) {
467 DUK__COMMA(); duk_fb_sprintf(fb, "__strict:true");
468 } else {
469 ;
470 }
471 if (DUK_HOBJECT_HAS_NEWENV(h)) {
472 DUK__COMMA(); duk_fb_sprintf(fb, "__newenv:true");
473 } else {
474 ;
475 }
476 if (DUK_HOBJECT_HAS_NAMEBINDING(h)) {
477 DUK__COMMA(); duk_fb_sprintf(fb, "__namebinding:true");
478 } else {
479 ;
480 }
481 if (DUK_HOBJECT_HAS_CREATEARGS(h)) {
482 DUK__COMMA(); duk_fb_sprintf(fb, "__createargs:true");
483 } else {
484 ;
485 }
486 if (DUK_HOBJECT_HAS_ENVRECCLOSED(h)) {
487 DUK__COMMA(); duk_fb_sprintf(fb, "__envrecclosed:true");
488 } else {
489 ;
490 }
491 if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(h)) {
492 DUK__COMMA(); duk_fb_sprintf(fb, "__special_array:true");
493 } else {
494 ;
495 }
496 if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(h)) {
497 DUK__COMMA(); duk_fb_sprintf(fb, "__special_stringobj:true");
498 } else {
499 ;
500 }
501 if (DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h)) {
502 DUK__COMMA(); duk_fb_sprintf(fb, "__special_arguments:true");
503 } else {
504 ;
505 }
506 if (DUK_HOBJECT_HAS_EXOTIC_DUKFUNC(h)) {
507 DUK__COMMA(); duk_fb_sprintf(fb, "__special_dukfunc:true");
508 } else {
509 ;
510 }
511 if (DUK_HOBJECT_IS_BUFFEROBJECT(h)) {
512 DUK__COMMA(); duk_fb_sprintf(fb, "__special_bufferobj:true");
513 } else {
514 ;
515 }
516 if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h)) {
517 DUK__COMMA(); duk_fb_sprintf(fb, "__special_proxyobj:true");
518 } else {
519 ;
520 }
521 }
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);
531 #endif
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? */
565
566 }
567 #ifdef DUK_USE_REFERENCE_COUNTING
568 if (st->internal) {
569 DUK__COMMA(); duk_fb_sprintf(fb, "__refcount:%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h));
570 }
571 #endif
572 if (st->internal) {
573 DUK__COMMA(); duk_fb_sprintf(fb, "__class:%ld", (long) DUK_HOBJECT_GET_CLASS_NUMBER(h));
574 }
575
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));
579 }
580
581 duk_fb_put_cstring(fb, brace2);
582
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);
588 if (i > 0) {
589 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_COMMA);
590 }
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");
595 } else {
596 duk_fb_sprintf(fb, "%ld", (long) h_idx);
597 }
598 }
599 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RANGLE);
600 }
601 #endif
602
603 finished:
604 st->depth--;
605 if (pushed_loopstack) {
606 st->loop_stack_index--;
607 st->loop_stack[st->loop_stack_index] = NULL;
608 }
609 }
610
611 #undef DUK__COMMA
612
613 DUK_LOCAL void duk__print_hbuffer(duk__dprint_state *st, duk_hbuffer *h) {
614 duk_fixedbuffer *fb = st->fb;
615 duk_size_t i, n;
616 duk_uint8_t *p;
617
618 if (duk_fb_is_full(fb)) {
619 return;
620 }
621
622 /* terminal type: no depth check */
623
624 if (!h) {
625 duk_fb_put_cstring(fb, "NULL");
626 return;
627 }
628
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));
635 } else {
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));
640 }
641 } else {
642 duk_fb_sprintf(fb, "buffer:fixed:%ld", (long) DUK_HBUFFER_GET_SIZE(h));
643 }
644
645 #ifdef DUK_USE_REFERENCE_COUNTING
646 duk_fb_sprintf(fb, "/%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT(&h->hdr));
647 #endif
648
649 if (st->hexdump) {
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]);
655 }
656 duk_fb_sprintf(fb, "]");
657 }
658 }
659
660 DUK_LOCAL void duk__print_heaphdr(duk__dprint_state *st, duk_heaphdr *h) {
661 duk_fixedbuffer *fb = st->fb;
662
663 if (duk_fb_is_full(fb)) {
664 return;
665 }
666
667 if (!h) {
668 duk_fb_put_cstring(fb, "NULL");
669 return;
670 }
671
672 switch (DUK_HEAPHDR_GET_TYPE(h)) {
673 case DUK_HTYPE_STRING:
674 duk__print_hstring(st, (duk_hstring *) h, 1);
675 break;
676 case DUK_HTYPE_OBJECT:
677 duk__print_hobject(st, (duk_hobject *) h);
678 break;
679 case DUK_HTYPE_BUFFER:
680 duk__print_hbuffer(st, (duk_hbuffer *) h);
681 break;
682 default:
683 duk_fb_sprintf(fb, "[unknown htype %ld]", (long) DUK_HEAPHDR_GET_TYPE(h));
684 break;
685 }
686 }
687
688 DUK_LOCAL void duk__print_tval(duk__dprint_state *st, duk_tval *tv) {
689 duk_fixedbuffer *fb = st->fb;
690
691 if (duk_fb_is_full(fb)) {
692 return;
693 }
694
695 /* depth check is done when printing an actual type */
696
697 if (st->heavy) {
698 duk_fb_sprintf(fb, "(%p)", (void *) tv);
699 }
700
701 if (!tv) {
702 duk_fb_put_cstring(fb, "NULL");
703 return;
704 }
705
706 if (st->binary) {
707 duk_size_t i;
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]);
711 }
712 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RBRACKET);
713 }
714
715 if (st->heavy) {
716 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LANGLE);
717 }
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");
722 } else {
723 duk_fb_put_cstring(fb, "undefined");
724 }
725 break;
726 }
727 case DUK_TAG_NULL: {
728 duk_fb_put_cstring(fb, "null");
729 break;
730 }
731 case DUK_TAG_BOOLEAN: {
732 duk_fb_put_cstring(fb, DUK_TVAL_GET_BOOLEAN(tv) ? "true" : "false");
733 break;
734 }
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);
738 break;
739 }
740 case DUK_TAG_OBJECT: {
741 duk__print_hobject(st, DUK_TVAL_GET_OBJECT(tv));
742 break;
743 }
744 case DUK_TAG_BUFFER: {
745 duk__print_hbuffer(st, DUK_TVAL_GET_BUFFER(tv));
746 break;
747 }
748 case DUK_TAG_POINTER: {
749 duk_fb_sprintf(fb, "pointer:%p", (void *) DUK_TVAL_GET_POINTER(tv));
750 break;
751 }
752 case DUK_TAG_LIGHTFUNC: {
753 duk_c_function func;
754 duk_small_uint_t lf_flags;
755
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);
760 break;
761 }
762 #if defined(DUK_USE_FASTINT)
763 case DUK_TAG_FASTINT:
764 #endif
765 default: {
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));
769 break;
770 }
771 }
772 if (st->heavy) {
773 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RANGLE);
774 }
775 }
776
777 DUK_LOCAL void duk__print_instr(duk__dprint_state *st, duk_instr_t ins) {
778 duk_fixedbuffer *fb = st->fb;
779 duk_small_int_t op;
780 const char *op_name;
781 const char *extraop_name;
782
783 op = (duk_small_int_t) DUK_DEC_OP(ins);
784 op_name = duk__bc_optab[op];
785
786 /* XXX: option to fix opcode length so it lines up nicely */
787
788 if (op == DUK_OP_EXTRA) {
789 extraop_name = duk__bc_extraoptab[DUK_DEC_A(ins)];
790
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 */
796
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));
801 } else {
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));
805 }
806 }
807
808 DUK_LOCAL void duk__print_opcode(duk__dprint_state *st, duk_small_int_t opcode) {
809 duk_fixedbuffer *fb = st->fb;
810
811 if (opcode < DUK_BC_OP_MIN || opcode > DUK_BC_OP_MAX) {
812 duk_fb_sprintf(fb, "?(%ld)", (long) opcode);
813 } else {
814 duk_fb_sprintf(fb, "%s", (const char *) duk__bc_optab[opcode]);
815 }
816 }
817
818 DUK_INTERNAL duk_int_t duk_debug_vsnprintf(char *str, duk_size_t size, const char *format, va_list ap) {
819 duk_fixedbuffer fb;
820 const char *p = format;
821 const char *p_end = p + DUK_STRLEN(format);
822 duk_int_t retval;
823
824 DUK_MEMZERO(&fb, sizeof(fb));
825 fb.buffer = (duk_uint8_t *) str;
826 fb.length = size;
827 fb.offset = 0;
828 fb.truncated = 0;
829
830 while (p < p_end) {
831 char ch = *p++;
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;
836
837 if (ch != DUK_ASC_PERCENT) {
838 duk_fb_put_byte(&fb, (duk_uint8_t) ch);
839 continue;
840 }
841
842 /*
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.
847 */
848
849 DUK_MEMZERO(&st, sizeof(st));
850 st.fb = &fb;
851 st.depth = 0;
852 st.depth_limit = 1;
853 st.loop_stack_index = 0;
854 st.loop_stack_limit = DUK__LOOP_STACK_DEPTH;
855
856 p_begfmt = p - 1;
857 while (p < p_end) {
858 ch = *p++;
859
860 if (ch == DUK_ASC_STAR) {
861 /* unsupported: would consume multiple args */
862 goto error;
863 } else if (ch == DUK_ASC_PERCENT) {
864 duk_fb_put_byte(&fb, (duk_uint8_t) DUK_ASC_PERCENT);
865 break;
866 } else if (ch == DUK_ASC_EXCLAMATION) {
867 got_exclamation = 1;
868 } else if (!got_exclamation && ch == DUK_ASC_LC_L) {
869 got_long = 1;
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) {
873 st.follow_proto = 1;
874 } else if (got_exclamation && ch == DUK_ASC_LC_I) {
875 st.internal = 1;
876 } else if (got_exclamation && ch == DUK_ASC_LC_X) {
877 st.hexdump = 1;
878 } else if (got_exclamation && ch == DUK_ASC_LC_H) {
879 st.heavy = 1;
880 } else if (got_exclamation && ch == DUK_ASC_ATSIGN) {
881 st.pointer = 1;
882 } else if (got_exclamation && ch == DUK_ASC_HASH) {
883 st.binary = 1;
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);
888 }
889 duk__print_tval(&st, t);
890 break;
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);
895 }
896 duk__print_heaphdr(&st, t);
897 break;
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);
901 break;
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);
905 break;
906 } else if (!got_exclamation && strchr(DUK__ALLOWED_STANDARD_SPECIFIERS, (int) ch)) {
907 char fmtbuf[DUK__MAX_FORMAT_TAG_LENGTH];
908 duk_size_t fmtlen;
909
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 */
914 goto error;
915 }
916 DUK_MEMZERO(fmtbuf, sizeof(fmtbuf));
917 DUK_MEMCPY(fmtbuf, p_begfmt, fmtlen);
918
919 /* assume exactly 1 arg, which is why '*' is forbidden; arg size still
920 * depends on type though.
921 */
922
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) {
928 /* %ld */
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) {
936 /* %lu */
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) {
944 /* %lx */
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) {
952 /* %s */
953 const char *arg = va_arg(ap, const char *);
954 if (arg == NULL) {
955 /* '%s' and NULL is not portable, so special case
956 * it for debug printing.
957 */
958 duk_fb_sprintf(&fb, "NULL");
959 } else {
960 duk_fb_sprintf(&fb, fmtbuf, arg);
961 }
962 } else if (ch == DUK_ASC_LC_P) {
963 /* %p */
964 void *arg = va_arg(ap, void *);
965 if (arg == NULL) {
966 /* '%p' and NULL is portable, but special case it
967 * anyway to get a standard NULL marker in logs.
968 */
969 duk_fb_sprintf(&fb, "NULL");
970 } else {
971 duk_fb_sprintf(&fb, fmtbuf, arg);
972 }
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);
977 } else {
978 /* Should not happen. */
979 duk_fb_sprintf(&fb, "INVALID-FORMAT(%s)", (const char *) fmtbuf);
980 }
981 break;
982 } else {
983 /* ignore */
984 }
985 }
986 }
987 goto done;
988
989 error:
990 duk_fb_put_cstring(&fb, "FMTERR");
991 /* fall through */
992
993 done:
994 retval = (duk_int_t) fb.offset;
995 duk_fb_put_byte(&fb, (duk_uint8_t) 0);
996
997 /* return total chars written excluding terminator */
998 return retval;
999 }
1000
1001 #if 0 /*unused*/
1002 DUK_INTERNAL duk_int_t duk_debug_snprintf(char *str, duk_size_t size, const char *format, ...) {
1003 duk_int_t retval;
1004 va_list ap;
1005 va_start(ap, format);
1006 retval = duk_debug_vsnprintf(str, size, format, ap);
1007 va_end(ap);
1008 return retval;
1009 }
1010 #endif
1011
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.
1016 */
1017 DUK_INTERNAL void duk_debug_format_funcptr(char *buf, duk_size_t buf_size, duk_uint8_t *fptr, duk_size_t fptr_size) {
1018 duk_size_t i;
1019 duk_uint8_t *p = (duk_uint8_t *) buf;
1020 duk_uint8_t *p_end = (duk_uint8_t *) (buf + buf_size - 1);
1021
1022 DUK_MEMZERO(buf, buf_size);
1023
1024 for (i = 0; i < fptr_size; i++) {
1025 duk_int_t left = (duk_int_t) (p_end - p);
1026 duk_uint8_t ch;
1027 if (left <= 0) {
1028 break;
1029 }
1030
1031 /* Quite approximate but should be useful for little and big endian. */
1032 #ifdef DUK_USE_INTEGER_BE
1033 ch = fptr[i];
1034 #else
1035 ch = fptr[fptr_size - 1 - i];
1036 #endif
1037 p += DUK_SNPRINTF((char *) p, left, "%02lx", (unsigned long) ch);
1038 }
1039 }
1040
1041 #endif /* DUK_USE_DEBUG */