shithub: libmujs

Download patch

ref: a4cc139e1cb81fc53b88b72fe8d15b9ac43d2ef4
parent: 89310461e81f069d353d4a53194c94258d9203b5
author: Tor Andersson <tor@ccxvii.net>
date: Mon Jan 13 16:32:33 EST 2014

Add js_Object with js_Property binary search tree.

Use object to represent a variable record and implement variable
loading and storing in the interpreter.

--- a/js.h
+++ b/js.h
@@ -11,15 +11,16 @@
 
 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);
+typedef struct js_Ast js_Ast;
+typedef struct js_Closure js_Closure;
+typedef struct js_Function js_Function;
+typedef struct js_Object js_Object;
+typedef struct js_Property js_Property;
+typedef struct js_RegExp js_RegExp;
+typedef struct js_StringNode js_StringNode;
+typedef struct js_Value js_Value;
 
 #define JS_REGEXP_G 1
 #define JS_REGEXP_I 2
--- a/jscompile.c
+++ b/jscompile.c
@@ -1,7 +1,6 @@
 #include "js.h"
 #include "jsparse.h"
 #include "jscompile.h"
-#include "jsrun.h"
 #include "jsstate.h"
 
 #define cexp js_cexp /* collision with math.h */
@@ -8,8 +7,7 @@
 
 #define JF js_State *J, js_Function *F
 
-static js_Function *newfun(js_State *J, js_Ast *name, js_Ast *params, js_Ast *body);
-
+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);
 
@@ -23,109 +21,131 @@
 	return n;
 }
 
-static int consteq(js_Value a, js_Value b)
+static js_Function *newfun(js_State *J, js_Ast *name, js_Ast *params, js_Ast *body)
 {
-	if (a.type != b.type) return 0;
-	if (a.type == JS_TNUMBER) return a.u.number == b.u.number;
-	if (a.type == JS_TSTRING) return a.u.string == b.u.string;
-	return a.u.p == b.u.p;
-}
+	js_Function *F = malloc(sizeof *F);
+	memset(F, 0, sizeof *F);
 
-static int addconst(JF, js_Value v)
-{
-	int i;
+	F->name = name ? name->string : "<anonymous>";
+	F->numparams = listlen(params);
 
-	for (i = 0; i < F->klen; i++) {
-		if (consteq(F->klist[i], v)) {
-			return i;
-		}
-	}
+	F->codecap = 256;
+	F->codelen = 0;
+	F->code = malloc(F->codecap * sizeof *F->code);
 
-	if (F->klen >= F->kcap) {
-		F->kcap *= 2;
-		F->klist = realloc(F->klist, F->kcap * sizeof(js_Value));
-	}
+	F->next = J->fun;
+	J->fun = F;
 
-	F->klist[F->klen] = v;
-	return F->klen++;
+	cfunbody(J, F, name, params, body);
+
+	return F;
 }
 
