shithub: libmujs

Download patch

ref: 5756da56d1026165a7c130b8aa3b81265f08c864
parent: 3776ea28c87ecc0a31d9230384587b11b72cb1cd
author: Tor Andersson <tor.andersson@artifex.com>
date: Sat Jan 11 10:21:49 EST 2014

Begin generating byte code.

--- a/js.h
+++ b/js.h
@@ -10,14 +10,20 @@
 #include <math.h>
 
 typedef struct js_State js_State;
+
 typedef struct js_StringNode js_StringNode;
 typedef struct js_Ast js_Ast;
 
+typedef enum js_ValueType js_ValueType;
+typedef struct js_Value js_Value;
+typedef struct js_RegExp js_RegExp;
+typedef struct js_Object js_Object;
+typedef struct js_Function js_Function;
+typedef int (*js_CFunction)(js_State *J);
+
 #define JS_REGEXP_G 1
 #define JS_REGEXP_I 2
 #define JS_REGEXP_M 4
-
-typedef int (*js_CFunction)(js_State *J);
 
 js_State *js_newstate(void);
 void js_close(js_State *J);
--- /dev/null
+++ b/jscompile.c
@@ -1,0 +1,450 @@
+#include "js.h"
+#include "jsparse.h"
+#include "jscompile.h"
+#include "jsrun.h"
+#include "jsstate.h"
+
+#define cexp js_cexp /* collision with math.h */
+
+#define JF js_State *J, js_Function *F
+
+static void cexp(JF, js_Ast *exp);
+static void cstmlist(JF, js_Ast *list);
+
+static int consteq(js_Value a, js_Value b)
+{
+	if (a.type != b.type)
+		return 0;
+	if (a.type == JS_TNUMBER)
+		return a.u.number == b.u.number;
+	else
+		return a.u.p == b.u.p;
+}
+
+static int addconst(JF, js_Value v)
+{
+	int i;
+
+	for (i = 0; i < F->klen; i++) {
+		if (consteq(F->klist[i], v)) {
+			return i;
+		}
+	}
+
+	if (F->klen >= F->kcap) {
+		F->kcap *= 2;
+		F->klist = realloc(F->klist, F->kcap * sizeof(js_Value));
+	}
+
+	F->klist[F->klen] = v;
+	return F->klen++;
+}
+
+static void emit(JF, int value)
+{
+	if (F->len >= F->cap) {
+		F->cap *= 2;
+		F->code = realloc(F->code, F->cap);
+	}
+
+	F->code[F->len++] = value;
+}
+
+static void emitnumber(JF, int opcode, double n)
+{
+	js_Value v;
+	v.type = JS_TNUMBER;
+	v.u.number = n;
+	emit(J, F, opcode);
+	emit(J, F, addconst(J, F, v));
+}
+
+static void emitstring(JF, int opcode, const char *s)
+{
+	js_Value v;
+	v.type = JS_TSTRING;
+	v.u.string = s;
+	emit(J, F, opcode);
+	emit(J, F, addconst(J, F, v));
+}
+
+static void unary(JF, js_Ast *exp, int opcode)
+{
+	cexp(J, F, exp->a);
+	emit(J, F, opcode);
+}
+
+static void binary(JF, js_Ast *exp, int opcode)
+{
+	cexp(J, F, exp->a);
+	cexp(J, F, exp->b);
+	emit(J, F, opcode);
+}
+
+static void carray(JF, js_Ast *list)
+{
+	while (list) {
+		cexp(J, F, list->a);
+		emit(J, F, OP_ARRAYPUT);
+		list = list->b;
+	}
+}
+
+static void cobject(JF, js_Ast *list)
+{
+	while (list) {
+		js_Ast *kv = list->a;
+		if (kv->type == EXP_PROP_VAL) {
+			js_Ast *prop = kv->a;
+			cexp(J, F, kv->b);
+			if (prop->type == AST_IDENTIFIER || prop->type == AST_STRING)
+				emitstring(J, F, OP_OBJECTPUT, prop->string);
+			else if (prop->type == AST_NUMBER)
+				emitnumber(J, F, OP_OBJECTPUT, prop->number);
+			else
+				jsC_error(J, list, "illegal property name in object initializer");
+		}
+		// TODO: set, get
+		list = list->b;
+	}
+}
+
+static int cargs(JF, js_Ast *list)
+{
+	int n = 0;
+	while (list) {
+		cexp(J, F, list->a);
+		list = list->b;
+		n++;
+	}
+	return n;
+}
+
+static void clval(JF, js_Ast *exp)
+{
+	switch (exp->type) {
+	case AST_IDENTIFIER:
+		emitstring(J, F, OP_VAR, exp->string);
+		break;
+	case EXP_INDEX:
+		cexp(J, F, exp->a);
+		cexp(J, F, exp->b);
+		emit(J, F, OP_INDEX);
+		break;
+	case EXP_MEMBER:
+		cexp(J, F, exp->a);
+		emitstring(J, F, OP_MEMBER, exp->b->string);
+		break;
+	default:
+		jsC_error(J, exp, "invalid l-value in assignment");
+		break;
+	}
+}
+
+static void assignop(JF, js_Ast *exp, int opcode)
+{
+	clval(J, F, exp->a);
+	emit(J, F, OP_DUPLOAD);
+	cexp(J, F, exp->b);
+	emit(J, F, opcode);
+	emit(J, F, OP_STORE);
+}
+
+static void cexp(JF, js_Ast *exp)
+{
+	int n;
+
+	switch (exp->type) {
+	case AST_IDENTIFIER:
+		emitstring(J, F, OP_VAR, exp->string);
+		emit(J, F, OP_LOAD);
+		break;
+
+	case AST_NUMBER: emitnumber(J, F, OP_CONST, exp->number); break;
+	case AST_STRING: emitstring(J, F, OP_CONST, exp->string); 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;
+	case EXP_FALSE: emit(J, F, OP_FALSE); break;
+	case EXP_THIS: emit(J, F, OP_THIS); break;
+
+	case EXP_OBJECT:
+		emit(J, F, OP_OBJECT);
+		cobject(J, F, exp->a);
+		break;
+
+	case EXP_ARRAY:
+		emit(J, F, OP_ARRAY);
+		carray(J, F, exp->a);
+		break;
+
+	case EXP_INDEX:
+		cexp(J, F, exp->a);
+		cexp(J, F, exp->b);
+		emit(J, F, OP_INDEX);
+		emit(J, F, OP_LOAD);
+		break;
+
+	case EXP_MEMBER:
+		cexp(J, F, exp->a);
+		emitstring(J, F, OP_MEMBER, exp->b->string);
+		emit(J, F, OP_LOAD);
+		break;
+
+	case EXP_CALL:
+		cexp(J, F, exp->a);
+		n = cargs(J, F, exp->b);
+		emit(J, F, OP_CALL);
+		emit(J, F, n);
+		break;
+
+	case EXP_NEW:
+		cexp(J, F, exp->a);
+		n = cargs(J, F, exp->b);
+		emit(J, F, OP_NEW);
+		emit(J, F, n);
+		break;
+
+	case EXP_VOID:
+		cexp(J, F, exp->a);
+		emit(J, F, OP_POP);
+		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_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_LOGOR: binary(J, F, exp, OP_LOGOR); break;
+	case EXP_LOGAND: binary(J, F, exp, OP_LOGAND); 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:
+		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);
+		cexp(J, F, exp->b);
+		break;
+
+	default:
+		jsC_error(J, exp, "unknown expression");
+	}
+}
+
+static void cvardec(JF, js_Ast *vardec)
+{
+	if (vardec->b)
+		cexp(J, F, vardec->b);
+	else
+		emit(J, F, OP_UNDEF);
+	emitstring(J, F, OP_DEFVAR, vardec->a->string);
+}
+
+static void cvardeclist(JF, js_Ast *list)
+{
+	while (list) {
+		cvardec(J, F, list->a);
+		list = list->b;
+	}
+}
+
+static int emitjump(JF, int opcode)
+{
+	int addr = F->len + 1;
+	emit(J, F, opcode);
+	emit(J, F, 0);
+	emit(J, F, 0);
+	return addr;
+}
+
+static void emitlabel(JF, int addr)
+{
+	int dest = F->len;
+	F->code[addr+0] = (dest >> 8) & 0xFF;
+	F->code[addr+1] = (dest) & 0xFF;
+}
+
+static void cstm(JF, js_Ast *stm)
+{
+	int lelse, lend;
+
+	switch (stm->type) {
+	case STM_BLOCK:
+		cstmlist(J, F, stm->a);
+		break;
+
+	case STM_NOP:
+		break;
+
+	case STM_VAR:
+		cvardeclist(J, F, stm->a);
+		break;
+
+	case STM_IF:
+		cexp(J, F, stm->a);
+		lelse = emitjump(J, F, OP_JFALSE);
+		cstm(J, F, stm->b);
+		emitlabel(J, F, lelse);
+		if (stm->c) {
+			lend = emitjump(J, F, OP_JUMP);
+			cstm(J, F, stm->c);
+			emitlabel(J, F, lend);
+		}
+		break;
+
+	case STM_RETURN:
+		if (stm->a)
+			cexp(J, F, stm->a);
+		else
+			emit(J, F, OP_UNDEF);
+		emit(J, F, OP_RETURN);
+		break;
+
+	case STM_WITH:
+		cexp(J, F, stm->a);
+		emit(J, F, OP_PUSHWITH);
+		cstm(J, F, stm->b);
+		emit(J, F, OP_POPWITH);
+		break;
+
+	// switch
+	// throw, try
+	// label
+
+	case STM_THROW:
+		cexp(J, F, stm->a);
+		emit(J, F, OP_THROW);
+		break;
+
+	case STM_DEBUGGER:
+		emit(J, F, OP_DEBUGGER);
+		break;
+
+	default:
+		cexp(J, F, stm);
+		emit(J, F, OP_POP);
+		break;
+	}
+}
+
+static void cstmlist(JF, js_Ast *list)
+{
+	while (list) {
+		cstm(J, F, list->a);
+		list = list->b;
+	}
+}
+
+static js_Function *newfun(js_State *J)
+{
+	js_Function *F = malloc(sizeof(js_Function));
+
+	F->cap = 256;
+	F->len = 0;
+	F->code = malloc(F->cap);
+
+	F->kcap = 16;
+	F->klen = 0;
+	F->klist = malloc(F->kcap * sizeof(js_Value));
+
+	F->next = J->fun;
+	J->fun = F;
+
+	return F;
+}
+
+static void freefun(js_State *J, js_Function *F)
+{
+	free(F->klist);
+	free(F->code);
+	free(F);
+}
+
+void jsC_freecompile(js_State *J)
+{
+	js_Function *F = J->fun;
+	while (F) {
+		js_Function *next = F->next;
+		freefun(J, F);
+		F = next;
+	}
+	J->fun = NULL;
+}
+
+int jsC_error(js_State *J, js_Ast *node, const char *fmt, ...)
+{
+	va_list ap;
+
+	fprintf(stderr, "%s:%d: error: ", J->filename, node->line);
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	fprintf(stderr, "\n");
+
+	longjmp(J->jb, 1);
+	return 0;
+}
+
+js_Function *jsC_compile(js_State *J, js_Ast *prog)
+{
+	js_Function *F;
+
+	if (setjmp(J->jb)) {
+		jsC_freecompile(J);
+		return NULL;
+	}
+
+	F = newfun(J);
+	cstmlist(J, F, prog);
+
+	emit(J, F, OP_UNDEF);
+	emit(J, F, OP_RETURN);
+
+	J->fun = NULL;
+	return F;
+}
+
--- /dev/null
+++ b/jscompile.h
@@ -1,0 +1,97 @@
+#ifndef js_compile_h
+#define js_compile_h
+
+enum
+{
+	OP_CONST,
+	OP_UNDEF,
+	OP_NULL,
+	OP_TRUE,
+	OP_FALSE,
+	OP_THIS,
+
+	OP_ARRAY,
+	OP_ARRAYPUT,
+	OP_OBJECT,
+	OP_OBJECTPUT,
+
+	OP_DEFVAR,
+	OP_VAR,
+	OP_INDEX,
+	OP_MEMBER,
+	OP_LOAD,
+	OP_DUPLOAD,
+	OP_STORE,
+
+	OP_CALL,
+	OP_NEW,
+	OP_CLOSURE,
+
+	OP_DELETE,
+	OP_VOID,
+	OP_TYPEOF,
+	OP_PREINC,
+	OP_POSTINC,
+	OP_PREDEC,
+	OP_POSTDEC,
+	OP_POS,
+	OP_NEG,
+	OP_BITNOT,
+	OP_LOGNOT,
+
+	OP_LOGOR,
+	OP_LOGAND,
+	OP_BITOR,
+	OP_BITXOR,
+	OP_BITAND,
+	OP_EQ,
+	OP_NE,
+	OP_EQ3,
+	OP_NE3,
+	OP_LT,
+	OP_GT,
+	OP_LE,
+	OP_GE,
+	OP_INSTANCEOF,
+	OP_IN,
+	OP_SHL,
+	OP_SHR,
+	OP_USHR,
+	OP_ADD,
+	OP_SUB,
+	OP_MUL,
+	OP_DIV,
+	OP_MOD,
+
+	OP_JUMP,
+	OP_JTRUE,
+	OP_JFALSE,
+
+	OP_TRY,
+	OP_THROW,
+	OP_RETURN,
+	OP_PUSHWITH,
+	OP_POPWITH,
+	OP_DEBUGGER,
+
+	OP_POP,
+};
+
+struct js_Function
+{
+	unsigned char *code;
+	int cap, len;
+
+	js_Value *klist;
+	int kcap, klen;
+
+	js_Function *next;
+};
+
+js_Function *jsC_compile(js_State *J, js_Ast *prog);
+void jsC_freecompile(js_State *J);
+int jsC_error(js_State *J, js_Ast *node, const char *fmt, ...);
+void jsC_dumpvalue(js_State *J, js_Value v);
+void jsC_dumpfunction(js_State *J, js_Function *fun);
+
+#endif
--- a/jsdump.c
+++ b/jsdump.c
@@ -1,9 +1,11 @@
 #include "js.h"
 #include "jsparse.h"
