5 Use an AA-tree to quickly look up properties in objects:
7 The level of every leaf node is one.
8 The level of every left child is one less than its parent.
9 The level of every right child is equal or one less than its parent.
10 The level of every right grandchild is less than its grandparent.
11 Every node of level greater than one has two children.
13 A link where the child's level is equal to that of its parent is called a horizontal link.
14 Individual right horizontal links are allowed, but consecutive ones are forbidden.
15 Left horizontal links are forbidden.
17 skew() fixes left horizontal links.
18 split() fixes consecutive right horizontal links.
21 static js_Property sentinel = {
26 { {0}, {0}, JS_TUNDEFINED },
30 static js_Property *newproperty(js_State *J, js_Object *obj, const char *name)
32 js_Property *node = js_malloc(J, sizeof *node);
33 node->name = js_intern(J, name);
34 node->left = node->right = &sentinel;
39 node->value.type = JS_TUNDEFINED;
40 node->value.u.number = 0;
47 static js_Property *lookup(js_Property *node, const char *name)
49 while (node != &sentinel) {
50 int c = strcmp(name, node->name);
61 static js_Property *skew(js_Property *node)
63 if (node->left->level == node->level) {
64 js_Property *temp = node;
66 temp->left = node->right;
72 static js_Property *split(js_Property *node)
74 if (node->right->right->level == node->level) {
75 js_Property *temp = node;
77 temp->right = node->left;
84 static js_Property *insert(js_State *J, js_Object *obj, js_Property *node, const char *name, js_Property **result)
86 if (node != &sentinel) {
87 int c = strcmp(name, node->name);
89 node->left = insert(J, obj, node->left, name, result);
91 node->right = insert(J, obj, node->right, name, result);
93 return *result = node;
98 return *result = newproperty(J, obj, name);
101 static void freeproperty(js_State *J, js_Object *obj, js_Property *node)
104 node->next->prevp = node->prevp;
106 obj->tailp = node->prevp;
107 *node->prevp = node->next;
112 static js_Property *delete(js_State *J, js_Object *obj, js_Property *node, const char *name)
114 js_Property *temp, *succ;
116 if (node != &sentinel) {
117 int c = strcmp(name, node->name);
119 node->left = delete(J, obj, node->left, name);
121 node->right = delete(J, obj, node->right, name);
123 if (node->left == &sentinel) {
126 freeproperty(J, obj, temp);
127 } else if (node->right == &sentinel) {
130 freeproperty(J, obj, temp);
133 while (succ->left != &sentinel)
135 node->name = succ->name;
136 node->atts = succ->atts;
137 node->value = succ->value;
138 node->right = delete(J, obj, node->right, succ->name);
142 if (node->left->level < node->level - 1 ||
143 node->right->level < node->level - 1)
145 if (node->right->level > --node->level)
146 node->right->level = node->level;
148 node->right = skew(node->right);
149 node->right->right = skew(node->right->right);
151 node->right = split(node->right);
158 js_Object *jsV_newobject(js_State *J, enum js_Class type, js_Object *prototype)
160 js_Object *obj = js_malloc(J, sizeof *obj);
161 memset(obj, 0, sizeof *obj);
163 obj->gcnext = J->gcobj;
168 obj->properties = &sentinel;
170 obj->tailp = &obj->head;
171 obj->prototype = prototype;
176 js_Property *jsV_getownproperty(js_State *J, js_Object *obj, const char *name)
178 return lookup(obj->properties, name);
181 js_Property *jsV_getpropertyx(js_State *J, js_Object *obj, const char *name, int *own)
185 js_Property *ref = lookup(obj->properties, name);
188 obj = obj->prototype;
194 js_Property *jsV_getproperty(js_State *J, js_Object *obj, const char *name)
197 js_Property *ref = lookup(obj->properties, name);
200 obj = obj->prototype;
205 js_Property *jsV_setproperty(js_State *J, js_Object *obj, const char *name)
209 if (!obj->extensible) {
210 result = lookup(obj->properties, name);
211 if (J->strict && !result)
212 js_typeerror(J, "object is non-extensible");
216 obj->properties = insert(J, obj, obj->properties, name, &result);
217 if (!result->prevp) {
218 result->prevp = obj->tailp;
219 *obj->tailp = result;
220 obj->tailp = &result->next;
225 void jsV_delproperty(js_State *J, js_Object *obj, const char *name)
227 obj->properties = delete(J, obj, obj->properties, name);
230 /* Flatten hierarchy of enumerable properties into an iterator object */
232 static int itshadow(js_State *J, js_Object *top, js_Object *bot, const char *name)
236 js_Property *prop = lookup(top->properties, name);
237 if (prop && !(prop->atts & JS_DONTENUM))
239 if (top->type == JS_CSTRING)
240 if (js_isarrayindex(J, name, &k) && k < top->u.s.length)
242 top = top->prototype;
247 static void itwalk(js_State *J, js_Object *io, js_Object *top, int own)
249 js_Object *obj = top;
250 js_Iterator *tail = NULL;
255 js_Iterator *node = js_malloc(J, sizeof *node); \
259 io->u.iter.head = tail = node; \
266 js_Property *prop = obj->head;
268 if (!(prop->atts & JS_DONTENUM) && !itshadow(J, top, obj, prop->name)) {
274 if (obj->type == JS_CSTRING) {
275 for (k = 0; k < obj->u.s.length; ++k) {
277 if (!itshadow(J, top, obj, buf)) {
278 ITADD(js_intern(J, buf));
285 obj = obj->prototype;
289 js_Object *jsV_newiterator(js_State *J, js_Object *obj, int own)
291 js_Object *io = jsV_newobject(J, JS_CITERATOR, NULL);
292 io->u.iter.target = obj;
293 io->u.iter.head = NULL;
294 itwalk(J, io, obj, own);
298 const char *jsV_nextiterator(js_State *J, js_Object *io)
301 if (io->type != JS_CITERATOR)
302 js_typeerror(J, "not an iterator");
303 while (io->u.iter.head) {
304 js_Iterator *next = io->u.iter.head->next;
305 const char *name = io->u.iter.head->name;
306 js_free(J, io->u.iter.head);
307 io->u.iter.head = next;
308 if (jsV_getproperty(J, io->u.iter.target, name))
310 if (io->u.iter.target->type == JS_CSTRING)
311 if (js_isarrayindex(J, name, &k) && k < io->u.iter.target->u.s.length)
317 /* Walk all the properties and delete them one by one for arrays */
319 void jsV_resizearray(js_State *J, js_Object *obj, unsigned int newlen)
324 if (newlen < obj->u.a.length) {
325 if (obj->u.a.length > obj->count * 2) {
326 js_Object *it = jsV_newiterator(J, obj, 1);
327 while ((s = jsV_nextiterator(J, it))) {
328 k = jsV_numbertouint32(jsV_stringtonumber(J, s));
329 if (k >= newlen && !strcmp(s, jsV_numbertostring(J, buf, k)))
330 jsV_delproperty(J, obj, s);
333 for (k = newlen; k < obj->u.a.length; ++k) {
334 jsV_delproperty(J, obj, js_itoa(buf, k));
338 obj->u.a.length = newlen;