1#include "jsi.h"
2#include "jsvalue.h"
3
4/*
5 Use an AA-tree to quickly look up properties in objects:
6
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.
12
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.
16
17 skew() fixes left horizontal links.
18 split() fixes consecutive right horizontal links.
19*/
20
21static js_Property sentinel = {
22 "",
23 &sentinel, &sentinel,
24 0, 0,
25 { {0}, {0}, JS_TUNDEFINED },
26 NULL, NULL
27};
28
29static js_Property *newproperty(js_State *J, js_Object *obj, const char *name)
30{
31 js_Property *node = js_malloc(J, sizeof *node);
32 node->name = js_intern(J, name);
33 node->left = node->right = &sentinel;
34 node->level = 1;
35 node->atts = 0;
36 node->value.type = JS_TUNDEFINED;
37 node->value.u.number = 0;
38 node->getter = NULL;
39 node->setter = NULL;
40 ++obj->count;
41 return node;
42}
43
44static js_Property *lookup(js_Property *node, const char *name)
45{
46 while (node != &sentinel) {
47 int c = strcmp(name, node->name);
48 if (c == 0)
49 return node;
50 else if (c < 0)
51 node = node->left;
52 else
53 node = node->right;
54 }
55 return NULL;
56}
57
58static js_Property *skew(js_Property *node)
59{
60 if (node->left->level == node->level) {
61 js_Property *temp = node;
62 node = node->left;
63 temp->left = node->right;
64 node->right = temp;
65 }
66 return node;
67}
68
69static js_Property *split(js_Property *node)
70{
71 if (node->right->right->level == node->level) {
72 js_Property *temp = node;
73 node = node->right;
74 temp->right = node->left;
75 node->left = temp;
76 ++node->level;
77 }
78 return node;
79}
80
81static js_Property *insert(js_State *J, js_Object *obj, js_Property *node, const char *name, js_Property **result)
82{
83 if (node != &sentinel) {
84 int c = strcmp(name, node->name);
85 if (c < 0)
86 node->left = insert(J, obj, node->left, name, result);
87 else if (c > 0)
88 node->right = insert(J, obj, node->right, name, result);
89 else
90 return *result = node;
91 node = skew(node);
92 node = split(node);
93 return node;
94 }
95 return *result = newproperty(J, obj, name);
96}
97
98static void freeproperty(js_State *J, js_Object *obj, js_Property *node)
99{
100 js_free(J, node);
101 --obj->count;
102}
103
104static js_Property *delete(js_State *J, js_Object *obj, js_Property *node, const char *name)
105{
106 js_Property *temp, *succ;
107
108 if (node != &sentinel) {
109 int c = strcmp(name, node->name);
110 if (c < 0) {
111 node->left = delete(J, obj, node->left, name);
112 } else if (c > 0) {
113 node->right = delete(J, obj, node->right, name);
114 } else {
115 if (node->left == &sentinel) {
116 temp = node;
117 node = node->right;
118 freeproperty(J, obj, temp);
119 } else if (node->right == &sentinel) {
120 temp = node;
121 node = node->left;
122 freeproperty(J, obj, temp);
123 } else {
124 succ = node->right;
125 while (succ->left != &sentinel)
126 succ = succ->left;
127 node->name = succ->name;
128 node->atts = succ->atts;
129 node->value = succ->value;
130 node->right = delete(J, obj, node->right, succ->name);
131 }
132 }
133
134 if (node->left->level < node->level - 1 ||
135 node->right->level < node->level - 1)
136 {
137 if (node->right->level > --node->level)
138 node->right->level = node->level;
139 node = skew(node);
140 node->right = skew(node->right);
141 node->right->right = skew(node->right->right);
142 node = split(node);
143 node->right = split(node->right);
144 }
145 }
146 return node;
147}
148
149js_Object *jsV_newobject(js_State *J, enum js_Class type, js_Object *prototype)
150{
151 js_Object *obj = js_malloc(J, sizeof *obj);
152 memset(obj, 0, sizeof *obj);
153 obj->gcmark = 0;
154 obj->gcnext = J->gcobj;
155 J->gcobj = obj;
156 ++J->gccounter;
157
158 obj->type = type;
159 obj->properties = &sentinel;
160 obj->prototype = prototype;
161 obj->extensible = 1;
162 return obj;
163}
164
165js_Property *jsV_getownproperty(js_State *J, js_Object *obj, const char *name)
166{
167 return lookup(obj->properties, name);
168}
169
170js_Property *jsV_getpropertyx(js_State *J, js_Object *obj, const char *name, int *own)
171{
172 *own = 1;
173 do {
174 js_Property *ref = lookup(obj->properties, name);
175 if (ref)
176 return ref;
177 obj = obj->prototype;
178 *own = 0;
179 } while (obj);
180 return NULL;
181}
182
183js_Property *jsV_getproperty(js_State *J, js_Object *obj, const char *name)
184{
185 do {
186 js_Property *ref = lookup(obj->properties, name);
187 if (ref)
188 return ref;
189 obj = obj->prototype;
190 } while (obj);
191 return NULL;
192}
193
194static js_Property *jsV_getenumproperty(js_State *J, js_Object *obj, const char *name)
195{
196 do {
197 js_Property *ref = lookup(obj->properties, name);
198 if (ref && !(ref->atts & JS_DONTENUM))
199 return ref;
200 obj = obj->prototype;
201 } while (obj);
202 return NULL;
203}
204
205js_Property *jsV_setproperty(js_State *J, js_Object *obj, const char *name)
206{
207 js_Property *result;
208
209 if (!obj->extensible) {
210 result = lookup(obj->properties, name);
211 if (J->strict && !result)
212 js_typeerror(J, "object is non-extensible");
213 return result;
214 }
215
216 obj->properties = insert(J, obj, obj->properties, name, &result);
217
218 return result;
219}
220
221void jsV_delproperty(js_State *J, js_Object *obj, const char *name)
222{
223 obj->properties = delete(J, obj, obj->properties, name);
224}
225
226/* Flatten hierarchy of enumerable properties into an iterator object */
227
228static js_Iterator *itwalk(js_State *J, js_Iterator *iter, js_Property *prop, js_Object *seen)
229{
230 if (prop->right != &sentinel)
231 iter = itwalk(J, iter, prop->right, seen);
232 if (!(prop->atts & JS_DONTENUM)) {
233 if (!seen || !jsV_getenumproperty(J, seen, prop->name)) {
234 js_Iterator *head = js_malloc(J, sizeof *head);
235 head->name = prop->name;
236 head->next = iter;
237 iter = head;
238 }
239 }
240 if (prop->left != &sentinel)
241 iter = itwalk(J, iter, prop->left, seen);
242 return iter;
243}
244
245static js_Iterator *itflatten(js_State *J, js_Object *obj)
246{
247 js_Iterator *iter = NULL;
248 if (obj->prototype)
249 iter = itflatten(J, obj->prototype);
250 if (obj->properties != &sentinel)
251 iter = itwalk(J, iter, obj->properties, obj->prototype);
252 return iter;
253}
254
255js_Object *jsV_newiterator(js_State *J, js_Object *obj, int own)
256{
257 char buf[32];
258 int k;
259 js_Object *io = jsV_newobject(J, JS_CITERATOR, NULL);
260 io->u.iter.target = obj;
261 if (own) {
262 io->u.iter.head = NULL;
263 if (obj->properties != &sentinel)
264 io->u.iter.head = itwalk(J, io->u.iter.head, obj->properties, NULL);
265 } else {
266 io->u.iter.head = itflatten(J, obj);
267 }
268 if (obj->type == JS_CSTRING) {
269 js_Iterator *tail = io->u.iter.head;
270 if (tail)
271 while (tail->next)
272 tail = tail->next;
273 for (k = 0; k < obj->u.s.length; ++k) {
274 js_itoa(buf, k);
275 if (!jsV_getenumproperty(J, obj, buf)) {
276 js_Iterator *node = js_malloc(J, sizeof *node);
277 node->name = js_intern(J, js_itoa(buf, k));
278 node->next = NULL;
279 if (!tail)
280 io->u.iter.head = tail = node;
281 else {
282 tail->next = node;
283 tail = node;
284 }
285 }
286 }
287 }
288 return io;
289}
290
291const char *jsV_nextiterator(js_State *J, js_Object *io)
292{
293 int k;
294 if (io->type != JS_CITERATOR)
295 js_typeerror(J, "not an iterator");
296 while (io->u.iter.head) {
297 js_Iterator *next = io->u.iter.head->next;
298 const char *name = io->u.iter.head->name;
299 js_free(J, io->u.iter.head);
300 io->u.iter.head = next;
301 if (jsV_getproperty(J, io->u.iter.target, name))
302 return name;
303 if (io->u.iter.target->type == JS_CSTRING)
304 if (js_isarrayindex(J, name, &k) && k < io->u.iter.target->u.s.length)
305 return name;
306 }
307 return NULL;
308}
309
310/* Walk all the properties and delete them one by one for arrays */
311
312void jsV_resizearray(js_State *J, js_Object *obj, int newlen)
313{
314 char buf[32];
315 const char *s;
316 int k;
317 if (newlen < obj->u.a.length) {
318 if (obj->u.a.length > obj->count * 2) {
319 js_Object *it = jsV_newiterator(J, obj, 1);
320 while ((s = jsV_nextiterator(J, it))) {
321 k = jsV_numbertointeger(jsV_stringtonumber(J, s));
322 if (k >= newlen && !strcmp(s, jsV_numbertostring(J, buf, k)))
323 jsV_delproperty(J, obj, s);
324 }
325 } else {
326 for (k = newlen; k < obj->u.a.length; ++k) {
327 jsV_delproperty(J, obj, js_itoa(buf, k));
328 }
329 }
330 }
331 obj->u.a.length = newlen;
332}
333