+static void freefun(js_State *J, js_Function *F)
+{
+//	int i;
+//	for (i = 0; i < F->funlen; i++)
+//		freefun(J, F->funlist[i]);
+	free(F->funlist);
+	free(F->numlist);
+	free(F->strlist);
+	free(F->code);
+	free(F);
+}
+
+/* Emit opcodes, constants and jumps */
+
 static void emit(JF, int value)
 {
-	if (F->len >= F->cap) {
-		F->cap *= 2;
-		F->code = realloc(F->code, F->cap);
+	if (F->codelen >= F->codecap) {
+		F->codecap *= 2;
+		F->code = realloc(F->code, F->codecap * sizeof *F->code);
 	}
-
-	F->code[F->len++] = value;
+	F->code[F->codelen++] = value;
 }
 
-static void emitfunction(JF, int opcode, js_Function *fun)
+static int addfunction(JF, js_Function *value)
 {
-	js_Value v;
-	v.type = JS_TFUNCTION;
-	v.u.function = fun;
-	emit(J, F, opcode);
-	emit(J, F, addconst(J, F, v));
+	if (F->funlen >= F->funcap) {
+		F->funcap = F->funcap ? F->funcap * 2 : 16;
+		F->funlist = realloc(F->funlist, F->funcap * sizeof *F->funlist);
+	}
+	F->funlist[F->funlen] = value;
+	return F->funlen++;
 }
 
-static void emitnumber(JF, int opcode, double n)
+static int addnumber(JF, double value)
 {
-	js_Value v;
-	v.type = JS_TNUMBER;
-	v.u.number = n;
-	emit(J, F, opcode);
-	emit(J, F, addconst(J, F, v));
+	int i;
+	for (i = 0; i < F->numlen; i++)
+		if (F->numlist[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->numlist[F->numlen] = value;
+	return F->numlen++;
 }
 
-static void emitstring(JF, int opcode, const char *s)
+static int addstring(JF, const char *value)
 {
-	js_Value v;
-	v.type = JS_TSTRING;
-	v.u.string = s;
-	emit(J, F, opcode);
-	emit(J, F, addconst(J, F, v));
+	int i;
+	for (i = 0; i < F->strlen; i++)
+		if (!strcmp(F->strlist[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->strlist[F->strlen] = value;
+	return F->strlen++;
 }
 
-static void emitname(JF, int opcode, const char *s)
+static void emitfunction(JF, int opcode, js_Function *fun)
 {
-	js_Value v;
-	v.type = JS_TSTRING;
-	v.u.string = s;
 	emit(J, F, opcode);
-	emit(J, F, addconst(J, F, v));
+	emit(J, F, addfunction(J, F, fun));
 }
 
-static int here(JF)
+static void emitnumber(JF, int opcode, double num)
 {
-	return F->len;
+	emit(J, F, opcode);
+	emit(J, F, addnumber(J, F, num));
 }
 
-static void labelto(JF, int inst, int dest)
+static void emitstring(JF, int opcode, const char *str)
 {
-	F->code[inst+0] = (dest >> 8) & 0xFF;
-	F->code[inst+1] = (dest) & 0xFF;
+	emit(J, F, opcode);
+	emit(J, F, addstring(J, F, str));
 }
 
-static void label(JF, int inst)
+static int here(JF)
 {
-	labelto(J, F, inst, here(J, F));
+	return F->codelen;
 }
 
 static int jump(JF, int opcode)
 {
-	int inst = F->len + 1;
+	int inst = F->codelen + 1;
 	emit(J, F, opcode);
 	emit(J, F, 0);
-	emit(J, F, 0);
 	return inst;
 }
 
 static void jumpto(JF, int opcode, int dest)
 {
-	labelto(J, F, jump(J, F, opcode), dest);
+	emit(J, F, opcode);
+	emit(J, F, dest);
 }
 
+static void label(JF, int inst)
+{
+	F->code[inst] = F->codelen;
+}
+
+/* Expressions */
+
 static void unary(JF, js_Ast *exp, int opcode)
 {
 	cexp(J, F, exp->a);
@@ -141,9 +161,11 @@
 
 static void carray(JF, js_Ast *list)
 {
+	int i = 0;
 	while (list) {
 		cexp(J, F, list->a);
 		emit(J, F, OP_ARRAYPUT);
+		emit(J, F, i++);
 		list = list->b;
 	}
 }
@@ -156,7 +178,7 @@
 			js_Ast *prop = kv->a;
 			cexp(J, F, kv->b);
 			if (prop->type == AST_IDENTIFIER || prop->type == AST_STRING)
-				emitname(J, F, OP_OBJECTPUT, prop->string);
+				emitstring(J, F, OP_OBJECTPUT, prop->string);
 			else if (prop->type == AST_STRING)
 				emitstring(J, F, OP_OBJECTPUT, prop->string);
 			else if (prop->type == AST_NUMBER)
@@ -184,7 +206,7 @@
 {
 	switch (exp->type) {
 	case AST_IDENTIFIER:
-		emitname(J, F, OP_AVAR, exp->string);
+		emitstring(J, F, OP_AVAR, exp->string);
 		break;
 	case EXP_INDEX:
 		cexp(J, F, exp->a);
@@ -193,7 +215,7 @@
 		break;
 	case EXP_MEMBER:
 		cexp(J, F, exp->a);
-		emitname(J, F, OP_AMEMBER, exp->b->string);
+		emitstring(J, F, OP_AMEMBER, exp->b->string);
 		break;
 	case EXP_CALL:
 		/* host functions may return an assignable l-value */
@@ -214,6 +236,20 @@
 	emit(J, F, OP_STORE);
 }
 
+static void cvarinit(JF, js_Ast *list)
+{
+	while (list) {
+		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);
+		}
+		list = list->b;
+	}
+}
+
 static void ccall(JF, js_Ast *fun, js_Ast *args)
 {
 	int n;
@@ -227,7 +263,7 @@
 	case EXP_MEMBER:
 		cexp(J, F, fun->a);
 		emit(J, F, OP_DUP);
-		emitname(J, F, OP_LOADMEMBER, fun->b->string);
+		emitstring(J, F, OP_LOADMEMBER, fun->b->string);
 		break;
 	default:
 		emit(J, F, OP_THIS);
@@ -245,9 +281,9 @@
 	int n;
 
 	switch (exp->type) {
-	case AST_IDENTIFIER: emitname(J, F, OP_LOADVAR, exp->string); break;
-	case AST_NUMBER: emitnumber(J, F, OP_CONST, exp->number); break;
-	case AST_STRING: emitstring(J, F, OP_CONST, exp->string); break;
+	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 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;
@@ -272,7 +308,7 @@
 
 	case EXP_MEMBER:
 		cexp(J, F, exp->a);
-		emitname(J, F, OP_LOADMEMBER, exp->b->string);
+		emitstring(J, F, OP_LOADMEMBER, exp->b->string);
 		break;
 
 	case EXP_CALL:
@@ -393,19 +429,7 @@
 	}
 }
 
-static void cvarinit(JF, js_Ast *list)
-{
-	while (list) {
-		js_Ast *var = list->a;
-		if (var->b) {
-			cexp(J, F, var->b);
-			emitname(J, F, OP_AVAR, var->a->string);
-			emit(J, F, OP_STORE);
-			emit(J, F, OP_POP);
-		}
-		list = list->b;
-	}
-}
+/* Statements */
 
 static void cstm(JF, js_Ast *stm)
 {
@@ -505,6 +529,8 @@
 	}
 }
 
+/* Declarations and programs */
+
 static void cfundecs(JF, js_Ast *list)
 {
 	while (list) {
@@ -511,7 +537,7 @@
 		js_Ast *stm = list->a;
 		if (stm->type == AST_FUNDEC) {
 			emitfunction(J, F, OP_CLOSURE, newfun(J, stm->a, stm->b, stm->c));
-			emitname(J, F, OP_FUNDEC, stm->a->string);
+			emitstring(J, F, OP_FUNDEC, stm->a->string);
 		}
 		list = list->b;
 	}
@@ -520,7 +546,7 @@
 static void cvardecs(JF, js_Ast *node)
 {
 	if (node->type == EXP_VAR) {
-		emitname(J, F, OP_VARDEC, node->a->string);
+		emitstring(J, F, OP_VARDEC, node->a->string);
 	} else if (node->type != EXP_FUN && node->type != AST_FUNDEC) {
 		if (node->a) cvardecs(J, F, node->a);
 		if (node->b) cvardecs(J, F, node->b);
@@ -533,7 +559,7 @@
 {
 	if (name) {
 		emitfunction(J, F, OP_CLOSURE, F);
-		emitname(J, F, OP_FUNDEC, name->string);
+		emitstring(J, F, OP_FUNDEC, name->string);
 	}
 
 	if (body) {
@@ -542,42 +568,26 @@
 		cstmlist(J, F, body);
 	}
 
-	if (F->len == 0 || F->code[F->len - 1] != OP_RETURN) {
+	if (F->codelen == 0 || F->code[F->codelen - 1] != OP_RETURN) {
 		emit(J, F, OP_UNDEF);
 		emit(J, F, OP_RETURN);
 	}
 }
 
-static js_Function *newfun(js_State *J, js_Ast *name, js_Ast *params, js_Ast *body)
+int jsC_error(js_State *J, js_Ast *node, const char *fmt, ...)
 {
-	js_Function *F = malloc(sizeof(js_Function));
+	va_list ap;
 
-	F->name = name ? name->string : "<anonymous>";
-	F->numparams = listlen(params);
+	fprintf(stderr, "%s:%d: error: ", J->filename, node->line);
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	fprintf(stderr, "\n");
 
-	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;
-
-	cfunbody(J, F, name, params, body);
-
-	return F;
+	longjmp(J->jb, 1);
+	return 0;
 }
 
-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;
@@ -587,20 +597,6 @@
 		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)
--- a/jscompile.h
+++ b/jscompile.h
@@ -7,7 +7,9 @@
 	OP_DUP,
 
 	OP_CLOSURE,
-	OP_CONST,
+	OP_NUMBER,
+	OP_STRING,
+
 	OP_UNDEF,
 	OP_NULL,
 	OP_TRUE,
@@ -23,12 +25,12 @@
 	OP_VARDEC,	/* -(name)- */
 
 	OP_LOADVAR,	/* -(name)- <value> */
-	OP_LOADINDEX,	/* <obj> <idx> -- <value> */
 	OP_LOADMEMBER,	/* <obj> -(name)- <value> */
+	OP_LOADINDEX,	/* <obj> <idx> -- <value> */
 
 	OP_AVAR,	/* -(name)- <addr> */
-	OP_AINDEX,	/* <obj> <idx> -- <addr> */
 	OP_AMEMBER,	/* <obj> -(name)- <addr> */
+	OP_AINDEX,	/* <obj> <idx> -- <addr> */
 
 	OP_LOAD,	/* <addr> -- <addr> <value> */
 	OP_STORE,	/* <addr> <value> -- <value> */
@@ -89,13 +91,19 @@
 	const char *name;
 	int numparams;
 
-	unsigned char *code;
-	int cap, len;
+	short *code;
+	int codecap, codelen;
 
-	js_Value *klist;
-	int kcap, klen;
+	js_Function **funlist;
+	int funcap, funlen;
 
-	js_Function *next;
+	double *numlist;
+	int numcap, numlen;
+
+	const char **strlist;
+	int strcap, strlen;
+
+	js_Function *next; /* alloc list */
 };
 
 js_Function *jsC_compile(js_State *J, js_Ast *prog);
--- a/jsdump.c
+++ b/jsdump.c
@@ -1,7 +1,8 @@
 #include "js.h"
 #include "jsparse.h"
 #include "jscompile.h"
-#include "jsrun.h"
+#include "jsvalue.h"
+#include "jsobject.h"
 
 #include <assert.h>
 
@@ -25,12 +26,6 @@
 #include "opnames.h"
 };
 
-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);
-static void slist(int d, js_Ast *list);
-static void sblock(int d, js_Ast *list);
-
 static inline void pc(int c)
 {
 	putchar(c);
@@ -52,6 +47,14 @@
 	putchar('\n');
 }
 
+/* Pretty-printed Javascript syntax */
+
+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);
+static void slist(int d, js_Ast *list);
+static void sblock(int d, js_Ast *list);
+
 static void pargs(int d, js_Ast *list)
 {
 	while (list) {
@@ -509,6 +512,8 @@
 	}
 }
 
+/* S-expression list representation */
+
 static void snode(int d, js_Ast *node)
 {
 	void (*afun)(int,js_Ast*) = snode;
@@ -583,64 +588,61 @@
 	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;
+/* Compiled code */
 
-	case JS_TFUNCTION: printf("<function %p>", v.u.function); 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)
+void jsC_dumpfunction(js_State *J, js_Function *F)
 {
-	unsigned char *p = fun->code;
-	unsigned char *end = fun->code + fun->len;
+	short *p = F->code;
+	short *end = F->code + F->codelen;
 	int i, dest;
 
-	printf("function %p, %s, %d parameters, %d constants\n",
-		fun, fun->name, fun->numparams, fun->klen);
+	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);
+	for (i = 0; i < F->strlen; i++) {
+		ps("\tstring "); pstr(F->strlist[i]); ps("\n");
+	}
+	// TODO: regexp
+	for (i = 0; i < F->numlen; i++)
+		printf("\tnumber %.9g\n", F->numlist[i]);
 
 	while (p < end) {
 		int c = *p++;
 
-		printf("%04d: ", (int)(p - fun->code) - 1);
+		printf("%04d: ", (int)(p - F->code) - 1);
 		ps(opname[c]);
 
 		switch (c) {
-		case OP_CONST:
+		case OP_CLOSURE:
+			pc(' ');
+			ps(F->funlist[*p++]->name);
+			break;
+		case OP_NUMBER:
+			printf(" %.9g", F->numlist[*p++]);
+			break;
+		case OP_STRING:
+			pc(' ');
+			pstr(F->strlist[*p++]);
+			break;
+
 		case OP_OBJECTPUT:
-		case OP_VARDEC:
 		case OP_FUNDEC:
+		case OP_VARDEC:
 		case OP_LOADVAR:
 		case OP_LOADMEMBER:
 		case OP_AVAR:
 		case OP_AMEMBER:
-		case OP_CLOSURE:
 			pc(' ');
-			jsC_dumpvalue(J, fun->klist[*p++]);
+			ps(F->strlist[*p++]);
 			break;
+
+		case OP_ARRAYPUT:
 		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);
+			printf(" %d", *p++);
 			break;
 		}
 
@@ -647,12 +649,10 @@
 		nl();
 	}
 
-	for (i = 0; i < fun->klen; i++) {
-		if (fun->klist[i].type == JS_TFUNCTION) {
-			if (fun->klist[i].u.function != fun) {
-				nl();
-				jsC_dumpfunction(J, fun->klist[i].u.function);
-			}
+	for (i = 0; i < F->funlen; i++) {
+		if (F->funlist[i] != F) {
+			nl();
+			jsC_dumpfunction(J, F->funlist[i]);
 		}
 	}
 }
--- /dev/null
+++ b/jsobject.c
@@ -1,0 +1,144 @@
+#include "js.h"
+#include "jsvalue.h"
+#include "jsobject.h"
+
+/*
+	Use an AA-tree to quickly look up properties in objects:
+
+	The level of every leaf node is one.
+	The level of every left child is one less than its parent.
+	The level of every right child is equal or one less than its parent.
+	The level of every right grandchild is less than its grandparent.
+	Every node of level greater than one has two children.
+
+	A link where the child's level is equal to that of its parent is called a horizontal link.
+	Individual right horizontal links are allowed, but consecutive ones are forbidden.
+	Left horizontal links are forbidden.
+
+	skew() fixes left horizontal links.
+	split() fixes consecutive right horizontal links.
+*/
+
+static js_Property sentinel = { "", &sentinel, &sentinel, 0 };
+
+static js_Property *newproperty(const char *key)
+{
+	js_Property *node = malloc(sizeof(js_Property));
+	node->key = strdup(key);
+	node->left = node->right = &sentinel;
+	node->level = 1;
+//	node->value = 0;
+	return node;
+}
+
+static js_Property *lookup(js_Property *node, const char *key)
+{
+	while (node != &sentinel) {
+		int c = strcmp(key, node->key);
+		if (c == 0)
+			return node;
+		else if (c < 0)
+			node = node->left;
+		else
+			node = node->right;
+	}
+	return NULL;
+}
+
+static inline js_Property *skew(js_Property *node)
+{
+	if (node->level != 0) {
+		if (node->left->level == node->level) {
+			js_Property *save = node;
+			node = node->left;
+			save->left = node->right;
+			node->right = save;
+		}
+		node->right = skew(node->right);
+	}
+	return node;
+}
+
+static inline js_Property *split(js_Property *node)
+{
+	if (node->level != 0 && node->right->right->level == node->level) {
+		js_Property *save = node;
+		node = node->right;
+		save->right = node->left;
+		node->left = save;
+		node->level++;
+		node->right = split(node->right);
+	}
+	return node;
+}
+
+static js_Property *insert(js_Property *node, const char *key, js_Property **result)
+{
+	if (node != &sentinel) {
+		int c = strcmp(key, node->key);
+		if (c < 0)
+			node->left = insert(node->left, key, result);
+		else if (c > 0)
+			node->right = insert(node->right, key, result);
+		else
+			return *result = node;
+		node = skew(node);
+		node = split(node);
+		return node;
+	}
+	return *result = newproperty(key);
+}
+
+js_Object *js_newobject(js_State *J)
+{
+	js_Object *obj = malloc(sizeof(js_Object));
+	obj->properties = &sentinel;
+	return obj;
+}
+
+js_Property *js_getproperty(js_State *J, js_Object *obj, const char *name)
+{
+	return lookup(obj->properties, name);
+}
+
+js_Property *js_setproperty(js_State *J, js_Object *obj, const char *name)
+{
+	js_Property *result;
+	obj->properties = insert(obj->properties, name, &result);
+	return result;
+}
+
+static void js_dumpvalue(js_State *J, js_Value v)
+{
+	switch (v.type) {
+	case JS_TUNDEFINED: printf("undefined"); break;
+	case JS_TNULL: printf("null"); break;
+	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;
+	}
+}
+
+static void js_dumpproperty(js_State *J, js_Property *node)
+{
+	if (node->left != &sentinel)
+		js_dumpproperty(J, node->left);
+	printf("\t%s: ", node->key);
+	js_dumpvalue(J, node->value);
+	printf(",\n");
+	if (node->right != &sentinel)
+		js_dumpproperty(J, node->right);
+}
+
+void js_dumpobject(js_State *J, js_Object *obj)
+{
+	printf("{\n");
+	if (obj->properties != &sentinel)
+		js_dumpproperty(J, obj->properties);
+	printf("}\n");
+}
--- /dev/null
+++ b/jsobject.h
@@ -1,0 +1,26 @@
+#ifndef js_object_h
+#define js_object_h
+
+struct js_Property
+{
+	char *key;
+	js_Property *left, *right;
+	int level;
+	js_Value value;
+};
+
+struct js_Object
+{
+	js_Property *properties;
+	js_Object *prototype;
+	js_Object *parent;
+};
+
+js_Object *js_newobject(js_State *J);
+js_Property *js_getproperty(js_State *J, js_Object *obj, const char *name);
+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 js_dumpobject(js_State *J, js_Object *obj);
+
+#endif
--- a/jsrun.c
+++ b/jsrun.c
@@ -1,4 +1,6 @@
 #include "js.h"
+#include "jsvalue.h"
+#include "jsobject.h"
 #include "jscompile.h"
 #include "jsrun.h"
 #include "jsstate.h"
@@ -37,6 +39,11 @@
 	return stack[--top];
 }
 
+static inline js_Value peek(js_State *J)
+{
+	return stack[top-1];
+}
+
 static inline void pushnumber(js_State *J, double number)
 {
 	js_Value v;
@@ -82,36 +89,85 @@
 	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;
 }
 
-static void dumpstack(js_State *J)
+static inline void pushreference(js_State *J, js_Property *reference)
 {
-	int i;
-	for (i = 0; i < top; i++) {
-		printf("stack %d: ", i);
-		jsC_dumpvalue(J, stack[i]);
-		putchar('\n');
-	}
+	js_Value v;
+	v.type = JS_TREFERENCE;
+	v.u.reference = reference;
+	push(J, v);
 }
 
+static inline js_Property *popreference(js_State *J)
+{
+	js_Value v = pop(J);
+	if (v.type == JS_TREFERENCE)
+		return v.u.reference;
+	return 0;
+}
+
+static inline js_Property *peekreference(js_State *J)
+{
+	js_Value v = peek(J);
+	if (v.type == JS_TREFERENCE)
+		return v.u.reference;
+	return 0;
+}
+
 #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)
+static void runfun(js_State *J, js_Function *F, js_Object *E)
 {
-	unsigned char *pc = F->code;
-	int opcode, i;
+	short *pc = F->code;
+	int opcode, addr;
+	js_Property *p;
+	js_Value v;
 	double a, b;
+	const char *s;
 
 	while (1) {
 		opcode = *pc++;
 		switch (opcode) {
-		case OP_CONST:
-			i = *pc++;
-			push(J, F->klist[i]);
+		case OP_NUMBER:
+			pushnumber(J, F->numlist[*pc++]);
 			break;
 
+		case OP_LOADVAR:
+			s = F->strlist[*pc++];
+			p = js_getproperty(J, E, s);
+			if (p)
+				push(J, p->value);
+			else
+				pushundefined(J);
+			break;
+
+		case OP_AVAR:
+			s = F->strlist[*pc++];
+			p = js_setproperty(J, E, s);
+			pushreference(J, p);
+			break;
+
+		case OP_LOAD:
+			p = peekreference(J);
+			if (p)
+				push(J, p->value);
+			else
+				pushundefined(J);
+			break;
+
+		case OP_STORE:
+			v = pop(J);
+			p = popreference(J);
+			if (p)
+				p->value = v;
+			push(J, v);
+			break;
+
 		case OP_UNDEF: pushundefined(J); break;
 		case OP_NULL: pushnull(J); break;
 		case OP_TRUE: pushboolean(J, 1); break;
@@ -120,6 +176,7 @@
 		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;
 
 		case OP_ADD: BINARY(a + b); break;
 		case OP_SUB: BINARY(a - b); break;
@@ -133,10 +190,34 @@
 		case OP_BITXOR: BINARY(i32(a) ^ i32(b)); break;
 		case OP_BITOR: BINARY(i32(a) | i32(b)); 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_JFALSE:
+			addr = *pc++;
+			if (!popboolean(J))
+				pc = F->code + addr;
+			break;
+
+		case OP_JTRUE:
+			addr = *pc++;
+			if (popboolean(J))
+				pc = F->code + addr;
+			break;
+
+		case OP_JUMP:
+			pc = F->code + *pc;
+			break;
+
+		case OP_POP: pop(J); break;
 		case OP_RETURN: return;
 
 		default:
-			fprintf(stderr, "illegal instruction: %d\n", opcode);
+			fprintf(stderr, "illegal instruction: %d (pc=%d)\n", opcode, (int)(pc - F->code - 1));
 			return;
 		}
 	}
@@ -144,6 +225,7 @@
 
 void jsR_runfunction(js_State *J, js_Function *F)
 {
-	runfun(J, F);
-	dumpstack(J);
+	js_Object *varenv = js_newobject(J);
+	runfun(J, F, varenv);
+	js_dumpobject(J, varenv);
 }
--- a/jsrun.h
+++ b/jsrun.h
@@ -1,36 +1,6 @@
 #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;
-		js_Function *function;
-		void *p;
-	} u;
-	int flag;
-	js_ValueType type;
-};
-
 void jsR_runfunction(js_State *J, js_Function *F);
 
 #endif
--- /dev/null
+++ b/jsvalue.h
@@ -1,0 +1,37 @@
+#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