Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/mujs/jscompile.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 #include "jsi.h" | |
| 2 | |
| 3 #define cexp jsC_cexp /* collision with math.h */ | |
| 4 | |
| 5 #define JF js_State *J, js_Function *F | |
| 6 | |
| 7 JS_NORETURN void jsC_error(js_State *J, js_Ast *node, const char *fmt, ...) JS_PRINTFLIKE(3,4); | |
| 8 | |
| 9 static void cfunbody(JF, js_Ast *name, js_Ast *params, js_Ast *body, int is_fun_exp); | |
| 10 static void cexp(JF, js_Ast *exp); | |
| 11 static void cstmlist(JF, js_Ast *list); | |
| 12 static void cstm(JF, js_Ast *stm); | |
| 13 | |
| 14 void jsC_error(js_State *J, js_Ast *node, const char *fmt, ...) | |
| 15 { | |
| 16 va_list ap; | |
| 17 char buf[512]; | |
| 18 char msgbuf[256]; | |
| 19 | |
| 20 va_start(ap, fmt); | |
| 21 vsnprintf(msgbuf, 256, fmt, ap); | |
| 22 va_end(ap); | |
| 23 | |
| 24 snprintf(buf, 256, "%s:%d: ", J->filename, node->line); | |
| 25 strcat(buf, msgbuf); | |
| 26 | |
| 27 js_newsyntaxerror(J, buf); | |
| 28 js_throw(J); | |
| 29 } | |
| 30 | |
| 31 static const char *futurewords[] = { | |
| 32 "class", "const", "enum", "export", "extends", "import", "super", | |
| 33 }; | |
| 34 | |
| 35 static const char *strictfuturewords[] = { | |
| 36 "implements", "interface", "let", "package", "private", "protected", | |
| 37 "public", "static", "yield", | |
| 38 }; | |
| 39 | |
| 40 static void checkfutureword(JF, js_Ast *exp) | |
| 41 { | |
| 42 if (jsY_findword(exp->string, futurewords, nelem(futurewords)) >= 0) | |
| 43 jsC_error(J, exp, "'%s' is a future reserved word", exp->string); | |
| 44 if (F->strict) { | |
| 45 if (jsY_findword(exp->string, strictfuturewords, nelem(strictfuturewords)) >= 0) | |
| 46 jsC_error(J, exp, "'%s' is a strict mode future reserved word", exp->string); | |
| 47 } | |
| 48 } | |
| 49 | |
| 50 static js_Function *newfun(js_State *J, int line, js_Ast *name, js_Ast *params, js_Ast *body, int script, int default_strict, int is_fun_exp) | |
| 51 { | |
| 52 js_Function *F = js_malloc(J, sizeof *F); | |
| 53 memset(F, 0, sizeof *F); | |
| 54 F->gcmark = 0; | |
| 55 F->gcnext = J->gcfun; | |
| 56 J->gcfun = F; | |
| 57 ++J->gccounter; | |
| 58 | |
| 59 F->filename = js_intern(J, J->filename); | |
| 60 F->line = line; | |
| 61 F->script = script; | |
| 62 F->strict = default_strict; | |
| 63 F->name = name ? name->string : ""; | |
| 64 | |
| 65 cfunbody(J, F, name, params, body, is_fun_exp); | |
| 66 | |
| 67 return F; | |
| 68 } | |
| 69 | |
| 70 /* Emit opcodes, constants and jumps */ | |
| 71 | |
| 72 static void emitraw(JF, int value) | |
| 73 { | |
| 74 if (value != (js_Instruction)value) | |
| 75 js_syntaxerror(J, "integer overflow in instruction coding"); | |
| 76 if (F->codelen >= F->codecap) { | |
| 77 F->codecap = F->codecap ? F->codecap * 2 : 64; | |
| 78 F->code = js_realloc(J, F->code, F->codecap * sizeof *F->code); | |
| 79 } | |
| 80 F->code[F->codelen++] = value; | |
| 81 } | |
| 82 | |
| 83 static void emit(JF, int value) | |
| 84 { | |
| 85 emitraw(J, F, F->lastline); | |
| 86 emitraw(J, F, value); | |
| 87 } | |
| 88 | |
| 89 static void emitarg(JF, int value) | |
| 90 { | |
| 91 emitraw(J, F, value); | |
| 92 } | |
| 93 | |
| 94 static void emitline(JF, js_Ast *node) | |
| 95 { | |
| 96 F->lastline = node->line; | |
| 97 } | |
| 98 | |
| 99 static int addfunction(JF, js_Function *value) | |
| 100 { | |
| 101 if (F->funlen >= F->funcap) { | |
| 102 F->funcap = F->funcap ? F->funcap * 2 : 16; | |
| 103 F->funtab = js_realloc(J, F->funtab, F->funcap * sizeof *F->funtab); | |
| 104 } | |
| 105 F->funtab[F->funlen] = value; | |
| 106 return F->funlen++; | |
| 107 } | |
| 108 | |
| 109 static int addlocal(JF, js_Ast *ident, int reuse) | |
| 110 { | |
| 111 const char *name = ident->string; | |
| 112 if (F->strict) { | |
| 113 if (!strcmp(name, "arguments")) | |
| 114 jsC_error(J, ident, "redefining 'arguments' is not allowed in strict mode"); | |
| 115 if (!strcmp(name, "eval")) | |
| 116 jsC_error(J, ident, "redefining 'eval' is not allowed in strict mode"); | |
| 117 } else { | |
| 118 if (!strcmp(name, "eval")) | |
| 119 js_evalerror(J, "%s:%d: invalid use of 'eval'", J->filename, ident->line); | |
| 120 } | |
| 121 if (reuse || F->strict) { | |
| 122 int i; | |
| 123 for (i = 0; i < F->varlen; ++i) { | |
| 124 if (!strcmp(F->vartab[i], name)) { | |
| 125 if (reuse) | |
| 126 return i+1; | |
| 127 if (F->strict) | |
| 128 jsC_error(J, ident, "duplicate formal parameter '%s'", name); | |
| 129 } | |
| 130 } | |
| 131 } | |
| 132 if (F->varlen >= F->varcap) { | |
| 133 F->varcap = F->varcap ? F->varcap * 2 : 16; | |
| 134 F->vartab = js_realloc(J, F->vartab, F->varcap * sizeof *F->vartab); | |
| 135 } | |
| 136 F->vartab[F->varlen] = name; | |
| 137 return ++F->varlen; | |
| 138 } | |
| 139 | |
| 140 static int findlocal(JF, const char *name) | |
| 141 { | |
| 142 int i; | |
| 143 for (i = F->varlen; i > 0; --i) | |
| 144 if (!strcmp(F->vartab[i-1], name)) | |
| 145 return i; | |
| 146 return -1; | |
| 147 } | |
| 148 | |
| 149 static void emitfunction(JF, js_Function *fun) | |
| 150 { | |
| 151 F->lightweight = 0; | |
| 152 emit(J, F, OP_CLOSURE); | |
| 153 emitarg(J, F, addfunction(J, F, fun)); | |
| 154 } | |
| 155 | |
| 156 static void emitnumber(JF, double num) | |
| 157 { | |
| 158 if (num == 0) { | |
| 159 emit(J, F, OP_INTEGER); | |
| 160 emitarg(J, F, 32768); | |
| 161 if (signbit(num)) | |
| 162 emit(J, F, OP_NEG); | |
| 163 } else if (num >= SHRT_MIN && num <= SHRT_MAX && num == (int)num) { | |
| 164 emit(J, F, OP_INTEGER); | |
| 165 emitarg(J, F, num + 32768); | |
| 166 } else { | |
| 167 #define N (sizeof(num) / sizeof(js_Instruction)) | |
| 168 js_Instruction x[N]; | |
| 169 size_t i; | |
| 170 emit(J, F, OP_NUMBER); | |
| 171 memcpy(x, &num, sizeof(num)); | |
| 172 for (i = 0; i < N; ++i) | |
| 173 emitarg(J, F, x[i]); | |
| 174 #undef N | |
| 175 } | |
| 176 } | |
| 177 | |
| 178 static void emitstring(JF, int opcode, const char *str) | |
| 179 { | |
| 180 #define N (sizeof(str) / sizeof(js_Instruction)) | |
| 181 js_Instruction x[N]; | |
| 182 size_t i; | |
| 183 emit(J, F, opcode); | |
| 184 memcpy(x, &str, sizeof(str)); | |
| 185 for (i = 0; i < N; ++i) | |
| 186 emitarg(J, F, x[i]); | |
| 187 #undef N | |
| 188 } | |
| 189 | |
| 190 static void emitlocal(JF, int oploc, int opvar, js_Ast *ident) | |
| 191 { | |
| 192 int is_arguments = !strcmp(ident->string, "arguments"); | |
| 193 int is_eval = !strcmp(ident->string, "eval"); | |
| 194 int i; | |
| 195 | |
| 196 if (is_arguments) { | |
| 197 F->lightweight = 0; | |
| 198 F->arguments = 1; | |
| 199 } | |
| 200 | |
| 201 checkfutureword(J, F, ident); | |
| 202 if (F->strict && oploc == OP_SETLOCAL) { | |
| 203 if (is_arguments) | |
| 204 jsC_error(J, ident, "'arguments' is read-only in strict mode"); | |
| 205 if (is_eval) | |
| 206 jsC_error(J, ident, "'eval' is read-only in strict mode"); | |
| 207 } | |
| 208 if (is_eval) | |
| 209 js_evalerror(J, "%s:%d: invalid use of 'eval'", J->filename, ident->line); | |
| 210 | |
| 211 i = findlocal(J, F, ident->string); | |
| 212 if (i < 0) { | |
| 213 emitstring(J, F, opvar, ident->string); | |
| 214 } else { | |
| 215 emit(J, F, oploc); | |
| 216 emitarg(J, F, i); | |
| 217 } | |
| 218 } | |
| 219 | |
| 220 static int here(JF) | |
| 221 { | |
| 222 return F->codelen; | |
| 223 } | |
| 224 | |
| 225 static int emitjump(JF, int opcode) | |
| 226 { | |
| 227 int inst; | |
| 228 emit(J, F, opcode); | |
| 229 inst = F->codelen; | |
| 230 emitarg(J, F, 0); | |
| 231 return inst; | |
| 232 } | |
| 233 | |
| 234 static void emitjumpto(JF, int opcode, int dest) | |
| 235 { | |
| 236 emit(J, F, opcode); | |
| 237 if (dest != (js_Instruction)dest) | |
| 238 js_syntaxerror(J, "jump address integer overflow"); | |
| 239 emitarg(J, F, dest); | |
| 240 } | |
| 241 | |
| 242 static void labelto(JF, int inst, int addr) | |
| 243 { | |
| 244 if (addr != (js_Instruction)addr) | |
| 245 js_syntaxerror(J, "jump address integer overflow"); | |
| 246 F->code[inst] = addr; | |
| 247 } | |
| 248 | |
| 249 static void label(JF, int inst) | |
| 250 { | |
| 251 labelto(J, F, inst, F->codelen); | |
| 252 } | |
| 253 | |
| 254 /* Expressions */ | |
| 255 | |
| 256 static void ctypeof(JF, js_Ast *exp) | |
| 257 { | |
| 258 if (exp->a->type == EXP_IDENTIFIER) { | |
| 259 emitline(J, F, exp->a); | |
| 260 emitlocal(J, F, OP_GETLOCAL, OP_HASVAR, exp->a); | |
| 261 } else { | |
| 262 cexp(J, F, exp->a); | |
| 263 } | |
| 264 emitline(J, F, exp); | |
| 265 emit(J, F, OP_TYPEOF); | |
| 266 } | |
| 267 | |
| 268 static void cunary(JF, js_Ast *exp, int opcode) | |
| 269 { | |
| 270 cexp(J, F, exp->a); | |
| 271 emitline(J, F, exp); | |
| 272 emit(J, F, opcode); | |
| 273 } | |
| 274 | |
| 275 static void cbinary(JF, js_Ast *exp, int opcode) | |
| 276 { | |
| 277 cexp(J, F, exp->a); | |
| 278 cexp(J, F, exp->b); | |
| 279 emitline(J, F, exp); | |
| 280 emit(J, F, opcode); | |
| 281 } | |
| 282 | |
| 283 static void carray(JF, js_Ast *list) | |
| 284 { | |
| 285 while (list) { | |
| 286 emitline(J, F, list->a); | |
| 287 if (list->a->type == EXP_ELISION) { | |
| 288 emit(J, F, OP_SKIPARRAY); | |
| 289 } else { | |
| 290 cexp(J, F, list->a); | |
| 291 emit(J, F, OP_INITARRAY); | |
| 292 } | |
| 293 list = list->b; | |
| 294 } | |
| 295 } | |
| 296 | |
| 297 static void checkdup(JF, js_Ast *list, js_Ast *end) | |
| 298 { | |
| 299 char nbuf[32], sbuf[32]; | |
| 300 const char *needle, *straw; | |
| 301 | |
| 302 if (end->a->type == EXP_NUMBER) | |
| 303 needle = jsV_numbertostring(J, nbuf, end->a->number); | |
| 304 else | |
| 305 needle = end->a->string; | |
| 306 | |
| 307 while (list->a != end) { | |
| 308 if (list->a->type == end->type) { | |
| 309 js_Ast *prop = list->a->a; | |
| 310 if (prop->type == EXP_NUMBER) | |
| 311 straw = jsV_numbertostring(J, sbuf, prop->number); | |
| 312 else | |
| 313 straw = prop->string; | |
| 314 if (!strcmp(needle, straw)) | |
| 315 jsC_error(J, list, "duplicate property '%s' in object literal", needle); | |
| 316 } | |
| 317 list = list->b; | |
| 318 } | |
| 319 } | |
| 320 | |
| 321 static void cobject(JF, js_Ast *list) | |
| 322 { | |
| 323 js_Ast *head = list; | |
| 324 | |
| 325 while (list) { | |
| 326 js_Ast *kv = list->a; | |
| 327 js_Ast *prop = kv->a; | |
| 328 | |
| 329 if (prop->type == AST_IDENTIFIER || prop->type == EXP_STRING) { | |
| 330 emitline(J, F, prop); | |
| 331 emitstring(J, F, OP_STRING, prop->string); | |
| 332 } else if (prop->type == EXP_NUMBER) { | |
| 333 emitline(J, F, prop); | |
| 334 emitnumber(J, F, prop->number); | |
| 335 } else { | |
| 336 jsC_error(J, prop, "invalid property name in object initializer"); | |
| 337 } | |
| 338 | |
| 339 if (F->strict) | |
| 340 checkdup(J, F, head, kv); | |
| 341 | |
| 342 switch (kv->type) { | |
| 343 default: /* impossible */ break; | |
| 344 case EXP_PROP_VAL: | |
| 345 cexp(J, F, kv->b); | |
| 346 emitline(J, F, kv); | |
| 347 emit(J, F, OP_INITPROP); | |
| 348 break; | |
| 349 case EXP_PROP_GET: | |
| 350 emitfunction(J, F, newfun(J, prop->line, NULL, NULL, kv->c, 0, F->strict, 1)); | |
| 351 emitline(J, F, kv); | |
| 352 emit(J, F, OP_INITGETTER); | |
| 353 break; | |
| 354 case EXP_PROP_SET: | |
| 355 emitfunction(J, F, newfun(J, prop->line, NULL, kv->b, kv->c, 0, F->strict, 1)); | |
| 356 emitline(J, F, kv); | |
| 357 emit(J, F, OP_INITSETTER); | |
| 358 break; | |
| 359 } | |
| 360 | |
| 361 list = list->b; | |
| 362 } | |
| 363 } | |
| 364 | |
| 365 static int cargs(JF, js_Ast *list) | |
| 366 { | |
| 367 int n = 0; | |
| 368 while (list) { | |
| 369 cexp(J, F, list->a); | |
| 370 list = list->b; | |
| 371 ++n; | |
| 372 } | |
| 373 return n; | |
| 374 } | |
| 375 | |
| 376 static void cassign(JF, js_Ast *exp) | |
| 377 { | |
| 378 js_Ast *lhs = exp->a; | |
| 379 js_Ast *rhs = exp->b; | |
| 380 switch (lhs->type) { | |
| 381 case EXP_IDENTIFIER: | |
| 382 cexp(J, F, rhs); | |
| 383 emitline(J, F, exp); | |
| 384 emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs); | |
| 385 break; | |
| 386 case EXP_INDEX: | |
| 387 cexp(J, F, lhs->a); | |
| 388 cexp(J, F, lhs->b); | |
| 389 cexp(J, F, rhs); | |
| 390 emitline(J, F, exp); | |
| 391 emit(J, F, OP_SETPROP); | |
| 392 break; | |
| 393 case EXP_MEMBER: | |
| 394 cexp(J, F, lhs->a); | |
| 395 cexp(J, F, rhs); | |
| 396 emitline(J, F, exp); | |
| 397 emitstring(J, F, OP_SETPROP_S, lhs->b->string); | |
| 398 break; | |
| 399 default: | |
| 400 jsC_error(J, lhs, "invalid l-value in assignment"); | |
| 401 } | |
| 402 } | |
| 403 | |
| 404 static void cassignforin(JF, js_Ast *stm) | |
| 405 { | |
| 406 js_Ast *lhs = stm->a; | |
| 407 | |
| 408 if (stm->type == STM_FOR_IN_VAR) { | |
| 409 if (lhs->b) | |
| 410 jsC_error(J, lhs->b, "more than one loop variable in for-in statement"); | |
| 411 emitline(J, F, lhs->a); | |
| 412 emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs->a->a); /* list(var-init(ident)) */ | |
| 413 emit(J, F, OP_POP); | |
| 414 return; | |
| 415 } | |
| 416 | |
| 417 switch (lhs->type) { | |
| 418 case EXP_IDENTIFIER: | |
| 419 emitline(J, F, lhs); | |
| 420 emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs); | |
| 421 emit(J, F, OP_POP); | |
| 422 break; | |
| 423 case EXP_INDEX: | |
| 424 cexp(J, F, lhs->a); | |
| 425 cexp(J, F, lhs->b); | |
| 426 emitline(J, F, lhs); | |
| 427 emit(J, F, OP_ROT3); | |
| 428 emit(J, F, OP_SETPROP); | |
| 429 emit(J, F, OP_POP); | |
| 430 break; | |
| 431 case EXP_MEMBER: | |
| 432 cexp(J, F, lhs->a); | |
| 433 emitline(J, F, lhs); | |
| 434 emit(J, F, OP_ROT2); | |
| 435 emitstring(J, F, OP_SETPROP_S, lhs->b->string); | |
| 436 emit(J, F, OP_POP); | |
| 437 break; | |
| 438 default: | |
| 439 jsC_error(J, lhs, "invalid l-value in for-in loop assignment"); | |
| 440 } | |
| 441 } | |
| 442 | |
| 443 static void cassignop1(JF, js_Ast *lhs) | |
| 444 { | |
| 445 switch (lhs->type) { | |
| 446 case EXP_IDENTIFIER: | |
| 447 emitline(J, F, lhs); | |
| 448 emitlocal(J, F, OP_GETLOCAL, OP_GETVAR, lhs); | |
| 449 break; | |
| 450 case EXP_INDEX: | |
| 451 cexp(J, F, lhs->a); | |
| 452 cexp(J, F, lhs->b); | |
| 453 emitline(J, F, lhs); | |
| 454 emit(J, F, OP_DUP2); | |
| 455 emit(J, F, OP_GETPROP); | |
| 456 break; | |
| 457 case EXP_MEMBER: | |
| 458 cexp(J, F, lhs->a); | |
| 459 emitline(J, F, lhs); | |
| 460 emit(J, F, OP_DUP); | |
| 461 emitstring(J, F, OP_GETPROP_S, lhs->b->string); | |
| 462 break; | |
| 463 default: | |
| 464 jsC_error(J, lhs, "invalid l-value in assignment"); | |
| 465 } | |
| 466 } | |
| 467 | |
| 468 static void cassignop2(JF, js_Ast *lhs, int postfix) | |
| 469 { | |
| 470 switch (lhs->type) { | |
| 471 case EXP_IDENTIFIER: | |
| 472 emitline(J, F, lhs); | |
| 473 if (postfix) emit(J, F, OP_ROT2); | |
| 474 emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs); | |
| 475 break; | |
| 476 case EXP_INDEX: | |
| 477 emitline(J, F, lhs); | |
| 478 if (postfix) emit(J, F, OP_ROT4); | |
| 479 emit(J, F, OP_SETPROP); | |
| 480 break; | |
| 481 case EXP_MEMBER: | |
| 482 emitline(J, F, lhs); | |
| 483 if (postfix) emit(J, F, OP_ROT3); | |
| 484 emitstring(J, F, OP_SETPROP_S, lhs->b->string); | |
| 485 break; | |
| 486 default: | |
| 487 jsC_error(J, lhs, "invalid l-value in assignment"); | |
| 488 } | |
| 489 } | |
| 490 | |
| 491 static void cassignop(JF, js_Ast *exp, int opcode) | |
| 492 { | |
| 493 js_Ast *lhs = exp->a; | |
| 494 js_Ast *rhs = exp->b; | |
| 495 cassignop1(J, F, lhs); | |
| 496 cexp(J, F, rhs); | |
| 497 emitline(J, F, exp); | |
| 498 emit(J, F, opcode); | |
| 499 cassignop2(J, F, lhs, 0); | |
| 500 } | |
| 501 | |
| 502 static void cdelete(JF, js_Ast *exp) | |
| 503 { | |
| 504 js_Ast *arg = exp->a; | |
| 505 switch (arg->type) { | |
| 506 case EXP_IDENTIFIER: | |
| 507 if (F->strict) | |
| 508 jsC_error(J, exp, "delete on an unqualified name is not allowed in strict mode"); | |
| 509 emitline(J, F, exp); | |
| 510 emitlocal(J, F, OP_DELLOCAL, OP_DELVAR, arg); | |
| 511 break; | |
| 512 case EXP_INDEX: | |
| 513 cexp(J, F, arg->a); | |
| 514 cexp(J, F, arg->b); | |
| 515 emitline(J, F, exp); | |
| 516 emit(J, F, OP_DELPROP); | |
| 517 break; | |
| 518 case EXP_MEMBER: | |
| 519 cexp(J, F, arg->a); | |
| 520 emitline(J, F, exp); | |
| 521 emitstring(J, F, OP_DELPROP_S, arg->b->string); | |
| 522 break; | |
| 523 default: | |
| 524 jsC_error(J, exp, "invalid l-value in delete expression"); | |
| 525 } | |
| 526 } | |
| 527 | |
| 528 static void ceval(JF, js_Ast *fun, js_Ast *args) | |
| 529 { | |
| 530 int n = cargs(J, F, args); | |
| 531 F->lightweight = 0; | |
| 532 F->arguments = 1; | |
| 533 if (n == 0) | |
| 534 emit(J, F, OP_UNDEF); | |
| 535 else while (n-- > 1) | |
| 536 emit(J, F, OP_POP); | |
| 537 emit(J, F, OP_EVAL); | |
| 538 } | |
| 539 | |
| 540 static void ccall(JF, js_Ast *fun, js_Ast *args) | |
| 541 { | |
| 542 int n; | |
| 543 switch (fun->type) { | |
| 544 case EXP_INDEX: | |
| 545 cexp(J, F, fun->a); | |
| 546 emit(J, F, OP_DUP); | |
| 547 cexp(J, F, fun->b); | |
| 548 emit(J, F, OP_GETPROP); | |
| 549 emit(J, F, OP_ROT2); | |
| 550 break; | |
| 551 case EXP_MEMBER: | |
| 552 cexp(J, F, fun->a); | |
| 553 emit(J, F, OP_DUP); | |
| 554 emitstring(J, F, OP_GETPROP_S, fun->b->string); | |
| 555 emit(J, F, OP_ROT2); | |
| 556 break; | |
| 557 case EXP_IDENTIFIER: | |
| 558 if (!strcmp(fun->string, "eval")) { | |
| 559 ceval(J, F, fun, args); | |
| 560 return; | |
| 561 } | |
| 562 /* fallthrough */ | |
| 563 default: | |
| 564 cexp(J, F, fun); | |
| 565 emit(J, F, OP_UNDEF); | |
| 566 break; | |
| 567 } | |
| 568 n = cargs(J, F, args); | |
| 569 emit(J, F, OP_CALL); | |
| 570 emitarg(J, F, n); | |
| 571 } | |
| 572 | |
| 573 static void cexp(JF, js_Ast *exp) | |
| 574 { | |
| 575 int then, end; | |
| 576 int n; | |
| 577 | |
| 578 switch (exp->type) { | |
| 579 case EXP_STRING: | |
| 580 emitline(J, F, exp); | |
| 581 emitstring(J, F, OP_STRING, exp->string); | |
| 582 break; | |
| 583 case EXP_NUMBER: | |
| 584 emitline(J, F, exp); | |
| 585 emitnumber(J, F, exp->number); | |
| 586 break; | |
| 587 case EXP_ELISION: | |
| 588 break; | |
| 589 case EXP_NULL: | |
| 590 emitline(J, F, exp); | |
| 591 emit(J, F, OP_NULL); | |
| 592 break; | |
| 593 case EXP_TRUE: | |
| 594 emitline(J, F, exp); | |
| 595 emit(J, F, OP_TRUE); | |
| 596 break; | |
| 597 case EXP_FALSE: | |
| 598 emitline(J, F, exp); | |
| 599 emit(J, F, OP_FALSE); | |
| 600 break; | |
| 601 case EXP_THIS: | |
| 602 emitline(J, F, exp); | |
| 603 emit(J, F, OP_THIS); | |
| 604 break; | |
| 605 | |
| 606 case EXP_REGEXP: | |
| 607 emitline(J, F, exp); | |
| 608 emitstring(J, F, OP_NEWREGEXP, exp->string); | |
| 609 emitarg(J, F, exp->number); | |
| 610 break; | |
| 611 | |
| 612 case EXP_OBJECT: | |
| 613 emitline(J, F, exp); | |
| 614 emit(J, F, OP_NEWOBJECT); | |
| 615 cobject(J, F, exp->a); | |
| 616 break; | |
| 617 | |
| 618 case EXP_ARRAY: | |
| 619 emitline(J, F, exp); | |
| 620 emit(J, F, OP_NEWARRAY); | |
| 621 carray(J, F, exp->a); | |
| 622 break; | |
| 623 | |
| 624 case EXP_FUN: | |
| 625 emitline(J, F, exp); | |
| 626 emitfunction(J, F, newfun(J, exp->line, exp->a, exp->b, exp->c, 0, F->strict, 1)); | |
| 627 break; | |
| 628 | |
| 629 case EXP_IDENTIFIER: | |
| 630 emitline(J, F, exp); | |
| 631 emitlocal(J, F, OP_GETLOCAL, OP_GETVAR, exp); | |
| 632 break; | |
| 633 | |
| 634 case EXP_INDEX: | |
| 635 cexp(J, F, exp->a); | |
| 636 cexp(J, F, exp->b); | |
| 637 emitline(J, F, exp); | |
| 638 emit(J, F, OP_GETPROP); | |
| 639 break; | |
| 640 | |
| 641 case EXP_MEMBER: | |
| 642 cexp(J, F, exp->a); | |
| 643 emitline(J, F, exp); | |
| 644 emitstring(J, F, OP_GETPROP_S, exp->b->string); | |
| 645 break; | |
| 646 | |
| 647 case EXP_CALL: | |
| 648 ccall(J, F, exp->a, exp->b); | |
| 649 break; | |
| 650 | |
| 651 case EXP_NEW: | |
| 652 cexp(J, F, exp->a); | |
| 653 n = cargs(J, F, exp->b); | |
| 654 emitline(J, F, exp); | |
| 655 emit(J, F, OP_NEW); | |
| 656 emitarg(J, F, n); | |
| 657 break; | |
| 658 | |
| 659 case EXP_DELETE: | |
| 660 cdelete(J, F, exp); | |
| 661 break; | |
| 662 | |
| 663 case EXP_PREINC: | |
| 664 cassignop1(J, F, exp->a); | |
| 665 emitline(J, F, exp); | |
| 666 emit(J, F, OP_INC); | |
| 667 cassignop2(J, F, exp->a, 0); | |
| 668 break; | |
| 669 | |
| 670 case EXP_PREDEC: | |
| 671 cassignop1(J, F, exp->a); | |
| 672 emitline(J, F, exp); | |
| 673 emit(J, F, OP_DEC); | |
| 674 cassignop2(J, F, exp->a, 0); | |
| 675 break; | |
| 676 | |
| 677 case EXP_POSTINC: | |
| 678 cassignop1(J, F, exp->a); | |
| 679 emitline(J, F, exp); | |
| 680 emit(J, F, OP_POSTINC); | |
| 681 cassignop2(J, F, exp->a, 1); | |
| 682 emit(J, F, OP_POP); | |
| 683 break; | |
| 684 | |
| 685 case EXP_POSTDEC: | |
| 686 cassignop1(J, F, exp->a); | |
| 687 emitline(J, F, exp); | |
| 688 emit(J, F, OP_POSTDEC); | |
| 689 cassignop2(J, F, exp->a, 1); | |
| 690 emit(J, F, OP_POP); | |
| 691 break; | |
| 692 | |
| 693 case EXP_VOID: | |
| 694 cexp(J, F, exp->a); | |
| 695 emitline(J, F, exp); | |
| 696 emit(J, F, OP_POP); | |
| 697 emit(J, F, OP_UNDEF); | |
| 698 break; | |
| 699 | |
| 700 case EXP_TYPEOF: ctypeof(J, F, exp); break; | |
| 701 case EXP_POS: cunary(J, F, exp, OP_POS); break; | |
| 702 case EXP_NEG: cunary(J, F, exp, OP_NEG); break; | |
| 703 case EXP_BITNOT: cunary(J, F, exp, OP_BITNOT); break; | |
| 704 case EXP_LOGNOT: cunary(J, F, exp, OP_LOGNOT); break; | |
| 705 | |
| 706 case EXP_BITOR: cbinary(J, F, exp, OP_BITOR); break; | |
| 707 case EXP_BITXOR: cbinary(J, F, exp, OP_BITXOR); break; | |
| 708 case EXP_BITAND: cbinary(J, F, exp, OP_BITAND); break; | |
| 709 case EXP_EQ: cbinary(J, F, exp, OP_EQ); break; | |
| 710 case EXP_NE: cbinary(J, F, exp, OP_NE); break; | |
| 711 case EXP_STRICTEQ: cbinary(J, F, exp, OP_STRICTEQ); break; | |
| 712 case EXP_STRICTNE: cbinary(J, F, exp, OP_STRICTNE); break; | |
| 713 case EXP_LT: cbinary(J, F, exp, OP_LT); break; | |
| 714 case EXP_GT: cbinary(J, F, exp, OP_GT); break; | |
| 715 case EXP_LE: cbinary(J, F, exp, OP_LE); break; | |
| 716 case EXP_GE: cbinary(J, F, exp, OP_GE); break; | |
| 717 case EXP_INSTANCEOF: cbinary(J, F, exp, OP_INSTANCEOF); break; | |
| 718 case EXP_IN: cbinary(J, F, exp, OP_IN); break; | |
| 719 case EXP_SHL: cbinary(J, F, exp, OP_SHL); break; | |
| 720 case EXP_SHR: cbinary(J, F, exp, OP_SHR); break; | |
| 721 case EXP_USHR: cbinary(J, F, exp, OP_USHR); break; | |
| 722 case EXP_ADD: cbinary(J, F, exp, OP_ADD); break; | |
| 723 case EXP_SUB: cbinary(J, F, exp, OP_SUB); break; | |
| 724 case EXP_MUL: cbinary(J, F, exp, OP_MUL); break; | |
| 725 case EXP_DIV: cbinary(J, F, exp, OP_DIV); break; | |
| 726 case EXP_MOD: cbinary(J, F, exp, OP_MOD); break; | |
| 727 | |
| 728 case EXP_ASS: cassign(J, F, exp); break; | |
| 729 case EXP_ASS_MUL: cassignop(J, F, exp, OP_MUL); break; | |
| 730 case EXP_ASS_DIV: cassignop(J, F, exp, OP_DIV); break; | |
| 731 case EXP_ASS_MOD: cassignop(J, F, exp, OP_MOD); break; | |
| 732 case EXP_ASS_ADD: cassignop(J, F, exp, OP_ADD); break; | |
| 733 case EXP_ASS_SUB: cassignop(J, F, exp, OP_SUB); break; | |
| 734 case EXP_ASS_SHL: cassignop(J, F, exp, OP_SHL); break; | |
| 735 case EXP_ASS_SHR: cassignop(J, F, exp, OP_SHR); break; | |
| 736 case EXP_ASS_USHR: cassignop(J, F, exp, OP_USHR); break; | |
| 737 case EXP_ASS_BITAND: cassignop(J, F, exp, OP_BITAND); break; | |
| 738 case EXP_ASS_BITXOR: cassignop(J, F, exp, OP_BITXOR); break; | |
| 739 case EXP_ASS_BITOR: cassignop(J, F, exp, OP_BITOR); break; | |
| 740 | |
| 741 case EXP_COMMA: | |
| 742 cexp(J, F, exp->a); | |
| 743 emitline(J, F, exp); | |
| 744 emit(J, F, OP_POP); | |
| 745 cexp(J, F, exp->b); | |
| 746 break; | |
| 747 | |
| 748 case EXP_LOGOR: | |
| 749 cexp(J, F, exp->a); | |
| 750 emitline(J, F, exp); | |
| 751 emit(J, F, OP_DUP); | |
| 752 end = emitjump(J, F, OP_JTRUE); | |
| 753 emit(J, F, OP_POP); | |
| 754 cexp(J, F, exp->b); | |
| 755 label(J, F, end); | |
| 756 break; | |
| 757 | |
| 758 case EXP_LOGAND: | |
| 759 cexp(J, F, exp->a); | |
| 760 emitline(J, F, exp); | |
| 761 emit(J, F, OP_DUP); | |
| 762 end = emitjump(J, F, OP_JFALSE); | |
| 763 emit(J, F, OP_POP); | |
| 764 cexp(J, F, exp->b); | |
| 765 label(J, F, end); | |
| 766 break; | |
| 767 | |
| 768 case EXP_COND: | |
| 769 cexp(J, F, exp->a); | |
| 770 emitline(J, F, exp); | |
| 771 then = emitjump(J, F, OP_JTRUE); | |
| 772 cexp(J, F, exp->c); | |
| 773 end = emitjump(J, F, OP_JUMP); | |
| 774 label(J, F, then); | |
| 775 cexp(J, F, exp->b); | |
| 776 label(J, F, end); | |
| 777 break; | |
| 778 | |
| 779 default: | |
| 780 jsC_error(J, exp, "unknown expression type"); | |
| 781 } | |
| 782 } | |
| 783 | |
| 784 /* Patch break and continue statements */ | |
| 785 | |
| 786 static void addjump(JF, enum js_AstType type, js_Ast *target, int inst) | |
| 787 { | |
| 788 js_JumpList *jump = js_malloc(J, sizeof *jump); | |
| 789 jump->type = type; | |
| 790 jump->inst = inst; | |
| 791 jump->next = target->jumps; | |
| 792 target->jumps = jump; | |
| 793 } | |
| 794 | |
| 795 static void labeljumps(JF, js_Ast *stm, int baddr, int caddr) | |
| 796 { | |
| 797 js_JumpList *jump = stm->jumps; | |
| 798 while (jump) { | |
| 799 js_JumpList *next = jump->next; | |
| 800 if (jump->type == STM_BREAK) | |
| 801 labelto(J, F, jump->inst, baddr); | |
| 802 if (jump->type == STM_CONTINUE) | |
| 803 labelto(J, F, jump->inst, caddr); | |
| 804 js_free(J, jump); | |
| 805 jump = next; | |
| 806 } | |
| 807 stm->jumps = NULL; | |
| 808 } | |
| 809 | |
| 810 static int isloop(enum js_AstType T) | |
| 811 { | |
| 812 return T == STM_DO || T == STM_WHILE || | |
| 813 T == STM_FOR || T == STM_FOR_VAR || | |
| 814 T == STM_FOR_IN || T == STM_FOR_IN_VAR; | |
| 815 } | |
| 816 | |
| 817 static int isfun(enum js_AstType T) | |
| 818 { | |
| 819 return T == AST_FUNDEC || T == EXP_FUN || T == EXP_PROP_GET || T == EXP_PROP_SET; | |
| 820 } | |
| 821 | |
| 822 static int matchlabel(js_Ast *node, const char *label) | |
| 823 { | |
| 824 while (node && node->type == STM_LABEL) { | |
| 825 if (!strcmp(node->a->string, label)) | |
| 826 return 1; | |
| 827 node = node->parent; | |
| 828 } | |
| 829 return 0; | |
| 830 } | |
| 831 | |
| 832 static js_Ast *breaktarget(JF, js_Ast *node, const char *label) | |
| 833 { | |
| 834 while (node) { | |
| 835 if (isfun(node->type)) | |
| 836 break; | |
| 837 if (!label) { | |
| 838 if (isloop(node->type) || node->type == STM_SWITCH) | |
| 839 return node; | |
| 840 } else { | |
| 841 if (matchlabel(node->parent, label)) | |
| 842 return node; | |
| 843 } | |
| 844 node = node->parent; | |
| 845 } | |
| 846 return NULL; | |
| 847 } | |
| 848 | |
| 849 static js_Ast *continuetarget(JF, js_Ast *node, const char *label) | |
| 850 { | |
| 851 while (node) { | |
| 852 if (isfun(node->type)) | |
| 853 break; | |
| 854 if (isloop(node->type)) { | |
| 855 if (!label) | |
| 856 return node; | |
| 857 else if (matchlabel(node->parent, label)) | |
| 858 return node; | |
| 859 } | |
| 860 node = node->parent; | |
| 861 } | |
| 862 return NULL; | |
| 863 } | |
| 864 | |
| 865 static js_Ast *returntarget(JF, js_Ast *node) | |
| 866 { | |
| 867 while (node) { | |
| 868 if (isfun(node->type)) | |
| 869 return node; | |
| 870 node = node->parent; | |
| 871 } | |
| 872 return NULL; | |
| 873 } | |
| 874 | |
| 875 /* Emit code to rebalance stack and scopes during an abrupt exit */ | |
| 876 | |
| 877 static void cexit(JF, enum js_AstType T, js_Ast *node, js_Ast *target) | |
| 878 { | |
| 879 js_Ast *prev; | |
| 880 do { | |
| 881 prev = node, node = node->parent; | |
| 882 switch (node->type) { | |
| 883 default: | |
| 884 /* impossible */ | |
| 885 break; | |
| 886 case STM_WITH: | |
| 887 emitline(J, F, node); | |
| 888 emit(J, F, OP_ENDWITH); | |
| 889 break; | |
| 890 case STM_FOR_IN: | |
| 891 case STM_FOR_IN_VAR: | |
| 892 emitline(J, F, node); | |
| 893 /* pop the iterator if leaving the loop */ | |
| 894 if (F->script) { | |
| 895 if (T == STM_RETURN || T == STM_BREAK || (T == STM_CONTINUE && target != node)) { | |
| 896 /* pop the iterator, save the return or exp value */ | |
| 897 emit(J, F, OP_ROT2); | |
| 898 emit(J, F, OP_POP); | |
| 899 } | |
| 900 if (T == STM_CONTINUE) | |
| 901 emit(J, F, OP_ROT2); /* put the iterator back on top */ | |
| 902 } else { | |
| 903 if (T == STM_RETURN) { | |
| 904 /* pop the iterator, save the return value */ | |
| 905 emit(J, F, OP_ROT2); | |
| 906 emit(J, F, OP_POP); | |
| 907 } | |
| 908 if (T == STM_BREAK || (T == STM_CONTINUE && target != node)) | |
| 909 emit(J, F, OP_POP); /* pop the iterator */ | |
| 910 } | |
| 911 break; | |
| 912 case STM_TRY: | |
| 913 emitline(J, F, node); | |
| 914 /* came from try block */ | |
| 915 if (prev == node->a) { | |
| 916 emit(J, F, OP_ENDTRY); | |
| 917 if (node->d) cstm(J, F, node->d); /* finally */ | |
| 918 } | |
| 919 /* came from catch block */ | |
| 920 if (prev == node->c) { | |
| 921 /* ... with finally */ | |
| 922 if (node->d) { | |
| 923 emit(J, F, OP_ENDCATCH); | |
| 924 emit(J, F, OP_ENDTRY); | |
| 925 cstm(J, F, node->d); /* finally */ | |
| 926 } else { | |
| 927 emit(J, F, OP_ENDCATCH); | |
| 928 } | |
| 929 } | |
| 930 break; | |
| 931 } | |
| 932 } while (node != target); | |
| 933 } | |
| 934 | |
| 935 /* Try/catch/finally */ | |
| 936 | |
| 937 static void ctryfinally(JF, js_Ast *trystm, js_Ast *finallystm) | |
| 938 { | |
| 939 int L1; | |
| 940 L1 = emitjump(J, F, OP_TRY); | |
| 941 { | |
| 942 /* if we get here, we have caught an exception in the try block */ | |
| 943 cstm(J, F, finallystm); /* inline finally block */ | |
| 944 emit(J, F, OP_THROW); /* rethrow exception */ | |
| 945 } | |
| 946 label(J, F, L1); | |
| 947 cstm(J, F, trystm); | |
| 948 emit(J, F, OP_ENDTRY); | |
| 949 cstm(J, F, finallystm); | |
| 950 } | |
| 951 | |
| 952 static void ctrycatch(JF, js_Ast *trystm, js_Ast *catchvar, js_Ast *catchstm) | |
| 953 { | |
| 954 int L1, L2; | |
| 955 L1 = emitjump(J, F, OP_TRY); | |
| 956 { | |
| 957 /* if we get here, we have caught an exception in the try block */ | |
| 958 checkfutureword(J, F, catchvar); | |
| 959 if (F->strict) { | |
| 960 if (!strcmp(catchvar->string, "arguments")) | |
| 961 jsC_error(J, catchvar, "redefining 'arguments' is not allowed in strict mode"); | |
| 962 if (!strcmp(catchvar->string, "eval")) | |
| 963 jsC_error(J, catchvar, "redefining 'eval' is not allowed in strict mode"); | |
| 964 } | |
| 965 emitline(J, F, catchvar); | |
| 966 emitstring(J, F, OP_CATCH, catchvar->string); | |
| 967 cstm(J, F, catchstm); | |
| 968 emit(J, F, OP_ENDCATCH); | |
| 969 L2 = emitjump(J, F, OP_JUMP); /* skip past the try block */ | |
| 970 } | |
| 971 label(J, F, L1); | |
| 972 cstm(J, F, trystm); | |
| 973 emit(J, F, OP_ENDTRY); | |
| 974 label(J, F, L2); | |
| 975 } | |
| 976 | |
| 977 static void ctrycatchfinally(JF, js_Ast *trystm, js_Ast *catchvar, js_Ast *catchstm, js_Ast *finallystm) | |
| 978 { | |
| 979 int L1, L2, L3; | |
| 980 L1 = emitjump(J, F, OP_TRY); | |
| 981 { | |
| 982 /* if we get here, we have caught an exception in the try block */ | |
| 983 L2 = emitjump(J, F, OP_TRY); | |
| 984 { | |
| 985 /* if we get here, we have caught an exception in the catch block */ | |
| 986 cstm(J, F, finallystm); /* inline finally block */ | |
| 987 emit(J, F, OP_THROW); /* rethrow exception */ | |
| 988 } | |
| 989 label(J, F, L2); | |
| 990 if (F->strict) { | |
| 991 checkfutureword(J, F, catchvar); | |
| 992 if (!strcmp(catchvar->string, "arguments")) | |
| 993 jsC_error(J, catchvar, "redefining 'arguments' is not allowed in strict mode"); | |
| 994 if (!strcmp(catchvar->string, "eval")) | |
| 995 jsC_error(J, catchvar, "redefining 'eval' is not allowed in strict mode"); | |
| 996 } | |
| 997 emitline(J, F, catchvar); | |
| 998 emitstring(J, F, OP_CATCH, catchvar->string); | |
| 999 cstm(J, F, catchstm); | |
| 1000 emit(J, F, OP_ENDCATCH); | |
| 1001 emit(J, F, OP_ENDTRY); | |
| 1002 L3 = emitjump(J, F, OP_JUMP); /* skip past the try block to the finally block */ | |
| 1003 } | |
| 1004 label(J, F, L1); | |
| 1005 cstm(J, F, trystm); | |
| 1006 emit(J, F, OP_ENDTRY); | |
| 1007 label(J, F, L3); | |
| 1008 cstm(J, F, finallystm); | |
| 1009 } | |
| 1010 | |
| 1011 /* Switch */ | |
| 1012 | |
| 1013 static void cswitch(JF, js_Ast *ref, js_Ast *head) | |
| 1014 { | |
| 1015 js_Ast *node, *clause, *def = NULL; | |
| 1016 int end; | |
| 1017 | |
| 1018 cexp(J, F, ref); | |
| 1019 | |
| 1020 /* emit an if-else chain of tests for the case clause expressions */ | |
| 1021 for (node = head; node; node = node->b) { | |
| 1022 clause = node->a; | |
| 1023 if (clause->type == STM_DEFAULT) { | |
| 1024 if (def) | |
| 1025 jsC_error(J, clause, "more than one default label in switch"); | |
| 1026 def = clause; | |
| 1027 } else { | |
| 1028 cexp(J, F, clause->a); | |
| 1029 emitline(J, F, clause); | |
| 1030 clause->casejump = emitjump(J, F, OP_JCASE); | |
| 1031 } | |
| 1032 } | |
| 1033 emit(J, F, OP_POP); | |
| 1034 if (def) { | |
| 1035 emitline(J, F, def); | |
| 1036 def->casejump = emitjump(J, F, OP_JUMP); | |
| 1037 end = 0; | |
| 1038 } else { | |
| 1039 end = emitjump(J, F, OP_JUMP); | |
| 1040 } | |
| 1041 | |
| 1042 /* emit the case clause bodies */ | |
| 1043 for (node = head; node; node = node->b) { | |
| 1044 clause = node->a; | |
| 1045 label(J, F, clause->casejump); | |
| 1046 if (clause->type == STM_DEFAULT) | |
| 1047 cstmlist(J, F, clause->a); | |
| 1048 else | |
| 1049 cstmlist(J, F, clause->b); | |
| 1050 } | |
| 1051 | |
| 1052 if (end) | |
| 1053 label(J, F, end); | |
| 1054 } | |
| 1055 | |
| 1056 /* Statements */ | |
| 1057 | |
| 1058 static void cvarinit(JF, js_Ast *list) | |
| 1059 { | |
| 1060 while (list) { | |
| 1061 js_Ast *var = list->a; | |
| 1062 if (var->b) { | |
| 1063 cexp(J, F, var->b); | |
| 1064 emitline(J, F, var); | |
| 1065 emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, var->a); | |
| 1066 emit(J, F, OP_POP); | |
| 1067 } | |
| 1068 list = list->b; | |
| 1069 } | |
| 1070 } | |
| 1071 | |
| 1072 static void cstm(JF, js_Ast *stm) | |
| 1073 { | |
| 1074 js_Ast *target; | |
| 1075 int loop, cont, then, end; | |
| 1076 | |
| 1077 emitline(J, F, stm); | |
| 1078 | |
| 1079 switch (stm->type) { | |
| 1080 case AST_FUNDEC: | |
| 1081 break; | |
| 1082 | |
| 1083 case STM_BLOCK: | |
| 1084 cstmlist(J, F, stm->a); | |
| 1085 break; | |
| 1086 | |
| 1087 case STM_EMPTY: | |
| 1088 if (F->script) { | |
| 1089 emitline(J, F, stm); | |
| 1090 emit(J, F, OP_POP); | |
| 1091 emit(J, F, OP_UNDEF); | |
| 1092 } | |
| 1093 break; | |
| 1094 | |
| 1095 case STM_VAR: | |
| 1096 cvarinit(J, F, stm->a); | |
| 1097 break; | |
| 1098 | |
| 1099 case STM_IF: | |
| 1100 if (stm->c) { | |
| 1101 cexp(J, F, stm->a); | |
| 1102 emitline(J, F, stm); | |
| 1103 then = emitjump(J, F, OP_JTRUE); | |
| 1104 cstm(J, F, stm->c); | |
| 1105 emitline(J, F, stm); | |
| 1106 end = emitjump(J, F, OP_JUMP); | |
| 1107 label(J, F, then); | |
| 1108 cstm(J, F, stm->b); | |
| 1109 label(J, F, end); | |
| 1110 } else { | |
| 1111 cexp(J, F, stm->a); | |
| 1112 emitline(J, F, stm); | |
| 1113 end = emitjump(J, F, OP_JFALSE); | |
| 1114 cstm(J, F, stm->b); | |
| 1115 label(J, F, end); | |
| 1116 } | |
| 1117 break; | |
| 1118 | |
| 1119 case STM_DO: | |
| 1120 loop = here(J, F); | |
| 1121 cstm(J, F, stm->a); | |
| 1122 cont = here(J, F); | |
| 1123 cexp(J, F, stm->b); | |
| 1124 emitline(J, F, stm); | |
| 1125 emitjumpto(J, F, OP_JTRUE, loop); | |
| 1126 labeljumps(J, F, stm, here(J,F), cont); | |
| 1127 break; | |
| 1128 | |
| 1129 case STM_WHILE: | |
| 1130 loop = here(J, F); | |
| 1131 cexp(J, F, stm->a); | |
| 1132 emitline(J, F, stm); | |
| 1133 end = emitjump(J, F, OP_JFALSE); | |
| 1134 cstm(J, F, stm->b); | |
| 1135 emitline(J, F, stm); | |
| 1136 emitjumpto(J, F, OP_JUMP, loop); | |
| 1137 label(J, F, end); | |
| 1138 labeljumps(J, F, stm, here(J,F), loop); | |
| 1139 break; | |
| 1140 | |
| 1141 case STM_FOR: | |
| 1142 case STM_FOR_VAR: | |
| 1143 if (stm->type == STM_FOR_VAR) { | |
| 1144 cvarinit(J, F, stm->a); | |
| 1145 } else { | |
| 1146 if (stm->a) { | |
| 1147 cexp(J, F, stm->a); | |
| 1148 emit(J, F, OP_POP); | |
| 1149 } | |
| 1150 } | |
| 1151 loop = here(J, F); | |
| 1152 if (stm->b) { | |
| 1153 cexp(J, F, stm->b); | |
| 1154 emitline(J, F, stm); | |
| 1155 end = emitjump(J, F, OP_JFALSE); | |
| 1156 } else { | |
| 1157 end = 0; | |
| 1158 } | |
| 1159 cstm(J, F, stm->d); | |
| 1160 cont = here(J, F); | |
| 1161 if (stm->c) { | |
| 1162 cexp(J, F, stm->c); | |
| 1163 emit(J, F, OP_POP); | |
| 1164 } | |
| 1165 emitline(J, F, stm); | |
| 1166 emitjumpto(J, F, OP_JUMP, loop); | |
| 1167 if (end) | |
| 1168 label(J, F, end); | |
| 1169 labeljumps(J, F, stm, here(J,F), cont); | |
| 1170 break; | |
| 1171 | |
| 1172 case STM_FOR_IN: | |
| 1173 case STM_FOR_IN_VAR: | |
| 1174 cexp(J, F, stm->b); | |
| 1175 emitline(J, F, stm); | |
| 1176 emit(J, F, OP_ITERATOR); | |
| 1177 loop = here(J, F); | |
| 1178 { | |
| 1179 emitline(J, F, stm); | |
| 1180 emit(J, F, OP_NEXTITER); | |
| 1181 end = emitjump(J, F, OP_JFALSE); | |
| 1182 cassignforin(J, F, stm); | |
| 1183 if (F->script) { | |
| 1184 emit(J, F, OP_ROT2); | |
| 1185 cstm(J, F, stm->c); | |
| 1186 emit(J, F, OP_ROT2); | |
| 1187 } else { | |
| 1188 cstm(J, F, stm->c); | |
| 1189 } | |
| 1190 emitline(J, F, stm); | |
| 1191 emitjumpto(J, F, OP_JUMP, loop); | |
| 1192 } | |
| 1193 label(J, F, end); | |
| 1194 labeljumps(J, F, stm, here(J,F), loop); | |
| 1195 break; | |
| 1196 | |
| 1197 case STM_SWITCH: | |
| 1198 cswitch(J, F, stm->a, stm->b); | |
| 1199 labeljumps(J, F, stm, here(J,F), 0); | |
| 1200 break; | |
| 1201 | |
| 1202 case STM_LABEL: | |
| 1203 cstm(J, F, stm->b); | |
| 1204 /* skip consecutive labels */ | |
| 1205 while (stm->type == STM_LABEL) | |
| 1206 stm = stm->b; | |
| 1207 /* loops and switches have already been labelled */ | |
| 1208 if (!isloop(stm->type) && stm->type != STM_SWITCH) | |
| 1209 labeljumps(J, F, stm, here(J,F), 0); | |
| 1210 break; | |
| 1211 | |
| 1212 case STM_BREAK: | |
| 1213 if (stm->a) { | |
| 1214 checkfutureword(J, F, stm->a); | |
| 1215 target = breaktarget(J, F, stm->parent, stm->a->string); | |
| 1216 if (!target) | |
| 1217 jsC_error(J, stm, "break label '%s' not found", stm->a->string); | |
| 1218 } else { | |
| 1219 target = breaktarget(J, F, stm->parent, NULL); | |
| 1220 if (!target) | |
| 1221 jsC_error(J, stm, "unlabelled break must be inside loop or switch"); | |
| 1222 } | |
| 1223 cexit(J, F, STM_BREAK, stm, target); | |
| 1224 emitline(J, F, stm); | |
| 1225 addjump(J, F, STM_BREAK, target, emitjump(J, F, OP_JUMP)); | |
| 1226 break; | |
| 1227 | |
| 1228 case STM_CONTINUE: | |
| 1229 if (stm->a) { | |
| 1230 checkfutureword(J, F, stm->a); | |
| 1231 target = continuetarget(J, F, stm->parent, stm->a->string); | |
| 1232 if (!target) | |
| 1233 jsC_error(J, stm, "continue label '%s' not found", stm->a->string); | |
| 1234 } else { | |
| 1235 target = continuetarget(J, F, stm->parent, NULL); | |
| 1236 if (!target) | |
| 1237 jsC_error(J, stm, "continue must be inside loop"); | |
| 1238 } | |
| 1239 cexit(J, F, STM_CONTINUE, stm, target); | |
| 1240 emitline(J, F, stm); | |
| 1241 addjump(J, F, STM_CONTINUE, target, emitjump(J, F, OP_JUMP)); | |
| 1242 break; | |
| 1243 | |
| 1244 case STM_RETURN: | |
| 1245 if (stm->a) | |
| 1246 cexp(J, F, stm->a); | |
| 1247 else | |
| 1248 emit(J, F, OP_UNDEF); | |
| 1249 target = returntarget(J, F, stm->parent); | |
| 1250 if (!target) | |
| 1251 jsC_error(J, stm, "return not in function"); | |
| 1252 cexit(J, F, STM_RETURN, stm, target); | |
| 1253 emitline(J, F, stm); | |
| 1254 emit(J, F, OP_RETURN); | |
| 1255 break; | |
| 1256 | |
| 1257 case STM_THROW: | |
| 1258 cexp(J, F, stm->a); | |
| 1259 emitline(J, F, stm); | |
| 1260 emit(J, F, OP_THROW); | |
| 1261 break; | |
| 1262 | |
| 1263 case STM_WITH: | |
| 1264 F->lightweight = 0; | |
| 1265 if (F->strict) | |
| 1266 jsC_error(J, stm->a, "'with' statements are not allowed in strict mode"); | |
| 1267 cexp(J, F, stm->a); | |
| 1268 emitline(J, F, stm); | |
| 1269 emit(J, F, OP_WITH); | |
| 1270 cstm(J, F, stm->b); | |
| 1271 emitline(J, F, stm); | |
| 1272 emit(J, F, OP_ENDWITH); | |
| 1273 break; | |
| 1274 | |
| 1275 case STM_TRY: | |
| 1276 emitline(J, F, stm); | |
| 1277 if (stm->b && stm->c) { | |
| 1278 F->lightweight = 0; | |
| 1279 if (stm->d) | |
| 1280 ctrycatchfinally(J, F, stm->a, stm->b, stm->c, stm->d); | |
| 1281 else | |
| 1282 ctrycatch(J, F, stm->a, stm->b, stm->c); | |
| 1283 } else { | |
| 1284 ctryfinally(J, F, stm->a, stm->d); | |
| 1285 } | |
| 1286 break; | |
| 1287 | |
| 1288 case STM_DEBUGGER: | |
| 1289 emitline(J, F, stm); | |
| 1290 emit(J, F, OP_DEBUGGER); | |
| 1291 break; | |
| 1292 | |
| 1293 default: | |
| 1294 if (F->script) { | |
| 1295 emitline(J, F, stm); | |
| 1296 emit(J, F, OP_POP); | |
| 1297 cexp(J, F, stm); | |
| 1298 } else { | |
| 1299 cexp(J, F, stm); | |
| 1300 emitline(J, F, stm); | |
| 1301 emit(J, F, OP_POP); | |
| 1302 } | |
| 1303 break; | |
| 1304 } | |
| 1305 } | |
| 1306 | |
| 1307 static void cstmlist(JF, js_Ast *list) | |
| 1308 { | |
| 1309 while (list) { | |
| 1310 cstm(J, F, list->a); | |
| 1311 list = list->b; | |
| 1312 } | |
| 1313 } | |
| 1314 | |
| 1315 /* Declarations and programs */ | |
| 1316 | |
| 1317 static int listlength(js_Ast *list) | |
| 1318 { | |
| 1319 int n = 0; | |
| 1320 while (list) ++n, list = list->b; | |
| 1321 return n; | |
| 1322 } | |
| 1323 | |
| 1324 static void cparams(JF, js_Ast *list, js_Ast *fname) | |
| 1325 { | |
| 1326 F->numparams = listlength(list); | |
| 1327 while (list) { | |
| 1328 checkfutureword(J, F, list->a); | |
| 1329 addlocal(J, F, list->a, 0); | |
| 1330 list = list->b; | |
| 1331 } | |
| 1332 } | |
| 1333 | |
| 1334 static void cvardecs(JF, js_Ast *node) | |
| 1335 { | |
| 1336 if (node->type == AST_LIST) { | |
| 1337 while (node) { | |
| 1338 cvardecs(J, F, node->a); | |
| 1339 node = node->b; | |
| 1340 } | |
| 1341 return; | |
| 1342 } | |
| 1343 | |
| 1344 if (isfun(node->type)) | |
| 1345 return; /* stop at inner functions */ | |
| 1346 | |
| 1347 if (node->type == EXP_VAR) { | |
| 1348 checkfutureword(J, F, node->a); | |
| 1349 addlocal(J, F, node->a, 1); | |
| 1350 } | |
| 1351 | |
| 1352 if (node->a) cvardecs(J, F, node->a); | |
| 1353 if (node->b) cvardecs(J, F, node->b); | |
| 1354 if (node->c) cvardecs(J, F, node->c); | |
| 1355 if (node->d) cvardecs(J, F, node->d); | |
| 1356 } | |
| 1357 | |
| 1358 static void cfundecs(JF, js_Ast *list) | |
| 1359 { | |
| 1360 while (list) { | |
| 1361 js_Ast *stm = list->a; | |
| 1362 if (stm->type == AST_FUNDEC) { | |
| 1363 emitline(J, F, stm); | |
| 1364 emitfunction(J, F, newfun(J, stm->line, stm->a, stm->b, stm->c, 0, F->strict, 0)); | |
| 1365 emitline(J, F, stm); | |
| 1366 emit(J, F, OP_SETLOCAL); | |
| 1367 emitarg(J, F, addlocal(J, F, stm->a, 1)); | |
| 1368 emit(J, F, OP_POP); | |
| 1369 } | |
| 1370 list = list->b; | |
| 1371 } | |
| 1372 } | |
| 1373 | |
| 1374 static void cfunbody(JF, js_Ast *name, js_Ast *params, js_Ast *body, int is_fun_exp) | |
| 1375 { | |
| 1376 F->lightweight = 1; | |
| 1377 F->arguments = 0; | |
| 1378 | |
| 1379 if (F->script) | |
| 1380 F->lightweight = 0; | |
| 1381 | |
| 1382 /* Check if first statement is 'use strict': */ | |
| 1383 if (body && body->type == AST_LIST && body->a && body->a->type == EXP_STRING) | |
| 1384 if (!strcmp(body->a->string, "use strict")) | |
| 1385 F->strict = 1; | |
| 1386 | |
| 1387 F->lastline = F->line; | |
| 1388 | |
| 1389 cparams(J, F, params, name); | |
| 1390 | |
| 1391 if (body) { | |
| 1392 cvardecs(J, F, body); | |
| 1393 cfundecs(J, F, body); | |
| 1394 } | |
| 1395 | |
| 1396 if (name) { | |
| 1397 checkfutureword(J, F, name); | |
| 1398 if (is_fun_exp) { | |
| 1399 if (findlocal(J, F, name->string) < 0) { | |
| 1400 /* TODO: make this binding immutable! */ | |
| 1401 emit(J, F, OP_CURRENT); | |
| 1402 emit(J, F, OP_SETLOCAL); | |
| 1403 emitarg(J, F, addlocal(J, F, name, 1)); | |
| 1404 emit(J, F, OP_POP); | |
| 1405 } | |
| 1406 } | |
| 1407 } | |
| 1408 | |
| 1409 if (F->script) { | |
| 1410 emit(J, F, OP_UNDEF); | |
| 1411 cstmlist(J, F, body); | |
| 1412 emit(J, F, OP_RETURN); | |
| 1413 } else { | |
| 1414 cstmlist(J, F, body); | |
| 1415 emit(J, F, OP_UNDEF); | |
| 1416 emit(J, F, OP_RETURN); | |
| 1417 } | |
| 1418 } | |
| 1419 | |
| 1420 js_Function *jsC_compilefunction(js_State *J, js_Ast *prog) | |
| 1421 { | |
| 1422 return newfun(J, prog->line, prog->a, prog->b, prog->c, 0, J->default_strict, 1); | |
| 1423 } | |
| 1424 | |
| 1425 js_Function *jsC_compilescript(js_State *J, js_Ast *prog, int default_strict) | |
| 1426 { | |
| 1427 return newfun(J, prog ? prog->line : 0, NULL, NULL, prog, 1, default_strict, 0); | |
| 1428 } |
