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