shithub: libmujs

Download patch

ref: 0fd3f364ec2556f16c26498cc9575ee9807f463b
parent: a65416046c993ed35a7e46c8b096eefc8fb7b2c7
author: Tor Andersson <tor@ccxvii.net>
date: Wed Jan 22 21:35:56 EST 2014

Add iterator object class for internal use in for-in loops.

Flatten all enumerable properties in object prototype chain
into a linked list to enumerate.

--- a/js.h
+++ b/js.h
@@ -85,9 +85,7 @@
 void js_getownproperty(js_State *J, int idx, const char *name);
 void js_getproperty(js_State *J, int idx, const char *name);
 void js_setproperty(js_State *J, int idx, const char *name);
-void js_cfgproperty(js_State *J, int idx, const char *name, int atts);
 void js_delproperty(js_State *J, int idx, const char *name);
-int js_nextproperty(js_State *J, int idx);
 
 void js_pushglobal(js_State *J);
 void js_pushundefined(js_State *J);
--- a/jscompile.c
+++ b/jscompile.c
@@ -253,6 +253,7 @@
 		if (lhs->b)
 			jsC_error(J, lhs->b, "more than one loop variable in for-in statement");
 		emitstring(J, F, OP_SETVAR, lhs->a->a->string); /* list(var-init(ident)) */
+		emit(J, F, OP_POP);
 		return;
 	}
 
@@ -259,6 +260,7 @@
 	switch (lhs->type) {
 	case AST_IDENTIFIER:
 		emitstring(J, F, OP_SETVAR, lhs->string);
+		emit(J, F, OP_POP);
 		break;
 	case EXP_INDEX:
 		cexp(J, F, lhs->a);
@@ -265,6 +267,7 @@
 		cexp(J, F, lhs->b);
 		emit(J, F, OP_ROT3);
 		emit(J, F, OP_SETPROP);
+		emit(J, F, OP_POP);
 		break;
 	case EXP_MEMBER:
 		cexp(J, F, lhs->a);
@@ -271,6 +274,7 @@
 		emitstring(J, F, OP_STRING, lhs->b->string);
 		emit(J, F, OP_ROT3);
 		emit(J, F, OP_SETPROP);
+		emit(J, F, OP_POP);
 		break;
 	default:
 		jsC_error(J, lhs, "invalid l-value in for-in loop assignment");
@@ -646,11 +650,11 @@
 			break;
 		case STM_FOR_IN:
 		case STM_FOR_IN_VAR:
-			/* pop the object and name pair we are iterating over if leaving the loop */
+			/* pop the iterator if leaving the loop */
 			if (T == STM_RETURN)
-				emit(J, F, OP_ROT3POP2); /* save the return value */
+				emit(J, F, OP_ROT2POP1); /* save the return value */
 			if (T == STM_BREAK)
-				emit(J, F, OP_POP2);
+				emit(J, F, OP_POP);
 			break;
 		case STM_TRY:
 			/* came from try block */
@@ -875,13 +879,15 @@
 	case STM_FOR_IN:
 	case STM_FOR_IN_VAR:
 		cexp(J, F, stm->b);
-		emit(J, F, OP_UNDEF);
+		emit(J, F, OP_ITERATOR);
 		loop = here(J, F);
-		emit(J, F, OP_NEXTPROP);
-		end = jump(J, F, OP_JFALSE);
-		cassignforin(J, F, stm);
-		cstm(J, F, stm->c);
-		jumpto(J, F, OP_JUMP, loop);
+		{
+			emit(J, F, OP_NEXTITER);
+			end = jump(J, F, OP_JFALSE);
+			cassignforin(J, F, stm);
+			cstm(J, F, stm->c);
+			jumpto(J, F, OP_JUMP, loop);
+		}
 		label(J, F, end);
 		labeljumps(J, F, stm->jumps, here(J,F), loop);
 		break;
--- a/jscompile.h
+++ b/jscompile.h
@@ -11,6 +11,7 @@
 	OP_DUP2,	/* A B -- A B A B */
 	OP_ROT2,	/* A B -- B A */
 	OP_ROT3,	/* A B C -- C A B */
+	OP_ROT2POP1,	/* A B -- B */
 	OP_ROT3POP2,	/* A B C -- C */
 	OP_DUP1ROT4,	/* A B C -- C A B C */
 
@@ -43,7 +44,9 @@
 	OP_GETPROP,	/* <obj> <name> -- <value> */
 	OP_SETPROP,	/* <obj> <name> <value> -- <value> */
 	OP_DELPROP,	/* <obj> <name> -- <success> */
-	OP_NEXTPROP,	/* <obj> <name> -- <obj> <name+1> true || false */
+
+	OP_ITERATOR,	/* <obj> -- <iobj> */
+	OP_NEXTITER,	/* <iobj> -- <iobj> <name> true || false */
 
 	OP_CALL,	/* <closure> <this> <args...> -(numargs)- <returnvalue> */
 	OP_NEW,		/* <closure> <args...> -(numargs)- <returnvalue> */
--- a/jsgc.c
+++ b/jsgc.c
@@ -27,10 +27,21 @@
 	free(node);
 }
 
