1 /*
2 * Copyright (c) 2003
3 * David Leonard. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of David Leonard nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #if HAVE_CONFIG_H
32 # include <config.h>
33 #endif
34
35 #if HAVE_STRING_H
36 # include <string.h>
37 #endif
38
39 #include <see/native.h>
40 #include <see/string.h>
41 #include <see/intern.h>
42 #include <see/mem.h>
43 #include <see/error.h>
44 #include <see/debug.h>
45 #include <see/system.h>
46 #include <see/interpreter.h>
47
48 #include "stringdefs.h"
49 #include "dprint.h"
50
51 static unsigned int hashfn(struct SEE_string *);
52 static struct SEE_property **find(struct SEE_interpreter *,
53 struct SEE_object *, struct SEE_string *);
54 static void native_enum_reset(struct SEE_interpreter *,
55 struct SEE_enum *);
56 static struct SEE_string *native_enum_next(struct SEE_interpreter *,
57 struct SEE_enum *, int *);
58
59 #ifndef NDEBUG
60 int SEE_native_debug = 0;
61 #endif
62
63 /*------------------------------------------------------------
64 * Native objects
65 * - maintains a simple hash table of named properties
66 * - cannot be called as functions
67 * - cannot be called as a constructor
68 */
69
70 struct SEE_property {
71 struct SEE_property *next;
72 struct SEE_string *name;
73 int attr;
74 struct SEE_value value;
75 };
76
77 /* Return a hash value for an interned string, in range [0..HASHLEN) */
78 static unsigned int
hashfn(s)79 hashfn(s)
80 struct SEE_string *s;
81 {
82 return (unsigned)(s - (struct SEE_string *)0) % SEE_NATIVE_HASHLEN;
83 }
84
85 /*
86 * Find an object property, if it exists.
87 * Assumes property is interned.
88 * Returns either a pointer to the pointer to the property,
89 * or a pointer to the NULL pointer at the end of a hash list.
90 * (ie never returns NULL);
91 */
92 static struct SEE_property **
find(interp,o,ip)93 find(interp, o, ip)
94 struct SEE_interpreter *interp;
95 struct SEE_object *o;
96 struct SEE_string *ip;
97 {
98 struct SEE_native *n = (struct SEE_native *)o;
99 struct SEE_property **x;
100
101 x = &n->properties[hashfn(_SEE_INTERN_ASSERT(interp, ip))];
102 while (*x && (*x)->name != ip)
103 x = &((*x)->next);
104 return x;
105 }
106
107 /* [[Get]] 8.6.2.1 */
108 void
SEE_native_get(interp,o,ip,res)109 SEE_native_get(interp, o, ip, res)
110 struct SEE_interpreter *interp;
111 struct SEE_object *o;
112 struct SEE_string *ip;
113 struct SEE_value *res;
114 {
115 struct SEE_property **x;
116 struct SEE_native *n = (struct SEE_native *)o;
117
118 if (n->lru && n->lru->name == ip) {
119 #ifndef NDEBUG
120 if (SEE_native_debug) {
121 dprintf("native_get: o=");
122 dprinto(interp, o);
123 dprintf(" ip=");
124 dprints(ip);
125 dprintf("(%p) LRU HIT -> ", ip);
126 dprintv(interp, &n->lru->value);
127 dprintf("\n");
128 }
129 #endif
130 SEE_VALUE_COPY(res, &n->lru->value);
131 return;
132 }
133
134 x = find(interp, o, ip);
135
136 #ifndef NDEBUG
137 if (SEE_native_debug) {
138 dprintf("native_get: o=");
139 dprinto(interp, o);
140 dprintf(" ip=");
141 dprints(ip);
142 dprintf("(%p)", ip);
143 if (*x) {
144 dprintf(" -> ");
145 dprintv(interp, &(*x)->value);
146 dprintf("\n");
147 } else
148 dprintf(" -> not found\n");
149 }
150 #endif
151
152 if (*x) {
153 n->lru = *x;
154 SEE_VALUE_COPY(res, &(*x)->value);
155 } else if (SEE_GET_JS_COMPAT(interp) &&
156 ip == STR(__proto__)) {
157 if (o->Prototype)
158 SEE_SET_OBJECT(res, o->Prototype);
159 else
160 SEE_SET_NULL(res);
161 } else {
162 #ifndef NDEBUG
163 if (SEE_native_debug) {
164 dprintf("native_get: o=");
165 dprinto(interp, o);
166 dprintf(" has prototype=");
167 dprinto(interp, o->Prototype);
168 dprintf("\n");
169 }
170 #endif
171 if (!o->Prototype)
172 SEE_SET_UNDEFINED(res);
173 else
174 SEE_OBJECT_GET(interp, o->Prototype, ip, res);
175 }
176 }
177
178 /* [[Put]] 8.6.2.2 */
179 void
SEE_native_put(interp,o,ip,val,attr)180 SEE_native_put(interp, o, ip, val, attr)
181 struct SEE_interpreter *interp;
182 struct SEE_object *o;
183 struct SEE_string *ip;
184 struct SEE_value *val;
185 int attr;
186 {
187 struct SEE_property **x;
188 struct SEE_native *n = (struct SEE_native *)o;
189
190 SEE_ASSERT(interp, SEE_VALUE_GET_TYPE(val) != SEE_REFERENCE);
191
192 if (n->lru && n->lru->name == ip &&
193 !(n->lru->attr & SEE_ATTR_READONLY) && !attr)
194 {
195 #ifndef NDEBUG
196 if (SEE_native_debug) {
197 dprintf("native_put: o=");
198 dprinto(interp, o);
199 dprintf(" ip=");
200 dprints(ip);
201 dprintf("(%p) LRU HIT <- ", ip);
202 dprintv(interp, val);
203 dprintf("\n");
204 }
205 #endif
206 SEE_VALUE_COPY(&n->lru->value, val);
207 return;
208 }
209
210 if (SEE_GET_JS_COMPAT(interp) && ip == STR(__proto__))
211 {
212 struct SEE_object *po;
213 if (SEE_VALUE_GET_TYPE(val) == SEE_NULL) {
214 o->Prototype = NULL;
215 return;
216 }
217 if (SEE_VALUE_GET_TYPE(val) != SEE_OBJECT)
218 /* XXX: better error message needed. 'bad proto'? */
219 SEE_error_throw_string(interp, interp->TypeError,
220 STR(internal_error));
221 /* Check for recursive prototype */
222 for (po = val->u.object; po; po = po->Prototype)
223 if (SEE_OBJECT_JOINED(o, po))
224 SEE_error_throw_string(interp, interp->TypeError,
225 STR(internal_error));
226 o->Prototype = val->u.object;
227 return;
228 }
229
230 /*
231 * Note: supply a non-zero attr implies that
232 * the caller knows what they're doing. This means
233 * we can ignore any restrictions on extant
234 * properties!
235 */
236 if (!attr && !SEE_OBJECT_CANPUT(interp, o, ip))
237 return;
238 x = find(interp, o, ip);
239 if (!*x) {
240 struct SEE_property *prop;
241 prop = SEE_NEW(interp, struct SEE_property);
242 prop->next = NULL;
243 prop->name = ip;
244 prop->attr = attr;
245 *x = prop;
246 } else if (attr)
247 (*x)->attr = attr;
248 n->lru = *x;
249 SEE_VALUE_COPY(&(*x)->value, val);
250
251 #ifndef NDEBUG
252 if (SEE_native_debug) {
253 dprintf("native_put: o=");
254 dprinto(interp, o);
255 dprintf(" ip=");
256 dprints(ip);
257 dprintf("(%p) <- ", ip);
258 dprintv(interp, val);
259 if (attr) {
260 dprintf("{");
261 if (attr & SEE_ATTR_READONLY) dprintf(" ReadOnly");
262 if (attr & SEE_ATTR_DONTENUM) dprintf(" DontEnum");
263 if (attr & SEE_ATTR_DONTDELETE) dprintf(" DontDelete");
264 if (attr & SEE_ATTR_INTERNAL) dprintf(" Internal");
265 dprintf(" }");
266 }
267 dprintf("\n");
268 }
269 #endif
270 }
271
272 /* [[CanPut]] 8.6.2.3 */
273 int
SEE_native_canput(interp,o,ip)274 SEE_native_canput(interp, o, ip)
275 struct SEE_interpreter *interp;
276 struct SEE_object *o;
277 struct SEE_string *ip;
278 {
279 struct SEE_property **x;
280 struct SEE_native *n = (struct SEE_native *)o;
281
282 if (n->lru && n->lru->name == ip) {
283 #ifndef NDEBUG
284 if (SEE_native_debug) {
285 dprintf("native_canput: o=");
286 dprinto(interp, o);
287 dprintf(" ip=");
288 dprints(ip);
289 dprintf("(%p) LRU HIT -> %d\n", ip,
290 (n->lru->attr & SEE_ATTR_READONLY) ? 0 : 1);
291 }
292 #endif
293 return (n->lru->attr & SEE_ATTR_READONLY) ? 0 : 1;
294 }
295
296 x = find(interp, o, ip);
297 if (*x) {
298 #ifndef NDEBUG
299 if (SEE_native_debug) {
300 dprintf("native_canput: o=");
301 dprinto(interp, o);
302 dprintf(" ip=");
303 dprints(ip);
304 dprintf("(%p) -> %d\n", ip,
305 ((*x)->attr & SEE_ATTR_READONLY) ? 0 : 1);
306 }
307 #endif
308 n->lru = *x;
309 return ((*x)->attr & SEE_ATTR_READONLY) ? 0 : 1;
310 }
311 if (!o->Prototype)
312 return 1;
313 return SEE_OBJECT_CANPUT(interp, o->Prototype, ip);
314 }
315
316 /* Test if a property is 'local' (ie not belonging to prototype) */
317 static int
native_hasownproperty(interp,o,ip)318 native_hasownproperty(interp, o, ip)
319 struct SEE_interpreter *interp;
320 struct SEE_object *o;
321 struct SEE_string *ip;
322 {
323 struct SEE_property **x;
324 struct SEE_native *n = (struct SEE_native *)o;
325
326 if (n->lru && n->lru->name == ip) {
327 #ifndef NDEBUG
328 if (SEE_native_debug) {
329 dprintf("hasownprop: o=");
330 dprinto(interp, o);
331 dprintf(" ip=");
332 dprints(ip);
333 dprintf(" LRU HIT -> 1\n");
334 }
335 #endif
336 return 1;
337 }
338
339 x = find(interp, o, ip);
340 #ifndef NDEBUG
341 if (SEE_native_debug) {
342 dprintf("hasownprop: o=");
343 dprinto(interp, o);
344 dprintf(" ip=");
345 dprints(ip);
346 dprintf(" -> %d\n", *x ? 1 : 0);
347 }
348 #endif
349 return *x ? 1 : 0;
350 }
351
352 /* [[HasProperty]] 8.6.2.4 */
353 int
SEE_native_hasproperty(interp,o,p)354 SEE_native_hasproperty(interp, o, p)
355 struct SEE_interpreter *interp;
356 struct SEE_object *o;
357 struct SEE_string *p;
358 {
359 /* The common case is that of traversing native objects
360 * so we do it in a loop to save call overhead. */
361 for (;;) {
362 if (SEE_native_hasownproperty(interp, o, p))
363 return 1;
364 if (!o->Prototype)
365 return 0;
366 o = o->Prototype;
367 if (o->objectclass->HasProperty != SEE_native_hasproperty)
368 return SEE_OBJECT_HASPROPERTY(interp, o, p);
369 }
370 }
371
372 int
SEE_native_hasownproperty(interp,o,ip)373 SEE_native_hasownproperty(interp, o, ip)
374 struct SEE_interpreter *interp;
375 struct SEE_object *o;
376 struct SEE_string *ip;
377 {
378 return native_hasownproperty(interp, o, ip);
379 }
380
381 /* Return the attribute of a local property, or 0 */
382 int
SEE_native_getownattr(interp,o,ip)383 SEE_native_getownattr(interp, o, ip)
384 struct SEE_interpreter *interp;
385 struct SEE_object *o;
386 struct SEE_string *ip;
387 {
388 struct SEE_property **x;
389
390 x = find(interp, o, ip);
391 return *x ? (*x)->attr : 0;
392 }
393
394 /* [[Delete]] 8.6.2.5 */
395 int
SEE_native_delete(interp,o,ip)396 SEE_native_delete(interp, o, ip)
397 struct SEE_interpreter *interp;
398 struct SEE_object *o;
399 struct SEE_string *ip;
400 {
401 struct SEE_property **x;
402 struct SEE_native *n = (struct SEE_native *)o;
403
404 x = find(interp, o, ip);
405 if (!*x)
406 return 1;
407 if ((*x)->attr & SEE_ATTR_DONTDELETE)
408 return 0;
409 if (n->lru == *x)
410 n->lru = NULL;
411 *x = (*x)->next;
412 return 1;
413 }
414
415 /* [[DefaultValue]] 8.6.2.6 */
416 void
SEE_native_defaultvalue(interp,o,hint,res)417 SEE_native_defaultvalue(interp, o, hint, res)
418 struct SEE_interpreter *interp;
419 struct SEE_object *o;
420 struct SEE_value *hint;
421 struct SEE_value *res;
422 {
423 struct SEE_value v;
424 struct SEE_object *effective_hint;
425
426 if (!hint)
427 effective_hint = interp->Number;
428 /* Note that Date instances' [[DefaultValue]] is
429 * provided by date_defaultvalue() */
430 else if (SEE_VALUE_GET_TYPE(hint) == SEE_OBJECT &&
431 hint->u.object == interp->String)
432 effective_hint = interp->String;
433 else if (SEE_VALUE_GET_TYPE(hint) == SEE_OBJECT &&
434 hint->u.object == interp->Number)
435 effective_hint = interp->Number;
436 else if (SEE_VALUE_GET_TYPE(hint) == SEE_OBJECT &&
437 hint->u.object == interp->Date)
438 effective_hint = interp->String;
439 else
440 effective_hint = interp->Number; /* XXX */
441
442 if (effective_hint == interp->String) {
443 SEE_OBJECT_GET(interp, o, STR(toString), &v);
444 if (SEE_VALUE_GET_TYPE(&v) == SEE_OBJECT &&
445 SEE_OBJECT_HAS_CALL(v.u.object))
446 {
447 SEE_OBJECT_CALL(interp, v.u.object, o, 0, NULL, res);
448 if (SEE_VALUE_GET_TYPE(res) != SEE_OBJECT)
449 return;
450 }
451 SEE_OBJECT_GET(interp, o, STR(valueOf), &v);
452 if (SEE_VALUE_GET_TYPE(&v) == SEE_OBJECT &&
453 SEE_OBJECT_HAS_CALL(v.u.object))
454 {
455 SEE_OBJECT_CALL(interp, v.u.object, o, 0, NULL, res);
456 if (SEE_VALUE_GET_TYPE(res) != SEE_OBJECT)
457 return;
458 }
459 if (SEE_COMPAT_JS(interp, >=, JS11)) /* EXT:5 */
460 SEE_SET_STRING(res, SEE_string_sprintf(interp,
461 "[object %p]", o));
462 else
463 SEE_error_throw_string(interp, interp->TypeError,
464 STR(defaultvalue_string_bad));
465 } else {
466 SEE_OBJECT_GET(interp, o, STR(valueOf), &v);
467 if (SEE_VALUE_GET_TYPE(&v) == SEE_OBJECT &&
468 SEE_OBJECT_HAS_CALL(v.u.object))
469 {
470 SEE_OBJECT_CALL(interp, v.u.object, o, 0, NULL, res);
471 if (SEE_VALUE_GET_TYPE(res) != SEE_OBJECT)
472 return;
473 }
474 SEE_OBJECT_GET(interp, o, STR(toString), &v);
475 if (SEE_VALUE_GET_TYPE(&v) == SEE_OBJECT &&
476 SEE_OBJECT_HAS_CALL(v.u.object))
477 {
478 SEE_OBJECT_CALL(interp, v.u.object, o, 0, NULL, res);
479 if (SEE_VALUE_GET_TYPE(res) != SEE_OBJECT)
480 return;
481 }
482 if (SEE_COMPAT_JS(interp, >=, JS11)) /* EXT:6 */
483 SEE_SET_STRING(res, SEE_string_sprintf(interp,
484 "[object %p]", o));
485 else
486 SEE_error_throw_string(interp, interp->TypeError,
487 STR(defaultvalue_number_bad));
488 }
489 }
490
491 /*
492 * Enumeration support.
493 *
494 * Object enumerators only reveal the names available
495 * at the local level. Object enumerators do not have to be
496 * insert/delete-safe - i.e. they can assume that no change
497 * to the object is made during enumeration.
498 * Local properties with the DONTENUM flag set are still
499 * enumerated, but the 'dont_enum' out-argument is set to non-zero
500 * when they are encountered.
501 */
502 struct native_enum {
503 struct SEE_enum base;
504 struct SEE_native *native;
505 int next_column;
506 struct SEE_property *next_prop;
507 };
508
509 static void
native_enum_reset(interp,e)510 native_enum_reset(interp, e)
511 struct SEE_interpreter *interp;
512 struct SEE_enum *e;
513 {
514 struct native_enum *ne = (struct native_enum *)e;
515 ne->next_column = 0;
516 ne->next_prop = NULL;
517 }
518
519 static struct SEE_string *
native_enum_next(interp,e,dont_enump)520 native_enum_next(interp, e, dont_enump)
521 struct SEE_interpreter *interp;
522 struct SEE_enum *e;
523 int *dont_enump;
524 {
525 struct native_enum *ne = (struct native_enum *)e;
526 struct SEE_native *n = ne->native;
527 struct SEE_property *p;
528
529 while (ne->next_prop == NULL) {
530 if (ne->next_column >= SEE_NATIVE_HASHLEN)
531 return NULL;
532 ne->next_prop = n->properties[ne->next_column++];
533 }
534 p = ne->next_prop;
535 ne->next_prop = p->next;
536
537 if (dont_enump)
538 *dont_enump = (p->attr & SEE_ATTR_DONTENUM);
539 return p->name;
540 }
541
542 static struct SEE_enumclass native_enumclass = {
543 0,
544 native_enum_next
545 };
546
547 struct SEE_enum *
SEE_native_enumerator(interp,o)548 SEE_native_enumerator(interp, o)
549 struct SEE_interpreter *interp;
550 struct SEE_object *o;
551 {
552 struct SEE_native *n = (struct SEE_native *)o;
553 struct native_enum *ne;
554
555 ne = SEE_NEW(interp, struct native_enum);
556 ne->native = n;
557 ne->base.enumclass = &native_enumclass;
558 native_enum_reset(interp, (struct SEE_enum *)ne);
559 return (struct SEE_enum *)ne;
560 }
561
562 /*------------------------------------------------------------
563 * The 'default' native object.
564 */
565
566 static struct SEE_objectclass native_class = {
567 "native", /* Class */
568 SEE_native_get, /* Get */
569 SEE_native_put, /* Put */
570 SEE_native_canput, /* CanPut */
571 SEE_native_hasproperty, /* HasProperty */
572 SEE_native_delete, /* Delete */
573 SEE_native_defaultvalue, /* DefaultValue */
574 SEE_native_enumerator, /* enumerator */
575 NULL, /* Construct */
576 NULL, /* Call */
577 NULL, /* HasInstance */
578 };
579
580 /* Return a new, native object */
581 struct SEE_object *
SEE_native_new(interp)582 SEE_native_new(interp)
583 struct SEE_interpreter *interp;
584 {
585 struct SEE_native *n;
586
587 n = SEE_NEW(interp, struct SEE_native);
588 SEE_native_init(n, interp, &native_class, NULL);
589 return (struct SEE_object *)n;
590 }
591
592 /* Initialise a native object to have no properties */
593 void
SEE_native_init(n,interp,objectclass,prototype)594 SEE_native_init(n, interp, objectclass, prototype)
595 struct SEE_native *n;
596 struct SEE_interpreter *interp;
597 struct SEE_objectclass *objectclass;
598 struct SEE_object *prototype;
599 {
600 int i;
601
602 n->object.objectclass = objectclass;
603 n->object.Prototype = prototype;
604 n->object.host_data = NULL;
605 n->lru = NULL;
606 for (i = 0; i < SEE_NATIVE_HASHLEN; i++)
607 n->properties[i] = NULL;
608 }
609