| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428 |
- #include "jsi.h"
- #define cexp jsC_cexp /* collision with math.h */
- #define JF js_State *J, js_Function *F
- JS_NORETURN void jsC_error(js_State *J, js_Ast *node, const char *fmt, ...) JS_PRINTFLIKE(3,4);
- static void cfunbody(JF, js_Ast *name, js_Ast *params, js_Ast *body, int is_fun_exp);
- static void cexp(JF, js_Ast *exp);
- static void cstmlist(JF, js_Ast *list);
- static void cstm(JF, js_Ast *stm);
- void jsC_error(js_State *J, js_Ast *node, const char *fmt, ...)
- {
- va_list ap;
- char buf[512];
- char msgbuf[256];
- va_start(ap, fmt);
- vsnprintf(msgbuf, 256, fmt, ap);
- va_end(ap);
- snprintf(buf, 256, "%s:%d: ", J->filename, node->line);
- strcat(buf, msgbuf);
- js_newsyntaxerror(J, buf);
- js_throw(J);
- }
- static const char *futurewords[] = {
- "class", "const", "enum", "export", "extends", "import", "super",
- };
- static const char *strictfuturewords[] = {
- "implements", "interface", "let", "package", "private", "protected",
- "public", "static", "yield",
- };
- static void checkfutureword(JF, js_Ast *exp)
- {
- if (jsY_findword(exp->string, futurewords, nelem(futurewords)) >= 0)
- jsC_error(J, exp, "'%s' is a future reserved word", exp->string);
- if (F->strict) {
- if (jsY_findword(exp->string, strictfuturewords, nelem(strictfuturewords)) >= 0)
- jsC_error(J, exp, "'%s' is a strict mode future reserved word", exp->string);
- }
- }
- 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)
- {
- js_Function *F = js_malloc(J, sizeof *F);
- memset(F, 0, sizeof *F);
- F->gcmark = 0;
- F->gcnext = J->gcfun;
- J->gcfun = F;
- ++J->gccounter;
- F->filename = js_intern(J, J->filename);
- F->line = line;
- F->script = script;
- F->strict = default_strict;
- F->name = name ? name->string : "";
- cfunbody(J, F, name, params, body, is_fun_exp);
- return F;
- }
- /* Emit opcodes, constants and jumps */
- static void emitraw(JF, int value)
- {
- if (value != (js_Instruction)value)
- js_syntaxerror(J, "integer overflow in instruction coding");
- if (F->codelen >= F->codecap) {
- F->codecap = F->codecap ? F->codecap * 2 : 64;
- F->code = js_realloc(J, F->code, F->codecap * sizeof *F->code);
- }
- F->code[F->codelen++] = value;
- }
- static void emit(JF, int value)
- {
- emitraw(J, F, F->lastline);
- emitraw(J, F, value);
- }
- static void emitarg(JF, int value)
- {
- emitraw(J, F, value);
- }
- static void emitline(JF, js_Ast *node)
- {
- F->lastline = node->line;
- }
- static int addfunction(JF, js_Function *value)
- {
- if (F->funlen >= F->funcap) {
- F->funcap = F->funcap ? F->funcap * 2 : 16;
- F->funtab = js_realloc(J, F->funtab, F->funcap * sizeof *F->funtab);
- }
- F->funtab[F->funlen] = value;
- return F->funlen++;
- }
- static int addlocal(JF, js_Ast *ident, int reuse)
- {
- const char *name = ident->string;
- if (F->strict) {
- if (!strcmp(name, "arguments"))
- jsC_error(J, ident, "redefining 'arguments' is not allowed in strict mode");
- if (!strcmp(name, "eval"))
- jsC_error(J, ident, "redefining 'eval' is not allowed in strict mode");
- } else {
- if (!strcmp(name, "eval"))
- js_evalerror(J, "%s:%d: invalid use of 'eval'", J->filename, ident->line);
- }
- if (reuse || F->strict) {
- int i;
- for (i = 0; i < F->varlen; ++i) {
- if (!strcmp(F->vartab[i], name)) {
- if (reuse)
- return i+1;
- if (F->strict)
- jsC_error(J, ident, "duplicate formal parameter '%s'", name);
- }
- }
- }
- if (F->varlen >= F->varcap) {
- F->varcap = F->varcap ? F->varcap * 2 : 16;
- F->vartab = js_realloc(J, F->vartab, F->varcap * sizeof *F->vartab);
- }
- F->vartab[F->varlen] = name;
- return ++F->varlen;
- }
- static int findlocal(JF, const char *name)
- {
- int i;
- for (i = F->varlen; i > 0; --i)
- if (!strcmp(F->vartab[i-1], name))
- return i;
- return -1;
- }
- static void emitfunction(JF, js_Function *fun)
- {
- F->lightweight = 0;
- emit(J, F, OP_CLOSURE);
- emitarg(J, F, addfunction(J, F, fun));
- }
- static void emitnumber(JF, double num)
- {
- if (num == 0) {
- emit(J, F, OP_INTEGER);
- emitarg(J, F, 32768);
- if (signbit(num))
- emit(J, F, OP_NEG);
- } else if (num >= SHRT_MIN && num <= SHRT_MAX && num == (int)num) {
- emit(J, F, OP_INTEGER);
- emitarg(J, F, num + 32768);
- } else {
- #define N (sizeof(num) / sizeof(js_Instruction))
- js_Instruction x[N];
- size_t i;
- emit(J, F, OP_NUMBER);
- memcpy(x, &num, sizeof(num));
- for (i = 0; i < N; ++i)
- emitarg(J, F, x[i]);
- #undef N
- }
- }
- static void emitstring(JF, int opcode, const char *str)
- {
- #define N (sizeof(str) / sizeof(js_Instruction))
- js_Instruction x[N];
- size_t i;
- emit(J, F, opcode);
- memcpy(x, &str, sizeof(str));
- for (i = 0; i < N; ++i)
- emitarg(J, F, x[i]);
- #undef N
- }
- static void emitlocal(JF, int oploc, int opvar, js_Ast *ident)
- {
- int is_arguments = !strcmp(ident->string, "arguments");
- int is_eval = !strcmp(ident->string, "eval");
- int i;
- if (is_arguments) {
- F->lightweight = 0;
- F->arguments = 1;
- }
- checkfutureword(J, F, ident);
- if (F->strict && oploc == OP_SETLOCAL) {
- if (is_arguments)
- jsC_error(J, ident, "'arguments' is read-only in strict mode");
- if (is_eval)
- jsC_error(J, ident, "'eval' is read-only in strict mode");
- }
- if (is_eval)
- js_evalerror(J, "%s:%d: invalid use of 'eval'", J->filename, ident->line);
- i = findlocal(J, F, ident->string);
- if (i < 0) {
- emitstring(J, F, opvar, ident->string);
- } else {
- emit(J, F, oploc);
- emitarg(J, F, i);
- }
- }
- static int here(JF)
- {
- return F->codelen;
- }
- static int emitjump(JF, int opcode)
- {
- int inst;
- emit(J, F, opcode);
- inst = F->codelen;
- emitarg(J, F, 0);
- return inst;
- }
- static void emitjumpto(JF, int opcode, int dest)
- {
- emit(J, F, opcode);
- if (dest != (js_Instruction)dest)
- js_syntaxerror(J, "jump address integer overflow");
- emitarg(J, F, dest);
- }
- static void labelto(JF, int inst, int addr)
- {
- if (addr != (js_Instruction)addr)
- js_syntaxerror(J, "jump address integer overflow");
- F->code[inst] = addr;
- }
- static void label(JF, int inst)
- {
- labelto(J, F, inst, F->codelen);
- }
- /* Expressions */
- static void ctypeof(JF, js_Ast *exp)
- {
- if (exp->a->type == EXP_IDENTIFIER) {
- emitline(J, F, exp->a);
- emitlocal(J, F, OP_GETLOCAL, OP_HASVAR, exp->a);
- } else {
- cexp(J, F, exp->a);
- }
- emitline(J, F, exp);
- emit(J, F, OP_TYPEOF);
- }
- static void cunary(JF, js_Ast *exp, int opcode)
- {
- cexp(J, F, exp->a);
- emitline(J, F, exp);
- emit(J, F, opcode);
- }
- static void cbinary(JF, js_Ast *exp, int opcode)
- {
- cexp(J, F, exp->a);
- cexp(J, F, exp->b);
- emitline(J, F, exp);
- emit(J, F, opcode);
- }
- static void carray(JF, js_Ast *list)
- {
- while (list) {
- emitline(J, F, list->a);
- if (list->a->type == EXP_ELISION) {
- emit(J, F, OP_SKIPARRAY);
- } else {
- cexp(J, F, list->a);
- emit(J, F, OP_INITARRAY);
- }
- list = list->b;
- }
- }
- static void checkdup(JF, js_Ast *list, js_Ast *end)
- {
- char nbuf[32], sbuf[32];
- const char *needle, *straw;
- if (end->a->type == EXP_NUMBER)
- needle = jsV_numbertostring(J, nbuf, end->a->number);
- else
- needle = end->a->string;
- while (list->a != end) {
- if (list->a->type == end->type) {
- js_Ast *prop = list->a->a;
- if (prop->type == EXP_NUMBER)
- straw = jsV_numbertostring(J, sbuf, prop->number);
- else
- straw = prop->string;
- if (!strcmp(needle, straw))
- jsC_error(J, list, "duplicate property '%s' in object literal", needle);
- }
- list = list->b;
- }
- }
- static void cobject(JF, js_Ast *list)
- {
- js_Ast *head = list;
- while (list) {
- js_Ast *kv = list->a;
- js_Ast *prop = kv->a;
- if (prop->type == AST_IDENTIFIER || prop->type == EXP_STRING) {
- emitline(J, F, prop);
- emitstring(J, F, OP_STRING, prop->string);
- } else if (prop->type == EXP_NUMBER) {
- emitline(J, F, prop);
- emitnumber(J, F, prop->number);
- } else {
- jsC_error(J, prop, "invalid property name in object initializer");
- }
- if (F->strict)
- checkdup(J, F, head, kv);
- switch (kv->type) {
- default: /* impossible */ break;
- case EXP_PROP_VAL:
- cexp(J, F, kv->b);
- emitline(J, F, kv);
- emit(J, F, OP_INITPROP);
- break;
- case EXP_PROP_GET:
- emitfunction(J, F, newfun(J, prop->line, NULL, NULL, kv->c, 0, F->strict, 1));
- emitline(J, F, kv);
- emit(J, F, OP_INITGETTER);
- break;
- case EXP_PROP_SET:
- emitfunction(J, F, newfun(J, prop->line, NULL, kv->b, kv->c, 0, F->strict, 1));
- emitline(J, F, kv);
- emit(J, F, OP_INITSETTER);
- break;
- }
- list = list->b;
- }
- }
- static int cargs(JF, js_Ast *list)
- {
- int n = 0;
- while (list) {
- cexp(J, F, list->a);
- list = list->b;
- ++n;
- }
- return n;
- }
- static void cassign(JF, js_Ast *exp)
- {
- js_Ast *lhs = exp->a;
- js_Ast *rhs = exp->b;
- switch (lhs->type) {
- case EXP_IDENTIFIER:
- cexp(J, F, rhs);
- emitline(J, F, exp);
- emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs);
- break;
- case EXP_INDEX:
- cexp(J, F, lhs->a);
- cexp(J, F, lhs->b);
- cexp(J, F, rhs);
- emitline(J, F, exp);
- emit(J, F, OP_SETPROP);
- break;
- case EXP_MEMBER:
- cexp(J, F, lhs->a);
- cexp(J, F, rhs);
- emitline(J, F, exp);
- emitstring(J, F, OP_SETPROP_S, lhs->b->string);
- break;
- default:
- jsC_error(J, lhs, "invalid l-value in assignment");
- }
- }
- static void cassignforin(JF, js_Ast *stm)
- {
- js_Ast *lhs = stm->a;
- if (stm->type == STM_FOR_IN_VAR) {
- if (lhs->b)
- jsC_error(J, lhs->b, "more than one loop variable in for-in statement");
- emitline(J, F, lhs->a);
- emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs->a->a); /* list(var-init(ident)) */
- emit(J, F, OP_POP);
- return;
- }
- switch (lhs->type) {
- case EXP_IDENTIFIER:
- emitline(J, F, lhs);
- emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs);
- emit(J, F, OP_POP);
- break;
- case EXP_INDEX:
- cexp(J, F, lhs->a);
- cexp(J, F, lhs->b);
- emitline(J, F, lhs);
- emit(J, F, OP_ROT3);
- emit(J, F, OP_SETPROP);
- emit(J, F, OP_POP);
- break;
- case EXP_MEMBER:
- cexp(J, F, lhs->a);
- emitline(J, F, lhs);
- emit(J, F, OP_ROT2);
- emitstring(J, F, OP_SETPROP_S, lhs->b->string);
- emit(J, F, OP_POP);
- break;
- default:
- jsC_error(J, lhs, "invalid l-value in for-in loop assignment");
- }
- }
- static void cassignop1(JF, js_Ast *lhs)
- {
- switch (lhs->type) {
- case EXP_IDENTIFIER:
- emitline(J, F, lhs);
- emitlocal(J, F, OP_GETLOCAL, OP_GETVAR, lhs);
- break;
- case EXP_INDEX:
- cexp(J, F, lhs->a);
- cexp(J, F, lhs->b);
- emitline(J, F, lhs);
- emit(J, F, OP_DUP2);
- emit(J, F, OP_GETPROP);
- break;
- case EXP_MEMBER:
- cexp(J, F, lhs->a);
- emitline(J, F, lhs);
- emit(J, F, OP_DUP);
- emitstring(J, F, OP_GETPROP_S, lhs->b->string);
- break;
- default:
- jsC_error(J, lhs, "invalid l-value in assignment");
- }
- }
- static void cassignop2(JF, js_Ast *lhs, int postfix)
- {
- switch (lhs->type) {
- case EXP_IDENTIFIER:
- emitline(J, F, lhs);
- if (postfix) emit(J, F, OP_ROT2);
- emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs);
- break;
- case EXP_INDEX:
- emitline(J, F, lhs);
- if (postfix) emit(J, F, OP_ROT4);
- emit(J, F, OP_SETPROP);
- break;
- case EXP_MEMBER:
- emitline(J, F, lhs);
- if (postfix) emit(J, F, OP_ROT3);
- emitstring(J, F, OP_SETPROP_S, lhs->b->string);
- break;
- default:
- jsC_error(J, lhs, "invalid l-value in assignment");
- }
- }
- static void cassignop(JF, js_Ast *exp, int opcode)
- {
- js_Ast *lhs = exp->a;
- js_Ast *rhs = exp->b;
- cassignop1(J, F, lhs);
- cexp(J, F, rhs);
- emitline(J, F, exp);
- emit(J, F, opcode);
- cassignop2(J, F, lhs, 0);
- }
- static void cdelete(JF, js_Ast *exp)
- {
- js_Ast *arg = exp->a;
- switch (arg->type) {
- case EXP_IDENTIFIER:
- if (F->strict)
- jsC_error(J, exp, "delete on an unqualified name is not allowed in strict mode");
- emitline(J, F, exp);
- emitlocal(J, F, OP_DELLOCAL, OP_DELVAR, arg);
- break;
- case EXP_INDEX:
- cexp(J, F, arg->a);
- cexp(J, F, arg->b);
- emitline(J, F, exp);
- emit(J, F, OP_DELPROP);
- break;
- case EXP_MEMBER:
- cexp(J, F, arg->a);
- emitline(J, F, exp);
- emitstring(J, F, OP_DELPROP_S, arg->b->string);
- break;
- default:
- jsC_error(J, exp, "invalid l-value in delete expression");
- }
- }
- static void ceval(JF, js_Ast *fun, js_Ast *args)
- {
- int n = cargs(J, F, args);
- F->lightweight = 0;
- F->arguments = 1;
- if (n == 0)
- emit(J, F, OP_UNDEF);
- else while (n-- > 1)
- emit(J, F, OP_POP);
- emit(J, F, OP_EVAL);
- }
- static void ccall(JF, js_Ast *fun, js_Ast *args)
- {
- int n;
- switch (fun->type) {
- case EXP_INDEX:
- cexp(J, F, fun->a);
- emit(J, F, OP_DUP);
- cexp(J, F, fun->b);
- emit(J, F, OP_GETPROP);
- emit(J, F, OP_ROT2);
- break;
- case EXP_MEMBER:
- cexp(J, F, fun->a);
- emit(J, F, OP_DUP);
- emitstring(J, F, OP_GETPROP_S, fun->b->string);
- emit(J, F, OP_ROT2);
- break;
- case EXP_IDENTIFIER:
- if (!strcmp(fun->string, "eval")) {
- ceval(J, F, fun, args);
- return;
- }
- /* fallthrough */
- default:
- cexp(J, F, fun);
- emit(J, F, OP_UNDEF);
- break;
- }
- n = cargs(J, F, args);
- emit(J, F, OP_CALL);
- emitarg(J, F, n);
- }
- static void cexp(JF, js_Ast *exp)
- {
- int then, end;
- int n;
- switch (exp->type) {
- case EXP_STRING:
- emitline(J, F, exp);
- emitstring(J, F, OP_STRING, exp->string);
- break;
- case EXP_NUMBER:
- emitline(J, F, exp);
- emitnumber(J, F, exp->number);
- break;
- case EXP_ELISION:
- break;
- case EXP_NULL:
- emitline(J, F, exp);
- emit(J, F, OP_NULL);
- break;
- case EXP_TRUE:
- emitline(J, F, exp);
- emit(J, F, OP_TRUE);
- break;
- case EXP_FALSE:
- emitline(J, F, exp);
- emit(J, F, OP_FALSE);
- break;
- case EXP_THIS:
- emitline(J, F, exp);
- emit(J, F, OP_THIS);
- break;
- case EXP_REGEXP:
- emitline(J, F, exp);
- emitstring(J, F, OP_NEWREGEXP, exp->string);
- emitarg(J, F, exp->number);
- break;
- case EXP_OBJECT:
- emitline(J, F, exp);
- emit(J, F, OP_NEWOBJECT);
- cobject(J, F, exp->a);
- break;
- case EXP_ARRAY:
- emitline(J, F, exp);
- emit(J, F, OP_NEWARRAY);
- carray(J, F, exp->a);
- break;
- case EXP_FUN:
- emitline(J, F, exp);
- emitfunction(J, F, newfun(J, exp->line, exp->a, exp->b, exp->c, 0, F->strict, 1));
- break;
- case EXP_IDENTIFIER:
- emitline(J, F, exp);
- emitlocal(J, F, OP_GETLOCAL, OP_GETVAR, exp);
- break;
- case EXP_INDEX:
- cexp(J, F, exp->a);
- cexp(J, F, exp->b);
- emitline(J, F, exp);
- emit(J, F, OP_GETPROP);
- break;
- case EXP_MEMBER:
- cexp(J, F, exp->a);
- emitline(J, F, exp);
- emitstring(J, F, OP_GETPROP_S, exp->b->string);
- break;
- case EXP_CALL:
- ccall(J, F, exp->a, exp->b);
- break;
- case EXP_NEW:
- cexp(J, F, exp->a);
- n = cargs(J, F, exp->b);
- emitline(J, F, exp);
- emit(J, F, OP_NEW);
- emitarg(J, F, n);
- break;
- case EXP_DELETE:
- cdelete(J, F, exp);
- break;
- case EXP_PREINC:
- cassignop1(J, F, exp->a);
- emitline(J, F, exp);
- emit(J, F, OP_INC);
- cassignop2(J, F, exp->a, 0);
- break;
- case EXP_PREDEC:
- cassignop1(J, F, exp->a);
- emitline(J, F, exp);
- emit(J, F, OP_DEC);
- cassignop2(J, F, exp->a, 0);
- break;
- case EXP_POSTINC:
- cassignop1(J, F, exp->a);
- emitline(J, F, exp);
- emit(J, F, OP_POSTINC);
- cassignop2(J, F, exp->a, 1);
- emit(J, F, OP_POP);
- break;
- case EXP_POSTDEC:
- cassignop1(J, F, exp->a);
- emitline(J, F, exp);
- emit(J, F, OP_POSTDEC);
- cassignop2(J, F, exp->a, 1);
- emit(J, F, OP_POP);
- break;
- case EXP_VOID:
- cexp(J, F, exp->a);
- emitline(J, F, exp);
- emit(J, F, OP_POP);
- emit(J, F, OP_UNDEF);
- break;
- case EXP_TYPEOF: ctypeof(J, F, exp); break;
- case EXP_POS: cunary(J, F, exp, OP_POS); break;
- case EXP_NEG: cunary(J, F, exp, OP_NEG); break;
- case EXP_BITNOT: cunary(J, F, exp, OP_BITNOT); break;
- case EXP_LOGNOT: cunary(J, F, exp, OP_LOGNOT); break;
- case EXP_BITOR: cbinary(J, F, exp, OP_BITOR); break;
- case EXP_BITXOR: cbinary(J, F, exp, OP_BITXOR); break;
- case EXP_BITAND: cbinary(J, F, exp, OP_BITAND); break;
- case EXP_EQ: cbinary(J, F, exp, OP_EQ); break;
- case EXP_NE: cbinary(J, F, exp, OP_NE); break;
- case EXP_STRICTEQ: cbinary(J, F, exp, OP_STRICTEQ); break;
- case EXP_STRICTNE: cbinary(J, F, exp, OP_STRICTNE); break;
- case EXP_LT: cbinary(J, F, exp, OP_LT); break;
- case EXP_GT: cbinary(J, F, exp, OP_GT); break;
- case EXP_LE: cbinary(J, F, exp, OP_LE); break;
- case EXP_GE: cbinary(J, F, exp, OP_GE); break;
- case EXP_INSTANCEOF: cbinary(J, F, exp, OP_INSTANCEOF); break;
- case EXP_IN: cbinary(J, F, exp, OP_IN); break;
- case EXP_SHL: cbinary(J, F, exp, OP_SHL); break;
- case EXP_SHR: cbinary(J, F, exp, OP_SHR); break;
- case EXP_USHR: cbinary(J, F, exp, OP_USHR); break;
- case EXP_ADD: cbinary(J, F, exp, OP_ADD); break;
- case EXP_SUB: cbinary(J, F, exp, OP_SUB); break;
- case EXP_MUL: cbinary(J, F, exp, OP_MUL); break;
- case EXP_DIV: cbinary(J, F, exp, OP_DIV); break;
- case EXP_MOD: cbinary(J, F, exp, OP_MOD); break;
- case EXP_ASS: cassign(J, F, exp); break;
- case EXP_ASS_MUL: cassignop(J, F, exp, OP_MUL); break;
- case EXP_ASS_DIV: cassignop(J, F, exp, OP_DIV); break;
- case EXP_ASS_MOD: cassignop(J, F, exp, OP_MOD); break;
- case EXP_ASS_ADD: cassignop(J, F, exp, OP_ADD); break;
- case EXP_ASS_SUB: cassignop(J, F, exp, OP_SUB); break;
- case EXP_ASS_SHL: cassignop(J, F, exp, OP_SHL); break;
- case EXP_ASS_SHR: cassignop(J, F, exp, OP_SHR); break;
- case EXP_ASS_USHR: cassignop(J, F, exp, OP_USHR); break;
- case EXP_ASS_BITAND: cassignop(J, F, exp, OP_BITAND); break;
- case EXP_ASS_BITXOR: cassignop(J, F, exp, OP_BITXOR); break;
- case EXP_ASS_BITOR: cassignop(J, F, exp, OP_BITOR); break;
- case EXP_COMMA:
- cexp(J, F, exp->a);
- emitline(J, F, exp);
- emit(J, F, OP_POP);
- cexp(J, F, exp->b);
- break;
- case EXP_LOGOR:
- cexp(J, F, exp->a);
- emitline(J, F, exp);
- emit(J, F, OP_DUP);
- end = emitjump(J, F, OP_JTRUE);
- emit(J, F, OP_POP);
- cexp(J, F, exp->b);
- label(J, F, end);
- break;
- case EXP_LOGAND:
- cexp(J, F, exp->a);
- emitline(J, F, exp);
- emit(J, F, OP_DUP);
- end = emitjump(J, F, OP_JFALSE);
- emit(J, F, OP_POP);
- cexp(J, F, exp->b);
- label(J, F, end);
- break;
- case EXP_COND:
- cexp(J, F, exp->a);
- emitline(J, F, exp);
- then = emitjump(J, F, OP_JTRUE);
- cexp(J, F, exp->c);
- end = emitjump(J, F, OP_JUMP);
- label(J, F, then);
- cexp(J, F, exp->b);
- label(J, F, end);
- break;
- default:
- jsC_error(J, exp, "unknown expression type");
- }
- }
- /* Patch break and continue statements */
- static void addjump(JF, enum js_AstType type, js_Ast *target, int inst)
- {
- js_JumpList *jump = js_malloc(J, sizeof *jump);
- jump->type = type;
- jump->inst = inst;
- jump->next = target->jumps;
- target->jumps = jump;
- }
- static void labeljumps(JF, js_Ast *stm, int baddr, int caddr)
- {
- js_JumpList *jump = stm->jumps;
- while (jump) {
- js_JumpList *next = jump->next;
- if (jump->type == STM_BREAK)
- labelto(J, F, jump->inst, baddr);
- if (jump->type == STM_CONTINUE)
- labelto(J, F, jump->inst, caddr);
- js_free(J, jump);
- jump = next;
- }
- stm->jumps = NULL;
- }
- static int isloop(enum js_AstType T)
- {
- return T == STM_DO || T == STM_WHILE ||
- T == STM_FOR || T == STM_FOR_VAR ||
- T == STM_FOR_IN || T == STM_FOR_IN_VAR;
- }
- static int isfun(enum js_AstType T)
- {
- return T == AST_FUNDEC || T == EXP_FUN || T == EXP_PROP_GET || T == EXP_PROP_SET;
- }
- static int matchlabel(js_Ast *node, const char *label)
- {
- while (node && node->type == STM_LABEL) {
- if (!strcmp(node->a->string, label))
- return 1;
- node = node->parent;
- }
- return 0;
- }
- static js_Ast *breaktarget(JF, js_Ast *node, const char *label)
- {
- while (node) {
- if (isfun(node->type))
- break;
- if (!label) {
- if (isloop(node->type) || node->type == STM_SWITCH)
- return node;
- } else {
- if (matchlabel(node->parent, label))
- return node;
- }
- node = node->parent;
- }
- return NULL;
- }
- static js_Ast *continuetarget(JF, js_Ast *node, const char *label)
- {
- while (node) {
- if (isfun(node->type))
- break;
- if (isloop(node->type)) {
- if (!label)
- return node;
- else if (matchlabel(node->parent, label))
- return node;
- }
- node = node->parent;
- }
- return NULL;
- }
- static js_Ast *returntarget(JF, js_Ast *node)
- {
- while (node) {
- if (isfun(node->type))
- return node;
- node = node->parent;
- }
- return NULL;
- }
- /* Emit code to rebalance stack and scopes during an abrupt exit */
- static void cexit(JF, enum js_AstType T, js_Ast *node, js_Ast *target)
- {
- js_Ast *prev;
- do {
- prev = node, node = node->parent;
- switch (node->type) {
- default:
- /* impossible */
- break;
- case STM_WITH:
- emitline(J, F, node);
- emit(J, F, OP_ENDWITH);
- break;
- case STM_FOR_IN:
- case STM_FOR_IN_VAR:
- emitline(J, F, node);
- /* pop the iterator if leaving the loop */
- if (F->script) {
- if (T == STM_RETURN || T == STM_BREAK || (T == STM_CONTINUE && target != node)) {
- /* pop the iterator, save the return or exp value */
- emit(J, F, OP_ROT2);
- emit(J, F, OP_POP);
- }
- if (T == STM_CONTINUE)
- emit(J, F, OP_ROT2); /* put the iterator back on top */
- } else {
- if (T == STM_RETURN) {
- /* pop the iterator, save the return value */
- emit(J, F, OP_ROT2);
- emit(J, F, OP_POP);
- }
- if (T == STM_BREAK || (T == STM_CONTINUE && target != node))
- emit(J, F, OP_POP); /* pop the iterator */
- }
- break;
- case STM_TRY:
- emitline(J, F, node);
- /* came from try block */
- if (prev == node->a) {
- emit(J, F, OP_ENDTRY);
- if (node->d) cstm(J, F, node->d); /* finally */
- }
- /* came from catch block */
- if (prev == node->c) {
- /* ... with finally */
- if (node->d) {
- emit(J, F, OP_ENDCATCH);
- emit(J, F, OP_ENDTRY);
- cstm(J, F, node->d); /* finally */
- } else {
- emit(J, F, OP_ENDCATCH);
- }
- }
- break;
- }
- } while (node != target);
- }
- /* Try/catch/finally */
- static void ctryfinally(JF, js_Ast *trystm, js_Ast *finallystm)
- {
- int L1;
- L1 = emitjump(J, F, OP_TRY);
- {
- /* if we get here, we have caught an exception in the try block */
- cstm(J, F, finallystm); /* inline finally block */
- emit(J, F, OP_THROW); /* rethrow exception */
- }
- label(J, F, L1);
- cstm(J, F, trystm);
- emit(J, F, OP_ENDTRY);
- cstm(J, F, finallystm);
- }
- static void ctrycatch(JF, js_Ast *trystm, js_Ast *catchvar, js_Ast *catchstm)
- {
- int L1, L2;
- L1 = emitjump(J, F, OP_TRY);
- {
- /* if we get here, we have caught an exception in the try block */
- checkfutureword(J, F, catchvar);
- if (F->strict) {
- if (!strcmp(catchvar->string, "arguments"))
- jsC_error(J, catchvar, "redefining 'arguments' is not allowed in strict mode");
- if (!strcmp(catchvar->string, "eval"))
- jsC_error(J, catchvar, "redefining 'eval' is not allowed in strict mode");
- }
- emitline(J, F, catchvar);
- emitstring(J, F, OP_CATCH, catchvar->string);
- cstm(J, F, catchstm);
- emit(J, F, OP_ENDCATCH);
- L2 = emitjump(J, F, OP_JUMP); /* skip past the try block */
- }
- label(J, F, L1);
- cstm(J, F, trystm);
- emit(J, F, OP_ENDTRY);
- label(J, F, L2);
- }
- static void ctrycatchfinally(JF, js_Ast *trystm, js_Ast *catchvar, js_Ast *catchstm, js_Ast *finallystm)
- {
- int L1, L2, L3;
- L1 = emitjump(J, F, OP_TRY);
- {
- /* if we get here, we have caught an exception in the try block */
- L2 = emitjump(J, F, OP_TRY);
- {
- /* if we get here, we have caught an exception in the catch block */
- cstm(J, F, finallystm); /* inline finally block */
- emit(J, F, OP_THROW); /* rethrow exception */
- }
- label(J, F, L2);
- if (F->strict) {
- checkfutureword(J, F, catchvar);
- if (!strcmp(catchvar->string, "arguments"))
- jsC_error(J, catchvar, "redefining 'arguments' is not allowed in strict mode");
- if (!strcmp(catchvar->string, "eval"))
- jsC_error(J, catchvar, "redefining 'eval' is not allowed in strict mode");
- }
- emitline(J, F, catchvar);
- emitstring(J, F, OP_CATCH, catchvar->string);
- cstm(J, F, catchstm);
- emit(J, F, OP_ENDCATCH);
- emit(J, F, OP_ENDTRY);
- L3 = emitjump(J, F, OP_JUMP); /* skip past the try block to the finally block */
- }
- label(J, F, L1);
- cstm(J, F, trystm);
- emit(J, F, OP_ENDTRY);
- label(J, F, L3);
- cstm(J, F, finallystm);
- }
- /* Switch */
- static void cswitch(JF, js_Ast *ref, js_Ast *head)
- {
- js_Ast *node, *clause, *def = NULL;
- int end;
- cexp(J, F, ref);
- /* emit an if-else chain of tests for the case clause expressions */
- for (node = head; node; node = node->b) {
- clause = node->a;
- if (clause->type == STM_DEFAULT) {
- if (def)
- jsC_error(J, clause, "more than one default label in switch");
- def = clause;
- } else {
- cexp(J, F, clause->a);
- emitline(J, F, clause);
- clause->casejump = emitjump(J, F, OP_JCASE);
- }
- }
- emit(J, F, OP_POP);
- if (def) {
- emitline(J, F, def);
- def->casejump = emitjump(J, F, OP_JUMP);
- end = 0;
- } else {
- end = emitjump(J, F, OP_JUMP);
- }
- /* emit the case clause bodies */
- for (node = head; node; node = node->b) {
- clause = node->a;
- label(J, F, clause->casejump);
- if (clause->type == STM_DEFAULT)
- cstmlist(J, F, clause->a);
- else
- cstmlist(J, F, clause->b);
- }
- if (end)
- label(J, F, end);
- }
- /* Statements */
- static void cvarinit(JF, js_Ast *list)
- {
- while (list) {
- js_Ast *var = list->a;
- if (var->b) {
- cexp(J, F, var->b);
- emitline(J, F, var);
- emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, var->a);
- emit(J, F, OP_POP);
- }
- list = list->b;
- }
- }
- static void cstm(JF, js_Ast *stm)
- {
- js_Ast *target;
- int loop, cont, then, end;
- emitline(J, F, stm);
- switch (stm->type) {
- case AST_FUNDEC:
- break;
- case STM_BLOCK:
- cstmlist(J, F, stm->a);
- break;
- case STM_EMPTY:
- if (F->script) {
- emitline(J, F, stm);
- emit(J, F, OP_POP);
- emit(J, F, OP_UNDEF);
- }
- break;
- case STM_VAR:
- cvarinit(J, F, stm->a);
- break;
- case STM_IF:
- if (stm->c) {
- cexp(J, F, stm->a);
- emitline(J, F, stm);
- then = emitjump(J, F, OP_JTRUE);
- cstm(J, F, stm->c);
- emitline(J, F, stm);
- end = emitjump(J, F, OP_JUMP);
- label(J, F, then);
- cstm(J, F, stm->b);
- label(J, F, end);
- } else {
- cexp(J, F, stm->a);
- emitline(J, F, stm);
- end = emitjump(J, F, OP_JFALSE);
- cstm(J, F, stm->b);
- label(J, F, end);
- }
- break;
- case STM_DO:
- loop = here(J, F);
- cstm(J, F, stm->a);
- cont = here(J, F);
- cexp(J, F, stm->b);
- emitline(J, F, stm);
- emitjumpto(J, F, OP_JTRUE, loop);
- labeljumps(J, F, stm, here(J,F), cont);
- break;
- case STM_WHILE:
- loop = here(J, F);
- cexp(J, F, stm->a);
- emitline(J, F, stm);
- end = emitjump(J, F, OP_JFALSE);
- cstm(J, F, stm->b);
- emitline(J, F, stm);
- emitjumpto(J, F, OP_JUMP, loop);
- label(J, F, end);
- labeljumps(J, F, stm, here(J,F), loop);
- break;
- case STM_FOR:
- case STM_FOR_VAR:
- if (stm->type == STM_FOR_VAR) {
- cvarinit(J, F, stm->a);
- } else {
- if (stm->a) {
- cexp(J, F, stm->a);
- emit(J, F, OP_POP);
- }
- }
- loop = here(J, F);
- if (stm->b) {
- cexp(J, F, stm->b);
- emitline(J, F, stm);
- end = emitjump(J, F, OP_JFALSE);
- } else {
- end = 0;
- }
- cstm(J, F, stm->d);
- cont = here(J, F);
- if (stm->c) {
- cexp(J, F, stm->c);
- emit(J, F, OP_POP);
- }
- emitline(J, F, stm);
- emitjumpto(J, F, OP_JUMP, loop);
- if (end)
- label(J, F, end);
- labeljumps(J, F, stm, here(J,F), cont);
- break;
- case STM_FOR_IN:
- case STM_FOR_IN_VAR:
- cexp(J, F, stm->b);
- emitline(J, F, stm);
- emit(J, F, OP_ITERATOR);
- loop = here(J, F);
- {
- emitline(J, F, stm);
- emit(J, F, OP_NEXTITER);
- end = emitjump(J, F, OP_JFALSE);
- cassignforin(J, F, stm);
- if (F->script) {
- emit(J, F, OP_ROT2);
- cstm(J, F, stm->c);
- emit(J, F, OP_ROT2);
- } else {
- cstm(J, F, stm->c);
- }
- emitline(J, F, stm);
- emitjumpto(J, F, OP_JUMP, loop);
- }
- label(J, F, end);
- labeljumps(J, F, stm, here(J,F), loop);
- break;
- case STM_SWITCH:
- cswitch(J, F, stm->a, stm->b);
- labeljumps(J, F, stm, here(J,F), 0);
- break;
- case STM_LABEL:
- cstm(J, F, stm->b);
- /* skip consecutive labels */
- while (stm->type == STM_LABEL)
- stm = stm->b;
- /* loops and switches have already been labelled */
- if (!isloop(stm->type) && stm->type != STM_SWITCH)
- labeljumps(J, F, stm, here(J,F), 0);
- break;
- case STM_BREAK:
- if (stm->a) {
- checkfutureword(J, F, stm->a);
- target = breaktarget(J, F, stm->parent, stm->a->string);
- if (!target)
- jsC_error(J, stm, "break label '%s' not found", stm->a->string);
- } else {
- target = breaktarget(J, F, stm->parent, NULL);
- if (!target)
- jsC_error(J, stm, "unlabelled break must be inside loop or switch");
- }
- cexit(J, F, STM_BREAK, stm, target);
- emitline(J, F, stm);
- addjump(J, F, STM_BREAK, target, emitjump(J, F, OP_JUMP));
- break;
- case STM_CONTINUE:
- if (stm->a) {
- checkfutureword(J, F, stm->a);
- target = continuetarget(J, F, stm->parent, stm->a->string);
- if (!target)
- jsC_error(J, stm, "continue label '%s' not found", stm->a->string);
- } else {
- target = continuetarget(J, F, stm->parent, NULL);
- if (!target)
- jsC_error(J, stm, "continue must be inside loop");
- }
- cexit(J, F, STM_CONTINUE, stm, target);
- emitline(J, F, stm);
- addjump(J, F, STM_CONTINUE, target, emitjump(J, F, OP_JUMP));
- break;
- case STM_RETURN:
- if (stm->a)
- cexp(J, F, stm->a);
- else
- emit(J, F, OP_UNDEF);
- target = returntarget(J, F, stm->parent);
- if (!target)
- jsC_error(J, stm, "return not in function");
- cexit(J, F, STM_RETURN, stm, target);
- emitline(J, F, stm);
- emit(J, F, OP_RETURN);
- break;
- case STM_THROW:
- cexp(J, F, stm->a);
- emitline(J, F, stm);
- emit(J, F, OP_THROW);
- break;
- case STM_WITH:
- F->lightweight = 0;
- if (F->strict)
- jsC_error(J, stm->a, "'with' statements are not allowed in strict mode");
- cexp(J, F, stm->a);
- emitline(J, F, stm);
- emit(J, F, OP_WITH);
- cstm(J, F, stm->b);
- emitline(J, F, stm);
- emit(J, F, OP_ENDWITH);
- break;
- case STM_TRY:
- emitline(J, F, stm);
- if (stm->b && stm->c) {
- F->lightweight = 0;
- if (stm->d)
- ctrycatchfinally(J, F, stm->a, stm->b, stm->c, stm->d);
- else
- ctrycatch(J, F, stm->a, stm->b, stm->c);
- } else {
- ctryfinally(J, F, stm->a, stm->d);
- }
- break;
- case STM_DEBUGGER:
- emitline(J, F, stm);
- emit(J, F, OP_DEBUGGER);
- break;
- default:
- if (F->script) {
- emitline(J, F, stm);
- emit(J, F, OP_POP);
- cexp(J, F, stm);
- } else {
- cexp(J, F, stm);
- emitline(J, F, stm);
- emit(J, F, OP_POP);
- }
- break;
- }
- }
- static void cstmlist(JF, js_Ast *list)
- {
- while (list) {
- cstm(J, F, list->a);
- list = list->b;
- }
- }
- /* Declarations and programs */
- static int listlength(js_Ast *list)
- {
- int n = 0;
- while (list) ++n, list = list->b;
- return n;
- }
- static void cparams(JF, js_Ast *list, js_Ast *fname)
- {
- F->numparams = listlength(list);
- while (list) {
- checkfutureword(J, F, list->a);
- addlocal(J, F, list->a, 0);
- list = list->b;
- }
- }
- static void cvardecs(JF, js_Ast *node)
- {
- if (node->type == AST_LIST) {
- while (node) {
- cvardecs(J, F, node->a);
- node = node->b;
- }
- return;
- }
- if (isfun(node->type))
- return; /* stop at inner functions */
- if (node->type == EXP_VAR) {
- checkfutureword(J, F, node->a);
- addlocal(J, F, node->a, 1);
- }
- if (node->a) cvardecs(J, F, node->a);
- if (node->b) cvardecs(J, F, node->b);
- if (node->c) cvardecs(J, F, node->c);
- if (node->d) cvardecs(J, F, node->d);
- }
- static void cfundecs(JF, js_Ast *list)
- {
- while (list) {
- js_Ast *stm = list->a;
- if (stm->type == AST_FUNDEC) {
- emitline(J, F, stm);
- emitfunction(J, F, newfun(J, stm->line, stm->a, stm->b, stm->c, 0, F->strict, 0));
- emitline(J, F, stm);
- emit(J, F, OP_SETLOCAL);
- emitarg(J, F, addlocal(J, F, stm->a, 1));
- emit(J, F, OP_POP);
- }
- list = list->b;
- }
- }
- static void cfunbody(JF, js_Ast *name, js_Ast *params, js_Ast *body, int is_fun_exp)
- {
- F->lightweight = 1;
- F->arguments = 0;
- if (F->script)
- F->lightweight = 0;
- /* Check if first statement is 'use strict': */
- if (body && body->type == AST_LIST && body->a && body->a->type == EXP_STRING)
- if (!strcmp(body->a->string, "use strict"))
- F->strict = 1;
- F->lastline = F->line;
- cparams(J, F, params, name);
- if (body) {
- cvardecs(J, F, body);
- cfundecs(J, F, body);
- }
- if (name) {
- checkfutureword(J, F, name);
- if (is_fun_exp) {
- if (findlocal(J, F, name->string) < 0) {
- /* TODO: make this binding immutable! */
- emit(J, F, OP_CURRENT);
- emit(J, F, OP_SETLOCAL);
- emitarg(J, F, addlocal(J, F, name, 1));
- emit(J, F, OP_POP);
- }
- }
- }
- if (F->script) {
- emit(J, F, OP_UNDEF);
- cstmlist(J, F, body);
- emit(J, F, OP_RETURN);
- } else {
- cstmlist(J, F, body);
- emit(J, F, OP_UNDEF);
- emit(J, F, OP_RETURN);
- }
- }
- js_Function *jsC_compilefunction(js_State *J, js_Ast *prog)
- {
- return newfun(J, prog->line, prog->a, prog->b, prog->c, 0, J->default_strict, 1);
- }
- js_Function *jsC_compilescript(js_State *J, js_Ast *prog, int default_strict)
- {
- return newfun(J, prog ? prog->line : 0, NULL, NULL, prog, 1, default_strict, 0);
- }
|