+static void jsG_freeiterator(js_State *J, js_Iterator *node)
+{
+	while (node) {
+		js_Iterator *next = node->next;
+		free(node);
+		node = next;
+	}
+}
+
 static void jsG_freeobject(js_State *J, js_Object *obj)
 {
 	if (obj->properties->level)
 		jsG_freeproperty(J, obj->properties);
+	if (obj->type == JS_CITERATOR)
+		jsG_freeiterator(J, obj->u.iterator.head);
 	free(obj);
 }
 
--- a/jsobject.c
+++ b/jsobject.c
@@ -36,6 +36,7 @@
 	case JS_CREGEXP: js_pushliteral(J, "[object RegExp]"); break;
 	case JS_CDATE: js_pushliteral(J, "[object Date]"); break;
 	case JS_CMATH: js_pushliteral(J, "[object Math]"); break;
+	case JS_CITERATOR: js_pushliteral(J, "[Iterator]"); break;
 	default: return 0;
 	}
 	return 1;
--- a/jsproperty.c
+++ b/jsproperty.c
@@ -90,45 +90,6 @@
 	return *result = newproperty(J, name);
 }
 
-static js_Property *lookupfirst(js_Property *node)
-{
-	while (node != &sentinel) {
-		if (node->left == &sentinel)
-			return node;
-		node = node->left;
-	}
-	return NULL;
-}
-
-static js_Property *lookupnext(js_Property *node, const char *name)
-{
-	js_Property *stack[100], *parent;
-	int top = 0;
-
-	stack[0] = NULL;
-	while (node != &sentinel) {
-		stack[++top] = node;
-		int c = strcmp(name, node->name);
-		if (c == 0)
-			goto found;
-		else if (c < 0)
-			node = node->left;
-		else
-			node = node->right;
-	}
-	return NULL;
-
-found:
-	if (node->right != &sentinel)
-		return lookupfirst(node->right);
-	parent = stack[--top];
-	while (parent && node == parent->right) {
-		node = parent;
-		parent = stack[--top];
-	}
-	return parent;
-}
-
 js_Object *jsV_newobject(js_State *J, js_Class type, js_Object *prototype)
 {
 	js_Object *obj = calloc(sizeof(js_Object), 1);
@@ -159,6 +120,17 @@
 	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;
@@ -166,9 +138,50 @@
 	return result;
 }
 
