shithub: libmujs

Download patch

ref: 8ade82bdfa0fc07740f9000c848f100d8367bf7b
parent: 1aa15582d6f842513a1570b7533effd580bc7789
author: Tor Andersson <tor@ccxvii.net>
date: Tue Jan 28 08:58:19 EST 2014

Maintain order of property enumeration by keeping a linked list.

Match behaviour with other JS implementations.

--- a/jsgc.c
+++ b/jsgc.c
@@ -22,9 +22,11 @@
 
 static void jsG_freeproperty(js_State *J, js_Property *node)
 {
-	if (node->left->level) jsG_freeproperty(J, node->left);
-	if (node->right->level) jsG_freeproperty(J, node->right);
-	free(node);
+	while (node) {
+		js_Property *next = node->next;
+		free(node);
+		node = next;
+	}
 }
 
 static void jsG_freeiterator(js_State *J, js_Iterator *node)
@@ -38,10 +40,10 @@
 
 static void jsG_freeobject(js_State *J, js_Object *obj)
 {
-	if (obj->properties->level)
-		jsG_freeproperty(J, obj->properties);
+	if (obj->head)
+		jsG_freeproperty(J, obj->head);
 	if (obj->type == JS_CITERATOR)
-		jsG_freeiterator(J, obj->u.iter);
+		jsG_freeiterator(J, obj->u.iter.head);
 	free(obj);
 }
 
@@ -79,6 +81,9 @@
 		jsG_markproperty(J, mark, obj->properties);
 	if (obj->prototype && obj->prototype->gcmark != mark)
 		jsG_markobject(J, mark, obj->prototype);
+	if (obj->type == JS_CITERATOR) {
+		jsG_markobject(J, mark, obj->u.iter.target);
+	}
 	if (obj->type == JS_CFUNCTION || obj->type == JS_CSCRIPT) {
 		if (obj->u.f.scope && obj->u.f.scope->gcmark != mark)
 			jsG_markenvironment(J, mark, obj->u.f.scope);
--- a/jsproperty.c
+++ b/jsproperty.c
@@ -25,6 +25,8 @@
 	js_Property *node = malloc(sizeof(js_Property));
 	node->name = js_intern(J, name);
 	node->left = node->right = &sentinel;
+	node->prevp = NULL;
+	node->next = NULL;
 	node->level = 1;
 	node->atts = 0;
 	node->value.type = JS_TUNDEFINED;
@@ -86,6 +88,14 @@
 	return *result = newproperty(J, name);
 }
 
