ref: d35cef7df90196d56b9cf8ef8280f5af4e4a8dce
parent: b659aa50e16dac8b0013e79ee28ed5853de57452
author: Tor Andersson <tor@ccxvii.net>
date: Thu Feb 6 09:24:44 EST 2014
Implement getters and setters. Does not work properly for setters on inherited properties.
--- a/jsgc.c
+++ b/jsgc.c
@@ -74,17 +74,22 @@
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) {
--- a/jsproperty.c
+++ b/jsproperty.c
@@ -31,6 +31,8 @@
node->atts = 0;
node->value.type = JS_TUNDEFINED;
node->value.u.number = 0;
+ node->getter = NULL;
+ node->setter = NULL;
return node;
}
@@ -159,6 +161,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)
--- a/jsrun.c
+++ b/jsrun.c
@@ -242,6 +242,15 @@
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 @@
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,9 +404,10 @@
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;
@@ -398,7 +414,7 @@
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 @@
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 @@
}
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_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_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_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 @@
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 @@
}
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,10 +943,24 @@
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;
@@ -919,7 +967,7 @@
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;
@@ -926,7 +974,7 @@
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,7 +995,7 @@
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;
@@ -954,7 +1002,7 @@
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;
--- a/jsvalue.c
+++ b/jsvalue.c
@@ -135,6 +135,11 @@
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)
{
--- a/jsvalue.h
+++ b/jsvalue.h
@@ -95,6 +95,8 @@
int level;
int atts;
js_Value value;
+ js_Object *getter;
+ js_Object *setter;
};
struct js_Iterator
@@ -113,6 +115,7 @@
/* 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 @@
/* 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);