shithub: libmujs

Download patch

ref: cf9867c8332f54ff9fbdc7a87778f2a74b62aa35
parent: 833d2a6fada91ac6db51ac489a0a07a4c70bb2e7
author: Tor Andersson <tor@ccxvii.net>
date: Wed Jan 22 13:04:11 EST 2014

Implement try/catch/finally exception handling.

TODO: return/break/continue and finally.

--- a/jscompile.c
+++ b/jscompile.c
@@ -11,6 +11,7 @@
 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);
 
 int jsC_error(js_State *J, js_Ast *node, const char *fmt, ...)
 {
@@ -648,8 +649,8 @@
 		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_RETURN)
+				emit(J, F, OP_ROT3POP2); /* save the return value */
 			if (T == STM_BREAK)
 				emit(J, F, OP_POP2);
 			break;
@@ -657,6 +658,71 @@
 	} while (node != target);
 }
 
+/* Try/catch/finally */
+
+static void ctryfinally(JF, js_Ast *trystm, js_Ast *finallystm)
+{
+	int L1;
+	L1 = jump(J, F, OP_TRY);
+	{
+		/* if we get here, we have caught an exception in the try block */
+		cstm(J, F, finallystm); /* inline finally block */
+		emit(J, F, OP_THROW); /* rethrow exception */
+	}
+	label(J, F, L1);
+	cstm(J, F, trystm);
+	emit(J, F, OP_ENDTRY);
+	cstm(J, F, finallystm);
+}
+
+static void ctrycatch(JF, js_Ast *trystm, js_Ast *catchvar, js_Ast *catchstm)
+{
+	int L1, L2, L3;
+	L1 = jump(J, F, OP_TRY);
+	{
+		/* if we get here, we have caught an exception in the try block */
+		L2 = jump(J, F, OP_CATCH);
+		emit(J, F, addstring(J, F, catchvar->string));
+		{
+			/* if we get here, we have caught an exception in the catch block */
+			emit(J, F, OP_THROW); /* rethrow exception */
+		}
+		label(J, F, L2);
+		cstm(J, F, catchstm);
+		emit(J, F, OP_ENDCATCH);
+		L3 = jump(J, F, OP_JUMP); /* skip past the try block */
+	}
+	label(J, F, L1);
+	cstm(J, F, trystm);
+	emit(J, F, OP_ENDTRY);
+	label(J, F, L3);
+}
+
+static void ctrycatchfinally(JF, js_Ast *trystm, js_Ast *catchvar, js_Ast *catchstm, js_Ast *finallystm)
+{
+	int L1, L2, L3;
+	L1 = jump(J, F, OP_TRY);
+	{
+		/* if we get here, we have caught an exception in the try block */
+		L2 = jump(J, F, OP_CATCH);
+		emit(J, F, addstring(J, F, catchvar->string));
+		{
+			/* if we get here, we have caught an exception in the catch block */
+			cstm(J, F, finallystm); /* inline finally block */
+			emit(J, F, OP_THROW); /* rethrow exception */
+		}
+		label(J, F, L2);
+		cstm(J, F, catchstm);
+		emit(J, F, OP_ENDCATCH);
+		L3 = jump(J, F, OP_JUMP); /* skip past the try block to the finally block */
+	}
+	label(J, F, L1);
+	cstm(J, F, trystm);
+	emit(J, F, OP_ENDTRY);
+	label(J, F, L3);
+	cstm(J, F, finallystm);
+}
+
 /* Statements */
 
 static void cstm(JF, js_Ast *stm)
@@ -805,6 +871,11 @@
 		emit(J, F, OP_RETURN);
 		break;
 
+	case STM_THROW:
+		cexp(J, F, stm->a);
+		emit(J, F, OP_THROW);
+		break;
+
 	case STM_WITH:
 		cexp(J, F, stm->a);
 		emit(J, F, OP_WITH);
@@ -814,12 +885,16 @@
 
 	// switch
 