+#include "jscompile.h"
+#include "jsrun.h"
 
 #include <assert.h>
 
-static const char *stype[] = {
+static const char *astname[] = {
 	"list", "ident", "number", "string", "regexp", "undef", "null", "true",
 	"false", "this", "array", "object", "prop_val", "prop_get", "prop_set",
 	"index", "member", "call", "new", "funexp", "delete", "void", "typeof",
@@ -15,10 +17,23 @@
 	"ass_ushr", "ass_bitand", "ass_bitxor", "ass_bitor", "comma",
 	"var-init", "block", "fundec", "nop", "var", "if", "do-while", "while",
 	"for", "for-var", "for-in", "for-in-var", "continue", "break",
-	"return", "with", "switch", "throw", "try", "label", "case", "default",
-	"debugger",
+	"return", "with", "switch", "throw", "try", "debugger", "label",
+	"case", "default",
 };
 
+static const char *opname[] = {
+	"const", "undef", "null", "true", "false", "this",
+	"array", "arrayput",
+	"object", "objectput",
+	"defvar", "var", "index", "member", "load", "dupload", "store", "call", "new",
+	"closure", "delete", "void", "typeof", "preinc", "postinc", "predec",
+	"postdec", "pos", "neg", "bitnot", "lognot", "logor", "logand",
+	"bitor", "bitxor", "bitand", "eq", "ne", "eq3", "ne3", "lt", "gt",
+	"le", "ge", "instanceof", "in", "shl", "shr", "ushr", "add", "sub",
+	"mul", "div", "mod", "jump", "jtrue", "jfalse", "try", "throw",
+	"return", "pushwith", "popwith", "debugger", "pop",
+};
+
 static void pstmlist(int d, js_Ast *list);
 static void pexpi(int d, int i, js_Ast *exp);
 static void pstm(int d, js_Ast *stm);
@@ -499,7 +514,7 @@
 		pstmlist(-1, prog);
 	else {
 		pstm(0, prog);
-		pc('\n');
+		nl();
 	}
 }
 
