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 */