shithub: libmujs

Download patch

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