mirror of
https://github.com/ccxvii/mujs.git
synced 2026-09-24 09:13:37 +08:00
Maintain order of property enumeration by keeping a linked list.
Match behaviour with other JS implementations.
This commit is contained in:
@@ -22,9 +22,11 @@ static void jsG_freefunction(js_State *J, js_Function *fun)
|
||||
|
||||
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_freeiterator(js_State *J, js_Iterator *node)
|
||||
|
||||
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 @@ static void jsG_markobject(js_State *J, int mark, js_Object *obj)
|
||||
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);
|
||||
|
||||
+66
-53
@@ -25,6 +25,8 @@ static js_Property *newproperty(js_State *J, const char *name)
|
||||
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 @@ static js_Property *insert(js_State *J, js_Property *node, const char *name, js_
|
||||
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 @@ static js_Property *delete(js_State *J, js_Property *node, const char *name)
|
||||
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 @@ js_Property *jsV_getproperty(js_State *J, js_Object *obj, const char *name)
|
||||
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 @@ void jsV_delproperty(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)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ struct js_Object
|
||||
{
|
||||
js_Class type;
|
||||
js_Property *properties;
|
||||
js_Property *head, *tail; /* for enumeration */
|
||||
js_Object *prototype;
|
||||
union {
|
||||
int boolean;
|
||||
@@ -64,7 +65,10 @@ struct js_Object
|
||||
js_CFunction function;
|
||||
js_CFunction constructor;
|
||||
} c;
|
||||
js_Iterator *iter;
|
||||
struct {
|
||||
js_Object *target;
|
||||
js_Iterator *head;
|
||||
} iter;
|
||||
struct {
|
||||
const char *tag;
|
||||
void *data;
|
||||
@@ -78,6 +82,7 @@ struct js_Property
|
||||
{
|
||||
const char *name;
|
||||
js_Property *left, *right;
|
||||
js_Property **prevp, *next; /* for enumeration */
|
||||
int level;
|
||||
int atts;
|
||||
js_Value value;
|
||||
@@ -118,7 +123,7 @@ js_Property *jsV_nextproperty(js_State *J, js_Object *obj, const char *name);
|
||||
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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user