ref: ede0f888ced2b7334e23a6a6ccf21c149712c116
parent: 6ba9e94d028d1158d405d7b10363a7b3b8514a02
author: Tor Andersson <tor@ccxvii.net>
date: Wed Jan 15 18:41:41 EST 2014
Prepare for objects with internal properties, and closures as objects.
--- a/js.h
+++ b/js.h
@@ -11,16 +11,16 @@
typedef struct js_State js_State;
-typedef enum js_ValueType js_ValueType;
-typedef int (*js_CFunction)(js_State *J, int n);
+typedef struct js_StringNode js_StringNode;
typedef struct js_Ast js_Ast;
-typedef struct js_Closure js_Closure;
typedef struct js_Function js_Function;
+typedef struct js_Environment js_Environment;
+
+typedef struct js_Value js_Value;
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;
+
+typedef int (*js_CFunction)(js_State *J, int argc);
#define JS_REGEXP_G 1
#define JS_REGEXP_I 2
--- a/jscompile.c
+++ b/jscompile.c
@@ -238,11 +238,6 @@
cexp(J, F, rhs);
emit(J, F, OP_SETPROP);
break;
- case EXP_CALL: /* host functions may return an assignable l-value */
- cexp(J, F, lhs);
- cexp(J, F, rhs);
- emit(J, F, OP_SETPROP);
- break;
default:
jsC_error(J, lhs, "invalid l-value in assignment");
break;
@@ -267,11 +262,6 @@
emit(J, F, OP_ROT3);
emit(J, F, OP_SETPROP);
break;
- case EXP_CALL: /* host functions may return an assignable l-value */
- cexp(J, F, lhs);
- emit(J, F, OP_ROT3);
- emit(J, F, OP_SETPROP);
- break;
default:
jsC_error(J, lhs, "invalid l-value in assignment");
break;
@@ -295,12 +285,6 @@
case EXP_MEMBER:
cexp(J, F, lhs->a);
emitstring(J, F, OP_STRING, lhs->b->string);
- emit(J, F, OP_DUP2);
- emit(J, F, OP_GETPROP);
- if (dup) emit(J, F, OP_DUP1ROT4);
- break;
- case EXP_CALL: /* host functions may return an assignable l-value */
- cexp(J, F, lhs);
emit(J, F, OP_DUP2);
emit(J, F, OP_GETPROP);
if (dup) emit(J, F, OP_DUP1ROT4);
--- a/jscompile.h
+++ b/jscompile.h
@@ -18,14 +18,14 @@
OP_STRING, /* -S- <string> */
OP_CLOSURE, /* -F- <closure> */
+ OP_NEWARRAY,
+ OP_NEWOBJECT,
+
OP_UNDEF,
OP_NULL,
OP_TRUE,
OP_FALSE,
OP_THIS,
-
- OP_NEWARRAY,
- OP_NEWOBJECT,
OP_FUNDEC, /* <closure> -S- */
OP_VARDEC, /* -S- */
--- a/jsdump.c
+++ b/jsdump.c
@@ -613,8 +613,7 @@
switch (c) {
case OP_CLOSURE:
- ps(" f:");
- ps(F->funtab[*p++]->name);
+ printf(" %p", F->funtab[*p++]);
break;
case OP_NUMBER:
printf(" %.9g", F->numtab[*p++]);
--- a/jsobject.c
+++ b/jsobject.c
@@ -27,6 +27,8 @@
node->left = node->right = &sentinel;
node->level = 1;
node->value.type = JS_TUNDEFINED;
+ node->value.u.number = 0;
+ node->flags = 0;
return node;
}
@@ -127,13 +129,71 @@
return parent;
}
-js_Object *js_newobject(js_State *J)
+js_Object *js_newobject(js_State *J, js_Class type)
{
js_Object *obj = malloc(sizeof(js_Object));
+ obj->type = type;
obj->properties = &sentinel;
+ obj->prototype = NULL;
+ obj->primitive.number = 0;
+ obj->scope = NULL;
+ obj->function = NULL;
+ obj->cfunction = NULL;
return obj;
}
+js_Object *js_newfunction(js_State *J, js_Function *function, js_Environment *scope)
+{
+ js_Object *obj = js_newobject(J, JS_CFUNCTION);
+ obj->function = function;
+ obj->scope = scope;
+ return obj;
+}
+
+js_Object *js_newcfunction(js_State *J, js_CFunction cfunction)
+{
+ js_Object *obj = js_newobject(J, JS_CCFUNCTION);
+ obj->cfunction = cfunction;
+ return obj;
+}
+
+js_Environment *js_newenvironment(js_State *J, js_Environment *outer, js_Object *vars)
+{
+ js_Environment *E = malloc(sizeof *E);
+ E->outer = outer;
+ E->variables = vars;
+ return E;
+}
+
+js_Property *js_decvar(js_State *J, js_Environment *E, const char *name)
+{
+ return js_setproperty(J, E->variables, name);
+}
+
+js_Property *js_getvar(js_State *J, js_Environment *E, const char *name)
+{
+ while (E) {
+ js_Property *ref = js_getproperty(J, E->variables, name);
+ if (ref)
+ return ref;
+ E = E->outer;
+ }
+ return NULL;
+}
+
+js_Property *js_setvar(js_State *J, js_Environment *E, const char *name)
+{
+ while (1) {
+ js_Property *ref = js_getproperty(J, E->variables, name);
+ if (ref)
+ return ref;
+ if (!E->outer)
+ break;
+ E = E->outer;
+ }
+ return js_setproperty(J, E->variables, name);
+}
+
js_Property *js_getproperty(js_State *J, js_Object *obj, const char *name)
{
return lookup(obj->properties, name);
@@ -156,7 +216,7 @@
return lookupnext(obj->properties, name);
}
-static void js_dumpvalue(js_State *J, js_Value v)
+void js_dumpvalue(js_State *J, js_Value v)
{
switch (v.type) {
case JS_TUNDEFINED: printf("undefined"); break;
@@ -165,8 +225,6 @@
case JS_TNUMBER: printf("%.9g", v.u.number); break;
case JS_TSTRING: printf("'%s'", v.u.string); 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;
}
}
--- a/jsobject.h
+++ b/jsobject.h
@@ -1,7 +1,16 @@
#ifndef js_object_h
#define js_object_h
-enum js_ValueType {
+typedef enum js_Class js_Class;
+typedef enum js_Type js_Type;
+
+struct js_Environment
+{
+ js_Environment *outer;
+ js_Object *variables;
+};
+
+enum js_Type {
JS_TUNDEFINED,
JS_TNULL,
JS_TBOOLEAN,
@@ -8,23 +17,25 @@
JS_TNUMBER,
JS_TSTRING,
JS_TOBJECT,
- JS_TCLOSURE,
- JS_TCFUNCTION,
};
struct js_Value
{
+ js_Type type;
union {
int boolean;
double number;
const char *string;
js_Object *object;
- js_Closure *closure;
- js_CFunction cfunction;
} u;
- js_ValueType type;
};
+enum {
+ JS_PWRITABLE = 1,
+ JS_PENUMERABLE = 2,
+ JS_PCONFIGURABLE = 4,
+};
+
struct js_Property
{
char *name;
@@ -31,16 +42,47 @@
js_Property *left, *right;
int level;
js_Value value;
+ int flags;
};
+enum js_Class {
+ JS_CARRAY,
+ JS_CBOOLEAN,
+ JS_CCFUNCTION,
+ JS_CDATE,
+ JS_CERROR,
+ JS_CFUNCTION,
+ JS_CMATH,
+ JS_CNUMBER,
+ JS_COBJECT,
+ JS_CREGEXP,
+ JS_CSTRING,
+};
+
struct js_Object
{
+ js_Class type;
js_Property *properties;
js_Object *prototype;
- js_Object *outer;
+ union {
+ int boolean;
+ double number;
+ const char *string;
+ } primitive;
+ js_Environment *scope;
+ js_Function *function;
+ js_CFunction cfunction;
};
-js_Object *js_newobject(js_State *J);
+js_Object *js_newobject(js_State *J, js_Class type);
+js_Object *js_newfunction(js_State *J, js_Function *function, js_Environment *scope);
+js_Object *js_newcfunction(js_State *J, js_CFunction cfunction);
+
+js_Environment *js_newenvironment(js_State *J, js_Environment *outer, js_Object *vars);
+js_Property *js_decvar(js_State *J, js_Environment *E, const char *name);
+js_Property *js_getvar(js_State *J, js_Environment *E, const char *name);
+js_Property *js_setvar(js_State *J, js_Environment *E, const char *name);
+
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);
@@ -49,5 +91,6 @@
js_Property *js_nextproperty(js_State *J, js_Object *obj, const char *name);
void js_dumpobject(js_State *J, js_Object *obj);
+void js_dumpvalue(js_State *J, js_Value v);
#endif
--- a/jsrun.c
+++ b/jsrun.c
@@ -8,6 +8,8 @@
static int top = 0;
static int bot = 0;
+static void js_call(js_State *J, js_Object *obj, int argc);
+
static inline double tointeger(double n)
{
double sign = n < 0 ? -1 : 1;
@@ -109,12 +111,6 @@
return stack[idx].type == JS_TSTRING;
}
-int js_iscfunction(js_State *J, int idx)
-{
- idx = stackidx(J, idx);
- return stack[idx].type == JS_TCFUNCTION;
-}
-
js_Value js_tovalue(js_State *J, int idx)
{
idx = stackidx(J, idx);
@@ -132,8 +128,9 @@
case JS_TBOOLEAN: return stack[idx].u.boolean;
case JS_TNUMBER: n = stack[idx].u.number; return n != 0 || !isnan(n);
case JS_TSTRING: s = stack[idx].u.string; return s[0] != 0;
- default: return 1;
+ case JS_TOBJECT: return 0;
}
+ return 0;
}
double js_tonumber(js_State *J, int idx)
@@ -145,8 +142,9 @@
case JS_TBOOLEAN: return stack[idx].u.boolean;
case JS_TNUMBER: return stack[idx].u.number;
case JS_TSTRING: return strtod(stack[idx].u.string, NULL);
- default: return 0;
+ case JS_TOBJECT: return 0;
}
+ return 0;
}
double js_tointeger(js_State *J, int idx)
@@ -164,8 +162,9 @@
case JS_TBOOLEAN: return stack[idx].u.boolean ? "true" : "false";
case JS_TNUMBER: sprintf(buf, "%.9g", stack[idx].u.number); return js_intern(J, buf);
case JS_TSTRING: return stack[idx].u.string;
- default: return "undefined";
+ case JS_TOBJECT: return "<object>";
}
+ return NULL;
}
js_Object *js_toobject(js_State *J, int idx)
@@ -172,13 +171,12 @@
{
idx = stackidx(J, idx);
switch (stack[idx].type) {
- case JS_TUNDEFINED: jsR_error(J, "TypeError");
- case JS_TNULL: jsR_error(J, "TypeError");
+ case JS_TUNDEFINED: jsR_error(J, "TypeError (undefined)");
+ case JS_TNULL: jsR_error(J, "TypeError (null)");
case JS_TBOOLEAN: jsR_error(J, "new Boolean()");
case JS_TNUMBER: jsR_error(J, "new Number()");
case JS_TSTRING: jsR_error(J, "new String()");
case JS_TOBJECT: return stack[idx].u.object;
- default: jsR_error(J, "TypeError");
}
return NULL;
}
@@ -239,7 +237,7 @@
{
}
-static void runfun(js_State *J, js_Function *F, js_Object *E)
+static void runfun(js_State *J, js_Function *F, js_Environment *E)
{
js_Function **FT = F->funtab;
double *NT = F->numtab;
@@ -252,7 +250,7 @@
js_Object *obj;
js_Property *ref;
double x, y;
- int b;
+ int b, argc;
while (1) {
opcode = *pc++;
@@ -269,8 +267,11 @@
case OP_NUMBER_X: js_pushnumber(J, *pc++); break;
case OP_NUMBER: js_pushnumber(J, NT[*pc++]); break;
case OP_STRING: js_pushlstring(J, ST[*pc++]); break;
- // case OP_CLOSURE: break;
+ case OP_CLOSURE: js_pushobject(J, js_newfunction(J, FT[*pc++], E)); break;
+ case OP_NEWOBJECT: js_pushobject(J, js_newobject(J, JS_COBJECT)); break;
+ case OP_NEWARRAY: js_pushobject(J, js_newobject(J, JS_CARRAY)); break;
+
case OP_UNDEF: js_pushundefined(J); break;
case OP_NULL: js_pushnull(J); break;
case OP_TRUE: js_pushboolean(J, 1); break;
@@ -278,30 +279,28 @@
case OP_THIS: js_pushnull(J); break;
- case OP_NEWOBJECT: js_pushobject(J, js_newobject(J)); break;
- case OP_NEWARRAY: js_pushobject(J, js_newobject(J)); break;
-
case OP_FUNDEC:
- ref = js_setproperty(J, E, ST[*pc++]);
-// if (ref)
-// ref->value = js_toclosure(J, -1);
+ ref = js_decvar(J, E, ST[*pc++]);
+ if (ref)
+ ref->value = js_tovalue(J, -1);
js_pop(J, 1);
break;
- case OP_VARDEC: pc++; break;
- ref = js_setproperty(J, E, ST[*pc++]);
+ case OP_VARDEC:
+ ref = js_decvar(J, E, ST[*pc++]);
break;
case OP_GETVAR:
- ref = js_getproperty(J, E, ST[*pc++]);
+ str = ST[*pc++];
+ ref = js_getvar(J, E, str);
if (ref)
js_pushvalue(J, ref->value);
else
- js_pushundefined(J);
+ jsR_error(J, "ReferenceError (%s)", str);
break;
case OP_SETVAR:
- ref = js_setproperty(J, E, ST[*pc++]);
+ ref = js_setvar(J, E, ST[*pc++]);
if (ref)
ref->value = js_tovalue(J, -1);
break;
@@ -490,7 +489,12 @@
// case OP_EQ:
// case OP_NE:
- // case OP_STRICTEQ:
+ case OP_STRICTEQ:
+ x = js_tonumber(J, -2);
+ y = js_tonumber(J, -1);
+ js_pop(J, 2);
+ js_pushboolean(J, x == y);
+ break;
// case OP_STRICTNE:
/* Branching */
@@ -532,19 +536,38 @@
void jsR_error(js_State *J, const char *message)
{
fprintf(stderr, "runtime error: %s\n", message);
+void jsR_error(js_State *J, const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "runtime error: ");
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+
longjmp(J->jb, 1);
}
-void jsR_runfunction(js_State *J, js_Function *F)
+static void js_dumpstack(js_State *J)
{
- js_Object *E = js_newobject(J);
+ int i;
+ for (i = 0; i < top; i++) {
+ printf("stack %d: ", i);
+ js_dumpvalue(J, stack[i]);
+ putchar('\n');
+ }
+}
+void jsR_runfunction(js_State *J, js_Function *F)
+{
if (setjmp(J->jb)) {
- js_dumpobject(J, E);
+ js_dumpobject(J, J->E->variables);
return;
}
- runfun(J, F, E);
+ runfun(J, F, J->E);
- js_dumpobject(J, E);
+ js_dumpobject(J, J->E->variables);
+ js_dumpstack(J);
}
--- a/jsrun.h
+++ b/jsrun.h
@@ -1,7 +1,7 @@
#ifndef js_run_h
#define js_run_h
-void jsR_error(js_State *J, const char *message);
+void jsR_error(js_State *J, const char *fmt, ...);
void jsR_runfunction(js_State *J, js_Function *F);
#endif
--- a/jsstate.c
+++ b/jsstate.c
@@ -1,10 +1,13 @@
#include "js.h"
#include "jsstate.h"
+#include "jsobject.h"
js_State *js_newstate(void)
{
js_State *J = malloc(sizeof *J);
memset(J, 0, sizeof(*J));
+ J->global = js_newobject(J, JS_COBJECT);
+ J->E = js_newenvironment(J, NULL, J->global);
return J;
}
--- a/jsstate.h
+++ b/jsstate.h
@@ -26,6 +26,10 @@
/* compiler */
js_Function *fun; /* list of allocated functions to free on errors */
+ /* runtime */
+ js_Environment *E;
+ js_Object *global;
+
int strict;
};