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