shithub: libmujs

Download patch

ref: 81b2bb9ca49fbfe6f2dcc25f18f684f83d3b8176
parent: e431170dae07e32179b8234d9af699f4491294db
author: Tor Andersson <[email protected]>
date: Fri Jan 17 17:03:35 EST 2014

Add constructors called with 'new' keyword.

Support built-in constructors that create their own objects. They get
called without a this object, which they should create and push on the
stack.

--- a/jsbuiltin.c
+++ b/jsbuiltin.c
@@ -33,7 +33,21 @@
 	return 1;
 }
 
-static int jsB_Object(js_State *J, int n) { return 0; }
+static int jsB_Object(js_State *J, int n) {
+	if (n == 0 || js_isundefined(J, 1) || js_isnull(J, 1))
+		js_newobject(J);
+	else
+		js_pushobject(J, js_toobject(J, 1));
+	return 1;
+}
+
+static int jsB_new_Object(js_State *J, int n) {
+	if (n == 0 || js_isundefined(J, 0) || js_isnull(J, 0))
+		js_newobject(J);
+	else
+		js_pushobject(J, js_toobject(J, 0));
+	return 1;
+}
 static int jsB_Array(js_State *J, int n) { return 0; }
 static int jsB_Function(js_State *J, int n) { return 0; }
 static int jsB_Boolean(js_State *J, int n) { return 0; }
@@ -43,7 +57,7 @@
 static void jsB_initobject(js_State *J)
 {
 	J->Object_prototype = jsR_newobject(J, JS_COBJECT, NULL);
-	js_pushcfunction(J, jsB_Object);
+	js_pushcconstructor(J, jsB_Object, jsB_new_Object);
 	js_pushobject(J, J->Object_prototype);
 	js_setproperty(J, -2, "prototype");
 	js_setglobal(J, "Object");
--- a/jsobject.c
+++ b/jsobject.c
@@ -21,6 +21,15 @@
 {
 	js_Object *obj = jsR_newobject(J, JS_CCFUNCTION, NULL);
 	obj->cfunction = cfunction;
+	obj->cconstructor = NULL;
+	return obj;
+}
+
+js_Object *jsR_newcconstructor(js_State *J, js_CFunction cfunction, js_CFunction cconstructor)
+{
+	js_Object *obj = jsR_newobject(J, JS_CCFUNCTION, NULL);
+	obj->cfunction = cfunction;
+	obj->cconstructor = cconstructor;
 	return obj;
 }
 
--- a/jsobject.h
+++ b/jsobject.h
@@ -67,6 +67,7 @@
 	js_Environment *scope;
 	js_Function *function;
 	js_CFunction cfunction;
+	js_CFunction cconstructor;
 };
 
 struct js_Property
@@ -96,6 +97,7 @@
 js_Object *jsR_newfunction(js_State *J, js_Function *function, js_Environment *scope);
 js_Object *jsR_newscript(js_State *J, js_Function *function);
 js_Object *jsR_newcfunction(js_State *J, js_CFunction cfunction);
+js_Object *jsR_newcconstructor(js_State *J, js_CFunction cfunction, js_CFunction cconstructor);
 js_Object *jsR_newboolean(js_State *J, int v);
 js_Object *jsR_newnumber(js_State *J, double v);
 js_Object *jsR_newstring(js_State *J, const char *v);
--- a/jsrun.c
+++ b/jsrun.c
@@ -121,11 +121,25 @@
 	js_setproperty(J, -2, "prototype");
 }
 
-void js_pushcfunction(js_State *J, js_CFunction v)
+void js_pushcfunction(js_State *J, js_CFunction fun)
 {
-	js_pushobject(J, jsR_newcfunction(J, v));
+	js_pushobject(J, jsR_newcfunction(J, fun));
+	// TODO: length property?
+	js_newobject(J);
+	js_copy(J, -2);
+	js_setproperty(J, -2, "constructor");
+	js_setproperty(J, -2, "prototype");
 }
 
+void js_pushcconstructor(js_State *J, js_CFunction fun, js_CFunction con)
+{
+	js_pushobject(J, jsR_newcconstructor(J, fun, con));
+	js_newobject(J);
+	js_copy(J, -2);
+	js_setproperty(J, -2, "constructor");
+	js_setproperty(J, -2, "prototype");
+}
+
 /* Read values from stack */
 
 static const js_Value *stackidx(js_State *J, int idx)
