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