shithub: libmujs

Download patch

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);
 	}
 }