mirror of
https://github.com/ccxvii/mujs.git
synced 2026-09-21 22:53:41 +08:00
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.
This commit is contained in:
@@ -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
|
||||
|
||||
+128
-132
@@ -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 : "<anonymous>";
|
||||
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 : "<anonymous>";
|
||||
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;
|
||||
|
||||
+16
-8
@@ -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)- <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 @@ 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);
|
||||
|
||||
@@ -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 @@ 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("<regexp %p>", v.u.p); break;
|
||||
case JS_TOBJECT: printf("<object %p>", v.u.p); break;
|
||||
|
||||
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)
|
||||
{
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+144
@@ -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("<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");
|
||||
}
|
||||
+26
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user