@@ -137,16 +151,14 @@
 	return stack + idx;
 }
 
-int js_isundefined(js_State *J, int idx)
-{
-	return stackidx(J, idx)->type == JS_TUNDEFINED;
-}
+int js_isundefined(js_State *J, int idx) { return stackidx(J, idx)->type == JS_TUNDEFINED; }
+int js_isnull(js_State *J, int idx) { return stackidx(J, idx)->type == JS_TNULL; }
+int js_isboolean(js_State *J, int idx) { return stackidx(J, idx)->type == JS_TBOOLEAN; }
+int js_isnumber(js_State *J, int idx) { return stackidx(J, idx)->type == JS_TNUMBER; }
+int js_isstring(js_State *J, int idx) { return stackidx(J, idx)->type == JS_TSTRING; }
+int js_isprimitive(js_State *J, int idx) { return stackidx(J, idx)->type != JS_TOBJECT; }
+int js_isobject(js_State *J, int idx) { return stackidx(J, idx)->type == JS_TOBJECT; }
 
-int js_isstring(js_State *J, int idx)
-{
-	return stackidx(J, idx)->type == JS_TSTRING;
-}
-
 js_Value js_tovalue(js_State *J, int idx)
 {
 	return *stackidx(J, idx);
@@ -237,6 +249,15 @@
 	++top;
 }
 
+void js_rot(js_State *J, int n)
+{
+	int i;
+	js_Value tmp = stack[top-1];
+	for (i = 1; i <= n; i++)
+		stack[top-i] = stack[top-i-1];
+	stack[top-i] = tmp;
+}
+
 /* Global and object property accessors */
 
 void js_getglobal(js_State *J, const char *name)
@@ -396,6 +417,37 @@
 	bot = savebot;
 }
 
+void js_construct(js_State *J, int n)
+{
+	js_Object *obj = js_toobject(J, -n - 1);
+	js_Object *prototype;
+
+	/* built-in constructors create their own objects */
+	if (obj->type == JS_CCFUNCTION && obj->cconstructor) {
+		int savebot = bot;
+		bot = top - n;
+		jsR_callcfunction(J, n, obj->cconstructor);
+		bot = savebot;
+		return;
+	}
+
+	/* extract the function object's prototype property */
+	js_getproperty(J, -n - 1, "prototype");
+	if (js_isobject(J, -1))
+		prototype = js_toobject(J, -1);
+	else
+		prototype = J->Object_prototype;
+	js_pop(J, 1);
+
+	/* create a new object with above prototype, and shift it into the 'this' slot */
+	js_pushobject(J, jsR_newobject(J, JS_COBJECT, prototype));
+	if (n > 0)
+		js_rot(J, n + 1);
+
+	/* call the function */
+	js_call(J, n);
+}
+
 /* Main interpreter loop */
 
 void jsR_dumpstack(js_State *J)
@@ -557,6 +609,10 @@
 
 		case OP_CALL:
 			js_call(J, *pc++);
+			break;
+
+		case OP_NEW:
+			js_construct(J, *pc++);
 			break;
 
 		/* Unary expressions */
--- a/jsrun.h
+++ b/jsrun.h
@@ -17,6 +17,7 @@
 /* public */
 
 void js_call(js_State *J, int n);
+void js_construct(js_State *J, int n);
 
 void js_getglobal(js_State *J, const char *name);
 void js_setglobal(js_State *J, const char *name);
@@ -33,10 +34,16 @@
 void js_pushstring(js_State *J, const char *v);
 void js_newobject(js_State *J);
 void js_newarray(js_State *J);
-void js_pushcfunction(js_State *J, js_CFunction v);
+void js_pushcfunction(js_State *J, js_CFunction fun);
+void js_pushcconstructor(js_State *J, js_CFunction fun, js_CFunction con);
 
 int js_isundefined(js_State *J, int idx);
+int js_isnull(js_State *J, int idx);
+int js_isboolean(js_State *J, int idx);
+int js_isnumber(js_State *J, int idx);
 int js_isstring(js_State *J, int idx);
+int js_isprimitive(js_State *J, int idx);
+int js_isobject(js_State *J, int idx);
 
 int js_toboolean(js_State *J, int idx);
 double js_tonumber(js_State *J, int idx);