@@ -520,7 +535,7 @@
 	}
 
 	pc('(');
-	ps(stype[node->type]);
+	ps(astname[node->type]);
 	switch (node->type) {
 	case AST_IDENTIFIER: pc(' '); ps(node->string); break;
 	case AST_STRING: pc(' '); pstr(node->string); break;
@@ -561,11 +576,11 @@
 		snode(d+1, list->a);
 		list = list->b;
 		if (list) {
-			pc('\n');
+			nl();
 			in(d+1);
 		}
 	}
-	pc('\n'); in(d); pc(']');
+	nl(); in(d); pc(']');
 }
 
 void jsP_dumplist(js_State *J, js_Ast *prog)
@@ -574,5 +589,65 @@
 		sblock(0, prog);
 	else
 		snode(0, prog);
-	pc('\n');
+	nl();
+}
+
+void jsC_dumpvalue(js_State *J, js_Value v)
+{
+	switch (v.type) {
+	case JS_TUNDEFINED: ps("undefined"); break;
+	case JS_TNULL: ps("null"); break;
+	case JS_TBOOLEAN: ps(v.u.boolean ? "true" : "false"); break;
+	case JS_TNUMBER: printf("%.9g", v.u.number); break;
+	case JS_TSTRING: pstr(v.u.string); break;
+	case JS_TREGEXP: printf("<regexp %p>", v.u.p); break;
+	case JS_TOBJECT: printf("<object %p>", v.u.p); break;
+
+	case JS_TFUNCTION: printf("<function %p>", v.u.p); break;
+	case JS_TCFUNCTION: printf("<cfunction %p>", v.u.p); break;
+	case JS_TCLOSURE: printf("<closure %p>", v.u.p); break;
+	case JS_TARGUMENTS: printf("<arguments %p>", v.u.p); break;
+
+	case JS_TOBJSLOT: printf("<objslot %p>", v.u.p); break;
+	}
+}
+
+void jsC_dumpfunction(js_State *J, js_Function *fun)
+{
+	unsigned char *p = fun->code;
+	unsigned char *end = fun->code + fun->len;
+	int dest;
+
+	printf("function with %d constants\n", fun->klen);
+
+	while (p < end) {
+		int c = *p++;
+
+		printf("%04d: ", (int)(p - fun->code) - 1);
+		ps(opname[c]);
+
+		switch (c) {
+		case OP_CONST:
+		case OP_OBJECTPUT:
+		case OP_DEFVAR:
+		case OP_VAR:
+		case OP_MEMBER:
+			pc(' ');
+			jsC_dumpvalue(J, fun->klist[*p++]);
+			break;
+		case OP_CALL:
+		case OP_NEW:
+			printf(" %d", *p++);
+			break;
+		case OP_JUMP:
+		case OP_JTRUE:
+		case OP_JFALSE:
+			dest = (*p++) << 8;
+			dest += (*p++);
+			printf(" %d", dest);
+			break;
+		}
+
+		nl();
+	}
 }
