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.
This commit is contained in:
Tor Andersson
2014-01-17 22:03:35 +01:00
parent e431170dae
commit 81b2bb9ca4
5 changed files with 102 additions and 14 deletions
+16 -2
View File
@@ -33,7 +33,21 @@ static int jsB_eval(js_State *J, int argc)
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 int jsB_String(js_State *J, int n) { return 0; }
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");
+9
View File
@@ -21,6 +21,15 @@ js_Object *jsR_newcfunction(js_State *J, js_CFunction cfunction)
{
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;
}
+2
View File
@@ -67,6 +67,7 @@ struct js_Object
js_Environment *scope;
js_Function *function;
js_CFunction cfunction;
js_CFunction cconstructor;
};
struct js_Property
@@ -96,6 +97,7 @@ js_Property *jsR_nextproperty(js_State *J, js_Object *obj, const char *name);
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);
+67 -11
View File
@@ -121,9 +121,23 @@ void js_newfunction(js_State *J, js_Function *F, js_Environment *scope)
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 */
@@ -137,15 +151,13 @@ static const js_Value *stackidx(js_State *J, int idx)
return stack + idx;
}
int js_isundefined(js_State *J, int idx)
{
return stackidx(J, idx)->type == JS_TUNDEFINED;
}
int js_isstring(js_State *J, int idx)
{
return stackidx(J, idx)->type == JS_TSTRING;
}
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; }
js_Value js_tovalue(js_State *J, int idx)
{
@@ -237,6 +249,15 @@ void js_dup1rot4(js_State *J)
++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 @@ void js_call(js_State *J, int n)
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)
@@ -559,6 +611,10 @@ static void jsR_run(js_State *J, js_Function *F)
js_call(J, *pc++);
break;
case OP_NEW:
js_construct(J, *pc++);
break;
/* Unary expressions */
case OP_POS:
+8 -1
View File
@@ -17,6 +17,7 @@ void js_pushobject(js_State *J, js_Object *v);
/* 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_pushnumber(js_State *J, double v);
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);