shithub: libmujs

Download patch

ref: f77a6e7e8e367ed8a6360ac7a44cac28ff5ed3ee
parent: 8d5ace5e79b80c4446d994527f5e1d88bd77fa22
author: Tor Andersson <tor@ccxvii.net>
date: Tue Jan 14 13:17:06 EST 2014

Implement value stack and use it in the interpreter.

We can now run simple programs that don't use function calls.

--- a/jscompile.c
+++ b/jscompile.c
@@ -45,10 +45,10 @@
 {
 //	int i;
 //	for (i = 0; i < F->funlen; i++)
-//		freefun(J, F->funlist[i]);
-	free(F->funlist);
-	free(F->numlist);
-	free(F->strlist);
+//		freefun(J, F->funtab[i]);
+	free(F->funtab);
+	free(F->numtab);
+	free(F->strtab);
 	free(F->code);
 	free(F);
 }
@@ -68,9 +68,9 @@
 {
 	if (F->funlen >= F->funcap) {
 		F->funcap = F->funcap ? F->funcap * 2 : 16;
-		F->funlist = realloc(F->funlist, F->funcap * sizeof *F->funlist);
+		F->funtab = realloc(F->funtab, F->funcap * sizeof *F->funtab);
 	}
-	F->funlist[F->funlen] = value;
+	F->funtab[F->funlen] = value;
 	return F->funlen++;
 }
 
@@ -78,13 +78,13 @@
 {
 	int i;
 	for (i = 0; i < F->numlen; i++)
-		if (F->numlist[i] == value)
+		if (F->numtab[i] == value)
 			return i;
 	if (F->numlen >= F->numcap) {
 		F->numcap = F->numcap ? F->numcap * 2 : 16;
-		F->numlist = realloc(F->numlist, F->numcap * sizeof *F->numlist);
+		F->numtab = realloc(F->numtab, F->numcap * sizeof *F->numtab);
 	}
-	F->numlist[F->numlen] = value;
+	F->numtab[F->numlen] = value;
 	return F->numlen++;
 }
 
@@ -92,26 +92,35 @@
 {
 	int i;
 	for (i = 0; i < F->strlen; i++)
-		if (!strcmp(F->strlist[i], value))
+		if (!strcmp(F->strtab[i], value))
 			return i;
 	if (F->strlen >= F->strcap) {
 		F->strcap = F->strcap ? F->strcap * 2 : 16;
-		F->strlist = realloc(F->strlist, F->strcap * sizeof *F->strlist);
+		F->strtab = realloc(F->strtab, F->strcap * sizeof *F->strtab);
 	}
-	F->strlist[F->strlen] = value;
+	F->strtab[F->strlen] = value;
 	return F->strlen++;
 }
 
-static void emitfunction(JF, int opcode, js_Function *fun)
+static void emitfunction(JF, js_Function *fun)
 {
-	emit(J, F, opcode);
+	emit(J, F, OP_CLOSURE);
 	emit(J, F, addfunction(J, F, fun));
 }
 
-static void emitnumber(JF, int opcode, double num)
+static void emitnumber(JF, double num)
 {
-	emit(J, F, opcode);
-	emit(J, F, addnumber(J, F, num));
+	if (num == 0)
+		emit(J, F, OP_NUMBER_0);
+	else if (num == 1)
+		emit(J, F, OP_NUMBER_1);
+	else if (num == (short)num) {
+		emit(J, F, OP_NUMBER_X);
+		emit(J, F, (short)num);
+	} else {
+		emit(J, F, OP_NUMBER);
+		emit(J, F, addnumber(J, F, num));
+	}
 }
 
 static void emitstring(JF, int opcode, const char *str)
@@ -146,13 +155,13 @@
 
 /* Expressions */
 
-static void unary(JF, js_Ast *exp, int opcode)
+static void cunary(JF, js_Ast *exp, int opcode)
 {
 	cexp(J, F, exp->a);
 	emit(J, F, opcode);
 }
 
-static void binary(JF, js_Ast *exp, int opcode)
+static void cbinary(JF, js_Ast *exp, int opcode)
 {
 	cexp(J, F, exp->a);
 	cexp(J, F, exp->b);
@@ -163,9 +172,12 @@
 {
 	int i = 0;
 	while (list) {
+		emit(J, F, OP_DUP);
+		emit(J, F, OP_NUMBER_X);
 		cexp(J, F, list->a);
-		emit(J, F, OP_ARRAYPUT);
 		emit(J, F, i++);
+		emit(J, F, OP_SETPROP);
+		emit(J, F, OP_POP);
 		list = list->b;
 	}
 }
@@ -176,15 +188,16 @@
 		js_Ast *kv = list->a;
 		if (kv->type == EXP_PROP_VAL) {
 			js_Ast *prop = kv->a;
-			cexp(J, F, kv->b);
+			emit(J, F, OP_DUP);
 			if (prop->type == AST_IDENTIFIER || prop->type == AST_STRING)
-				emitstring(J, F, OP_OBJECTPUT, prop->string);
-			else if (prop->type == AST_STRING)
-				emitstring(J, F, OP_OBJECTPUT, prop->string);
+				emitstring(J, F, OP_STRING, prop->string);
 			else if (prop->type == AST_NUMBER)
-				emitnumber(J, F, OP_OBJECTPUT, prop->number);
+				emitnumber(J, F, prop->number);
 			else
 				jsC_error(J, list, "illegal property name in object initializer");
+			cexp(J, F, kv->b);
+			emit(J, F, OP_SETPROP);
+			emit(J, F, OP_POP);
 		}
 		// TODO: set, get
 		list = list->b;
@@ -202,40 +215,116 @@
 	return n;
 }
 