--- a/jsload.c
+++ b/jsload.c
@@ -1,13 +1,22 @@
 #include "js.h"
 #include "jsparse.h"
+#include "jscompile.h"
+#include "jsrun.h"
 
 static int jsP_loadstring(js_State *J, const char *filename, const char *source)
 {
 	js_Ast *prog = jsP_parse(J, filename, source);
 	if (prog) {
-		jsP_optimize(J, prog);
-//		jsP_dumpsyntax(J, prog);
+		js_Function *fun;
+		//jsP_optimize(J, prog);
+		//jsP_dumpsyntax(J, prog);
 		jsP_dumplist(J, prog);
+		fun = jsC_compile(J, prog);
+		if (fun) {
+			jsC_dumpfunction(J, fun);
+			jsR_runfunction(J, fun);
+		}
+		jsC_freecompile(J);
 		jsP_freeparse(J);
 		return 0;
 	}
--- a/jsparse.h
+++ b/jsparse.h
@@ -114,10 +114,11 @@
 	STM_SWITCH,
 	STM_THROW,
 	STM_TRY,
+	STM_DEBUGGER,
+
 	STM_LABEL,
 	STM_CASE,
 	STM_DEFAULT,
-	STM_DEBUGGER,
 };
 
 js_Ast *jsP_parse(js_State *J, const char *filename, const char *source);
