#include "jsi.h"#include "jslex.h"#include "jsparse.h"#include "jscompile.h"#include "jsvalue.h" /* for jsV_numbertostring */ #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);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){ 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); 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); 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)); 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)); 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);}Lines 1–500 of 1423. Load more
#include "jsi.h" #include "jslex.h" #include "jsparse.h" #include "jscompile.h" #include "jsvalue.h" /* for jsV_numbertostring */ #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); 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) { 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); 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); 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)); 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)); 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); }