Add stack and scope balancing to exits from try/catch blocks.

Because finally blocks are inlined, their break and continue instructions
are duplicated so we need to use a list of jumps to patch instead of
a single instruction and target pointer.

Also save the top return value from the stack, since an interrupted
return (return out of a try block that has a finally with another
jump or return) will leave multiple return values on the stack.
This commit is contained in:
Tor Andersson
2014-01-22 23:15:18 +01:00
parent bcc6631659
commit 60253cedab
4 changed files with 84 additions and 45 deletions
+52 -38
View File
@@ -568,15 +568,23 @@ static void cexp(JF, js_Ast *exp)
/* Patch break and continue statements */
static void labelexit(JF, js_Ast *top, js_Ast *node, js_AstType T, int addr)
static void addjump(JF, js_AstType type, js_Ast *target, int inst)
{
if (node->type == T && node->target == top) {
labelto(J, F, node->inst, addr);
} else if (node->type >= STM_BLOCK || node->type == AST_LIST) {
if (node->a) labelexit(J, F, top, node->a, T, addr);
if (node->b) labelexit(J, F, top, node->b, T, addr);
if (node->c) labelexit(J, F, top, node->c, T, addr);
if (node->d) labelexit(J, F, top, node->d, T, addr);
js_JumpList *jump = malloc(sizeof *jump);
jump->type = type;
jump->inst = inst;
jump->next = target->jumps;
target->jumps = jump;
}
static void labeljumps(JF, js_JumpList *jump, int baddr, int caddr)
{
while (jump) {
if (jump->type == STM_BREAK)
labelto(J, F, jump->inst, baddr);
if (jump->type == STM_CONTINUE)
labelto(J, F, jump->inst, caddr);
jump = jump->next;
}
}
@@ -640,8 +648,9 @@ static js_Ast *returntarget(JF, js_Ast *node)
static void cexit(JF, js_AstType T, js_Ast *node, js_Ast *target)
{
js_Ast *prev;
do {
node = node->parent;
prev = node, node = node->parent;
switch (node->type) {
case STM_WITH:
emit(J, F, OP_ENDWITH);
@@ -654,6 +663,18 @@ static void cexit(JF, js_AstType T, js_Ast *node, js_Ast *target)
if (T == STM_BREAK)
emit(J, F, OP_POP2);
break;
case STM_TRY:
/* 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) {
emit(J, F, OP_ENDCATCH);
if (node->d) cstm(J, F, node->d); /* finally */
}
break;
}
} while (node != target);
}
@@ -727,6 +748,7 @@ static void ctrycatchfinally(JF, js_Ast *trystm, js_Ast *catchvar, js_Ast *catch
static void cstm(JF, js_Ast *stm)
{
js_Ast *target;
int loop, cont, then, end;
switch (stm->type) {
@@ -766,8 +788,7 @@ static void cstm(JF, js_Ast *stm)
cstm(J, F, stm->a);
cexp(J, F, stm->b);
jumpto(J, F, OP_JTRUE, loop);
labelexit(J, F, stm, stm->a, STM_CONTINUE, loop);
labelexit(J, F, stm, stm->a, STM_BREAK, here(J, F));
labeljumps(J, F, stm->jumps, here(J,F), loop);
break;
case STM_WHILE:
@@ -777,8 +798,7 @@ static void cstm(JF, js_Ast *stm)
cstm(J, F, stm->b);
jumpto(J, F, OP_JUMP, loop);
label(J, F, end);
labelexit(J, F, stm, stm->b, STM_CONTINUE, loop);
labelexit(J, F, stm, stm->b, STM_BREAK, here(J, F));
labeljumps(J, F, stm->jumps, here(J,F), loop);
break;
case STM_FOR:
@@ -806,8 +826,7 @@ static void cstm(JF, js_Ast *stm)
if (stm->b)
label(J, F, end);
printf("labeling for statement: %d %d\n", cont, here(J,F));
labelexit(J, F, stm, stm->d, STM_CONTINUE, cont);
labelexit(J, F, stm, stm->d, STM_BREAK, here(J, F));
labeljumps(J, F, stm->jumps, here(J,F), cont);
break;
case STM_FOR_IN:
@@ -821,8 +840,7 @@ static void cstm(JF, js_Ast *stm)
cstm(J, F, stm->c);
jumpto(J, F, OP_JUMP, loop);
label(J, F, end);
labelexit(J, F, stm, stm->c, STM_CONTINUE, loop);
labelexit(J, F, stm, stm->c, STM_BREAK, here(J, F));
labeljumps(J, F, stm->jumps, here(J,F), loop);
break;
case STM_LABEL:
@@ -831,40 +849,36 @@ static void cstm(JF, js_Ast *stm)
while (stm->type == STM_LABEL)
stm = stm->b;
/* loops and switches have already been labelled */
if (!isloop(stm->type) && stm->type != STM_SWITCH) {
if (stm->a) labelexit(J, F, stm, stm->a, STM_BREAK, here(J, F));
if (stm->b) labelexit(J, F, stm, stm->b, STM_BREAK, here(J, F));
if (stm->c) labelexit(J, F, stm, stm->c, STM_BREAK, here(J, F));
if (stm->d) labelexit(J, F, stm, stm->d, STM_BREAK, here(J, F));
}
if (!isloop(stm->type) && stm->type != STM_SWITCH)
labeljumps(J, F, stm->jumps, here(J,F), 0);
break;
case STM_BREAK:
if (stm->a) {
stm->target = breaktarget(J, F, stm, stm->a->string);
if (!stm->target)
target = breaktarget(J, F, stm, stm->a->string);
if (!target)
jsC_error(J, stm, "break label not found: %s", stm->a->string);
} else {
stm->target = breaktarget(J, F, stm, NULL);
if (!stm->target)
target = breaktarget(J, F, stm, NULL);
if (!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);
cexit(J, F, STM_BREAK, stm, target);
addjump(J, F, STM_BREAK, target, jump(J, F, OP_JUMP));
break;
case STM_CONTINUE:
if (stm->a) {
stm->target = continuetarget(J, F, stm, stm->a->string);
if (!stm->target)
target = continuetarget(J, F, stm, stm->a->string);
if (!target)
jsC_error(J, stm, "continue label not found: %s", stm->a->string);
} else {
stm->target = continuetarget(J, F, stm, NULL);
if (!stm->target)
target = continuetarget(J, F, stm, NULL);
if (!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);
cexit(J, F, STM_CONTINUE, stm, target);
addjump(J, F, STM_CONTINUE, target, jump(J, F, OP_JUMP));
break;
case STM_RETURN:
@@ -872,10 +886,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)
target = returntarget(J, F, stm);
if (!target)
jsC_error(J, stm, "return not in function");
cexit(J, F, STM_RETURN, stm, stm->target);
cexit(J, F, STM_RETURN, stm, target);
emit(J, F, OP_RETURN);
break;
+11 -1
View File
@@ -66,7 +66,7 @@ static js_Ast *jsP_newnode(js_State *J, int type, js_Ast *a, js_Ast *b, js_Ast *
node->d = d;
node->number = 0;
node->string = NULL;
node->inst = 0;
node->jumps = NULL;
node->parent = NULL;
if (a) a->parent = node;
@@ -106,11 +106,21 @@ static js_Ast *jsP_newnumnode(js_State *J, int type, double n)
return node;
}
static void jsP_freejumps(js_State *J, js_JumpList *node)
{
while (node) {
js_JumpList *next = node->next;
free(node);
node = next;
}
}
void jsP_freeparse(js_State *J)
{
js_Ast *node = J->gcast;
while (node) {
js_Ast *next = node->gcnext;
jsP_freejumps(J, node->jumps);
free(node);
node = next;
}
+11 -2
View File
@@ -114,14 +114,23 @@ enum js_AstType
STM_DEFAULT,
};
typedef struct js_JumpList js_JumpList;
struct js_JumpList
{
js_AstType type;
int inst;
js_JumpList *next;
};
struct js_Ast
{
int type;
int line;
js_Ast *parent, *target, *a, *b, *c, *d;
js_Ast *parent, *a, *b, *c, *d;
double number;
const char *string;
int inst; /* for patching jumps */
js_JumpList *jumps; /* list of break/continue jumps to patch */
js_Ast *gcnext; /* next in alloc list */
};
+10 -4
View File
@@ -344,6 +344,7 @@ static js_Property *js_setvar(js_State *J, const char *name)
static void jsR_callfunction(js_State *J, int n, js_Function *F, js_Environment *scope)
{
js_Environment *saveE;
js_Value v;
int i;
saveE = J->E;
@@ -357,16 +358,21 @@ static void jsR_callfunction(js_State *J, int n, js_Function *F, js_Environment
js_pop(J, n);
jsR_run(J, F);
js_rot3pop2(J);
v = js_tovalue(J, -1);
TOP = --BOT; /* clear stack */
js_pushvalue(J, v);
J->E = saveE;
}
static void jsR_callscript(js_State *J, int n, js_Function *F)
{
js_Value v;
js_pop(J, n);
jsR_run(J, F);
js_rot3pop2(J);
v = js_tovalue(J, -1);
TOP = --BOT; /* clear stack */
js_pushvalue(J, v);
}
static void jsR_callcfunction(js_State *J, int n, js_CFunction F)
@@ -374,10 +380,10 @@ static void jsR_callcfunction(js_State *J, int n, js_CFunction F)
int rv = F(J, n);
if (rv) {
js_Value v = js_tovalue(J, -1);
TOP = --BOT; /* pop down to below function */
TOP = --BOT; /* clear stack */
js_pushvalue(J, v);
} else {
TOP = --BOT; /* pop down to below function */
TOP = --BOT; /* clear stack */
js_pushundefined(J);
}
}