shithub: libmujs

Download patch

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;
 };