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