+static void freenode(js_State *J, js_Property *node)
+{
+	if (node->next)
+		node->next->prevp = node->prevp;
+	*node->prevp = node->next;
+	free(node);
+}
+
 static js_Property *delete(js_State *J, js_Property *node, const char *name)
 {
 	js_Property *temp, *succ;
@@ -100,11 +110,11 @@
 			if (node->left == &sentinel) {
 				temp = node;
 				node = node->right;
-				free(temp);
+				freenode(J, temp);
 			} else if (node->right == &sentinel) {
 				temp = node;
 				node = node->left;
-				free(temp);
+				freenode(J, temp);
 			} else {
 				succ = node->right;
 				while (succ->left != &sentinel)
@@ -162,21 +172,20 @@
 	return NULL;
 }
 
-static js_Property *jsV_getenumproperty(js_State *J, js_Object *obj, const char *name)
-{
-	do {
-		js_Property *ref = lookup(obj->properties, name);
-		if (ref && !(ref->atts & JS_DONTENUM))
-			return ref;
-		obj = obj->prototype;
-	} while (obj);
-	return NULL;
-}
-
 js_Property *jsV_setproperty(js_State *J, js_Object *obj, const char *name)
 {
 	js_Property *result;
 	obj->properties = insert(J, obj->properties, name, &result);
+	if (!result->prevp) {
+		if (!obj->head) {
+			result->prevp = &obj->head;
+			obj->tail = obj->head = result;
+		} else {
+			result->prevp = &obj->tail->next;
+			obj->tail->next = result;
+			obj->tail = result;
+		}
+	}
 	return result;
 }
 
@@ -187,59 +196,63 @@
 
 /* Flatten hierarchy of enumerable properties into an iterator object */
 
-static js_Iterator *itwalk(js_State *J, js_Iterator *iter, js_Property *prop, js_Object *seen)
+static int itshadow(js_State *J, js_Object *top, js_Object *bot, const char *name)
 {
-	if (prop->right != &sentinel)
-		iter = itwalk(J, iter, prop->right, seen);
-	if (!(prop->atts & JS_DONTENUM)) {
-		if (!seen || !jsV_getenumproperty(J, seen, prop->name)) {
-			js_Iterator *head = malloc(sizeof *head);
-			head->name = prop->name;
-			head->next = iter;
-			iter = head;
-		}
+	while (top != bot) {
+		js_Property *prop = lookup(top->properties, name);
+		if (prop && !(prop->atts & JS_DONTENUM))
+			return 1;
+		top = top->prototype;
 	}
-	if (prop->left != &sentinel)
-		iter = itwalk(J, iter, prop->left, seen);
-	return iter;
+	return 0;
 }
 
-static js_Iterator *itflatten(js_State *J, js_Object *obj)
+static void itwalk(js_State *J, js_Object *io, js_Object *top, int own)
 {
-	js_Iterator *iter = NULL;
-	if (obj->prototype)
-		iter = itflatten(J, obj->prototype);
-	if (obj->properties != &sentinel)
-		iter = itwalk(J, iter, obj->properties, obj->prototype);
-	return iter;
+	js_Object *obj = top;
+	js_Iterator *tail = NULL;
+	while (obj) {
+		js_Property *prop = obj->head;
+		while (prop) {
+			if (!(prop->atts & JS_DONTENUM) && !itshadow(J, top, obj, prop->name)) {
+				js_Iterator *node = malloc(sizeof *node);
+				node->name = prop->name;
+				node->next = NULL;
+				if (!io->u.iter.head) {
+					io->u.iter.head = tail = node;
+				} else {
+					tail->next = node;
+					tail = node;
+				}
+			}
+			prop = prop->next;
+		}
+		if (own)
+			break;
+		obj = obj->prototype;
+	}
 }
 
 js_Object *jsV_newiterator(js_State *J, js_Object *obj, int own)
 {
-	js_Object *iobj = jsV_newobject(J, JS_CITERATOR, NULL);
-	if (own) {
-		iobj->u.iter = NULL;
-		if (obj->properties != &sentinel)
-			iobj->u.iter = itwalk(J, iobj->u.iter, obj->properties, NULL);
-	} else {
-		iobj->u.iter = itflatten(J, obj);
-	}
-	return iobj;
+	js_Object *io = jsV_newobject(J, JS_CITERATOR, NULL);
+	io->u.iter.target = obj;
+	io->u.iter.head = NULL;
+	itwalk(J, io, obj, own);
+	return io;
 }
 
-const char *jsV_nextiterator(js_State *J, js_Object *iobj)
+const char *jsV_nextiterator(js_State *J, js_Object *io)
 {
-	js_Iterator *iter, *next;
-	const char *name;
-	if (iobj->type != JS_CITERATOR)
+	if (io->type != JS_CITERATOR)
 		js_typeerror(J, "not an iterator");
-	iter = iobj->u.iter;
-	if (iter) {
-		name = iter->name;
-		next = iter->next;
-		free(iter);
-		iobj->u.iter = next;
-		return name;
+	while (io->u.iter.head) {
+		js_Iterator *next = io->u.iter.head->next;
+		const char *name = io->u.iter.head->name;
+		free(io->u.iter.head);
+		io->u.iter.head = next;
+		if (jsV_getproperty(J, io->u.iter.target, name))
+			return name;
 	}
 	return NULL;
 }
--- a/jsvalue.h
+++ b/jsvalue.h
@@ -48,6 +48,7 @@
 {
 	js_Class type;
 	js_Property *properties;
+	js_Property *head, *tail; /* for enumeration */
 	js_Object *prototype;
 	union {
 		int boolean;
@@ -64,8 +65,11 @@
 			js_CFunction function;
 			js_CFunction constructor;
 		} c;
-		js_Iterator *iter;
 		struct {
+			js_Object *target;
+			js_Iterator *head;
+		} iter;
+		struct {
 			const char *tag;
 			void *data;
 		} user;
@@ -78,6 +82,7 @@
 {
 	const char *name;
 	js_Property *left, *right;
+	js_Property **prevp, *next; /* for enumeration */
 	int level;
 	int atts;
 	js_Value value;
@@ -118,7 +123,7 @@
 void jsV_delproperty(js_State *J, js_Object *obj, const char *name);
 
 js_Object *jsV_newiterator(js_State *J, js_Object *obj, int own);
-const char *jsV_nextiterator(js_State *J, js_Object *iobj);
+const char *jsV_nextiterator(js_State *J, js_Object *iter);
 
 void jsV_resizearray(js_State *J, js_Object *obj, unsigned int newlen);