1 | #include "jsi.h" |
2 | #include "jsparse.h" |
3 | #include "jscompile.h" |
4 | #include "jsvalue.h" |
5 | |
6 | #include "utf.h" |
7 | |
8 | #include <assert.h> |
9 | |
10 | static const char *astname[] = { |
11 | #include "astnames.h" |
12 | NULL |
13 | }; |
14 | |
15 | static const char *opname[] = { |
16 | #include "opnames.h" |
17 | NULL |
18 | }; |
19 | |
20 | static int minify = 0; |
21 | |
22 | const char *jsP_aststring(enum js_AstType type) |
23 | { |
24 | if (type < nelem(astname)-1) |
25 | return astname[type]; |
26 | return "<unknown>" ; |
27 | } |
28 | |
29 | const char *jsC_opcodestring(enum js_OpCode opcode) |
30 | { |
31 | if (opcode < nelem(opname)-1) |
32 | return opname[opcode]; |
33 | return "<unknown>" ; |
34 | } |
35 | |
36 | static int prec(enum js_AstType type) |
37 | { |
38 | switch (type) { |
39 | case AST_IDENTIFIER: |
40 | case EXP_IDENTIFIER: |
41 | case EXP_NUMBER: |
42 | case EXP_STRING: |
43 | case EXP_REGEXP: |
44 | case EXP_UNDEF: |
45 | case EXP_NULL: |
46 | case EXP_TRUE: |
47 | case EXP_FALSE: |
48 | case EXP_THIS: |
49 | case EXP_ARRAY: |
50 | case EXP_OBJECT: |
51 | return 170; |
52 | |
53 | case EXP_FUN: |
54 | case EXP_INDEX: |
55 | case EXP_MEMBER: |
56 | case EXP_CALL: |
57 | case EXP_NEW: |
58 | return 160; |
59 | |
60 | case EXP_POSTINC: |
61 | case EXP_POSTDEC: |
62 | return 150; |
63 | |
64 | case EXP_DELETE: |
65 | case EXP_VOID: |
66 | case EXP_TYPEOF: |
67 | case EXP_PREINC: |
68 | case EXP_PREDEC: |
69 | case EXP_POS: |
70 | case EXP_NEG: |
71 | case EXP_BITNOT: |
72 | case EXP_LOGNOT: |
73 | return 140; |
74 | |
75 | case EXP_MOD: |
76 | case EXP_DIV: |
77 | case EXP_MUL: |
78 | return 130; |
79 | |
80 | case EXP_SUB: |
81 | case EXP_ADD: |
82 | return 120; |
83 | |
84 | case EXP_USHR: |
85 | case EXP_SHR: |
86 | case EXP_SHL: |
87 | return 110; |
88 | |
89 | case EXP_IN: |
90 | case EXP_INSTANCEOF: |
91 | case EXP_GE: |
92 | case EXP_LE: |
93 | case EXP_GT: |
94 | case EXP_LT: |
95 | return 100; |
96 | |
97 | case EXP_STRICTNE: |
98 | case EXP_STRICTEQ: |
99 | case EXP_NE: |
100 | case EXP_EQ: |
101 | return 90; |
102 | |
103 | case EXP_BITAND: return 80; |
104 | case EXP_BITXOR: return 70; |
105 | case EXP_BITOR: return 60; |
106 | case EXP_LOGAND: return 50; |
107 | case EXP_LOGOR: return 40; |
108 | |
109 | case EXP_COND: |
110 | return 30; |
111 | |
112 | case EXP_ASS: |
113 | case EXP_ASS_MUL: |
114 | case EXP_ASS_DIV: |
115 | case EXP_ASS_MOD: |
116 | case EXP_ASS_ADD: |
117 | case EXP_ASS_SUB: |
118 | case EXP_ASS_SHL: |
119 | case EXP_ASS_SHR: |
120 | case EXP_ASS_USHR: |
121 | case EXP_ASS_BITAND: |
122 | case EXP_ASS_BITXOR: |
123 | case EXP_ASS_BITOR: |
124 | return 20; |
125 | |
126 | #define COMMA 15 |
127 | |
128 | case EXP_COMMA: |
129 | return 10; |
130 | |
131 | default: |
132 | return 0; |
133 | } |
134 | } |
135 | |
136 | static void pc(int c) |
137 | { |
138 | putchar(c); |
139 | } |
140 | |
141 | static void ps(const char *s) |
142 | { |
143 | fputs(s, stdout); |
144 | } |
145 | |
146 | static void pn(int n) |
147 | { |
148 | printf("%d" , n); |
149 | } |
150 | |
151 | static void in(int d) |
152 | { |
153 | if (minify < 1) |
154 | while (d-- > 0) |
155 | putchar('\t'); |
156 | } |
157 | |
158 | static void nl(void) |
159 | { |
160 | if (minify < 2) |
161 | putchar('\n'); |
162 | } |
163 | |
164 | static void sp(void) |
165 | { |
166 | if (minify < 1) |
167 | putchar(' '); |
168 | } |
169 | |
170 | static void comma(void) |
171 | { |
172 | putchar(','); |
173 | sp(); |
174 | } |
175 | |
176 | /* Pretty-printed Javascript syntax */ |
177 | |
178 | static void pstmlist(int d, js_Ast *list); |
179 | static void pexpi(int d, int i, js_Ast *exp); |
180 | static void pstm(int d, js_Ast *stm); |
181 | static void slist(int d, js_Ast *list); |
182 | static void sblock(int d, js_Ast *list); |
183 | |
184 | static void pargs(int d, js_Ast *list) |
185 | { |
186 | while (list) { |
187 | assert(list->type == AST_LIST); |
188 | pexpi(d, COMMA, list->a); |
189 | list = list->b; |
190 | if (list) |
191 | comma(); |
192 | } |
193 | } |
194 | |
195 | static void parray(int d, js_Ast *list) |
196 | { |
197 | pc('['); |
198 | while (list) { |
199 | assert(list->type == AST_LIST); |
200 | pexpi(d, COMMA, list->a); |
201 | list = list->b; |
202 | if (list) |
203 | comma(); |
204 | } |
205 | pc(']'); |
206 | } |
207 | |
208 | static void pobject(int d, js_Ast *list) |
209 | { |
210 | pc('{'); |
211 | if (list) { |
212 | nl(); |
213 | in(d+1); |
214 | } |
215 | while (list) { |
216 | js_Ast *kv = list->a; |
217 | assert(list->type == AST_LIST); |
218 | switch (kv->type) { |
219 | default: break; |
220 | case EXP_PROP_VAL: |
221 | pexpi(d+1, COMMA, kv->a); |
222 | pc(':'); sp(); |
223 | pexpi(d+1, COMMA, kv->b); |
224 | break; |
225 | case EXP_PROP_GET: |
226 | ps("get " ); |
227 | pexpi(d+1, COMMA, kv->a); |
228 | ps("()" ); sp(); pc('{'); nl(); |
229 | pstmlist(d+1, kv->c); |
230 | in(d+1); pc('}'); |
231 | break; |
232 | case EXP_PROP_SET: |
233 | ps("set " ); |
234 | pexpi(d+1, COMMA, kv->a); |
235 | pc('('); |
236 | pargs(d+1, kv->b); |
237 | pc(')'); sp(); pc('{'); nl(); |
238 | pstmlist(d+1, kv->c); |
239 | in(d+1); pc('}'); |
240 | break; |
241 | } |
242 | list = list->b; |
243 | if (list) { |
244 | pc(','); |
245 | nl(); |
246 | in(d+1); |
247 | } else { |
248 | nl(); |
249 | in(d); |
250 | } |
251 | } |
252 | pc('}'); |
253 | } |
254 | |
255 | static void pstr(const char *s) |
256 | { |
257 | static const char *HEX = "0123456789ABCDEF" ; |
258 | Rune c; |
259 | pc(minify ? '\'' : '"'); |
260 | while (*s) { |
261 | s += chartorune(&c, s); |
262 | switch (c) { |
263 | case '\'': ps("\\'" ); break; |
264 | case '"': ps("\\\"" ); break; |
265 | case '\\': ps("\\\\" ); break; |
266 | case '\b': ps("\\b" ); break; |
267 | case '\f': ps("\\f" ); break; |
268 | case '\n': ps("\\n" ); break; |
269 | case '\r': ps("\\r" ); break; |
270 | case '\t': ps("\\t" ); break; |
271 | default: |
272 | if (c < ' ' || c > 127) { |
273 | ps("\\u" ); |
274 | pc(HEX[(c>>12)&15]); |
275 | pc(HEX[(c>>8)&15]); |
276 | pc(HEX[(c>>4)&15]); |
277 | pc(HEX[c&15]); |
278 | } else { |
279 | pc(c); break; |
280 | } |
281 | } |
282 | } |
283 | pc(minify ? '\'' : '"'); |
284 | } |
285 | |
286 | static void pregexp(const char *prog, int flags) |
287 | { |
288 | pc('/'); |
289 | ps(prog); |
290 | pc('/'); |
291 | if (flags & JS_REGEXP_G) pc('g'); |
292 | if (flags & JS_REGEXP_I) pc('i'); |
293 | if (flags & JS_REGEXP_M) pc('m'); |
294 | } |
295 | |
296 | static void pbin(int d, int p, js_Ast *exp, const char *op) |
297 | { |
298 | pexpi(d, p, exp->a); |
299 | sp(); |
300 | ps(op); |
301 | sp(); |
302 | pexpi(d, p, exp->b); |
303 | } |
304 | |
305 | static void puna(int d, int p, js_Ast *exp, const char *pre, const char *suf) |
306 | { |
307 | ps(pre); |
308 | pexpi(d, p, exp->a); |
309 | ps(suf); |
310 | } |
311 | |
312 | static void pexpi(int d, int p, js_Ast *exp) |
313 | { |
314 | int tp, paren; |
315 | |
316 | if (!exp) return; |
317 | |
318 | tp = prec(exp->type); |
319 | paren = 0; |
320 | if (tp < p) { |
321 | pc('('); |
322 | paren = 1; |
323 | } |
324 | p = tp; |
325 | |
326 | switch (exp->type) { |
327 | case AST_IDENTIFIER: ps(exp->string); break; |
328 | case EXP_IDENTIFIER: ps(exp->string); break; |
329 | case EXP_NUMBER: printf("%.9g" , exp->number); break; |
330 | case EXP_STRING: pstr(exp->string); break; |
331 | case EXP_REGEXP: pregexp(exp->string, exp->number); break; |
332 | |
333 | case EXP_UNDEF: break; |
334 | case EXP_NULL: ps("null" ); break; |
335 | case EXP_TRUE: ps("true" ); break; |
336 | case EXP_FALSE: ps("false" ); break; |
337 | case EXP_THIS: ps("this" ); break; |
338 | |
339 | case EXP_OBJECT: pobject(d, exp->a); break; |
340 | case EXP_ARRAY: parray(d, exp->a); break; |
341 | |
342 | case EXP_DELETE: puna(d, p, exp, "delete " , "" ); break; |
343 | case EXP_VOID: puna(d, p, exp, "void " , "" ); break; |
344 | case EXP_TYPEOF: puna(d, p, exp, "typeof " , "" ); break; |
345 | case EXP_PREINC: puna(d, p, exp, "++" , "" ); break; |
346 | case EXP_PREDEC: puna(d, p, exp, "--" , "" ); break; |
347 | case EXP_POSTINC: puna(d, p, exp, "" , "++" ); break; |
348 | case EXP_POSTDEC: puna(d, p, exp, "" , "--" ); break; |
349 | case EXP_POS: puna(d, p, exp, "+" , "" ); break; |
350 | case EXP_NEG: puna(d, p, exp, "-" , "" ); break; |
351 | case EXP_BITNOT: puna(d, p, exp, "~" , "" ); break; |
352 | case EXP_LOGNOT: puna(d, p, exp, "!" , "" ); break; |
353 | |
354 | case EXP_LOGOR: pbin(d, p, exp, "||" ); break; |
355 | case EXP_LOGAND: pbin(d, p, exp, "&&" ); break; |
356 | case EXP_BITOR: pbin(d, p, exp, "|" ); break; |
357 | case EXP_BITXOR: pbin(d, p, exp, "^" ); break; |
358 | case EXP_BITAND: pbin(d, p, exp, "&" ); break; |
359 | case EXP_EQ: pbin(d, p, exp, "==" ); break; |
360 | case EXP_NE: pbin(d, p, exp, "!=" ); break; |
361 | case EXP_STRICTEQ: pbin(d, p, exp, "===" ); break; |
362 | case EXP_STRICTNE: pbin(d, p, exp, "!==" ); break; |
363 | case EXP_LT: pbin(d, p, exp, "<" ); break; |
364 | case EXP_GT: pbin(d, p, exp, ">" ); break; |
365 | case EXP_LE: pbin(d, p, exp, "<=" ); break; |
366 | case EXP_GE: pbin(d, p, exp, ">=" ); break; |
367 | case EXP_IN: pbin(d, p, exp, "in" ); break; |
368 | case EXP_SHL: pbin(d, p, exp, "<<" ); break; |
369 | case EXP_SHR: pbin(d, p, exp, ">>" ); break; |
370 | case EXP_USHR: pbin(d, p, exp, ">>>" ); break; |
371 | case EXP_ADD: pbin(d, p, exp, "+" ); break; |
372 | case EXP_SUB: pbin(d, p, exp, "-" ); break; |
373 | case EXP_MUL: pbin(d, p, exp, "*" ); break; |
374 | case EXP_DIV: pbin(d, p, exp, "/" ); break; |
375 | case EXP_MOD: pbin(d, p, exp, "%" ); break; |
376 | case EXP_ASS: pbin(d, p, exp, "=" ); break; |
377 | case EXP_ASS_MUL: pbin(d, p, exp, "*=" ); break; |
378 | case EXP_ASS_DIV: pbin(d, p, exp, "/=" ); break; |
379 | case EXP_ASS_MOD: pbin(d, p, exp, "%=" ); break; |
380 | case EXP_ASS_ADD: pbin(d, p, exp, "+=" ); break; |
381 | case EXP_ASS_SUB: pbin(d, p, exp, "-=" ); break; |
382 | case EXP_ASS_SHL: pbin(d, p, exp, "<<=" ); break; |
383 | case EXP_ASS_SHR: pbin(d, p, exp, ">>=" ); break; |
384 | case EXP_ASS_USHR: pbin(d, p, exp, ">>>=" ); break; |
385 | case EXP_ASS_BITAND: pbin(d, p, exp, "&=" ); break; |
386 | case EXP_ASS_BITXOR: pbin(d, p, exp, "^=" ); break; |
387 | case EXP_ASS_BITOR: pbin(d, p, exp, "|=" ); break; |
388 | |
389 | case EXP_INSTANCEOF: |
390 | pexpi(d, p, exp->a); |
391 | ps(" instanceof " ); |
392 | pexpi(d, p, exp->b); |
393 | break; |
394 | |
395 | case EXP_COMMA: |
396 | pexpi(d, p, exp->a); |
397 | pc(','); sp(); |
398 | pexpi(d, p, exp->b); |
399 | break; |
400 | |
401 | case EXP_COND: |
402 | pexpi(d, p, exp->a); |
403 | sp(); pc('?'); sp(); |
404 | pexpi(d, p, exp->b); |
405 | sp(); pc(':'); sp(); |
406 | pexpi(d, p, exp->c); |
407 | break; |
408 | |
409 | case EXP_INDEX: |
410 | pexpi(d, p, exp->a); |
411 | pc('['); |
412 | pexpi(d, 0, exp->b); |
413 | pc(']'); |
414 | break; |
415 | |
416 | case EXP_MEMBER: |
417 | pexpi(d, p, exp->a); |
418 | pc('.'); |
419 | pexpi(d, 0, exp->b); |
420 | break; |
421 | |
422 | case EXP_CALL: |
423 | pexpi(d, p, exp->a); |
424 | pc('('); |
425 | pargs(d, exp->b); |
426 | pc(')'); |
427 | break; |
428 | |
429 | case EXP_NEW: |
430 | ps("new " ); |
431 | pexpi(d, p, exp->a); |
432 | pc('('); |
433 | pargs(d, exp->b); |
434 | pc(')'); |
435 | break; |
436 | |
437 | case EXP_FUN: |
438 | if (p == 0) pc('('); |
439 | ps("function " ); |
440 | pexpi(d, 0, exp->a); |
441 | pc('('); |
442 | pargs(d, exp->b); |
443 | pc(')'); sp(); pc('{'); nl(); |
444 | pstmlist(d, exp->c); |
445 | in(d); pc('}'); |
446 | if (p == 0) pc(')'); |
447 | break; |
448 | |
449 | default: |
450 | ps("<UNKNOWN>" ); |
451 | break; |
452 | } |
453 | |
454 | if (paren) pc(')'); |
455 | } |
456 | |
457 | static void pexp(int d, js_Ast *exp) |
458 | { |
459 | pexpi(d, 0, exp); |
460 | } |
461 | |
462 | static void pvar(int d, js_Ast *var) |
463 | { |
464 | assert(var->type == EXP_VAR); |
465 | pexp(d, var->a); |
466 | if (var->b) { |
467 | sp(); pc('='); sp(); |
468 | pexp(d, var->b); |
469 | } |
470 | } |
471 | |
472 | static void pvarlist(int d, js_Ast *list) |
473 | { |
474 | while (list) { |
475 | assert(list->type == AST_LIST); |
476 | pvar(d, list->a); |
477 | list = list->b; |
478 | if (list) |
479 | comma(); |
480 | } |
481 | } |
482 | |
483 | static void pblock(int d, js_Ast *block) |
484 | { |
485 | assert(block->type == STM_BLOCK); |
486 | pc('{'); nl(); |
487 | pstmlist(d, block->a); |
488 | in(d); pc('}'); |
489 | } |
490 | |
491 | static void pstmh(int d, js_Ast *stm) |
492 | { |
493 | if (stm->type == STM_BLOCK) { |
494 | sp(); |
495 | pblock(d, stm); |
496 | } else { |
497 | nl(); |
498 | pstm(d+1, stm); |
499 | } |
500 | } |
501 | |
502 | static void pcaselist(int d, js_Ast *list) |
503 | { |
504 | while (list) { |
505 | js_Ast *stm = list->a; |
506 | if (stm->type == STM_CASE) { |
507 | in(d); ps("case " ); pexp(d, stm->a); pc(':'); nl(); |
508 | pstmlist(d, stm->b); |
509 | } |
510 | if (stm->type == STM_DEFAULT) { |
511 | in(d); ps("default:" ); nl(); |
512 | pstmlist(d, stm->a); |
513 | } |
514 | list = list->b; |
515 | } |
516 | } |
517 | |
518 | static void pstm(int d, js_Ast *stm) |
519 | { |
520 | if (stm->type == STM_BLOCK) { |
521 | pblock(d, stm); |
522 | return; |
523 | } |
524 | |
525 | in(d); |
526 | |
527 | switch (stm->type) { |
528 | case AST_FUNDEC: |
529 | ps("function " ); |
530 | pexp(d, stm->a); |
531 | pc('('); |
532 | pargs(d, stm->b); |
533 | pc(')'); sp(); pc('{'); nl(); |
534 | pstmlist(d, stm->c); |
535 | in(d); pc('}'); |
536 | break; |
537 | |
538 | case STM_EMPTY: |
539 | pc(';'); |
540 | break; |
541 | |
542 | case STM_VAR: |
543 | ps("var " ); |
544 | pvarlist(d, stm->a); |
545 | pc(';'); |
546 | break; |
547 | |
548 | case STM_IF: |
549 | ps("if" ); sp(); pc('('); pexp(d, stm->a); pc(')'); |
550 | pstmh(d, stm->b); |
551 | if (stm->c) { |
552 | nl(); in(d); ps("else" ); |
553 | pstmh(d, stm->c); |
554 | } |
555 | break; |
556 | |
557 | case STM_DO: |
558 | ps("do" ); |
559 | pstmh(d, stm->a); |
560 | nl(); |
561 | in(d); ps("while" ); sp(); pc('('); pexp(d, stm->b); pc(')'); pc(';'); |
562 | break; |
563 | |
564 | case STM_WHILE: |
565 | ps("while" ); sp(); pc('('); pexp(d, stm->a); pc(')'); |
566 | pstmh(d, stm->b); |
567 | break; |
568 | |
569 | case STM_FOR: |
570 | ps("for" ); sp(); pc('('); |
571 | pexp(d, stm->a); pc(';'); sp(); |
572 | pexp(d, stm->b); pc(';'); sp(); |
573 | pexp(d, stm->c); pc(')'); |
574 | pstmh(d, stm->d); |
575 | break; |
576 | case STM_FOR_VAR: |
577 | ps("for" ); sp(); ps("(var " ); |
578 | pvarlist(d, stm->a); pc(';'); sp(); |
579 | pexp(d, stm->b); pc(';'); sp(); |
580 | pexp(d, stm->c); pc(')'); |
581 | pstmh(d, stm->d); |
582 | break; |
583 | case STM_FOR_IN: |
584 | ps("for" ); sp(); pc('('); |
585 | pexp(d, stm->a); ps(" in " ); |
586 | pexp(d, stm->b); pc(')'); |
587 | pstmh(d, stm->c); |
588 | break; |
589 | case STM_FOR_IN_VAR: |
590 | ps("for" ); sp(); ps("(var " ); |
591 | pvarlist(d, stm->a); ps(" in " ); |
592 | pexp(d, stm->b); pc(')'); |
593 | pstmh(d, stm->c); |
594 | break; |
595 | |
596 | case STM_CONTINUE: |
597 | ps("continue" ); |
598 | if (stm->a) { |
599 | pc(' '); pexp(d, stm->a); |
600 | } |
601 | pc(';'); |
602 | break; |
603 | |
604 | case STM_BREAK: |
605 | ps("break" ); |
606 | if (stm->a) { |
607 | pc(' '); pexp(d, stm->a); |
608 | } |
609 | pc(';'); |
610 | break; |
611 | |
612 | case STM_RETURN: |
613 | ps("return" ); |
614 | if (stm->a) { |
615 | pc(' '); pexp(d, stm->a); |
616 | } |
617 | pc(';'); |
618 | break; |
619 | |
620 | case STM_WITH: |
621 | ps("with" ); sp(); pc('('); pexp(d, stm->a); pc(')'); |
622 | pstmh(d, stm->b); |
623 | break; |
624 | |
625 | case STM_SWITCH: |
626 | ps("switch" ); sp(); pc('('); |
627 | pexp(d, stm->a); |
628 | pc(')'); sp(); pc('{'); nl(); |
629 | pcaselist(d, stm->b); |
630 | in(d); pc('}'); |
631 | break; |
632 | |
633 | case STM_THROW: |
634 | ps("throw " ); pexp(d, stm->a); pc(';'); |
635 | break; |
636 | |
637 | case STM_TRY: |
638 | ps("try" ); |
639 | if (minify && stm->a->type != STM_BLOCK) |
640 | pc(' '); |
641 | pstmh(d, stm->a); |
642 | if (stm->b && stm->c) { |
643 | nl(); in(d); ps("catch" ); sp(); pc('('); pexp(d, stm->b); pc(')'); |
644 | pstmh(d, stm->c); |
645 | } |
646 | if (stm->d) { |
647 | nl(); in(d); ps("finally" ); |
648 | pstmh(d, stm->d); |
649 | } |
650 | break; |
651 | |
652 | case STM_LABEL: |
653 | pexp(d, stm->a); pc(':'); sp(); pstm(d, stm->b); |
654 | break; |
655 | |
656 | case STM_DEBUGGER: |
657 | ps("debugger" ); |
658 | pc(';'); |
659 | break; |
660 | |
661 | default: |
662 | pexp(d, stm); |
663 | pc(';'); |
664 | } |
665 | } |
666 | |
667 | static void pstmlist(int d, js_Ast *list) |
668 | { |
669 | while (list) { |
670 | assert(list->type == AST_LIST); |
671 | pstm(d+1, list->a); |
672 | nl(); |
673 | list = list->b; |
674 | } |
675 | } |
676 | |
677 | void jsP_dumpsyntax(js_State *J, js_Ast *prog, int dominify) |
678 | { |
679 | minify = dominify; |
680 | if (prog->type == AST_LIST) |
681 | pstmlist(-1, prog); |
682 | else { |
683 | pstm(0, prog); |
684 | nl(); |
685 | } |
686 | if (minify > 1) |
687 | putchar('\n'); |
688 | } |
689 | |
690 | /* S-expression list representation */ |
691 | |
692 | static void snode(int d, js_Ast *node) |
693 | { |
694 | void (*afun)(int,js_Ast*) = snode; |
695 | void (*bfun)(int,js_Ast*) = snode; |
696 | void (*cfun)(int,js_Ast*) = snode; |
697 | void (*dfun)(int,js_Ast*) = snode; |
698 | |
699 | if (!node) { |
700 | return; |
701 | } |
702 | |
703 | if (node->type == AST_LIST) { |
704 | slist(d, node); |
705 | return; |
706 | } |
707 | |
708 | pc('('); |
709 | ps(astname[node->type]); |
710 | pc(':'); |
711 | pn(node->line); |
712 | switch (node->type) { |
713 | default: break; |
714 | case AST_IDENTIFIER: pc(' '); ps(node->string); break; |
715 | case EXP_IDENTIFIER: pc(' '); ps(node->string); break; |
716 | case EXP_STRING: pc(' '); pstr(node->string); break; |
717 | case EXP_REGEXP: pc(' '); pregexp(node->string, node->number); break; |
718 | case EXP_NUMBER: printf(" %.9g" , node->number); break; |
719 | case STM_BLOCK: afun = sblock; break; |
720 | case AST_FUNDEC: case EXP_FUN: cfun = sblock; break; |
721 | case EXP_PROP_GET: cfun = sblock; break; |
722 | case EXP_PROP_SET: cfun = sblock; break; |
723 | case STM_SWITCH: bfun = sblock; break; |
724 | case STM_CASE: bfun = sblock; break; |
725 | case STM_DEFAULT: afun = sblock; break; |
726 | } |
727 | if (node->a) { pc(' '); afun(d, node->a); } |
728 | if (node->b) { pc(' '); bfun(d, node->b); } |
729 | if (node->c) { pc(' '); cfun(d, node->c); } |
730 | if (node->d) { pc(' '); dfun(d, node->d); } |
731 | pc(')'); |
732 | } |
733 | |
734 | static void slist(int d, js_Ast *list) |
735 | { |
736 | pc('['); |
737 | while (list) { |
738 | assert(list->type == AST_LIST); |
739 | snode(d, list->a); |
740 | list = list->b; |
741 | if (list) |
742 | pc(' '); |
743 | } |
744 | pc(']'); |
745 | } |
746 | |
747 | static void sblock(int d, js_Ast *list) |
748 | { |
749 | ps("[\n" ); |
750 | in(d+1); |
751 | while (list) { |
752 | assert(list->type == AST_LIST); |
753 | snode(d+1, list->a); |
754 | list = list->b; |
755 | if (list) { |
756 | nl(); |
757 | in(d+1); |
758 | } |
759 | } |
760 | nl(); in(d); pc(']'); |
761 | } |
762 | |
763 | void jsP_dumplist(js_State *J, js_Ast *prog) |
764 | { |
765 | minify = 0; |
766 | if (prog->type == AST_LIST) |
767 | sblock(0, prog); |
768 | else |
769 | snode(0, prog); |
770 | nl(); |
771 | } |
772 | |
773 | /* Compiled code */ |
774 | |
775 | void jsC_dumpfunction(js_State *J, js_Function *F) |
776 | { |
777 | js_Instruction *p = F->code; |
778 | js_Instruction *end = F->code + F->codelen; |
779 | int i; |
780 | |
781 | minify = 0; |
782 | |
783 | printf("%s(%d)\n" , F->name, F->numparams); |
784 | if (F->lightweight) printf("\tlightweight\n" ); |
785 | if (F->arguments) printf("\targuments\n" ); |
786 | printf("\tsource %s:%d\n" , F->filename, F->line); |
787 | for (i = 0; i < F->funlen; ++i) |
788 | printf("\tfunction %d %s\n" , i, F->funtab[i]->name); |
789 | for (i = 0; i < F->varlen; ++i) |
790 | printf("\tlocal %d %s\n" , i + 1, F->vartab[i]); |
791 | |
792 | printf("{\n" ); |
793 | while (p < end) { |
794 | int ln = *p++; |
795 | int c = *p++; |
796 | |
797 | printf("%5d(%3d): " , (int)(p - F->code) - 2, ln); |
798 | ps(opname[c]); |
799 | |
800 | switch (c) { |
801 | case OP_INTEGER: |
802 | printf(" %d" , (*p++) - 32768); |
803 | break; |
804 | case OP_NUMBER: |
805 | printf(" %.9g" , F->numtab[*p++]); |
806 | break; |
807 | case OP_STRING: |
808 | pc(' '); |
809 | pstr(F->strtab[*p++]); |
810 | break; |
811 | case OP_NEWREGEXP: |
812 | pc(' '); |
813 | pregexp(F->strtab[p[0]], p[1]); |
814 | p += 2; |
815 | break; |
816 | |
817 | case OP_GETVAR: |
818 | case OP_HASVAR: |
819 | case OP_SETVAR: |
820 | case OP_DELVAR: |
821 | case OP_GETPROP_S: |
822 | case OP_SETPROP_S: |
823 | case OP_DELPROP_S: |
824 | case OP_CATCH: |
825 | pc(' '); |
826 | ps(F->strtab[*p++]); |
827 | break; |
828 | |
829 | case OP_GETLOCAL: |
830 | case OP_SETLOCAL: |
831 | case OP_DELLOCAL: |
832 | printf(" %s" , F->vartab[*p++ - 1]); |
833 | break; |
834 | |
835 | case OP_CLOSURE: |
836 | case OP_CALL: |
837 | case OP_NEW: |
838 | case OP_JUMP: |
839 | case OP_JTRUE: |
840 | case OP_JFALSE: |
841 | case OP_JCASE: |
842 | case OP_TRY: |
843 | printf(" %d" , *p++); |
844 | break; |
845 | } |
846 | |
847 | nl(); |
848 | } |
849 | printf("}\n" ); |
850 | |
851 | for (i = 0; i < F->funlen; ++i) { |
852 | if (F->funtab[i] != F) { |
853 | printf("function %d " , i); |
854 | jsC_dumpfunction(J, F->funtab[i]); |
855 | } |
856 | } |
857 | } |
858 | |
859 | /* Runtime values */ |
860 | |
861 | void js_dumpvalue(js_State *J, js_Value v) |
862 | { |
863 | minify = 0; |
864 | switch (v.type) { |
865 | case JS_TUNDEFINED: printf("undefined" ); break; |
866 | case JS_TNULL: printf("null" ); break; |
867 | case JS_TBOOLEAN: printf(v.u.boolean ? "true" : "false" ); break; |
868 | case JS_TNUMBER: printf("%.9g" , v.u.number); break; |
869 | case JS_TSHRSTR: printf("'%s'" , v.u.shrstr); break; |
870 | case JS_TLITSTR: printf("'%s'" , v.u.litstr); break; |
871 | case JS_TMEMSTR: printf("'%s'" , v.u.memstr->p); break; |
872 | case JS_TOBJECT: |
873 | if (v.u.object == J->G) { |
874 | printf("[Global]" ); |
875 | break; |
876 | } |
877 | switch (v.u.object->type) { |
878 | case JS_COBJECT: printf("[Object %p]" , (void*)v.u.object); break; |
879 | case JS_CARRAY: printf("[Array %p]" , (void*)v.u.object); break; |
880 | case JS_CFUNCTION: |
881 | printf("[Function %p, %s, %s:%d]" , |
882 | (void*)v.u.object, |
883 | v.u.object->u.f.function->name, |
884 | v.u.object->u.f.function->filename, |
885 | v.u.object->u.f.function->line); |
886 | break; |
887 | case JS_CSCRIPT: printf("[Script %s]" , v.u.object->u.f.function->filename); break; |
888 | case JS_CCFUNCTION: printf("[CFunction %s]" , v.u.object->u.c.name); break; |
889 | case JS_CBOOLEAN: printf("[Boolean %d]" , v.u.object->u.boolean); break; |
890 | case JS_CNUMBER: printf("[Number %g]" , v.u.object->u.number); break; |
891 | case JS_CSTRING: printf("[String'%s']" , v.u.object->u.s.string); break; |
892 | case JS_CERROR: printf("[Error]" ); break; |
893 | case JS_CARGUMENTS: printf("[Arguments %p]" , (void*)v.u.object); break; |
894 | case JS_CITERATOR: printf("[Iterator %p]" , (void*)v.u.object); break; |
895 | case JS_CUSERDATA: |
896 | printf("[Userdata %s %p]" , v.u.object->u.user.tag, v.u.object->u.user.data); |
897 | break; |
898 | default: printf("[Object %p]" , (void*)v.u.object); break; |
899 | } |
900 | break; |
901 | } |
902 | } |
903 | |
904 | static void js_dumpproperty(js_State *J, js_Property *node) |
905 | { |
906 | minify = 0; |
907 | if (node->left->level) |
908 | js_dumpproperty(J, node->left); |
909 | printf("\t%s: " , node->name); |
910 | js_dumpvalue(J, node->value); |
911 | printf(",\n" ); |
912 | if (node->right->level) |
913 | js_dumpproperty(J, node->right); |
914 | } |
915 | |
916 | void js_dumpobject(js_State *J, js_Object *obj) |
917 | { |
918 | minify = 0; |
919 | printf("{\n" ); |
920 | if (obj->properties->level) |
921 | js_dumpproperty(J, obj->properties); |
922 | printf("}\n" ); |
923 | } |
924 | |