--- /dev/null
+++ b/jsrun.c
@@ -1,0 +1,149 @@
+#include "js.h"
+#include "jscompile.h"
+#include "jsrun.h"
+#include "jsstate.h"
+
+static js_Value stack[256];
+static int top = 0;
+
+static inline int i32(double d)
+{
+	double two32 = 4294967296.0;
+	double two31 = 2147483648.0;
+
+	if (!isfinite(d) || d == 0)
+		return 0;
+
+	d = fmod(d, two32);
+	d = d >= 0 ? floor(d) : ceil(d) + two32;
+	if (d >= two31)
+		return d - two32;
+	else
+		return d;
+}
+
+static inline unsigned int u32(double d)
+{
+	return i32(d);
+}
+
+static inline void push(js_State *J, js_Value v)
+{
+	stack[top++] = v;
+}
+
+static inline js_Value pop(js_State *J)
+{
+	return stack[--top];
+}
+
+static inline void pushnumber(js_State *J, double number)
+{
+	js_Value v;
+	v.type = JS_TNUMBER;
+	v.u.number = number;
+	push(J, v);
+}
+
+static inline double popnumber(js_State *J)
+{
+	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;
+}
+
+static inline void pushundefined(js_State *J)
+{
+	js_Value v;
+	v.type = JS_TUNDEFINED;
+	push(J, v);
+}
+
+static inline void pushnull(js_State *J)
+{
+	js_Value v;
+	v.type = JS_TNULL;
+	push(J, v);
+}
+
+static inline void pushboolean(js_State *J, int boolean)
+{
+	js_Value v;
+	v.type = JS_TBOOLEAN;
+	v.u.boolean = boolean;
+	push(J, v);
+}
+
+static inline int popboolean(js_State *J)
+{
+	js_Value v = pop(J);
+	if (v.type == JS_TBOOLEAN)
+		return v.u.boolean;
+	return 0;
+}
+
+static void dumpstack(js_State *J)
+{
+	int i;
+	for (i = 0; i < top; i++) {
+		printf("stack %d: ", i);
+		jsC_dumpvalue(J, stack[i]);
+		putchar('\n');
+	}
+}
+
+#define UNARY(X) a = popnumber(J); pushnumber(J, X)
+#define BINARY(X) b = popnumber(J); a = popnumber(J); pushnumber(J, X)
+
+static void runfun(js_State *J, js_Function *F)
+{
+	unsigned char *pc = F->code;
+	int opcode, i;
+	double a, b;
+
+	while (1) {
+		opcode = *pc++;
+		switch (opcode) {
+		case OP_CONST:
+			i = *pc++;
+			push(J, F->klist[i]);
+			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;
+
+		case OP_POS: UNARY(a); break;
+		case OP_NEG: UNARY(-a); break;
+		case OP_BITNOT: UNARY(~i32(a)); break;
+
+		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_RETURN: return;
+
+		default:
+			fprintf(stderr, "illegal instruction: %d\n", opcode);
+			return;
+		}
+	}
+}
+
+void jsR_runfunction(js_State *J, js_Function *F)
+{
+	runfun(J, F);
+	dumpstack(J);
+}
--- /dev/null
+++ b/jsrun.h
@@ -1,0 +1,35 @@
+#ifndef js_run_h
+#define js_run_h
+
+enum js_ValueType {
+	JS_TUNDEFINED,
+	JS_TNULL,
+	JS_TBOOLEAN,
+	JS_TNUMBER,
+	JS_TSTRING,
+	JS_TREGEXP,
+	JS_TOBJECT,
+
+	JS_TFUNCTION,
+	JS_TCFUNCTION,
+	JS_TCLOSURE,
+	JS_TARGUMENTS,
+
+	JS_TOBJSLOT,
+};
+
+struct js_Value
+{
+	union {
+		double number;
+		const char *string;
+		int boolean;
+		void *p;
+	} u;
+	int flag;
+	js_ValueType type;
+};
+
+void jsR_runfunction(js_State *J, js_Function *F);
+
+#endif
--- a/jsstate.h
+++ b/jsstate.h
@@ -23,6 +23,9 @@
 	double number;
 	js_Ast *ast; /* list of allocated nodes to free after parsing */
 
+	/* compiler */
+	js_Function *fun; /* list of allocated functions to free on errors */
+
 	int strict;
 };