-	case STM_THROW:
-		cexp(J, F, stm->a);
-		emit(J, F, OP_THROW);
+	case STM_TRY:
+		if (stm->b && stm->c) {
+			if (stm->d)
+				ctrycatchfinally(J, F, stm->a, stm->b, stm->c, stm->d);
+			else
+				ctrycatch(J, F, stm->a, stm->b, stm->c);
+		} else {
+			ctryfinally(J, F, stm->a, stm->d);
+		}
 		break;
-
-	// try
 
 	case STM_DEBUGGER:
 		emit(J, F, OP_DEBUGGER);
--- a/jscompile.h
+++ b/jscompile.h
@@ -75,9 +75,11 @@
 	OP_INSTANCEOF,
 
 	OP_THROW,
-	OP_TRY,
+
+	OP_TRY,		/* -ADDR- /jump/ or -ADDR- <exception> */
 	OP_ENDTRY,
-	OP_CATCH,
+
+	OP_CATCH,	/* -ADDR,S- /jump/ or -ADDR,S- <exception> */
 	OP_ENDCATCH,
 	OP_WITH,
 	OP_ENDWITH,
--- a/jsdump.c
+++ b/jsdump.c
@@ -658,8 +658,14 @@
 		case OP_JUMP:
 		case OP_JTRUE:
 		case OP_JFALSE:
+		case OP_TRY:
 			printf(" %d", *p++);
 			break;
+		case OP_CATCH:
+			printf(" %d", *p++);
+			pc(' ');
+			ps(F->strtab[*p++]);
+			break;
 		}
 
 		nl();
@@ -698,8 +704,8 @@
 				v.u.object->u.f.function->filename,
 				v.u.object->u.f.function->line);
 			break;
-		case JS_CSCRIPT: printf("[Function %s]", v.u.object->u.f.function->filename); break;
-		case JS_CCFUNCTION: printf("[Function %p]", v.u.object->u.c.function); break;
+		case JS_CSCRIPT: printf("[Script %s]", v.u.object->u.f.function->filename); break;
+		case JS_CCFUNCTION: printf("[CFunction %p]", v.u.object->u.c.function); break;
 		case JS_CBOOLEAN: printf("[Boolean %d]", v.u.object->u.boolean); break;
 		case JS_CNUMBER: printf("[Number %g]", v.u.object->u.number); break;
 		case JS_CSTRING: printf("[String'%s']", v.u.object->u.string); break;
--- a/jsrun.c
+++ b/jsrun.c
@@ -201,6 +201,13 @@
 	STACK[TOP-3] = tmp;		/* C A B */
 }
 
+void js_rot2pop1(js_State *J)
+{
+	/* A B -> B */
+	STACK[TOP-2] = STACK[TOP-1];
+	--TOP;
+}
+
 void js_rot3pop2(js_State *J)
 {
 	/* A B C -> C */
@@ -766,6 +773,46 @@
 			iy = js_toint32(J, -1);
 			js_pop(J, 2);
 			js_pushnumber(J, ix | iy);
+			break;
+
+		/* Try and Catch */
+
+		case OP_THROW:
+			js_throw(J);
+			break;
+
+		case OP_TRY:
+			offset = *pc++;
+			if (js_trypc(J, pc)) {
+				pc = J->trybuf[J->trylen].pc;
+			} else {
+				pc = pcstart + offset;
+			}
+			break;
+
+		case OP_ENDTRY:
+			js_endtry(J);
+			break;
+
+		case OP_CATCH:
+			offset = *pc++;
+			str = ST[*pc++];
+			if (js_trypc(J, pc)) {
+				pc = J->trybuf[J->trylen].pc;
+			} else {
+				obj = jsV_newobject(J, JS_COBJECT, NULL);
+				js_pushobject(J, obj);
+				js_rot2(J);
+				js_setproperty(J, -2, str);
+				J->E = jsR_newenvironment(J, obj, J->E);
+				js_pop(J, 1);
+				pc = pcstart + offset;
+			}
+			break;
+
+		case OP_ENDCATCH:
+			js_endtry(J);
+			J->E = J->E->outer;
 			break;
 
 		/* With */