-js_Property *jsV_nextproperty(js_State *J, js_Object *obj, const char *name)
+/* 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)
 {
-	if (!name)
-		return lookupfirst(obj->properties);
-	return lookupnext(obj->properties, name);
+	if (prop->right != &sentinel)
+		iter = itwalk(J, iter, prop->right, seen);
+	if (!seen || !jsV_getenumproperty(J, seen, prop->name)) {
+		js_Iterator *head = malloc(sizeof *head);
+		head->name = prop->name;
+		head->next = iter;
+		iter = head;
+	}
+	if (prop->left != &sentinel)
+		iter = itwalk(J, iter, prop->left, seen);
+	return iter;
+}
+
+static js_Iterator *itflatten(js_State *J, js_Object *obj)
+{
+	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 *jsV_newiterator(js_State *J, js_Object *obj)
+{
+	js_Object *iobj = jsV_newobject(J, JS_CITERATOR, NULL);
+	iobj->u.iterator.head = itflatten(J, obj);
+	iobj->u.iterator.next = iobj->u.iterator.head;
+	return iobj;
+}
+
+const char *jsV_nextiterator(js_State *J, js_Object *iobj)
+{
+	js_Iterator *result;
+	if (iobj->type != JS_CITERATOR)
+		js_typeerror(J, "not an iterator");
+	result = iobj->u.iterator.next;
+	if (result) {
+		iobj->u.iterator.next = result->next;
+		return result->name;
+	}
+	return NULL;
 }
--- a/jsrun.c
+++ b/jsrun.c
@@ -282,19 +282,6 @@
 	js_pop(J, 1);
 }
 
-int js_nextproperty(js_State *J, int idx)
-{
-	js_Object *obj = js_toobject(J, idx);
-	js_Property *ref = jsV_nextproperty(J, obj, js_tostring(J, -1));
-	js_pop(J, 1);
-	if (ref) {
-		js_pushliteral(J, ref->name);
-		js_pushvalue(J, ref->value);
-		return 1;
-	}
-	return 0;
-}
-
 /* Environment records */
 
 js_Environment *jsR_newenvironment(js_State *J, js_Object *vars, js_Environment *outer)
@@ -537,6 +524,7 @@
 		case OP_DUP2: js_dup2(J); break;
 		case OP_ROT2: js_rot2(J); break;
 		case OP_ROT3: js_rot3(J); break;
+		case OP_ROT2POP1: js_rot2pop1(J); break;
 		case OP_ROT3POP2: js_rot3pop2(J); break;
 		case OP_DUP1ROT4: js_dup1rot4(J); break;
 
@@ -619,25 +607,20 @@
 
 		// OP_DELPROP
 
-		case OP_NEXTPROP:
-			obj = js_toobject(J, -2);
-			if (js_isundefined(J, -1))
-				str = NULL;
-			else
-				str = js_tostring(J, -1);
+		case OP_ITERATOR:
+			obj = jsV_newiterator(J, js_toobject(J, -1));
+			js_pop(J, 1);
+			js_pushobject(J, obj);
+			break;
 
-			ref = jsV_nextproperty(J, obj, str);
-			if (!ref && obj->prototype) {
-				obj = obj->prototype;
-				ref = jsV_nextproperty(J, obj, NULL);
-			}
-
-			js_pop(J, 2);
-			if (ref) {
-				js_pushobject(J, obj);
-				js_pushliteral(J, ref->name);
+		case OP_NEXTITER:
+			obj = js_toobject(J, -1);
+			str = jsV_nextiterator(J, obj);
+			if (str) {
+				js_pushliteral(J, str);
 				js_pushboolean(J, 1);
 			} else {
+				js_pop(J, 1);
 				js_pushboolean(J, 0);
 			}
 			break;
--- a/jsvalue.h
+++ b/jsvalue.h
@@ -5,6 +5,7 @@
 typedef enum js_Class js_Class;
 
 typedef struct js_Property js_Property;
+typedef struct js_Iterator js_Iterator;
 
 enum js_Type {
 	JS_TUNDEFINED,
@@ -28,6 +29,7 @@
 	JS_CREGEXP,
 	JS_CDATE,
 	JS_CMATH,
+	JS_CITERATOR,
 };
 
 struct js_Value
@@ -58,6 +60,10 @@
 			js_CFunction function;
 			js_CFunction constructor;
 		} c;
+		struct {
+			js_Iterator *head;
+			js_Iterator *next;
+		} iterator;
 	} u;
 	js_Object *gcnext;
 	int gcmark;
@@ -72,6 +78,12 @@
 	js_Value value;
 };
 
+struct js_Iterator
+{
+	const char *name;
+	js_Iterator *next;
+};
+
 /* jsrun.c */
 js_Value js_tovalue(js_State *J, int idx);
 js_Value js_toprimitive(js_State *J, int idx, int hint);
@@ -98,6 +110,9 @@
 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);
+
+js_Object *jsV_newiterator(js_State *J, js_Object *obj);
+const char *jsV_nextiterator(js_State *J, js_Object *iobj);
 
 /* jsdump.c */
 void js_dumpobject(js_State *J, js_Object *obj);