diff --git a/jsgc.c b/jsgc.c index e653dde..b5a597c 100644 --- a/jsgc.c +++ b/jsgc.c @@ -74,17 +74,22 @@ static void jsG_markenvironment(js_State *J, int mark, js_Environment *env) static void jsG_markproperty(js_State *J, int mark, js_Property *node) { - if (node->left->level) jsG_markproperty(J, mark, node->left); - if (node->right->level) jsG_markproperty(J, mark, node->right); - if (node->value.type == JS_TOBJECT && node->value.u.object->gcmark != mark) - jsG_markobject(J, mark, node->value.u.object); + while (node) { + if (node->value.type == JS_TOBJECT && node->value.u.object->gcmark != mark) + jsG_markobject(J, mark, node->value.u.object); + if (node->getter && node->getter->gcmark != mark) + jsG_markobject(J, mark, node->getter); + if (node->setter && node->setter->gcmark != mark) + jsG_markobject(J, mark, node->setter); + node = node->next; + } } static void jsG_markobject(js_State *J, int mark, js_Object *obj) { obj->gcmark = mark; - if (obj->properties->level) - jsG_markproperty(J, mark, obj->properties); + if (obj->head) + jsG_markproperty(J, mark, obj->head); if (obj->prototype && obj->prototype->gcmark != mark) jsG_markobject(J, mark, obj->prototype); if (obj->type == JS_CITERATOR) { diff --git a/jsproperty.c b/jsproperty.c index bfad8f3..eb6d2da 100644 --- a/jsproperty.c +++ b/jsproperty.c @@ -31,6 +31,8 @@ static js_Property *newproperty(js_State *J, const char *name) node->atts = 0; node->value.type = JS_TUNDEFINED; node->value.u.number = 0; + node->getter = NULL; + node->setter = NULL; return node; } @@ -161,6 +163,19 @@ js_Property *jsV_getownproperty(js_State *J, js_Object *obj, const char *name) return lookup(obj->properties, name); } +js_Property *jsV_getpropertyx(js_State *J, js_Object *obj, const char *name, int *own) +{ + *own = 1; + do { + js_Property *ref = lookup(obj->properties, name); + if (ref) + return ref; + obj = obj->prototype; + *own = 0; + } while (obj); + return NULL; +} + js_Property *jsV_getproperty(js_State *J, js_Object *obj, const char *name) { do { diff --git a/jsrun.c b/jsrun.c index cb2140b..a52b5f3 100644 --- a/jsrun.c +++ b/jsrun.c @@ -242,6 +242,15 @@ void *js_touserdata(js_State *J, const char *tag, int idx) js_typeerror(J, "not a %s", tag); } +static js_Object *jsR_tofunction(js_State *J, int idx) +{ + const js_Value *v = stackidx(J, idx); + if (v->type == JS_TOBJECT) + if (v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION) + return v->u.object; + js_typeerror(J, "not a function"); +} + /* Stack manipulation */ void js_pop(js_State *J, int n) @@ -376,7 +385,13 @@ static int jsR_hasproperty(js_State *J, js_Object *obj, const char *name) ref = jsV_getproperty(J, obj, name); if (ref) { - js_pushvalue(J, ref->value); + if (ref->getter) { + js_pushobject(J, ref->getter); + js_pushobject(J, obj); + js_call(J, 0); + } else { + js_pushvalue(J, ref->value); + } return 1; } @@ -389,16 +404,17 @@ static void jsR_getproperty(js_State *J, js_Object *obj, const char *name) js_pushundefined(J); } -static void jsR_setproperty(js_State *J, js_Object *obj, const char *name, int idx) +static void jsR_setproperty(js_State *J, js_Object *obj, const char *name, const js_Value *value) { js_Property *ref; + int own; if (obj->type == JS_CARRAY) { unsigned int k; char buf[32]; if (!strcmp(name, "length")) { - double rawlen = js_tonumber(J, idx); + double rawlen = jsV_tonumber(J, value); unsigned int newlen = jsV_numbertouint32(rawlen); if (newlen != rawlen) js_rangeerror(J, "array length"); @@ -422,17 +438,32 @@ static void jsR_setproperty(js_State *J, js_Object *obj, const char *name, int i if (!strcmp(name, "ignoreCase")) return; if (!strcmp(name, "multiline")) return; if (!strcmp(name, "lastIndex")) { - obj->u.r.last = js_tointeger(J, idx); + obj->u.r.last = jsV_tointeger(J, value); return; } } - ref = jsV_setproperty(J, obj, name); - if (ref && !(ref->atts & JS_READONLY)) - ref->value = js_tovalue(J, idx); + /* First try to find a setter in prototype chain */ + ref = jsV_getpropertyx(J, obj, name, &own); + if (ref && ref->setter) { + js_pushobject(J, ref->setter); + js_pushobject(J, obj); + js_pushvalue(J, *value); + js_call(J, 1); + js_pop(J, 1); + return; + } + + /* Property not found on this object, so create one */ + if (!ref || !own) + ref = jsV_setproperty(J, obj, name); + + if (!(ref->atts & JS_READONLY)) + ref->value = *value; } -static void jsR_defproperty(js_State *J, js_Object *obj, const char *name, int idx, int atts) +static void jsR_defproperty(js_State *J, js_Object *obj, const char *name, + int atts, const js_Value *value, js_Object *getter, js_Object *setter) { js_Property *ref; @@ -451,10 +482,13 @@ static void jsR_defproperty(js_State *J, js_Object *obj, const char *name, int i } ref = jsV_setproperty(J, obj, name); - if (ref) { - ref->value = js_tovalue(J, idx); - ref->atts = atts; - } + if (value && !(ref->atts & JS_READONLY)) + ref->value = *value; + if (getter && !(ref->atts & JS_DONTDELETE)) + ref->getter = getter; + if (setter && !(ref->atts & JS_DONTDELETE)) + ref->setter = setter; + ref->atts |= atts; } static int jsR_delproperty(js_State *J, js_Object *obj, const char *name) @@ -521,7 +555,7 @@ void js_getregistry(js_State *J, const char *name) void js_setregistry(js_State *J, const char *name) { - jsR_setproperty(J, J->R, name, -1); + jsR_setproperty(J, J->R, name, stackidx(J, -1)); js_pop(J, 1); } @@ -537,13 +571,13 @@ void js_getglobal(js_State *J, const char *name) void js_setglobal(js_State *J, const char *name) { - jsR_setproperty(J, J->G, name, -1); + jsR_setproperty(J, J->G, name, stackidx(J, -1)); js_pop(J, 1); } void js_defglobal(js_State *J, const char *name, int atts) { - jsR_defproperty(J, J->G, name, -1, atts); + jsR_defproperty(J, J->G, name, atts, stackidx(J, -1), NULL, NULL); js_pop(J, 1); } @@ -554,13 +588,13 @@ void js_getproperty(js_State *J, int idx, const char *name) void js_setproperty(js_State *J, int idx, const char *name) { - jsR_setproperty(J, js_toobject(J, idx), name, -1); + jsR_setproperty(J, js_toobject(J, idx), name, stackidx(J, -1)); js_pop(J, 1); } void js_defproperty(js_State *J, int idx, const char *name, int atts) { - jsR_defproperty(J, js_toobject(J, idx), name, -1, atts); + jsR_defproperty(J, js_toobject(J, idx), name, atts, stackidx(J, -1), NULL, NULL); js_pop(J, 1); } @@ -591,7 +625,7 @@ js_Environment *jsR_newenvironment(js_State *J, js_Object *vars, js_Environment static void js_decvar(js_State *J, const char *name, int idx) { - jsR_defproperty(J, J->E->variables, name, idx, JS_DONTENUM | JS_DONTDELETE); + jsR_defproperty(J, J->E->variables, name, JS_DONTENUM | JS_DONTDELETE, stackidx(J, idx), NULL, NULL); } static void js_getvar(js_State *J, const char *name) @@ -622,7 +656,7 @@ static void js_setvar(js_State *J, const char *name) } E = E->outer; } while (E); - jsR_setproperty(J, J->G, name, -1); + jsR_setproperty(J, J->G, name, stackidx(J, -1)); } static int js_delvar(js_State *J, const char *name) @@ -909,24 +943,38 @@ static void jsR_run(js_State *J, js_Function *F) js_pushboolean(J, b); break; + case OP_INITGETTER: + obj = js_toobject(J, -3); + str = js_tostring(J, -2); + jsR_defproperty(J, obj, str, 0, NULL, jsR_tofunction(J, -1), NULL); + js_pop(J, 2); + break; + + case OP_INITSETTER: + obj = js_toobject(J, -3); + str = js_tostring(J, -2); + jsR_defproperty(J, obj, str, 0, NULL, NULL, jsR_tofunction(J, -1)); + js_pop(J, 2); + break; + case OP_INITPROP: str = js_tostring(J, -2); obj = js_toobject(J, -3); - jsR_setproperty(J, obj, str, -1); + jsR_setproperty(J, obj, str, stackidx(J, -1)); js_pop(J, 2); break; case OP_INITPROP_S: str = ST[*pc++]; obj = js_toobject(J, -2); - jsR_setproperty(J, obj, str, -1); + jsR_setproperty(J, obj, str, stackidx(J, -1)); js_pop(J, 1); break; case OP_INITPROP_N: str = jsV_numbertostring(J, *pc++); obj = js_toobject(J, -2); - jsR_setproperty(J, obj, str, -1); + jsR_setproperty(J, obj, str, stackidx(J, -1)); js_pop(J, 1); break; @@ -947,14 +995,14 @@ static void jsR_run(js_State *J, js_Function *F) case OP_SETPROP: str = js_tostring(J, -2); obj = js_toobject(J, -3); - jsR_setproperty(J, obj, str, -1); + jsR_setproperty(J, obj, str, stackidx(J, -1)); js_rot3pop2(J); break; case OP_SETPROP_S: str = ST[*pc++]; obj = js_toobject(J, -2); - jsR_setproperty(J, obj, str, -1); + jsR_setproperty(J, obj, str, stackidx(J, -1)); js_rot2pop1(J); break; diff --git a/jsvalue.c b/jsvalue.c index 02a3405..033a1b6 100644 --- a/jsvalue.c +++ b/jsvalue.c @@ -135,6 +135,11 @@ double jsV_tonumber(js_State *J, const js_Value *v) return 0; } +double jsV_tointeger(js_State *J, const js_Value *v) +{ + return jsV_numbertointeger(jsV_tonumber(J, v)); +} + /* ToString() on a number */ const char *jsV_numbertostring(js_State *J, double n) { diff --git a/jsvalue.h b/jsvalue.h index 5ac3416..bff3b12 100644 --- a/jsvalue.h +++ b/jsvalue.h @@ -95,6 +95,8 @@ struct js_Property int level; int atts; js_Value value; + js_Object *getter; + js_Object *setter; }; struct js_Iterator @@ -113,6 +115,7 @@ void js_pushobject(js_State *J, js_Object *v); /* jsvalue.c */ int jsV_toboolean(js_State *J, const js_Value *v); double jsV_tonumber(js_State *J, const js_Value *v); +double jsV_tointeger(js_State *J, const js_Value *v); const char *jsV_tostring(js_State *J, const js_Value *v); js_Object *jsV_toobject(js_State *J, const js_Value *v); js_Value jsV_toprimitive(js_State *J, const js_Value *v, int preferred); @@ -126,6 +129,7 @@ double jsV_stringtonumber(js_State *J, const char *string); /* jsproperty.c */ js_Object *jsV_newobject(js_State *J, js_Class type, js_Object *prototype); js_Property *jsV_getownproperty(js_State *J, js_Object *obj, const char *name); +js_Property *jsV_getpropertyx(js_State *J, js_Object *obj, const char *name, int *own); js_Property *jsV_getproperty(js_State *J, js_Object *obj, const char *name); js_Property *jsV_setproperty(js_State *J, js_Object *obj, const char *name); js_Property *jsV_nextproperty(js_State *J, js_Object *obj, const char *name);