ref: 60253cedab034cda3765a882fe2525a215c4d390
parent: bcc663165933c272232d616e0321556b24f3df7a
author: Tor Andersson <tor@ccxvii.net>
date: Wed Jan 22 18:15:18 EST 2014
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.
--- a/jscompile.c
+++ b/jscompile.c
@@ -568,15 +568,23 @@
/* 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 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 @@
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 cstm(JF, js_Ast *stm)
{
+ js_Ast *target;
int loop, cont, then, end;
switch (stm->type) {
@@ -766,8 +788,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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;
--- a/jsparse.c
+++ b/jsparse.c
@@ -66,7 +66,7 @@
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 @@
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;
}
--- a/jsparse.h
+++ b/jsparse.h
@@ -114,14 +114,23 @@
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 */
};
--- a/jsrun.c
+++ b/jsrun.c
@@ -344,6 +344,7 @@
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,7 +358,9 @@
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;
}
@@ -364,9 +367,12 @@
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 @@
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);
}
}