-static void clval(JF, js_Ast *exp)
+static void cassign(JF, js_Ast *lhs, js_Ast *rhs)
 {
-	switch (exp->type) {
+	switch (lhs->type) {
 	case AST_IDENTIFIER:
-		emitstring(J, F, OP_AVAR, exp->string);
+		cexp(J, F, rhs);
+		emitstring(J, F, OP_SETVAR, lhs->string);
 		break;
 	case EXP_INDEX:
-		cexp(J, F, exp->a);
-		cexp(J, F, exp->b);
-		emit(J, F, OP_AINDEX);
+		cexp(J, F, lhs->a);
+		cexp(J, F, lhs->b);
+		cexp(J, F, rhs);
+		emit(J, F, OP_SETPROP);
 		break;
 	case EXP_MEMBER:
-		cexp(J, F, exp->a);
-		emitstring(J, F, OP_AMEMBER, exp->b->string);
+		cexp(J, F, lhs->a);
+		emitstring(J, F, OP_STRING, lhs->b->string);
+		cexp(J, F, rhs);
+		emit(J, F, OP_SETPROP);
 		break;
+	case EXP_CALL: /* host functions may return an assignable l-value */
+		cexp(J, F, lhs);
+		cexp(J, F, rhs);
+		emit(J, F, OP_SETPROP);
+		break;
+	default:
+		jsC_error(J, lhs, "invalid l-value in assignment");
+		break;
+	}
+}
+
+static void cassignop1(JF, js_Ast *lhs, int dup)
+{
+	switch (lhs->type) {
+	case AST_IDENTIFIER:
+		emitstring(J, F, OP_GETVAR, lhs->string);
+		if (dup) emit(J, F, OP_DUP);
+		break;
+	case EXP_INDEX:
+		cexp(J, F, lhs->a);
+		cexp(J, F, lhs->b);
+		emit(J, F, OP_DUP2);
+		emit(J, F, OP_GETPROP);
+		if (dup) emit(J, F, OP_DUP1ROT4);
+		break;
+	case EXP_MEMBER:
+		cexp(J, F, lhs->a);
+		emitstring(J, F, OP_STRING, lhs->b->string);
+		emit(J, F, OP_DUP2);
+		emit(J, F, OP_GETPROP);
+		if (dup) emit(J, F, OP_DUP1ROT4);
+		break;
+	case EXP_CALL: /* host functions may return an assignable l-value */
+		cexp(J, F, lhs);
+		emit(J, F, OP_DUP2);
+		emit(J, F, OP_GETPROP);
+		if (dup) emit(J, F, OP_DUP1ROT4);
+		break;
+	default:
+		jsC_error(J, lhs, "invalid l-value in assignment");
+		break;
+	}
+}
+
+static void cassignop2(JF, js_Ast *lhs)
+{
+	switch (lhs->type) {
+	case AST_IDENTIFIER:
+		emitstring(J, F, OP_SETVAR, lhs->string);
+		break;
+	case EXP_INDEX:
+	case EXP_MEMBER:
 	case EXP_CALL:
-		/* host functions may return an assignable l-value */
-		cexp(J, F, exp);
+		emit(J, F, OP_SETPROP);
 		break;
 	default:
-		jsC_error(J, exp, "invalid l-value in assignment");
+		jsC_error(J, lhs, "invalid l-value in assignment");
 		break;
 	}
 }
 
-static void assignop(JF, js_Ast *exp, int opcode)
+static void cassignop(JF, js_Ast *lhs, js_Ast *rhs, int opcode)
 {
-	clval(J, F, exp->a);
-	emit(J, F, OP_LOAD);
-	cexp(J, F, exp->b);
+	cassignop1(J, F, lhs, 0);
+	cexp(J, F, rhs);
 	emit(J, F, opcode);
-	emit(J, F, OP_STORE);
+	cassignop2(J, F, lhs);
 }
 
+static void cdelete(JF, js_Ast *exp)
+{
+	switch (exp->type) {
+	case AST_IDENTIFIER:
+		emitstring(J, F, OP_DELVAR, exp->string);
+		break;
+	case EXP_INDEX:
+		cexp(J, F, exp->a);
+		cexp(J, F, exp->b);
+		emit(J, F, OP_DELPROP);
+		break;
+	case EXP_MEMBER:
+		cexp(J, F, exp->a);
+		emitstring(J, F, OP_STRING, exp->b->string);
+		emit(J, F, OP_DELPROP);
+		break;
+	default:
+		jsC_error(J, exp, "invalid l-value in delete expression");
+		break;
+	}
+}
+
 static void cvarinit(JF, js_Ast *list)
 {
 	while (list) {
@@ -242,9 +331,7 @@
 		js_Ast *var = list->a;
 		if (var->b) {
 			cexp(J, F, var->b);
-			emitstring(J, F, OP_AVAR, var->a->string);
-			emit(J, F, OP_STORE);
-			emit(J, F, OP_POP);
+			emitstring(J, F, OP_SETVAR, var->a->string);
 		}
 		list = list->b;
 	}
@@ -258,12 +345,13 @@
 		cexp(J, F, fun->a);
 		emit(J, F, OP_DUP);
 		cexp(J, F, fun->b);
-		emit(J, F, OP_LOADINDEX);
+		emit(J, F, OP_GETPROP);
 		break;
 	case EXP_MEMBER:
 		cexp(J, F, fun->a);
 		emit(J, F, OP_DUP);
-		emitstring(J, F, OP_LOADMEMBER, fun->b->string);
+		emitstring(J, F, OP_STRING, fun->b->string);
+		emit(J, F, OP_GETPROP);
 		break;
 	default:
 		emit(J, F, OP_THIS);
@@ -281,9 +369,8 @@
 	int n;
 
 	switch (exp->type) {
-	case AST_IDENTIFIER: emitstring(J, F, OP_LOADVAR, exp->string); break;
-	case AST_NUMBER: emitnumber(J, F, OP_NUMBER, exp->number); break;
 	case AST_STRING: emitstring(J, F, OP_STRING, exp->string); break;
+	case AST_NUMBER: emitnumber(J, F, exp->number); break;
 	case EXP_UNDEF: emit(J, F, OP_UNDEF); break;
 	case EXP_NULL: emit(J, F, OP_NULL); break;
 	case EXP_TRUE: emit(J, F, OP_TRUE); break;
@@ -300,15 +387,24 @@
 		carray(J, F, exp->a);
 		break;
 
+	case EXP_FUN:
+		emitfunction(J, F, newfun(J, exp->a, exp->b, exp->c));
+		break;
+
+	case AST_IDENTIFIER:
+		emitstring(J, F, OP_GETVAR, exp->string);
+		break;
+
 	case EXP_INDEX:
 		cexp(J, F, exp->a);
 		cexp(J, F, exp->b);
-		emit(J, F, OP_LOADINDEX);
+		emit(J, F, OP_GETPROP);
 		break;
 
 	case EXP_MEMBER:
 		cexp(J, F, exp->a);
-		emitstring(J, F, OP_LOADMEMBER, exp->b->string);
+		emitstring(J, F, OP_STRING, exp->b->string);
+		emit(J, F, OP_GETPROP);
 		break;
 
 	case EXP_CALL:
@@ -322,15 +418,40 @@
 		emit(J, F, n);
 		break;
 
-	case EXP_FUN:
-		emitfunction(J, F, OP_CLOSURE, newfun(J, exp->a, exp->b, exp->c));
+	case EXP_DELETE:
+		cdelete(J, F, exp->a);
 		break;
 
-	case EXP_DELETE:
-		clval(J, F, exp->a);
-		emit(J, F, OP_DELETE);
+	case EXP_PREINC:
+		cassignop1(J, F, exp->a, 0);
+		emit(J, F, OP_NUMBER_1);
+		emit(J, F, OP_ADD);
+		cassignop2(J, F, exp->a);
 		break;
 
+	case EXP_PREDEC:
+		cassignop1(J, F, exp->a, 0);
+		emit(J, F, OP_NUMBER_1);
+		emit(J, F, OP_SUB);
+		cassignop2(J, F, exp->a);
+		break;
+
+	case EXP_POSTINC:
+		cassignop1(J, F, exp->a, 1);
+		emit(J, F, OP_NUMBER_1);
+		emit(J, F, OP_ADD);
+		cassignop2(J, F, exp->a);
+		emit(J, F, OP_POP);
+		break;
+
+	case EXP_POSTDEC:
+		cassignop1(J, F, exp->a, 1);
+		emit(J, F, OP_NUMBER_1);
+		emit(J, F, OP_SUB);
+		cassignop2(J, F, exp->a);
+		emit(J, F, OP_POP);
+		break;
+
 	case EXP_VOID:
 		cexp(J, F, exp->a);
 		emit(J, F, OP_POP);
@@ -337,57 +458,47 @@
 		emit(J, F, OP_UNDEF);
 		break;
 
-	case EXP_TYPEOF: unary(J, F, exp, OP_TYPEOF); break;
-	case EXP_POS: unary(J, F, exp, OP_POS); break;
-	case EXP_NEG: unary(J, F, exp, OP_NEG); break;
-	case EXP_BITNOT: unary(J, F, exp, OP_BITNOT); break;
-	case EXP_LOGNOT: unary(J, F, exp, OP_LOGNOT); break;
+	case EXP_TYPEOF: cunary(J, F, exp, OP_TYPEOF); break;
+	case EXP_POS: cunary(J, F, exp, OP_POS); break;
+	case EXP_NEG: cunary(J, F, exp, OP_NEG); break;
+	case EXP_BITNOT: cunary(J, F, exp, OP_BITNOT); break;
+	case EXP_LOGNOT: cunary(J, F, exp, OP_LOGNOT); break;
 
-	case EXP_PREINC: clval(J, F, exp->a); emit(J, F, OP_PREINC); break;
-	case EXP_PREDEC: clval(J, F, exp->a); emit(J, F, OP_PREDEC); break;
-	case EXP_POSTINC: clval(J, F, exp->a); emit(J, F, OP_POSTINC); break;
-	case EXP_POSTDEC: clval(J, F, exp->a); emit(J, F, OP_POSTDEC); break;
+	case EXP_BITOR: cbinary(J, F, exp, OP_BITOR); break;
+	case EXP_BITXOR: cbinary(J, F, exp, OP_BITXOR); break;
+	case EXP_BITAND: cbinary(J, F, exp, OP_BITAND); break;
+	case EXP_EQ: cbinary(J, F, exp, OP_EQ); break;
+	case EXP_NE: cbinary(J, F, exp, OP_NE); break;
+	case EXP_STRICTEQ: cbinary(J, F, exp, OP_STRICTEQ); break;
+	case EXP_STRICTNE: cbinary(J, F, exp, OP_STRICTNE); break;
+	case EXP_LT: cbinary(J, F, exp, OP_LT); break;
+	case EXP_GT: cbinary(J, F, exp, OP_GT); break;
+	case EXP_LE: cbinary(J, F, exp, OP_LE); break;
+	case EXP_GE: cbinary(J, F, exp, OP_GE); break;
+	case EXP_INSTANCEOF: cbinary(J, F, exp, OP_INSTANCEOF); break;
+	case EXP_IN: cbinary(J, F, exp, OP_IN); break;
+	case EXP_SHL: cbinary(J, F, exp, OP_SHL); break;
+	case EXP_SHR: cbinary(J, F, exp, OP_SHR); break;
+	case EXP_USHR: cbinary(J, F, exp, OP_USHR); break;
+	case EXP_ADD: cbinary(J, F, exp, OP_ADD); break;
+	case EXP_SUB: cbinary(J, F, exp, OP_SUB); break;
+	case EXP_MUL: cbinary(J, F, exp, OP_MUL); break;
+	case EXP_DIV: cbinary(J, F, exp, OP_DIV); break;
+	case EXP_MOD: cbinary(J, F, exp, OP_MOD); break;
 
-	case EXP_BITOR: binary(J, F, exp, OP_BITOR); break;
-	case EXP_BITXOR: binary(J, F, exp, OP_BITXOR); break;
-	case EXP_BITAND: binary(J, F, exp, OP_BITAND); break;
-	case EXP_EQ: binary(J, F, exp, OP_EQ); break;
-	case EXP_NE: binary(J, F, exp, OP_NE); break;
-	case EXP_EQ3: binary(J, F, exp, OP_EQ3); break;
-	case EXP_NE3: binary(J, F, exp, OP_NE3); break;
-	case EXP_LT: binary(J, F, exp, OP_LT); break;
-	case EXP_GT: binary(J, F, exp, OP_GT); break;
-	case EXP_LE: binary(J, F, exp, OP_LE); break;
-	case EXP_GE: binary(J, F, exp, OP_GE); break;
-	case EXP_INSTANCEOF: binary(J, F, exp, OP_INSTANCEOF); break;
-	case EXP_IN: binary(J, F, exp, OP_IN); break;
-	case EXP_SHL: binary(J, F, exp, OP_SHL); break;
-	case EXP_SHR: binary(J, F, exp, OP_SHR); break;
-	case EXP_USHR: binary(J, F, exp, OP_USHR); break;
-	case EXP_ADD: binary(J, F, exp, OP_ADD); break;
-	case EXP_SUB: binary(J, F, exp, OP_SUB); break;
-	case EXP_MUL: binary(J, F, exp, OP_MUL); break;
-	case EXP_DIV: binary(J, F, exp, OP_DIV); break;
-	case EXP_MOD: binary(J, F, exp, OP_MOD); break;
+	case EXP_ASS: cassign(J, F, exp->a, exp->b); break;
+	case EXP_ASS_MUL: cassignop(J, F, exp->a, exp->b, OP_MUL); break;
+	case EXP_ASS_DIV: cassignop(J, F, exp->a, exp->b, OP_DIV); break;
+	case EXP_ASS_MOD: cassignop(J, F, exp->a, exp->b, OP_MOD); break;
+	case EXP_ASS_ADD: cassignop(J, F, exp->a, exp->b, OP_ADD); break;
+	case EXP_ASS_SUB: cassignop(J, F, exp->a, exp->b, OP_SUB); break;
+	case EXP_ASS_SHL: cassignop(J, F, exp->a, exp->b, OP_SHL); break;
+	case EXP_ASS_SHR: cassignop(J, F, exp->a, exp->b, OP_SHR); break;
+	case EXP_ASS_USHR: cassignop(J, F, exp->a, exp->b, OP_USHR); break;
+	case EXP_ASS_BITAND: cassignop(J, F, exp->a, exp->b, OP_BITAND); break;
+	case EXP_ASS_BITXOR: cassignop(J, F, exp->a, exp->b, OP_BITXOR); break;
+	case EXP_ASS_BITOR: cassignop(J, F, exp->a, exp->b, OP_BITOR); break;
 
-	case EXP_ASS:
-		clval(J, F, exp->a);
-		cexp(J, F, exp->b);
-		emit(J, F, OP_STORE);
-		break;
-
-	case EXP_ASS_MUL: assignop(J, F, exp, OP_MUL); break;
-	case EXP_ASS_DIV: assignop(J, F, exp, OP_DIV); break;
-	case EXP_ASS_MOD: assignop(J, F, exp, OP_MOD); break;
-	case EXP_ASS_ADD: assignop(J, F, exp, OP_ADD); break;
-	case EXP_ASS_SUB: assignop(J, F, exp, OP_SUB); break;
-	case EXP_ASS_SHL: assignop(J, F, exp, OP_SHL); break;
-	case EXP_ASS_SHR: assignop(J, F, exp, OP_SHR); break;
-	case EXP_ASS_USHR: assignop(J, F, exp, OP_USHR); break;
-	case EXP_ASS_BITAND: assignop(J, F, exp, OP_BITAND); break;
-	case EXP_ASS_BITXOR: assignop(J, F, exp, OP_BITXOR); break;
-	case EXP_ASS_BITOR: assignop(J, F, exp, OP_BITOR); break;
-
 	case EXP_COMMA:
 		cexp(J, F, exp->a);
 		emit(J, F, OP_POP);
@@ -536,7 +647,7 @@
 	while (list) {
 		js_Ast *stm = list->a;
 		if (stm->type == AST_FUNDEC) {
-			emitfunction(J, F, OP_CLOSURE, newfun(J, stm->a, stm->b, stm->c));
+			emitfunction(J, F, newfun(J, stm->a, stm->b, stm->c));
 			emitstring(J, F, OP_FUNDEC, stm->a->string);
 		}
 		list = list->b;
@@ -558,7 +669,7 @@
 static void cfunbody(JF, js_Ast *name, js_Ast *params, js_Ast *body)
 {
 	if (name) {
-		emitfunction(J, F, OP_CLOSURE, F);
+		emitfunction(J, F, F);
 		emitstring(J, F, OP_FUNDEC, name->string);
 	}
 
@@ -568,10 +679,8 @@
 		cstmlist(J, F, body);
 	}
 
-	if (F->codelen == 0 || F->code[F->codelen - 1] != OP_RETURN) {
-		emit(J, F, OP_UNDEF);
-		emit(J, F, OP_RETURN);
-	}
+	emit(J, F, OP_UNDEF);
+	emit(J, F, OP_RETURN);
 }
 
 int jsC_error(js_State *J, js_Ast *node, const char *fmt, ...)
--- a/jscompile.h
+++ b/jscompile.h
@@ -3,13 +3,19 @@
 
 enum
 {
-	OP_POP,
-	OP_DUP,
+	OP_POP,		/* A -- */
+	OP_DUP,		/* A -- A A */
+	OP_DUP2,	/* A B -- A B A B */
+	OP_DUP1ROT4,	/* A B C -- C A B C */
 
-	OP_CLOSURE,
-	OP_NUMBER,
-	OP_STRING,
+	OP_NUMBER_0,	/* -- 0 */
+	OP_NUMBER_1,	/* -- 1 */
+	OP_NUMBER_X,	/* -K- K */
 
+	OP_NUMBER,	/* -N- <number> */
+	OP_STRING,	/* -S- <string> */
+	OP_CLOSURE,	/* -F- <closure> */
+
 	OP_UNDEF,
 	OP_NULL,
 	OP_TRUE,
@@ -17,33 +23,23 @@
 	OP_THIS,
 
 	OP_NEWARRAY,
-	OP_ARRAYPUT,
 	OP_NEWOBJECT,
-	OP_OBJECTPUT,
 
-	OP_FUNDEC,	/* <closure> -(name)- */
-	OP_VARDEC,	/* -(name)- */
+	OP_FUNDEC,	/* <closure> -S- */
+	OP_VARDEC,	/* -S- */
 
-	OP_LOADVAR,	/* -(name)- <value> */
-	OP_LOADMEMBER,	/* <obj> -(name)- <value> */
-	OP_LOADINDEX,	/* <obj> <idx> -- <value> */
+	OP_GETVAR,	/* -S- <value> */
+	OP_SETVAR,	/* <value> -S- <value> */
+	OP_DELVAR,	/* -S- <success> */
 
-	OP_AVAR,	/* -(name)- <addr> */
-	OP_AMEMBER,	/* <obj> -(name)- <addr> */
-	OP_AINDEX,	/* <obj> <idx> -- <addr> */
+	OP_IN,		/* <name> <obj> -- <exists?> */
+	OP_GETPROP,	/* <obj> <name> -- <value> */
+	OP_SETPROP,	/* <obj> <name> <value> -- <value> */
+	OP_DELPROP,	/* <obj> <name> -- <success> */
 
-	OP_LOAD,	/* <addr> -- <addr> <value> */
-	OP_STORE,	/* <addr> <value> -- <value> */
-
 	OP_CALL,	/* <thisvalue> <closure> <args...> -(numargs)- <returnvalue> */
 	OP_NEW,		/* <closure> <args...> -(numargs)- <returnvalue> */
 
-	OP_DELETE,	/* <addr> -- <success> */
-	OP_PREINC,	/* <addr> -- <value+1> */
-	OP_PREDEC,	/* <addr> -- <value-1> */
-	OP_POSTINC,	/* <addr> -- <value> */
-	OP_POSTDEC,	/* <addr> -- <value> */
-
 	OP_VOID,
 	OP_TYPEOF,
 	OP_POS,
@@ -56,14 +52,12 @@
 	OP_BITAND,
 	OP_EQ,
 	OP_NE,
-	OP_EQ3,
-	OP_NE3,
+	OP_STRICTEQ,
+	OP_STRICTNE,
 	OP_LT,
 	OP_GT,
 	OP_LE,
 	OP_GE,
-	OP_INSTANCEOF,
-	OP_IN,
 	OP_SHL,
 	OP_SHR,
 	OP_USHR,
@@ -73,17 +67,20 @@
 	OP_DIV,
 	OP_MOD,
 
-	OP_TRY,
-	OP_THROW,
-	OP_RETURN,
-	OP_DEBUGGER,
+	OP_INSTANCEOF,
 
+	OP_THROW,
+	OP_TRY,
+	OP_CATCH,
+	OP_ENDCATCH,
 	OP_WITH,
 	OP_ENDWITH,
 
+	OP_DEBUGGER,
 	OP_JUMP,
 	OP_JTRUE,
 	OP_JFALSE,
+	OP_RETURN,
 };
 
 struct js_Function
@@ -94,13 +91,13 @@
 	short *code;
 	int codecap, codelen;
 
-	js_Function **funlist;
+	js_Function **funtab;
 	int funcap, funlen;
 
-	double *numlist;
+	double *numtab;
 	int numcap, numlen;
 
-	const char **strlist;
+	const char **strtab;
 	int strcap, strlen;
 
 	js_Function *next; /* alloc list */
--- a/jsdump.c
+++ b/jsdump.c
@@ -1,7 +1,6 @@
 #include "js.h"
 #include "jsparse.h"
 #include "jscompile.h"
-#include "jsvalue.h"
 #include "jsobject.h"
 
 #include <assert.h>
@@ -194,8 +193,8 @@
 	case EXP_BITAND: pbin(d, i, exp, " & "); break;
 	case EXP_EQ: pbin(d, i, exp, " == "); break;
 	case EXP_NE: pbin(d, i, exp, " != "); break;
-	case EXP_EQ3: pbin(d, i, exp, " === "); break;
-	case EXP_NE3: pbin(d, i, exp, " !== "); break;
+	case EXP_STRICTEQ: pbin(d, i, exp, " === "); break;
+	case EXP_STRICTNE: pbin(d, i, exp, " !== "); break;
 	case EXP_LT: pbin(d, i, exp, " < "); break;
 	case EXP_GT: pbin(d, i, exp, " > "); break;
 	case EXP_LE: pbin(d, i, exp, " <= "); break;
@@ -598,13 +597,13 @@
 
 	printf("function %p %s(%d)\n", F, F->name, F->numparams);
 	for (i = 0; i < F->funlen; i++)
-		printf("\tfunction %p %s\n", F->funlist[i], F->funlist[i]->name);
+		printf("\tfunction %p %s\n", F->funtab[i], F->funtab[i]->name);
 	for (i = 0; i < F->strlen; i++) {
-		ps("\tstring "); pstr(F->strlist[i]); ps("\n");
+		ps("\tstring "); pstr(F->strtab[i]); ps("\n");
 	}
 	// TODO: regexp
 	for (i = 0; i < F->numlen; i++)
-		printf("\tnumber %.9g\n", F->numlist[i]);
+		printf("\tnumber %.9g\n", F->numtab[i]);
 
 	while (p < end) {
 		int c = *p++;
@@ -614,29 +613,27 @@
 
 		switch (c) {
 		case OP_CLOSURE:
-			pc(' ');
-			ps(F->funlist[*p++]->name);
+			ps(" f:");
+			ps(F->funtab[*p++]->name);
 			break;
 		case OP_NUMBER:
-			printf(" %.9g", F->numlist[*p++]);
+			printf(" %.9g", F->numtab[*p++]);
 			break;
 		case OP_STRING:
 			pc(' ');
-			pstr(F->strlist[*p++]);
+			pstr(F->strtab[*p++]);
 			break;
 
-		case OP_OBJECTPUT:
 		case OP_FUNDEC:
 		case OP_VARDEC:
-		case OP_LOADVAR:
-		case OP_LOADMEMBER:
-		case OP_AVAR:
-		case OP_AMEMBER:
+		case OP_GETVAR:
+		case OP_SETVAR:
+		case OP_DELVAR:
 			pc(' ');
-			ps(F->strlist[*p++]);
+			ps(F->strtab[*p++]);
 			break;
 
-		case OP_ARRAYPUT:
+		case OP_NUMBER_X:
 		case OP_CALL:
 		case OP_NEW:
 		case OP_JUMP:
@@ -650,9 +647,9 @@
 	}
 
 	for (i = 0; i < F->funlen; i++) {
-		if (F->funlist[i] != F) {
+		if (F->funtab[i] != F) {
 			nl();
-			jsC_dumpfunction(J, F->funlist[i]);
+			jsC_dumpfunction(J, F->funtab[i]);
 		}
 	}
 }
--- a/jslex.c
+++ b/jslex.c
@@ -525,7 +525,7 @@
 		case '=':
 			if (ACCEPT('=')) {
 				if (ACCEPT('='))
-					return TK_EQ3;
+					return TK_STRICTEQ;
 				return TK_EQ;
 			}
 			return '=';
@@ -533,7 +533,7 @@
 		case '!':
 			if (ACCEPT('=')) {
 				if (ACCEPT('='))
-					return TK_NE3;
+					return TK_STRICTNE;
 				return TK_NE;
 			}
 			return '!';
--- a/jslex.h
+++ b/jslex.h
@@ -12,8 +12,8 @@
 	TK_GE,
 	TK_EQ,
 	TK_NE,
-	TK_EQ3,
-	TK_NE3,
+	TK_STRICTEQ,
+	TK_STRICTNE,
 	TK_SHL,
 	TK_SHR,
 	TK_USHR,
--- a/jsobject.c
+++ b/jsobject.c
@@ -1,5 +1,4 @@
 #include "js.h"
-#include "jsvalue.h"
 #include "jsobject.h"
 
 /*
@@ -21,6 +20,8 @@
 
 static js_Property sentinel = { "", &sentinel, &sentinel, 0 };
 
+static js_Property undefined = { "", &sentinel, &sentinel, 0, { {0}, JS_TUNDEFINED } };
+
 static js_Property *newproperty(const char *key)
 {
 	js_Property *node = malloc(sizeof(js_Property));
@@ -116,11 +117,9 @@
 	case JS_TBOOLEAN: printf(v.u.boolean ? "true" : "false"); break;
 	case JS_TNUMBER: printf("%.9g", v.u.number); break;
 	case JS_TSTRING: printf("'%s'", v.u.string); break;
-	case JS_TREGEXP: printf("/%s/", v.u.regexp.prog); break;
 	case JS_TOBJECT: printf("<object %p>", v.u.object); break;
 	case JS_TCLOSURE: printf("<closure %p>", v.u.closure); break;
 	case JS_TCFUNCTION: printf("<cfunction %p>", v.u.cfunction); break;
-	case JS_TREFERENCE: printf("<reference %p>", v.u.reference); break;
 	}
 }
 
--- a/jsobject.h
+++ b/jsobject.h
@@ -1,6 +1,30 @@
 #ifndef js_object_h
 #define js_object_h
 
+enum js_ValueType {
+	JS_TUNDEFINED,
+	JS_TNULL,
+	JS_TBOOLEAN,
+	JS_TNUMBER,
+	JS_TSTRING,
+	JS_TOBJECT,
+	JS_TCLOSURE,
+	JS_TCFUNCTION,
+};
+
+struct js_Value
+{
+	union {
+		int boolean;
+		double number;
+		const char *string;
+		js_Object *object;
+		js_Closure *closure;
+		js_CFunction *cfunction;
+	} u;
+	js_ValueType type;
+};
+
 struct js_Property
 {
 	char *key;
@@ -13,7 +37,7 @@
 {
 	js_Property *properties;
 	js_Object *prototype;
-	js_Object *parent;
+	js_Object *outer;
 };
 
 js_Object *js_newobject(js_State *J);
@@ -21,6 +45,7 @@
 js_Property *js_setproperty(js_State *J, js_Object *obj, const char *name);
 void js_deleteproperty(js_State *J, js_Object *obj, const char *name);
 
+void jsC_dumpvalue(js_State *J, js_Value v);
 void js_dumpobject(js_State *J, js_Object *obj);
 
 #endif
--- a/jsparse.c
+++ b/jsparse.c
@@ -458,8 +458,8 @@
 loop:
 	if (accept(J, TK_EQ)) { a = EXP2(EQ, a, relational(J, notin)); goto loop; }
 	if (accept(J, TK_NE)) { a = EXP2(NE, a, relational(J, notin)); goto loop; }
-	if (accept(J, TK_EQ3)) { a = EXP2(EQ3, a, relational(J, notin)); goto loop; }
-	if (accept(J, TK_NE3)) { a = EXP2(NE3, a, relational(J, notin)); goto loop; }
+	if (accept(J, TK_STRICTEQ)) { a = EXP2(STRICTEQ, a, relational(J, notin)); goto loop; }
+	if (accept(J, TK_STRICTNE)) { a = EXP2(STRICTNE, a, relational(J, notin)); goto loop; }
 	return a;
 }
 
--- a/jsparse.h
+++ b/jsparse.h
@@ -62,8 +62,8 @@
 	EXP_BITAND,
 	EXP_EQ,
 	EXP_NE,
-	EXP_EQ3,
-	EXP_NE3,
+	EXP_STRICTEQ,
+	EXP_STRICTNE,
 	EXP_LT,
 	EXP_GT,
 	EXP_LE,
--- a/jsrun.c
+++ b/jsrun.c
@@ -1,5 +1,4 @@
 #include "js.h"
-#include "jsvalue.h"
 #include "jsobject.h"
 #include "jscompile.h"
 #include "jsrun.h"
@@ -8,214 +7,453 @@
 static js_Value stack[256];
 static int top = 0;
 
-static inline int i32(double d)
+static inline double tointeger(double n)
 {
+	double sign = n < 0 ? -1 : 1;
+	if (isnan(n)) return 0;
+	if (n == 0 || isinf(n)) return n;
+	return sign * floor(abs(n));
+}
+
+static inline int toint32(double n)
+{
 	double two32 = 4294967296.0;
 	double two31 = 2147483648.0;
 
-	if (!isfinite(d) || d == 0)
+	if (!isfinite(n) || n == 0)
 		return 0;
 
-	d = fmod(d, two32);
-	d = d >= 0 ? floor(d) : ceil(d) + two32;
-	if (d >= two31)
-		return d - two32;
+	n = fmod(n, two32);
+	n = n >= 0 ? floor(n) : ceil(n) + two32;
+	if (n >= two31)
+		return n - two32;
 	else
-		return d;
+		return n;
 }
 
-static inline unsigned int u32(double d)
+static inline unsigned int touint32(double n)
 {
-	return i32(d);
+	return toint32(n);
 }
 
-static inline void push(js_State *J, js_Value v)
+static void js_pushvalue(js_State *J, js_Value v)
 {
-	stack[top++] = v;
+	stack[top] = v;
+	++top;
 }
 
-static inline js_Value pop(js_State *J)
+void js_pushundefined(js_State *J)
 {
-	return stack[--top];
+	stack[top].type = JS_TUNDEFINED;
+	++top;
 }
 
-static inline js_Value peek(js_State *J)
+void js_pushnull(js_State *J)
 {
-	return stack[top-1];
+	stack[top].type = JS_TNULL;
+	++top;
 }
 
-static inline void pushnumber(js_State *J, double number)
+void js_pushboolean(js_State *J, int v)
 {
-	js_Value v;
-	v.type = JS_TNUMBER;
-	v.u.number = number;
-	push(J, v);
+	stack[top].type = JS_TBOOLEAN;
+	stack[top].u.boolean = !!v;
+	++top;
 }
 
-static inline double popnumber(js_State *J)
+void js_pushnumber(js_State *J, double v)
 {
-	js_Value v = pop(J);
-	if (v.type == JS_TNUMBER)
-		return v.u.number;
-	if (v.type == JS_TSTRING)
-		return strtod(v.u.string, 0);
-	return 0;
+	stack[top].type = JS_TNUMBER;
+	stack[top].u.number = v;
+	++top;
 }
 
-static inline void pushundefined(js_State *J)
+void js_pushstring(js_State *J, const char *v)
 {
-	js_Value v;
-	v.type = JS_TUNDEFINED;
-	push(J, v);
+	stack[top].type = JS_TSTRING;
+	stack[top].u.string = v;
+	++top;
 }
 
-static inline void pushnull(js_State *J)
+void js_pushobject(js_State *J, js_Object *v)
 {
-	js_Value v;
-	v.type = JS_TNULL;
-	push(J, v);
+	stack[top].type = JS_TOBJECT;
+	stack[top].u.object = v;
+	++top;
 }
 
-static inline void pushboolean(js_State *J, int boolean)
+js_Value js_tovalue(js_State *J, int idx)
 {
-	js_Value v;
-	v.type = JS_TBOOLEAN;
-	v.u.boolean = boolean;
-	push(J, v);
+	idx += top;
+	return stack[idx];
 }
 
-static inline int popboolean(js_State *J)
+int js_toboolean(js_State *J, int idx)
 {
-	js_Value v = pop(J);
-	if (v.type == JS_TBOOLEAN)
-		return v.u.boolean;
-	if (v.type == JS_TNUMBER)
-		return v.u.number != 0;
-	return 0;
+	const char *s;
+	double n;
+	idx += top;
+	switch (stack[idx].type) {
+	case JS_TUNDEFINED: return 0;
+	case JS_TNULL: return 0;
+	case JS_TBOOLEAN: return stack[idx].u.boolean;
+	case JS_TNUMBER: n = stack[idx].u.number; return n != 0 || !isnan(n);
+	case JS_TSTRING: s = stack[idx].u.string; return s[0] != 0;
+	default: return 1;
+	}
 }
 
-static inline void pushreference(js_State *J, js_Property *reference)
+double js_tonumber(js_State *J, int idx)
 {
-	js_Value v;
-	v.type = JS_TREFERENCE;
-	v.u.reference = reference;
-	push(J, v);
+	idx += top;
+	switch (stack[idx].type) {
+	case JS_TUNDEFINED: return NAN;
+	case JS_TNULL: return 0;
+	case JS_TBOOLEAN: return stack[idx].u.boolean;
+	case JS_TNUMBER: return stack[idx].u.number;
+	case JS_TSTRING: return strtod(stack[idx].u.string, NULL);
+	default: return 0;
+	}
 }
 
-static inline js_Property *popreference(js_State *J)
+double js_tointeger(js_State *J, int idx)
 {
-	js_Value v = pop(J);
-	if (v.type == JS_TREFERENCE)
-		return v.u.reference;
-	return 0;
+	return toint32(js_tonumber(J, idx));
 }
 
-static inline js_Property *peekreference(js_State *J)
+int js_toint32(js_State *J, int idx)
 {
-	js_Value v = peek(J);
-	if (v.type == JS_TREFERENCE)
-		return v.u.reference;
-	return 0;
+	return toint32(js_tonumber(J, idx));
 }
 
-#define UNARY(X) a = popnumber(J); pushnumber(J, X)
-#define BINARY(X) b = popnumber(J); a = popnumber(J); pushnumber(J, X)
+unsigned int js_touint32(js_State *J, int idx)
+{
+	return toint32(js_tonumber(J, idx));
+}
 
+const char *js_tostring(js_State *J, int idx)
+{
+	char buf[20];
+	idx += top;
+	switch (stack[idx].type) {
+	case JS_TUNDEFINED: return "undefined";
+	case JS_TNULL: return "null";
+	case JS_TBOOLEAN: return stack[idx].u.boolean ? "true" : "false";
+	case JS_TNUMBER: sprintf(buf, "%.9g", stack[idx].u.number); return js_intern(J, buf);
+	case JS_TSTRING: return stack[idx].u.string;
+	default: return "undefined";
+	}
+}
+
+js_Object *js_toobject(js_State *J, int idx)
+{
+	idx += top;
+	switch (stack[idx].type) {
+	case JS_TUNDEFINED: jsR_error(J, "TypeError");
+	case JS_TNULL: jsR_error(J, "TypeError");
+	case JS_TBOOLEAN: jsR_error(J, "new Boolean()");
+	case JS_TNUMBER: jsR_error(J, "new Number()");
+	case JS_TSTRING: jsR_error(J, "new String()");
+	case JS_TOBJECT: return stack[idx].u.object;
+	default: jsR_error(J, "TypeError");
+	}
+	return NULL;
+}
+
+void js_pop(js_State *J, int n)
+{
+	top -= n;
+}
+
+void js_dup(js_State *J)
+{
+	stack[top] = stack[top-1];
+	++top;
+}
+
+void js_dup2(js_State *J)
+{
+	stack[top] = stack[top-2];
+	stack[top+1] = stack[top-1];
+	top += 2;
+}
+
+void js_rot3pop2(js_State *J)
+{
+	/* A B C -> C */
+	stack[top-3] = stack[top-1];
+	top -= 2;
+}
+
+void js_dup1rot4(js_State *J)
+{
+	/* A B C -> C A B C */
+	stack[top] = stack[top-1];	/* A B C C */
+	stack[top-1] = stack[top-2];	/* A B B C */
+	stack[top-2] = stack[top-3];	/* A A B C */
+	stack[top-3] = stack[top];	/* C A B C */
+}
+
+void js_trap(js_State *J)
+{
+}
+
 static void runfun(js_State *J, js_Function *F, js_Object *E)
 {
+	js_Function **FT = F->funtab;
+	double *NT = F->numtab;
+	const char **ST = F->strtab;
+	short *pcstart = F->code;
 	short *pc = F->code;
-	int opcode, addr;
-	js_Property *p;
-	js_Value v;
-	double a, b;
-	const char *s;
+	int opcode, offset;
 
+	const char *str;
+	js_Object *obj;
+	js_Property *ref;
+	double x, y;
+	int b;
+
 	while (1) {
 		opcode = *pc++;
 		switch (opcode) {
-		case OP_NUMBER:
-			pushnumber(J, F->numlist[*pc++]);
-			break;
+		case OP_POP: js_pop(J, 1); break;
+		case OP_DUP: js_dup(J); break;
+		case OP_DUP2: js_dup2(J); break;
+		case OP_DUP1ROT4: js_dup1rot4(J); break;
 
-		case OP_LOADVAR:
-			s = F->strlist[*pc++];
-			p = js_getproperty(J, E, s);
-			if (p)
-				push(J, p->value);
+		case OP_NUMBER_0: js_pushnumber(J, 0); break;
+		case OP_NUMBER_1: js_pushnumber(J, 1); break;
+		case OP_NUMBER_X: js_pushnumber(J, *pc++); break;
+		case OP_NUMBER: js_pushnumber(J, NT[*pc++]); break;
+		case OP_STRING: js_pushstring(J, ST[*pc++]); break;
+		// case OP_CLOSURE: break;
+
+		case OP_UNDEF: js_pushundefined(J); break;
+		case OP_NULL: js_pushnull(J); break;
+		case OP_TRUE: js_pushboolean(J, 1); break;
+		case OP_FALSE: js_pushboolean(J, 0); break;
+		// case OP_THIS: break;
+
+		case OP_NEWOBJECT: js_pushobject(J, js_newobject(J)); break;
+		case OP_NEWARRAY: js_pushobject(J, js_newobject(J)); break;
+
+		// FUNDEC
+		// VARDEC
+
+		case OP_GETVAR:
+			ref = js_getproperty(J, E, ST[*pc++]);
+			if (ref)
+				js_pushvalue(J, ref->value);
 			else
-				pushundefined(J);
+				js_pushundefined(J);
 			break;
 
-		case OP_AVAR:
-			s = F->strlist[*pc++];
-			p = js_setproperty(J, E, s);
-			pushreference(J, p);
+		case OP_SETVAR:
+			ref = js_setproperty(J, E, ST[*pc++]);
+			if (ref)
+				ref->value = js_tovalue(J, -1);
 			break;
 
-		case OP_LOAD:
-			p = peekreference(J);
-			if (p)
-				push(J, p->value);
+		// OP_DELVAR
+
+		case OP_IN:
+			str = js_tostring(J, -2);
+			obj = js_toobject(J, -1);
+			ref = js_getproperty(J, obj, str);
+			js_pop(J, 2);
+			js_pushboolean(J, ref != NULL);
+			break;
+
+		case OP_GETPROP:
+			obj = js_toobject(J, -2);
+			str = js_tostring(J, -1);
+			ref = js_getproperty(J, obj, str);
+			js_pop(J, 2);
+			if (ref)
+				js_pushvalue(J, ref->value);
 			else
-				pushundefined(J);
+				js_pushundefined(J);
 			break;
 
-		case OP_STORE:
-			v = pop(J);
-			p = popreference(J);
-			if (p)
-				p->value = v;
-			push(J, v);
+		case OP_SETPROP:
+			obj = js_toobject(J, -3);
+			str = js_tostring(J, -2);
+			ref = js_setproperty(J, obj, str);
+			if (ref)
+				ref->value = js_tovalue(J, -1);
+			js_rot3pop2(J);
 			break;
 
-		case OP_UNDEF: pushundefined(J); break;
-		case OP_NULL: pushnull(J); break;
-		case OP_TRUE: pushboolean(J, 1); break;
-		case OP_FALSE: pushboolean(J, 0); break;
+		// OP_DELPROP
 
-		case OP_POS: UNARY(a); break;
-		case OP_NEG: UNARY(-a); break;
-		case OP_BITNOT: UNARY(~i32(a)); break;
-		case OP_LOGNOT: UNARY(!a); break;
+		/* Unary expressions */
 
-		case OP_ADD: BINARY(a + b); break;
-		case OP_SUB: BINARY(a - b); break;
-		case OP_MUL: BINARY(a * b); break;
-		case OP_DIV: BINARY(a / b); break;
-		case OP_MOD: BINARY(fmod(a, b)); break;
-		case OP_SHL: BINARY(i32(a) << (u32(b) & 0x1F)); break;
-		case OP_SHR: BINARY(i32(a) >> (u32(b) & 0x1F)); break;
-		case OP_USHR: BINARY(u32(a) >> (u32(b) & 0x1F)); break;
-		case OP_BITAND: BINARY(i32(a) & i32(b)); break;
-		case OP_BITXOR: BINARY(i32(a) ^ i32(b)); break;
-		case OP_BITOR: BINARY(i32(a) | i32(b)); break;
+		case OP_POS:
+			x = js_tonumber(J, -1);
+			js_pop(J, 1);
+			js_pushnumber(J, x);
+			break;
 
-		case OP_LT: BINARY(a < b); break;
-		case OP_GT: BINARY(a > b); break;
-		case OP_LE: BINARY(a <= b); break;
-		case OP_GE: BINARY(a >= b); break;
-		case OP_EQ: BINARY(a == b); break;
-		case OP_NE: BINARY(a != b); break;
+		case OP_NEG:
+			x = js_tonumber(J, -1);
+			js_pop(J, 1);
+			js_pushnumber(J, -x);
+			break;
 
-		case OP_JFALSE:
-			addr = *pc++;
-			if (!popboolean(J))
-				pc = F->code + addr;
+		case OP_BITNOT:
+			x = js_tonumber(J, -1);
+			js_pop(J, 1);
+			js_pushnumber(J, ~toint32(x));
 			break;
 
-		case OP_JTRUE:
-			addr = *pc++;
-			if (popboolean(J))
-				pc = F->code + addr;
+		case OP_LOGNOT:
+			b = js_toboolean(J, -1);
+			js_pop(J, 1);
+			js_pushnumber(J, !b);
 			break;
 
+		/* Binary expressions */
+
+		case OP_ADD:
+			// TODO: check string concatenation
+			x = js_tonumber(J, -2);
+			y = js_tonumber(J, -1);
+			js_pop(J, 2);
+			js_pushnumber(J, x + y);
+			break;
+
+		case OP_SUB:
+			x = js_tonumber(J, -2);
+			y = js_tonumber(J, -1);
+			js_pop(J, 2);
+			js_pushnumber(J, x - y);
+			break;
+
+		case OP_MUL:
+			x = js_tonumber(J, -2);
+			y = js_tonumber(J, -1);
+			js_pop(J, 2);
+			js_pushnumber(J, x * y);
+			break;
+
+		case OP_DIV:
+			x = js_tonumber(J, -2);
+			y = js_tonumber(J, -1);
+			js_pop(J, 2);
+			js_pushnumber(J, x / y);
+			break;
+
+		case OP_MOD:
+			x = js_tonumber(J, -2);
+			y = js_tonumber(J, -1);
+			js_pop(J, 2);
+			js_pushnumber(J, fmod(x, y));
+			break;
+
+		case OP_SHL:
+			x = js_tonumber(J, -2);
+			y = js_tonumber(J, -1);
+			js_pop(J, 2);
+			js_pushnumber(J, toint32(x) << (touint32(y) & 0x1F));
+			break;
+
+		case OP_SHR:
+			x = js_tonumber(J, -2);
+			y = js_tonumber(J, -1);
+			js_pop(J, 2);
+			js_pushnumber(J, toint32(x) >> (touint32(y) & 0x1F)); break;
+			break;
+
+		case OP_USHR:
+			x = js_tonumber(J, -2);
+			y = js_tonumber(J, -1);
+			js_pop(J, 2);
+			js_pushnumber(J, touint32(x) >> (touint32(y) & 0x1F)); break;
+			break;
+
+		case OP_BITAND:
+			x = js_tonumber(J, -2);
+			y = js_tonumber(J, -1);
+			js_pop(J, 2);
+			js_pushnumber(J, toint32(x) & toint32(y));
+			break;
+
+		case OP_BITXOR:
+			x = js_tonumber(J, -2);
+			y = js_tonumber(J, -1);
+			js_pop(J, 2);
+			js_pushnumber(J, toint32(x) ^ toint32(y));
+			break;
+
+		case OP_BITOR:
+			x = js_tonumber(J, -2);
+			y = js_tonumber(J, -1);
+			js_pop(J, 2);
+			js_pushnumber(J, toint32(x) | toint32(y));
+			break;
+
+		/* Relational expressions */
+
+		/* TODO: string comparisons */
+		case OP_LT:
+			x = js_tonumber(J, -2);
+			y = js_tonumber(J, -1);
+			js_pop(J, 2);
+			js_pushboolean(J, x < y);
+			break;
+		case OP_GT:
+			x = js_tonumber(J, -2);
+			y = js_tonumber(J, -1);
+			js_pop(J, 2);
+			js_pushboolean(J, x > y);
+			break;
+		case OP_LE:
+			x = js_tonumber(J, -2);
+			y = js_tonumber(J, -1);
+			js_pop(J, 2);
+			js_pushboolean(J, x <= y);
+			break;
+		case OP_GE:
+			x = js_tonumber(J, -2);
+			y = js_tonumber(J, -1);
+			js_pop(J, 2);
+			js_pushboolean(J, x >= y);
+			break;
+
+		// case OP_EQ:
+		// case OP_NE:
+		// case OP_STRICTEQ:
+		// case OP_STRICTNE:
+
+		/* Branching */
+
+		case OP_DEBUGGER:
+			js_trap(J);
+			break;
+
 		case OP_JUMP:
-			pc = F->code + *pc;
+			pc = pcstart + *pc;
 			break;
 
-		case OP_POP: pop(J); break;
-		case OP_RETURN: return;
+		case OP_JTRUE:
+			offset = *pc++;
+			b = js_toboolean(J, -1);
+			js_pop(J, 1);
+			if (b)
+				pc = pcstart + offset;
+			break;
 
+		case OP_JFALSE:
+			offset = *pc++;
+			b = js_toboolean(J, -1);
+			js_pop(J, 1);
+			if (!b)
+				pc = pcstart + offset;
+			break;
+
+		case OP_RETURN:
+			return;
+
 		default:
 			fprintf(stderr, "illegal instruction: %d (pc=%d)\n", opcode, (int)(pc - F->code - 1));
 			return;
@@ -223,9 +461,22 @@
 	}
 }
 
+void jsR_error(js_State *J, const char *message)
+{
+	fprintf(stderr, "runtime error: %s\n", message);
+	longjmp(J->jb, 1);
+}
+
 void jsR_runfunction(js_State *J, js_Function *F)
 {
 	js_Object *varenv = js_newobject(J);
+
+	if (setjmp(J->jb)) {
+		js_dumpobject(J, varenv);
+		return;
+	}
+
 	runfun(J, F, varenv);
+
 	js_dumpobject(J, varenv);
 }
--- a/jsrun.h
+++ b/jsrun.h
@@ -1,6 +1,7 @@
 #ifndef js_run_h
 #define js_run_h
 
+void jsR_error(js_State *J, const char *message);
 void jsR_runfunction(js_State *J, js_Function *F);
 
 #endif
--- a/jsvalue.h
+++ /dev/null
@@ -1,37 +1,0 @@
-#ifndef js_value_h
-#define js_value_h
-
-enum js_ValueType {
-	JS_TUNDEFINED,
-	JS_TNULL,
-	JS_TBOOLEAN,
-	JS_TNUMBER,
-	JS_TSTRING,
-	JS_TREGEXP,
-	JS_TOBJECT,
-	JS_TCLOSURE,
-	JS_TCFUNCTION,
-	JS_TREFERENCE,	/* l-value from aval/aindex/amember */
-};
-
-struct js_Value
-{
-	union {
-		int boolean;
-		double number;
-		const char *string;
-		struct {
-			const char *prog;
-			unsigned char flags;
-		} regexp;
-		js_Object *object;
-		js_Closure *closure;
-		js_CFunction *cfunction;
-		js_Property *reference;
-	} u;
-	js_ValueType type;
-};
-
-void jsC_dumpvalue(js_State *J, js_Value v);
-
-#endif