From a4cc139e1cb81fc53b88b72fe8d15b9ac43d2ef4 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Mon, 13 Jan 2014 21:32:33 +0100 Subject: [PATCH] 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. --- js.h | 15 +-- jscompile.c | 260 ++++++++++++++++++++++++++-------------------------- jscompile.h | 24 +++-- jsdump.c | 96 +++++++++---------- jsobject.c | 144 +++++++++++++++++++++++++++++ jsobject.h | 26 ++++++ jsrun.c | 114 +++++++++++++++++++---- jsrun.h | 30 ------ jsvalue.h | 37 ++++++++ 9 files changed, 505 insertions(+), 241 deletions(-) create mode 100644 jsobject.c create mode 100644 jsobject.h create mode 100644 jsvalue.h diff --git a/js.h b/js.h index a28fe96..67c31b9 100644 --- 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 diff --git a/jscompile.c b/jscompile.c index 615231e..f900e52 100644 --- a/jscompile.c +++ b/jscompile.c @@ -1,15 +1,13 @@ #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 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 @@ static int listlen(js_Ast *list) 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); + + F->name = name ? name->string : ""; + F->numparams = listlen(params); + + F->codecap = 256; + F->codelen = 0; + F->code = malloc(F->codecap * sizeof *F->code); + + F->next = J->fun; + J->fun = F; + + cfunbody(J, F, name, params, body); + + return F; } -static int addconst(JF, js_Value v) +static void freefun(js_State *J, js_Function *F) { - 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++; +// 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->codelen++] = value; +} - F->code[F->len++] = value; +static int addfunction(JF, js_Function *value) +{ + 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 int addnumber(JF, double value) +{ + 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 int addstring(JF, const char *value) +{ + 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 emitfunction(JF, int opcode, js_Function *fun) { - js_Value v; - v.type = JS_TFUNCTION; - v.u.function = fun; emit(J, F, opcode); - emit(J, F, addconst(J, F, v)); + emit(J, F, addfunction(J, F, fun)); } -static void emitnumber(JF, int opcode, double n) +static void emitnumber(JF, int opcode, double num) { - js_Value v; - v.type = JS_TNUMBER; - v.u.number = n; emit(J, F, opcode); - emit(J, F, addconst(J, F, v)); + emit(J, F, addnumber(J, F, num)); } -static void emitstring(JF, int opcode, const char *s) +static void emitstring(JF, int opcode, const char *str) { - js_Value v; - v.type = JS_TSTRING; - v.u.string = s; emit(J, F, opcode); - emit(J, F, addconst(J, F, v)); -} - -static void emitname(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)); + emit(J, F, addstring(J, F, str)); } static int here(JF) { - return F->len; -} - -static void labelto(JF, int inst, int dest) -{ - F->code[inst+0] = (dest >> 8) & 0xFF; - F->code[inst+1] = (dest) & 0xFF; -} - -static void label(JF, int inst) -{ - 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 binary(JF, js_Ast *exp, int opcode) 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 @@ static void cobject(JF, js_Ast *list) 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 @@ static void clval(JF, js_Ast *exp) { 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 @@ static void clval(JF, js_Ast *exp) 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 @@ static void assignop(JF, js_Ast *exp, int opcode) 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 @@ static void ccall(JF, js_Ast *fun, js_Ast *args) 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 @@ static void cexp(JF, js_Ast *exp) 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 @@ static void cexp(JF, js_Ast *exp) 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 cexp(JF, js_Ast *exp) } } -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,13 +529,15 @@ static void cstmlist(JF, js_Ast *list) } } +/* Declarations and programs */ + static void cfundecs(JF, js_Ast *list) { 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)); - 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 cfundecs(JF, js_Ast *list) 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 @@ static void cfunbody(JF, js_Ast *name, js_Ast *params, js_Ast *body) { 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,53 +568,12 @@ static void cfunbody(JF, js_Ast *name, js_Ast *params, js_Ast *body) 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) -{ - js_Function *F = malloc(sizeof(js_Function)); - - F->name = name ? name->string : ""; - F->numparams = listlen(params); - - 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; -} - -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; @@ -603,6 +588,17 @@ int jsC_error(js_State *J, js_Ast *node, const char *fmt, ...) return 0; } +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; +} + js_Function *jsC_compile(js_State *J, js_Ast *prog) { js_Function *F; diff --git a/jscompile.h b/jscompile.h index 3df8168..3ca47ec 100644 --- a/jscompile.h +++ b/jscompile.h @@ -7,7 +7,9 @@ enum OP_DUP, OP_CLOSURE, - OP_CONST, + OP_NUMBER, + OP_STRING, + OP_UNDEF, OP_NULL, OP_TRUE, @@ -23,12 +25,12 @@ enum OP_VARDEC, /* -(name)- */ OP_LOADVAR, /* -(name)- */ - OP_LOADINDEX, /* -- */ OP_LOADMEMBER, /* -(name)- */ + OP_LOADINDEX, /* -- */ OP_AVAR, /* -(name)- */ - OP_AINDEX, /* -- */ OP_AMEMBER, /* -(name)- */ + OP_AINDEX, /* -- */ OP_LOAD, /* -- */ OP_STORE, /* -- */ @@ -89,13 +91,19 @@ struct js_Function 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); diff --git a/jsdump.c b/jsdump.c index a192059..72788fb 100644 --- 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 @@ -25,12 +26,6 @@ static const char *opname[] = { #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 @@ static inline void nl(void) 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 @@ void jsP_dumpsyntax(js_State *J, js_Ast *prog) } } +/* S-expression list representation */ + static void snode(int d, js_Ast *node) { void (*afun)(int,js_Ast*) = snode; @@ -583,76 +588,71 @@ void jsP_dumplist(js_State *J, js_Ast *prog) nl(); } -void jsC_dumpvalue(js_State *J, js_Value v) +/* Compiled code */ + +void jsC_dumpfunction(js_State *J, js_Function *F) { - 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("", v.u.p); break; - case JS_TOBJECT: printf("", v.u.p); break; - - case JS_TFUNCTION: printf("", v.u.function); break; - case JS_TCFUNCTION: printf("", v.u.p); break; - case JS_TCLOSURE: printf("", v.u.p); break; - case JS_TARGUMENTS: printf("", v.u.p); break; - - case JS_TOBJSLOT: printf("", 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; + 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; } 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]); } } } diff --git a/jsobject.c b/jsobject.c new file mode 100644 index 0000000..11d5059 --- /dev/null +++ b/jsobject.c @@ -0,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("", v.u.object); break; + case JS_TCLOSURE: printf("", v.u.closure); break; + case JS_TCFUNCTION: printf("", v.u.cfunction); break; + case JS_TREFERENCE: printf("", 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"); +} diff --git a/jsobject.h b/jsobject.h new file mode 100644 index 0000000..ee68684 --- /dev/null +++ b/jsobject.h @@ -0,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 diff --git a/jsrun.c b/jsrun.c index 0852824..2da9550 100644 --- 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 @@ static inline js_Value pop(js_State *J) 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,34 +89,83 @@ static inline int popboolean(js_State *J) 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; @@ -120,6 +176,7 @@ static void runfun(js_State *J, js_Function *F) 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 @@ static void runfun(js_State *J, js_Function *F) 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 @@ static void runfun(js_State *J, js_Function *F) 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); } diff --git a/jsrun.h b/jsrun.h index 180b46f..8c31272 100644 --- 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 diff --git a/jsvalue.h b/jsvalue.h new file mode 100644 index 0000000..e5c53fb --- /dev/null +++ b/jsvalue.h @@ -0,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