From 81b2bb9ca49fbfe6f2dcc25f18f684f83d3b8176 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 17 Jan 2014 22:03:35 +0100 Subject: [PATCH] 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. --- jsbuiltin.c | 18 +++++++++++-- jsobject.c | 9 +++++++ jsobject.h | 2 ++ jsrun.c | 78 +++++++++++++++++++++++++++++++++++++++++++++-------- jsrun.h | 9 ++++++- 5 files changed, 102 insertions(+), 14 deletions(-) diff --git a/jsbuiltin.c b/jsbuiltin.c index 3ff5109..4a44bbe 100644 --- a/jsbuiltin.c +++ b/jsbuiltin.c @@ -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"); diff --git a/jsobject.c b/jsobject.c index 27edafe..d0325ca 100644 --- a/jsobject.c +++ b/jsobject.c @@ -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; } diff --git a/jsobject.h b/jsobject.h index 51536b9..e7ca1eb 100644 --- a/jsobject.h +++ b/jsobject.h @@ -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); diff --git a/jsrun.c b/jsrun.c index 7e078a1..66e35d5 100644 --- a/jsrun.c +++ b/jsrun.c @@ -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: diff --git a/jsrun.h b/jsrun.h index 4087645..edef0e7 100644 --- a/jsrun.h +++ b/jsrun.h @@ -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);