| 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 | |