migrate substitution keywords to SVN
[reactos.git] / reactos / lib / kjs / ksrc / object.c
1 /*
2 * User object handling.
3 * Copyright (c) 1998 New Generation Software (NGS) Oy
4 *
5 * Author: Markku Rossi <mtr@ngs.fi>
6 */
7
8 /*
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the Free
21 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
22 * MA 02111-1307, USA
23 */
24
25 /*
26 * $Source: /cygdrive/c/RCVS/CVS/ReactOS/reactos/lib/kjs/ksrc/object.c,v $
27 * $Id$
28 */
29
30 #include "ddk/ntddk.h"
31 #include "jsint.h"
32 #include "jsconfig.h"
33
34 /*
35 * Types and definitions.
36 */
37
38 #define HASH_SIZE 128
39
40 /*
41 * Prototypes for static functions.
42 */
43
44 static void hash_create (JSVirtualMachine *vm, JSObject *obj);
45
46 static void hash_insert (JSVirtualMachine *vm, JSObject *obj, const char *name,
47 unsigned int name_len, int pos);
48
49 static void hash_delete (JSVirtualMachine *vm, JSObject *obj, const char *name,
50 unsigned int name_len);
51
52 static int hash_lookup (JSObject *obj, char *name, unsigned int name_len);
53
54
55 /*
56 * Global functions.
57 */
58
59 JSObject *
60 js_vm_object_new (JSVirtualMachine *vm)
61 {
62 JSObject *obj;
63
64 obj = js_vm_alloc (vm, sizeof (*obj));
65 obj->hash = NULL;
66 obj->num_props = 0;
67 obj->props = NULL;
68
69 return obj;
70 }
71
72
73 void
74 js_vm_object_mark (JSObject *obj)
75 {
76 int i;
77 unsigned int num_objects;
78
79 if (obj == NULL)
80 return;
81
82 tail_recursive:
83
84 if (!js_vm_mark_ptr (obj))
85 /* This object has already been marked. Nothing to do here. */
86 return;
87
88 js_vm_mark_ptr (obj->props);
89
90 /* Mark property hash. */
91 if (obj->hash)
92 {
93 JSObjectPropHashBucket *b;
94 int i;
95
96 js_vm_mark_ptr (obj->hash);
97 js_vm_mark_ptr (obj->hash_lengths);
98
99 for (i = 0; i < HASH_SIZE; i++)
100 for (b = obj->hash[i]; b; b = b->next)
101 {
102 js_vm_mark_ptr (b);
103 js_vm_mark_ptr (b->data);
104 }
105 }
106
107 /* Mark all non-object properties. */
108 num_objects = 0;
109 for (i = 0; i < obj->num_props; i++)
110 {
111 if (obj->props[i].value.type == JS_OBJECT)
112 {
113 if (!js_vm_is_marked_ptr (obj->props[i].value.u.vobject))
114 num_objects++;
115 }
116 else
117 js_vm_mark (&obj->props[i].value);
118 }
119
120 /* And finally, mark all objects we have left. */
121 if (num_objects > 0)
122 {
123 /* Find the objects. */
124 for (i = 0; i < obj->num_props; i++)
125 if (obj->props[i].value.type == JS_OBJECT
126 && !js_vm_is_marked_ptr (obj->props[i].value.u.vobject))
127 {
128 if (num_objects == 1)
129 {
130 /*
131 * Hahaa, this is the only non-marked object. We can
132 * do a tail-recursion optimization.
133 */
134 obj = obj->props[i].value.u.vobject;
135 goto tail_recursive;
136 }
137
138 /* Just mark it. */
139 js_vm_mark (&obj->props[i].value);
140 }
141 }
142 }
143
144
145 int
146 js_vm_object_load_property (JSVirtualMachine *vm, JSObject *obj,
147 JSSymbol prop, JSNode *value_return)
148 {
149 unsigned int ui;
150 JSSymbol link_sym = vm->syms.s___proto__;
151 JSObject *link_obj = NULL;
152
153 follow_link:
154
155 /* Check if we know this property. */
156 for (ui = 0; ui < obj->num_props; ui++)
157 if (obj->props[ui].name == prop)
158 {
159 JS_COPY (value_return, &obj->props[ui].value);
160 return JS_PROPERTY_FOUND;
161 }
162 else if (obj->props[ui].name == link_sym
163 && obj->props[ui].value.type == JS_OBJECT)
164 link_obj = obj->props[ui].value.u.vobject;
165
166 /* Undefined so far. */
167 if (link_obj)
168 {
169 /* Follow the link. */
170 obj = link_obj;
171
172 link_obj = NULL;
173 goto follow_link;
174 }
175
176 /* Undefined. Make it undef. */
177 value_return->type = JS_UNDEFINED;
178 return JS_PROPERTY_UNKNOWN;
179 }
180
181
182 void
183 js_vm_object_store_property (JSVirtualMachine *vm, JSObject *obj,
184 JSSymbol prop, JSNode *val)
185 {
186 unsigned int ui;
187 JSSymbol free_slot = JS_SYMBOL_NULL;
188
189 /* Check if we already know this property. */
190 for (ui = 0; ui < obj->num_props; ui++)
191 if (obj->props[ui].name == prop)
192 {
193 JS_COPY (&obj->props[ui].value, val);
194 return;
195 }
196 else if (obj->props[ui].name == JS_SYMBOL_NULL)
197 free_slot = ui;
198
199 /* Must create a new property. */
200
201 if (free_slot == JS_SYMBOL_NULL)
202 {
203 /* Expand our array of properties. */
204 obj->props = js_vm_realloc (vm, obj->props,
205 (obj->num_props + 1) * sizeof (JSProperty));
206 free_slot = obj->num_props++;
207 }
208
209 obj->props[free_slot].name = prop;
210 obj->props[free_slot].attributes = 0;
211 JS_COPY (&obj->props[free_slot].value, val);
212
213 /* Insert it to the hash (if the hash has been created). */
214 if (obj->hash)
215 {
216 const char *name;
217
218 name = js_vm_symname (vm, prop);
219 hash_insert (vm, obj, name, strlen (name), free_slot);
220 }
221 }
222
223
224 void
225 js_vm_object_delete_property (JSVirtualMachine *vm, JSObject *obj,
226 JSSymbol prop)
227 {
228 unsigned int ui;
229
230 /* Check if we already know this property. */
231 for (ui = 0; ui < obj->num_props; ui++)
232 if (obj->props[ui].name == prop)
233 {
234 /* Found, remove it from our list of properties. */
235 obj->props[ui].name = JS_SYMBOL_NULL;
236 obj->props[ui].value.type = JS_UNDEFINED;
237
238 /* Remove its name from the hash (if present). */
239 if (obj->hash)
240 {
241 const char *name = js_vm_symname (vm, prop);
242 hash_delete (vm, obj, name, strlen (name));
243 }
244
245 /* All done here. */
246 return;
247 }
248 }
249
250
251 void
252 js_vm_object_load_array (JSVirtualMachine *vm, JSObject *obj, JSNode *sel,
253 JSNode *value_return)
254 {
255 if (sel->type == JS_INTEGER)
256 {
257 if (sel->u.vinteger < 0 || sel->u.vinteger >= obj->num_props)
258 value_return->type = JS_UNDEFINED;
259 else
260 JS_COPY (value_return, &obj->props[sel->u.vinteger].value);
261 }
262 else if (sel->type == JS_STRING)
263 {
264 int pos;
265
266 if (obj->hash == NULL)
267 hash_create (vm, obj);
268
269 pos = hash_lookup (obj, (char *) sel->u.vstring->data,
270 sel->u.vstring->len);
271 if (pos < 0)
272 value_return->type = JS_UNDEFINED;
273 else
274 JS_COPY (value_return, &obj->props[pos].value);
275 }
276 else
277 {
278 sprintf (vm->error, "load_property: illegal array index");
279 js_vm_error (vm);
280 }
281 }
282
283
284 void
285 js_vm_object_store_array (JSVirtualMachine *vm, JSObject *obj, JSNode *sel,
286 JSNode *value)
287 {
288 if (sel->type == JS_INTEGER)
289 {
290 if (sel->u.vinteger < 0)
291 {
292 sprintf (vm->error, "store_array: array index can't be nagative");
293 js_vm_error (vm);
294 }
295 if (sel->u.vinteger >= obj->num_props)
296 {
297 /* Expand properties. */
298 obj->props = js_vm_realloc (vm, obj->props,
299 (sel->u.vinteger + 1)
300 * sizeof (JSProperty));
301
302 /* Init the possible gap. */
303 for (; obj->num_props <= sel->u.vinteger; obj->num_props++)
304 {
305 obj->props[obj->num_props].name = 0;
306 obj->props[obj->num_props].attributes = 0;
307 obj->props[obj->num_props].value.type = JS_UNDEFINED;
308 }
309 }
310
311 JS_COPY (&obj->props[sel->u.vinteger].value, value);
312 }
313 else if (sel->type == JS_STRING)
314 {
315 int pos;
316
317 if (obj->hash == NULL)
318 hash_create (vm, obj);
319
320 pos = hash_lookup (obj, (char *) sel->u.vstring->data,
321 sel->u.vstring->len);
322 if (pos < 0)
323 {
324 /* It is undefined, define it. */
325 obj->props = js_vm_realloc (vm, obj->props,
326 (obj->num_props + 1)
327 * sizeof (JSProperty));
328
329 /*
330 * XXX if <sel> is a valid symbol, intern it and set symbol's
331 * name below.
332 */
333 obj->props[obj->num_props].name = JS_SYMBOL_NULL;
334 obj->props[obj->num_props].attributes = 0;
335 JS_COPY (&obj->props[obj->num_props].value, value);
336
337 hash_insert (vm, obj, (char *) sel->u.vstring->data,
338 sel->u.vstring->len, obj->num_props);
339
340 obj->num_props++;
341 }
342 else
343 JS_COPY (&obj->props[pos].value, value);
344 }
345 }
346
347
348 void
349 js_vm_object_delete_array (JSVirtualMachine *vm, JSObject *obj, JSNode *sel)
350 {
351 if (sel->type == JS_INTEGER)
352 {
353 if (0 <= sel->u.vinteger && sel->u.vinteger < obj->num_props)
354 {
355 JSSymbol sym;
356
357 sym = obj->props[sel->u.vinteger].name;
358 obj->props[sel->u.vinteger].name = JS_SYMBOL_NULL;
359 obj->props[sel->u.vinteger].value.type = JS_UNDEFINED;
360
361 /* Remove its name from the hash (if present and it is not NULL). */
362 if (sym != JS_SYMBOL_NULL && obj->hash)
363 {
364 const char *name = js_vm_symname (vm, sym);
365 hash_delete (vm, obj, name, strlen (name));
366 }
367 }
368 }
369 else if (sel->type == JS_STRING)
370 {
371 int pos;
372
373 if (obj->hash == NULL)
374 hash_create (vm, obj);
375
376 pos = hash_lookup (obj, (char *) sel->u.vstring->data,
377 sel->u.vstring->len);
378 if (pos >= 0)
379 {
380 /* Found it. */
381 obj->props[pos].name = JS_SYMBOL_NULL;
382 obj->props[pos].value.type = JS_UNDEFINED;
383
384 /* And, delete its name from the hash. */
385 hash_delete (vm, obj, (char *) sel->u.vstring->data,
386 sel->u.vstring->len);
387 }
388 }
389 else
390 {
391 sprintf (vm->error, "delete_array: illegal array index");
392 js_vm_error (vm);
393 }
394 }
395
396
397 int
398 js_vm_object_nth (JSVirtualMachine *vm, JSObject *obj, int nth,
399 JSNode *value_return)
400 {
401 int i;
402 JSObjectPropHashBucket *b;
403
404 value_return->type = JS_UNDEFINED;
405
406 if (nth < 0)
407 return 0;
408
409 if (obj->hash == NULL)
410 hash_create (vm, obj);
411
412 for (i = 0; i < HASH_SIZE && nth >= obj->hash_lengths[i]; i++)
413 nth -= obj->hash_lengths[i];
414
415 if (i >= HASH_SIZE)
416 return 0;
417
418 /* The chain <i> is the correct one. */
419 for (b = obj->hash[i]; b && nth > 0; b = b->next, nth--)
420 ;
421 if (b == NULL)
422 {
423 char buf[512];
424
425 sprintf (buf,
426 "js_vm_object_nth(): chain didn't contain that many items%s",
427 JS_HOST_LINE_BREAK);
428 js_iostream_write (vm->s_stderr, buf, strlen (buf));
429 js_iostream_flush (vm->s_stderr);
430
431 abort ();
432 }
433
434 js_vm_make_string (vm, value_return, (char*)b->data, b->len);
435
436 return 1;
437 }
438
439 \f
440 /*
441 * Static functions.
442 */
443
444 static void
445 hash_create (JSVirtualMachine *vm, JSObject *obj)
446 {
447 int i;
448
449 obj->hash = js_vm_alloc (vm, HASH_SIZE * sizeof (JSObjectPropHashBucket *));
450 memset (obj->hash, 0, HASH_SIZE * sizeof (JSObjectPropHashBucket *));
451
452 obj->hash_lengths = js_vm_alloc (vm, HASH_SIZE * sizeof (unsigned int));
453 memset (obj->hash_lengths, 0, HASH_SIZE * sizeof (unsigned int));
454
455 /* Insert all known properties to the hash. */
456 for (i = 0; i < obj->num_props; i++)
457 if (obj->props[i].name != JS_SYMBOL_NULL)
458 {
459 const char *name;
460
461 name = js_vm_symname (vm, obj->props[i].name);
462 hash_insert (vm, obj, name, strlen (name), i);
463 }
464 }
465
466
467 static void
468 hash_insert (JSVirtualMachine *vm, JSObject *obj, const char *name,
469 unsigned int name_len, int pos)
470 {
471 unsigned int hash;
472 JSObjectPropHashBucket *b;
473
474 hash = js_count_hash (name, name_len) % HASH_SIZE;
475 for (b = obj->hash[hash]; b; b = b->next)
476 if (b->len == name_len
477 && memcmp (b->data, name, name_len) == 0)
478 {
479 /* Ok, we already have a bucket */
480 b->value = pos;
481 return;
482 }
483
484 /* Create a new bucket. */
485 b = js_vm_alloc (vm, sizeof (*b));
486 b->len = name_len;
487 b->data = js_vm_alloc (vm, b->len);
488 memcpy (b->data, name, b->len);
489
490 b->value = pos;
491
492 b->next = obj->hash[hash];
493 obj->hash[hash] = b;
494
495 obj->hash_lengths[hash]++;
496 }
497
498
499 static void
500 hash_delete (JSVirtualMachine *vm, JSObject *obj, const char *name,
501 unsigned int name_len)
502 {
503 unsigned int hash;
504 JSObjectPropHashBucket *b, *prev;
505
506 hash = js_count_hash (name, name_len) % HASH_SIZE;
507 for (prev = NULL, b = obj->hash[hash]; b; prev = b, b = b->next)
508 if (b->len == name_len
509 && memcmp (b->data, name, name_len) == 0)
510 {
511 /* Ok, found it. */
512 if (prev)
513 prev->next = b->next;
514 else
515 obj->hash[hash] = b->next;
516
517 obj->hash_lengths[hash]--;
518
519 break;
520 }
521 }
522
523
524 static int
525 hash_lookup (JSObject *obj, char *name, unsigned int name_len)
526 {
527 unsigned int hash;
528 JSObjectPropHashBucket *b;
529
530 hash = js_count_hash (name, name_len) % HASH_SIZE;
531 for (b = obj->hash[hash]; b; b = b->next)
532 if (b->len == name_len
533 && memcmp (b->data, name, name_len) == 0)
534 return b->value;
535
536 return -1;
537 }