]> git.proxmox.com Git - ceph.git/blame - ceph/src/jaegertracing/opentelemetry-cpp/third_party/prometheus-cpp/3rdparty/civetweb/src/third_party/duktape-1.5.2/src-separate/duk_debug_vsnprintf.c
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / jaegertracing / opentelemetry-cpp / third_party / prometheus-cpp / 3rdparty / civetweb / src / third_party / duktape-1.5.2 / src-separate / duk_debug_vsnprintf.c
CommitLineData
1e59de90
TL
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? */
76DUK_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
86DUK_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
120typedef struct duk__dprint_state duk__dprint_state;
121struct 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 */
143DUK_LOCAL_DECL void duk__print_hstring(duk__dprint_state *st, duk_hstring *k, duk_bool_t quotes);
144DUK_LOCAL_DECL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h);
145DUK_LOCAL_DECL void duk__print_hbuffer(duk__dprint_state *st, duk_hbuffer *h);
146DUK_LOCAL_DECL void duk__print_tval(duk__dprint_state *st, duk_tval *tv);
147DUK_LOCAL_DECL void duk__print_instr(duk__dprint_state *st, duk_instr_t ins);
148DUK_LOCAL_DECL void duk__print_heaphdr(duk__dprint_state *st, duk_heaphdr *h);
149DUK_LOCAL_DECL void duk__print_shared_heaphdr(duk__dprint_state *st, duk_heaphdr *h);
150DUK_LOCAL_DECL void duk__print_shared_heaphdr_string(duk__dprint_state *st, duk_heaphdr_string *h);
151
152DUK_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
200DUK_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
244DUK_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
315DUK_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_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, "__exotic_array:true");
493 } else {
494 ;
495 }
496 if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(h)) {
497 DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_stringobj:true");
498 } else {
499 ;
500 }
501 if (DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h)) {
502 DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_arguments:true");
503 } else {
504 ;
505 }
506 if (DUK_HOBJECT_HAS_EXOTIC_DUKFUNC(h)) {
507 DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_dukfunc:true");
508 } else {
509 ;
510 }
511 if (DUK_HOBJECT_IS_BUFFEROBJECT(h)) {
512 DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_bufferobj:true");
513 } else {
514 ;
515 }
516 if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h)) {
517 DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_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 DUK__COMMA(); duk_fb_sprintf(fb, "__magic:%ld", (long) f->magic);
540 } else if (st->internal && DUK_HOBJECT_IS_BUFFEROBJECT(h)) {
541 duk_hbufferobject *b = (duk_hbufferobject *) h;
542 DUK__COMMA(); duk_fb_sprintf(fb, "__buf:");
543 duk__print_hbuffer(st, (duk_hbuffer *) b->buf);
544 DUK__COMMA(); duk_fb_sprintf(fb, "__offset:%ld", (long) b->offset);
545 DUK__COMMA(); duk_fb_sprintf(fb, "__length:%ld", (long) b->length);
546 DUK__COMMA(); duk_fb_sprintf(fb, "__shift:%ld", (long) b->shift);
547 DUK__COMMA(); duk_fb_sprintf(fb, "__elemtype:%ld", (long) b->elem_type);
548 } else if (st->internal && DUK_HOBJECT_IS_THREAD(h)) {
549 duk_hthread *t = (duk_hthread *) h;
550 DUK__COMMA(); duk_fb_sprintf(fb, "__strict:%ld", (long) t->strict);
551 DUK__COMMA(); duk_fb_sprintf(fb, "__state:%ld", (long) t->state);
552 DUK__COMMA(); duk_fb_sprintf(fb, "__unused1:%ld", (long) t->unused1);
553 DUK__COMMA(); duk_fb_sprintf(fb, "__unused2:%ld", (long) t->unused2);
554 DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_max:%ld", (long) t->valstack_max);
555 DUK__COMMA(); duk_fb_sprintf(fb, "__callstack_max:%ld", (long) t->callstack_max);
556 DUK__COMMA(); duk_fb_sprintf(fb, "__catchstack_max:%ld", (long) t->catchstack_max);
557 DUK__COMMA(); duk_fb_sprintf(fb, "__valstack:%p", (void *) t->valstack);
558 DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_end:%p/%ld", (void *) t->valstack_end, (long) (t->valstack_end - t->valstack));
559 DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_bottom:%p/%ld", (void *) t->valstack_bottom, (long) (t->valstack_bottom - t->valstack));
560 DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_top:%p/%ld", (void *) t->valstack_top, (long) (t->valstack_top - t->valstack));
561 DUK__COMMA(); duk_fb_sprintf(fb, "__catchstack:%p", (void *) t->catchstack);
562 DUK__COMMA(); duk_fb_sprintf(fb, "__catchstack_size:%ld", (long) t->catchstack_size);
563 DUK__COMMA(); duk_fb_sprintf(fb, "__catchstack_top:%ld", (long) t->catchstack_top);
564 DUK__COMMA(); duk_fb_sprintf(fb, "__resumer:"); duk__print_hobject(st, (duk_hobject *) t->resumer);
565 /* XXX: print built-ins array? */
566
567 }
568#ifdef DUK_USE_REFERENCE_COUNTING
569 if (st->internal) {
570 DUK__COMMA(); duk_fb_sprintf(fb, "__refcount:%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h));
571 }
572#endif
573 if (st->internal) {
574 DUK__COMMA(); duk_fb_sprintf(fb, "__class:%ld", (long) DUK_HOBJECT_GET_CLASS_NUMBER(h));
575 }
576
577 DUK__COMMA(); duk_fb_sprintf(fb, "__heapptr:%p", (void *) h); /* own pointer */
578
579 /* prototype should be last, for readability */
580 if (DUK_HOBJECT_GET_PROTOTYPE(NULL, h)) {
581 if (st->follow_proto) {
582 DUK__COMMA(); duk_fb_put_cstring(fb, "__prototype:"); duk__print_hobject(st, DUK_HOBJECT_GET_PROTOTYPE(NULL, h));
583 } else {
584 DUK__COMMA(); duk_fb_sprintf(fb, "__prototype:%p", (void *) DUK_HOBJECT_GET_PROTOTYPE(NULL, h));
585 }
586 }
587
588 duk_fb_put_cstring(fb, brace2);
589
590#if defined(DUK_USE_HOBJECT_HASH_PART)
591 if (st->heavy && DUK_HOBJECT_GET_HSIZE(h) > 0) {
592 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LANGLE);
593 for (i = 0; i < DUK_HOBJECT_GET_HSIZE(h); i++) {
594 duk_uint_t h_idx = DUK_HOBJECT_H_GET_INDEX(NULL, h, i);
595 if (i > 0) {
596 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_COMMA);
597 }
598 if (h_idx == DUK_HOBJECT_HASHIDX_UNUSED) {
599 duk_fb_sprintf(fb, "u");
600 } else if (h_idx == DUK_HOBJECT_HASHIDX_DELETED) {
601 duk_fb_sprintf(fb, "d");
602 } else {
603 duk_fb_sprintf(fb, "%ld", (long) h_idx);
604 }
605 }
606 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RANGLE);
607 }
608#endif
609
610 finished:
611 st->depth--;
612 if (pushed_loopstack) {
613 st->loop_stack_index--;
614 st->loop_stack[st->loop_stack_index] = NULL;
615 }
616}
617
618#undef DUK__COMMA
619
620DUK_LOCAL void duk__print_hbuffer(duk__dprint_state *st, duk_hbuffer *h) {
621 duk_fixedbuffer *fb = st->fb;
622 duk_size_t i, n;
623 duk_uint8_t *p;
624
625 if (duk_fb_is_full(fb)) {
626 return;
627 }
628
629 /* terminal type: no depth check */
630
631 if (!h) {
632 duk_fb_put_cstring(fb, "NULL");
633 return;
634 }
635
636 if (DUK_HBUFFER_HAS_DYNAMIC(h)) {
637 if (DUK_HBUFFER_HAS_EXTERNAL(h)) {
638 duk_hbuffer_external *g = (duk_hbuffer_external *) h;
639 duk_fb_sprintf(fb, "buffer:external:%p:%ld",
640 (void *) DUK_HBUFFER_EXTERNAL_GET_DATA_PTR(NULL, g),
641 (long) DUK_HBUFFER_EXTERNAL_GET_SIZE(g));
642 } else {
643 duk_hbuffer_dynamic *g = (duk_hbuffer_dynamic *) h;
644 duk_fb_sprintf(fb, "buffer:dynamic:%p:%ld",
645 (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(NULL, g),
646 (long) DUK_HBUFFER_DYNAMIC_GET_SIZE(g));
647 }
648 } else {
649 duk_fb_sprintf(fb, "buffer:fixed:%ld", (long) DUK_HBUFFER_GET_SIZE(h));
650 }
651
652#ifdef DUK_USE_REFERENCE_COUNTING
653 duk_fb_sprintf(fb, "/%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT(&h->hdr));
654#endif
655
656 if (st->hexdump) {
657 duk_fb_sprintf(fb, "=[");
658 n = DUK_HBUFFER_GET_SIZE(h);
659 p = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(NULL, h);
660 for (i = 0; i < n; i++) {
661 duk_fb_sprintf(fb, "%02lx", (unsigned long) p[i]);
662 }
663 duk_fb_sprintf(fb, "]");
664 }
665}
666
667DUK_LOCAL void duk__print_heaphdr(duk__dprint_state *st, duk_heaphdr *h) {
668 duk_fixedbuffer *fb = st->fb;
669
670 if (duk_fb_is_full(fb)) {
671 return;
672 }
673
674 if (!h) {
675 duk_fb_put_cstring(fb, "NULL");
676 return;
677 }
678
679 switch (DUK_HEAPHDR_GET_TYPE(h)) {
680 case DUK_HTYPE_STRING:
681 duk__print_hstring(st, (duk_hstring *) h, 1);
682 break;
683 case DUK_HTYPE_OBJECT:
684 duk__print_hobject(st, (duk_hobject *) h);
685 break;
686 case DUK_HTYPE_BUFFER:
687 duk__print_hbuffer(st, (duk_hbuffer *) h);
688 break;
689 default:
690 duk_fb_sprintf(fb, "[unknown htype %ld]", (long) DUK_HEAPHDR_GET_TYPE(h));
691 break;
692 }
693}
694
695DUK_LOCAL void duk__print_tval(duk__dprint_state *st, duk_tval *tv) {
696 duk_fixedbuffer *fb = st->fb;
697
698 if (duk_fb_is_full(fb)) {
699 return;
700 }
701
702 /* depth check is done when printing an actual type */
703
704 if (st->heavy) {
705 duk_fb_sprintf(fb, "(%p)", (void *) tv);
706 }
707
708 if (!tv) {
709 duk_fb_put_cstring(fb, "NULL");
710 return;
711 }
712
713 if (st->binary) {
714 duk_size_t i;
715 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LBRACKET);
716 for (i = 0; i < (duk_size_t) sizeof(*tv); i++) {
717 duk_fb_sprintf(fb, "%02lx", (unsigned long) ((duk_uint8_t *)tv)[i]);
718 }
719 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RBRACKET);
720 }
721
722 if (st->heavy) {
723 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LANGLE);
724 }
725 switch (DUK_TVAL_GET_TAG(tv)) {
726 case DUK_TAG_UNDEFINED: {
727 duk_fb_put_cstring(fb, "undefined");
728 break;
729 }
730 case DUK_TAG_UNUSED: {
731 duk_fb_put_cstring(fb, "unused");
732 break;
733 }
734 case DUK_TAG_NULL: {
735 duk_fb_put_cstring(fb, "null");
736 break;
737 }
738 case DUK_TAG_BOOLEAN: {
739 duk_fb_put_cstring(fb, DUK_TVAL_GET_BOOLEAN(tv) ? "true" : "false");
740 break;
741 }
742 case DUK_TAG_STRING: {
743 /* Note: string is a terminal heap object, so no depth check here */
744 duk__print_hstring(st, DUK_TVAL_GET_STRING(tv), 1);
745 break;
746 }
747 case DUK_TAG_OBJECT: {
748 duk__print_hobject(st, DUK_TVAL_GET_OBJECT(tv));
749 break;
750 }
751 case DUK_TAG_BUFFER: {
752 duk__print_hbuffer(st, DUK_TVAL_GET_BUFFER(tv));
753 break;
754 }
755 case DUK_TAG_POINTER: {
756 duk_fb_sprintf(fb, "pointer:%p", (void *) DUK_TVAL_GET_POINTER(tv));
757 break;
758 }
759 case DUK_TAG_LIGHTFUNC: {
760 duk_c_function func;
761 duk_small_uint_t lf_flags;
762
763 DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags);
764 duk_fb_sprintf(fb, "lightfunc:");
765 duk_fb_put_funcptr(fb, (duk_uint8_t *) &func, sizeof(func));
766 duk_fb_sprintf(fb, ":%04lx", (long) lf_flags);
767 break;
768 }
769#if defined(DUK_USE_FASTINT)
770 case DUK_TAG_FASTINT:
771#endif
772 default: {
773 /* IEEE double is approximately 16 decimal digits; print a couple extra */
774 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
775 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
776 duk_fb_sprintf(fb, "%.18g", (double) DUK_TVAL_GET_NUMBER(tv));
777 break;
778 }
779 }
780 if (st->heavy) {
781 duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RANGLE);
782 }
783}
784
785DUK_LOCAL void duk__print_instr(duk__dprint_state *st, duk_instr_t ins) {
786 duk_fixedbuffer *fb = st->fb;
787 duk_small_int_t op;
788 const char *op_name;
789 const char *extraop_name;
790
791 op = (duk_small_int_t) DUK_DEC_OP(ins);
792 op_name = duk__bc_optab[op];
793
794 /* XXX: option to fix opcode length so it lines up nicely */
795
796 if (op == DUK_OP_EXTRA) {
797 extraop_name = duk__bc_extraoptab[DUK_DEC_A(ins)];
798
799 duk_fb_sprintf(fb, "%s %ld, %ld",
800 (const char *) extraop_name, (long) DUK_DEC_B(ins), (long) DUK_DEC_C(ins));
801 } else if (op == DUK_OP_JUMP) {
802 duk_int_t diff1 = DUK_DEC_ABC(ins) - DUK_BC_JUMP_BIAS; /* from next pc */
803 duk_int_t diff2 = diff1 + 1; /* from curr pc */
804
805 duk_fb_sprintf(fb, "%s %ld (to pc%c%ld)",
806 (const char *) op_name, (long) diff1,
807 (int) (diff2 >= 0 ? '+' : '-'), /* char format: use int */
808 (long) (diff2 >= 0 ? diff2 : -diff2));
809 } else {
810 duk_fb_sprintf(fb, "%s %ld, %ld, %ld",
811 (const char *) op_name, (long) DUK_DEC_A(ins),
812 (long) DUK_DEC_B(ins), (long) DUK_DEC_C(ins));
813 }
814}
815
816DUK_LOCAL void duk__print_opcode(duk__dprint_state *st, duk_small_int_t opcode) {
817 duk_fixedbuffer *fb = st->fb;
818
819 if (opcode < DUK_BC_OP_MIN || opcode > DUK_BC_OP_MAX) {
820 duk_fb_sprintf(fb, "?(%ld)", (long) opcode);
821 } else {
822 duk_fb_sprintf(fb, "%s", (const char *) duk__bc_optab[opcode]);
823 }
824}
825
826DUK_INTERNAL duk_int_t duk_debug_vsnprintf(char *str, duk_size_t size, const char *format, va_list ap) {
827 duk_fixedbuffer fb;
828 const char *p = format;
829 const char *p_end = p + DUK_STRLEN(format);
830 duk_int_t retval;
831
832 DUK_MEMZERO(&fb, sizeof(fb));
833 fb.buffer = (duk_uint8_t *) str;
834 fb.length = size;
835 fb.offset = 0;
836 fb.truncated = 0;
837
838 while (p < p_end) {
839 char ch = *p++;
840 const char *p_begfmt = NULL;
841 duk_bool_t got_exclamation = 0;
842 duk_bool_t got_long = 0; /* %lf, %ld etc */
843 duk__dprint_state st;
844
845 if (ch != DUK_ASC_PERCENT) {
846 duk_fb_put_byte(&fb, (duk_uint8_t) ch);
847 continue;
848 }
849
850 /*
851 * Format tag parsing. Since we don't understand all the
852 * possible format tags allowed, we just scan for a terminating
853 * specifier and keep track of relevant modifiers that we do
854 * understand. See man 3 printf.
855 */
856
857 DUK_MEMZERO(&st, sizeof(st));
858 st.fb = &fb;
859 st.depth = 0;
860 st.depth_limit = 1;
861 st.loop_stack_index = 0;
862 st.loop_stack_limit = DUK__LOOP_STACK_DEPTH;
863
864 p_begfmt = p - 1;
865 while (p < p_end) {
866 ch = *p++;
867
868 if (ch == DUK_ASC_STAR) {
869 /* unsupported: would consume multiple args */
870 goto error;
871 } else if (ch == DUK_ASC_PERCENT) {
872 duk_fb_put_byte(&fb, (duk_uint8_t) DUK_ASC_PERCENT);
873 break;
874 } else if (ch == DUK_ASC_EXCLAMATION) {
875 got_exclamation = 1;
876 } else if (!got_exclamation && ch == DUK_ASC_LC_L) {
877 got_long = 1;
878 } else if (got_exclamation && ch == DUK_ASC_LC_D) {
879 st.depth_limit = DUK__DEEP_DEPTH_LIMIT;
880 } else if (got_exclamation && ch == DUK_ASC_LC_P) {
881 st.follow_proto = 1;
882 } else if (got_exclamation && ch == DUK_ASC_LC_I) {
883 st.internal = 1;
884 } else if (got_exclamation && ch == DUK_ASC_LC_X) {
885 st.hexdump = 1;
886 } else if (got_exclamation && ch == DUK_ASC_LC_H) {
887 st.heavy = 1;
888 } else if (got_exclamation && ch == DUK_ASC_ATSIGN) {
889 st.pointer = 1;
890 } else if (got_exclamation && ch == DUK_ASC_HASH) {
891 st.binary = 1;
892 } else if (got_exclamation && ch == DUK_ASC_UC_T) {
893 duk_tval *t = va_arg(ap, duk_tval *);
894 if (st.pointer && !st.heavy) {
895 duk_fb_sprintf(&fb, "(%p)", (void *) t);
896 }
897 duk__print_tval(&st, t);
898 break;
899 } else if (got_exclamation && ch == DUK_ASC_UC_O) {
900 duk_heaphdr *t = va_arg(ap, duk_heaphdr *);
901 if (st.pointer && !st.heavy) {
902 duk_fb_sprintf(&fb, "(%p)", (void *) t);
903 }
904 duk__print_heaphdr(&st, t);
905 break;
906 } else if (got_exclamation && ch == DUK_ASC_UC_I) {
907 duk_instr_t t = va_arg(ap, duk_instr_t);
908 duk__print_instr(&st, t);
909 break;
910 } else if (got_exclamation && ch == DUK_ASC_UC_C) {
911 long t = va_arg(ap, long);
912 duk__print_opcode(&st, (duk_small_int_t) t);
913 break;
914 } else if (!got_exclamation && strchr(DUK__ALLOWED_STANDARD_SPECIFIERS, (int) ch)) {
915 char fmtbuf[DUK__MAX_FORMAT_TAG_LENGTH];
916 duk_size_t fmtlen;
917
918 DUK_ASSERT(p >= p_begfmt);
919 fmtlen = (duk_size_t) (p - p_begfmt);
920 if (fmtlen >= sizeof(fmtbuf)) {
921 /* format is too large, abort */
922 goto error;
923 }
924 DUK_MEMZERO(fmtbuf, sizeof(fmtbuf));
925 DUK_MEMCPY(fmtbuf, p_begfmt, fmtlen);
926
927 /* assume exactly 1 arg, which is why '*' is forbidden; arg size still
928 * depends on type though.
929 */
930
931 if (ch == DUK_ASC_LC_F || ch == DUK_ASC_LC_G || ch == DUK_ASC_LC_E) {
932 /* %f and %lf both consume a 'long' */
933 double arg = va_arg(ap, double);
934 duk_fb_sprintf(&fb, fmtbuf, arg);
935 } else if (ch == DUK_ASC_LC_D && got_long) {
936 /* %ld */
937 long arg = va_arg(ap, long);
938 duk_fb_sprintf(&fb, fmtbuf, arg);
939 } else if (ch == DUK_ASC_LC_D) {
940 /* %d; only 16 bits are guaranteed */
941 int arg = va_arg(ap, int);
942 duk_fb_sprintf(&fb, fmtbuf, arg);
943 } else if (ch == DUK_ASC_LC_U && got_long) {
944 /* %lu */
945 unsigned long arg = va_arg(ap, unsigned long);
946 duk_fb_sprintf(&fb, fmtbuf, arg);
947 } else if (ch == DUK_ASC_LC_U) {
948 /* %u; 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_X && got_long) {
952 /* %lx */
953 unsigned long arg = va_arg(ap, unsigned long);
954 duk_fb_sprintf(&fb, fmtbuf, arg);
955 } else if (ch == DUK_ASC_LC_X) {
956 /* %x; only 16 bits are guaranteed */
957 unsigned int arg = va_arg(ap, unsigned int);
958 duk_fb_sprintf(&fb, fmtbuf, arg);
959 } else if (ch == DUK_ASC_LC_S) {
960 /* %s */
961 const char *arg = va_arg(ap, const char *);
962 if (arg == NULL) {
963 /* '%s' and NULL is not portable, so special case
964 * it for debug printing.
965 */
966 duk_fb_sprintf(&fb, "NULL");
967 } else {
968 duk_fb_sprintf(&fb, fmtbuf, arg);
969 }
970 } else if (ch == DUK_ASC_LC_P) {
971 /* %p */
972 void *arg = va_arg(ap, void *);
973 if (arg == NULL) {
974 /* '%p' and NULL is portable, but special case it
975 * anyway to get a standard NULL marker in logs.
976 */
977 duk_fb_sprintf(&fb, "NULL");
978 } else {
979 duk_fb_sprintf(&fb, fmtbuf, arg);
980 }
981 } else if (ch == DUK_ASC_LC_C) {
982 /* '%c', passed concretely as int */
983 int arg = va_arg(ap, int);
984 duk_fb_sprintf(&fb, fmtbuf, arg);
985 } else {
986 /* Should not happen. */
987 duk_fb_sprintf(&fb, "INVALID-FORMAT(%s)", (const char *) fmtbuf);
988 }
989 break;
990 } else {
991 /* ignore */
992 }
993 }
994 }
995 goto done;
996
997 error:
998 duk_fb_put_cstring(&fb, "FMTERR");
999 /* fall through */
1000
1001 done:
1002 retval = (duk_int_t) fb.offset;
1003 duk_fb_put_byte(&fb, (duk_uint8_t) 0);
1004
1005 /* return total chars written excluding terminator */
1006 return retval;
1007}
1008
1009#if 0 /*unused*/
1010DUK_INTERNAL duk_int_t duk_debug_snprintf(char *str, duk_size_t size, const char *format, ...) {
1011 duk_int_t retval;
1012 va_list ap;
1013 va_start(ap, format);
1014 retval = duk_debug_vsnprintf(str, size, format, ap);
1015 va_end(ap);
1016 return retval;
1017}
1018#endif
1019
1020/* Formatting function pointers is tricky: there is no standard pointer for
1021 * function pointers and the size of a function pointer may depend on the
1022 * specific pointer type. This helper formats a function pointer based on
1023 * its memory layout to get something useful on most platforms.
1024 */
1025DUK_INTERNAL void duk_debug_format_funcptr(char *buf, duk_size_t buf_size, duk_uint8_t *fptr, duk_size_t fptr_size) {
1026 duk_size_t i;
1027 duk_uint8_t *p = (duk_uint8_t *) buf;
1028 duk_uint8_t *p_end = (duk_uint8_t *) (buf + buf_size - 1);
1029
1030 DUK_MEMZERO(buf, buf_size);
1031
1032 for (i = 0; i < fptr_size; i++) {
1033 duk_int_t left = (duk_int_t) (p_end - p);
1034 duk_uint8_t ch;
1035 if (left <= 0) {
1036 break;
1037 }
1038
1039 /* Quite approximate but should be useful for little and big endian. */
1040#ifdef DUK_USE_INTEGER_BE
1041 ch = fptr[i];
1042#else
1043 ch = fptr[fptr_size - 1 - i];
1044#endif
1045 p += DUK_SNPRINTF((char *) p, left, "%02lx", (unsigned long) ch);
1046 }
1047}
1048
1049#endif /* DUK_USE_DEBUG */