diff --git a/jscompile.c b/jscompile.c index 626abb8..1965dab 100644 --- a/jscompile.c +++ b/jscompile.c @@ -242,8 +242,17 @@ static void cassign(JF, js_Ast *lhs, js_Ast *rhs) } } -static void cassignloop(JF, js_Ast *lhs) +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"); + emitstring(J, F, OP_SETVAR, lhs->a->a->string); /* list(var-init(ident)) */ + return; + } + switch (lhs->type) { case AST_IDENTIFIER: emitstring(J, F, OP_SETVAR, lhs->string); @@ -261,7 +270,7 @@ static void cassignloop(JF, js_Ast *lhs) emit(J, F, OP_SETPROP); break; default: - jsC_error(J, lhs, "invalid l-value in assignment"); + jsC_error(J, lhs, "invalid l-value in for-in loop assignment"); break; } } @@ -616,6 +625,38 @@ static js_Ast *continuetarget(JF, js_Ast *node, const char *label) return NULL; } +static js_Ast *returntarget(JF, js_Ast *node) +{ + while (node) { + if (node->type == AST_FUNDEC || node->type == EXP_FUN) + return node; + node = node->parent; + } + return NULL; +} + +/* Emit code to rebalance stack and scopes during an abrupt exit */ + +static void cexit(JF, js_AstType T, js_Ast *node, js_Ast *target) +{ + do { + node = node->parent; + switch (node->type) { + case STM_WITH: + emit(J, F, OP_ENDWITH); + break; + case STM_FOR_IN: + case STM_FOR_IN_VAR: + /* pop the object and name pair we are iterating over if leaving the loop */ + if (T == STM_RETURN || T == STM_THROW) + emit(J, F, OP_ROT3POP2); /* save the return / exception value */ + if (T == STM_BREAK) + emit(J, F, OP_POP2); + break; + } + } while (node != target); +} + /* Statements */ static void cstm(JF, js_Ast *stm) @@ -702,13 +743,7 @@ static void cstm(JF, js_Ast *stm) loop = here(J, F); emit(J, F, OP_NEXTPROP); end = jump(J, F, OP_JFALSE); - if (stm->type == STM_FOR_IN_VAR) { - if (stm->a->b) - jsC_error(J, stm->a->b, "more than one loop variable in for-in statement"); - emitstring(J, F, OP_SETVAR, stm->a->a->a->string); /* stm(list(var-init(ident))) */ - } else { - cassignloop(J, F, stm->a); - } + cassignforin(J, F, stm); cstm(J, F, stm->c); jumpto(J, F, OP_JUMP, loop); label(J, F, end); @@ -740,6 +775,7 @@ static void cstm(JF, js_Ast *stm) if (!stm->target) jsC_error(J, stm, "unlabelled break must be inside loop or switch"); } + cexit(J, F, STM_BREAK, stm, stm->target); stm->inst = jump(J, F, OP_JUMP); break; @@ -753,6 +789,7 @@ static void cstm(JF, js_Ast *stm) if (!stm->target) jsC_error(J, stm, "continue must be inside loop"); } + cexit(J, F, STM_CONTINUE, stm, stm->target); stm->inst = jump(J, F, OP_JUMP); break; @@ -761,6 +798,10 @@ static void cstm(JF, js_Ast *stm) cexp(J, F, stm->a); else emit(J, F, OP_UNDEF); + stm->target = returntarget(J, F, stm); + if (!stm->target) + jsC_error(J, stm, "return not in function"); + cexit(J, F, STM_RETURN, stm, stm->target); emit(J, F, OP_RETURN); break; diff --git a/jscompile.h b/jscompile.h index 636827c..4adce06 100644 --- a/jscompile.h +++ b/jscompile.h @@ -4,10 +4,12 @@ enum { OP_POP, /* A -- */ + OP_POP2, /* A B -- */ OP_DUP, /* A -- A A */ OP_DUP2, /* A B -- A B A B */ OP_ROT2, /* A B -- B A */ OP_ROT3, /* A B C -- C A B */ + OP_ROT3POP2, /* A B C -- C */ OP_DUP1ROT4, /* A B C -- C A B C */ OP_NUMBER_0, /* -- 0 */ diff --git a/jsdump.c b/jsdump.c index 5751acc..5c24531 100644 --- a/jsdump.c +++ b/jsdump.c @@ -684,6 +684,10 @@ void js_dumpvalue(js_State *J, js_Value v) case JS_TNUMBER: printf("%.9g", v.u.number); break; case JS_TSTRING: printf("'%s'", v.u.string); break; case JS_TOBJECT: + if (v.u.object == J->G) { + printf("[Global]"); + break; + } switch (v.u.object->type) { case JS_COBJECT: printf("[Object %p]", v.u.object); break; case JS_CARRAY: printf("[Array %p]", v.u.object); break; diff --git a/jsrun.c b/jsrun.c index 672e755..40c84e9 100644 --- a/jsrun.c +++ b/jsrun.c @@ -518,10 +518,12 @@ static void jsR_run(js_State *J, js_Function *F) opcode = *pc++; switch (opcode) { case OP_POP: js_pop(J, 1); break; + case OP_POP2: js_pop(J, 2); break; case OP_DUP: js_dup(J); break; case OP_DUP2: js_dup2(J); break; case OP_ROT2: js_rot2(J); break; case OP_ROT3: js_rot3(J); break; + case OP_ROT3POP2: js_rot3pop2(J); break; case OP_DUP1ROT4: js_dup1rot4(J); break; case OP_NUMBER_0: js_pushnumber(J, 0); break;