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