1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sw=4 et tw=78:
3  *
4  * ***** BEGIN LICENSE BLOCK *****
5  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is Mozilla Communicator client code, released
18  * March 31, 1998.
19  *
20  * The Initial Developer of the Original Code is
21  * Netscape Communications Corporation.
22  * Portions created by the Initial Developer are Copyright (C) 1998
23  * the Initial Developer. All Rights Reserved.
24  *
25  * Contributor(s):
26  *
27  * Alternatively, the contents of this file may be used under the terms of
28  * either of the GNU General Public License Version 2 or later (the "GPL"),
29  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30  * in which case the provisions of the GPL or the LGPL are applicable instead
31  * of those above. If you wish to allow use of your version of this file only
32  * under the terms of either the GPL or the LGPL, and not to allow others to
33  * use your version of this file under the terms of the MPL, indicate your
34  * decision by deleting the provisions above and replace them with the notice
35  * and other provisions required by the GPL or the LGPL. If you do not delete
36  * the provisions above, a recipient may use your version of this file under
37  * the terms of any one of the MPL, the GPL or the LGPL.
38  *
39  * ***** END LICENSE BLOCK ***** */
40
41 /*
42  * JS object implementation.
43  */
44 #include "jsstddef.h"
45 #include <stdlib.h>
46 #include <string.h>
47 #include "jstypes.h"
48 #include "jsarena.h" /* Added by JSIFY */
49 #include "jsutil.h" /* Added by JSIFY */
50 #include "jshash.h" /* Added by JSIFY */
51 #include "jsdhash.h"
52 #include "jsprf.h"
53 #include "jsapi.h"
54 #include "jsarray.h"
55 #include "jsatom.h"
56 #include "jsbool.h"
57 #include "jscntxt.h"
58 #include "jsconfig.h"
59 #include "jsfun.h"
60 #include "jsgc.h"
61 #include "jsinterp.h"
62 #include "jslock.h"
63 #include "jsnum.h"
64 #include "jsobj.h"
65 #include "jsscope.h"
66 #include "jsscript.h"
67 #include "jsstr.h"
68 #include "jsopcode.h"
69
70 #include "jsdbgapi.h"   /* whether or not JS_HAS_OBJ_WATCHPOINT */
71
72 #if JS_HAS_XML_SUPPORT
73 #include "jsxml.h"
74 #endif
75
76 #ifdef JS_THREADSAFE
77 #define NATIVE_DROP_PROPERTY js_DropProperty
78
79 extern void
80 js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);
81 #else
82 #define NATIVE_DROP_PROPERTY NULL
83 #endif
84
85 JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
86     js_NewObjectMap,        js_DestroyObjectMap,
87     js_LookupProperty,      js_DefineProperty,
88     js_GetProperty,         js_SetProperty,
89     js_GetAttributes,       js_SetAttributes,
90     js_DeleteProperty,      js_DefaultValue,
91     js_Enumerate,           js_CheckAccess,
92     NULL,                   NATIVE_DROP_PROPERTY,
93     js_Call,                js_Construct,
94     NULL,                   js_HasInstance,
95     js_SetProtoOrParent,    js_SetProtoOrParent,
96     js_Mark,                js_Clear,
97     js_GetRequiredSlot,     js_SetRequiredSlot
98 };
99
100 JSClass js_ObjectClass = {
101     js_Object_str,
102     0,
103     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
104     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
105     JSCLASS_NO_OPTIONAL_MEMBERS
106 };
107
108 #if JS_HAS_OBJ_PROTO_PROP
109
110 static JSBool
111 obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
112
113 static JSBool
114 obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
115
116 static JSBool
117 obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
118
119 static JSPropertySpec object_props[] = {
120     /* These two must come first; see object_props[slot].name usage below. */
121     {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED,
122                                                   obj_getSlot,  obj_setSlot},
123     {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
124                                                   obj_getSlot,  obj_setSlot},
125     {js_count_str, 0,            JSPROP_PERMANENT,obj_getCount, obj_getCount},
126     {0,0,0,0,0}
127 };
128
129 /* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */
130 #define JSSLOT_COUNT 2
131
132 static JSBool
133 ReportStrictSlot(JSContext *cx, uint32 slot)
134 0 {
135 0     if (slot == JSSLOT_PROTO)
136 0         return JS_TRUE;
137 0     return JS_ReportErrorFlagsAndNumber(cx,
138                                         JSREPORT_WARNING | JSREPORT_STRICT,
139                                         js_GetErrorMessage, NULL,
140                                         JSMSG_DEPRECATED_USAGE,
141                                         object_props[slot].name);
142 }
143
144 static JSBool
145 obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
146 0 {
147 0     uint32 slot;
148 0     jsid propid;
149 0     JSAccessMode mode;
150 0     uintN attrs;
151 0     JSObject *pobj;
152 0     JSClass *clasp;
153 0     JSExtendedClass *xclasp;
154
155 0     slot = (uint32) JSVAL_TO_INT(id);
156 0     if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
157 0         propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
158 0         mode = JSACC_PROTO;
159     } else {
160 0         propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
161 0         mode = JSACC_PARENT;
162     }
163
164     /* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */
165 0     if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs))
166 0         return JS_FALSE;
167
168 0     pobj = JSVAL_TO_OBJECT(*vp);
169 0     if (pobj) {
170 0         clasp = OBJ_GET_CLASS(cx, pobj);
171 0         if (clasp->flags & JSCLASS_IS_EXTENDED) {
172 0             xclasp = (JSExtendedClass *) clasp;
173 0             if (xclasp->outerObject) {
174 0                 pobj = xclasp->outerObject(cx, pobj);
175 0                 if (!pobj)
176 0                     return JS_FALSE;
177 0                 *vp = OBJECT_TO_JSVAL(pobj);
178             }
179         }
180     }
181 0     return JS_TRUE;
182 }
183
184 static JSBool
185 obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
186 0 {
187 0     JSObject *pobj;
188 0     uint32 slot;
189 0     jsid propid;
190 0     uintN attrs;
191
192 0     if (!JSVAL_IS_OBJECT(*vp))
193 0         return JS_TRUE;
194 0     pobj = JSVAL_TO_OBJECT(*vp);
195 0     slot = (uint32) JSVAL_TO_INT(id);
196 0     if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
197 0         return JS_FALSE;
198
199     /* __parent__ is readonly and permanent, only __proto__ may be set. */
200 0     propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
201 0     if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_PROTO|JSACC_WRITE, vp, &attrs))
202 0         return JS_FALSE;
203
204 0     return js_SetProtoOrParent(cx, obj, slot, pobj);
205 }
206
207 static JSBool
208 obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
209 0 {
210 0     jsval iter_state;
211 0     jsid num_properties;
212 0     JSBool ok;
213
214 0     if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))
215 0         return JS_FALSE;
216
217     /* Get the number of properties to enumerate. */
218 0     iter_state = JSVAL_NULL;
219 0     ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties);
220 0     if (!ok)
221 0         goto out;
222
223 0     if (!JSVAL_IS_INT(num_properties)) {
224 0         JS_ASSERT(0);
225 0         *vp = JSVAL_ZERO;
226 0         goto out;
227     }
228 0     *vp = num_properties;
229
230 out:
231 0     if (iter_state != JSVAL_NULL)
232 0         ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
233 0     return ok;
234 }
235
236 #else  /* !JS_HAS_OBJ_PROTO_PROP */
237
238 #define object_props NULL
239
240 #endif /* !JS_HAS_OBJ_PROTO_PROP */
241
242 JSBool
243 js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)
244 0 {
245 0     JSRuntime *rt;
246 0     JSObject *obj2, *oldproto;
247 0     JSScope *scope, *newscope;
248
249     /*
250      * Serialize all proto and parent setting in order to detect cycles.
251      * We nest locks in this function, and only here, in the following orders:
252      *
253      * (1)  rt->setSlotLock < pobj's scope lock;
254      *      rt->setSlotLock < pobj's proto-or-parent's scope lock;
255      *      rt->setSlotLock < pobj's grand-proto-or-parent's scope lock;
256      *      etc...
257      * (2)  rt->setSlotLock < obj's scope lock < pobj's scope lock.
258      *
259      * We avoid AB-BA deadlock by restricting obj from being on pobj's parent
260      * or proto chain (pobj may already be on obj's parent or proto chain; it
261      * could be moving up or down).  We finally order obj with respect to pobj
262      * at the bottom of this routine (just before releasing rt->setSlotLock),
263      * by making pobj be obj's prototype or parent.
264      *
265      * After we have set the slot and released rt->setSlotLock, another call
266      * to js_SetProtoOrParent could nest locks according to the first order
267      * list above, but it cannot deadlock with any other thread.  For there
268      * to be a deadlock, other parts of the engine would have to nest scope
269      * locks in the opposite order.  XXXbe ensure they don't!
270      */
271 0     rt = cx->runtime;
272 #ifdef JS_THREADSAFE
273
274     JS_ACQUIRE_LOCK(rt->setSlotLock);
275     while (rt->setSlotBusy) {
276         jsrefcount saveDepth;
277
278         /* Take pains to avoid nesting rt->gcLock inside rt->setSlotLock! */
279         JS_RELEASE_LOCK(rt->setSlotLock);
280         saveDepth = JS_SuspendRequest(cx);
281         JS_ACQUIRE_LOCK(rt->setSlotLock);
282         if (rt->setSlotBusy)
283             JS_WAIT_CONDVAR(rt->setSlotDone, JS_NO_TIMEOUT);
284         JS_RELEASE_LOCK(rt->setSlotLock);
285         JS_ResumeRequest(cx, saveDepth);
286         JS_ACQUIRE_LOCK(rt->setSlotLock);
287     }
288     rt->setSlotBusy = JS_TRUE;
289     JS_RELEASE_LOCK(rt->setSlotLock);
290
291 #define SET_SLOT_DONE(rt)                                                     \
292     JS_BEGIN_MACRO                                                            \
293         JS_ACQUIRE_LOCK((rt)->setSlotLock);                                   \
294         (rt)->setSlotBusy = JS_FALSE;                                         \
295         JS_NOTIFY_ALL_CONDVAR((rt)->setSlotDone);                             \
296         JS_RELEASE_LOCK((rt)->setSlotLock);                                   \
297     JS_END_MACRO
298
299 #else
300
301 #define SET_SLOT_DONE(rt)       /* nothing */
302
303 #endif
304
305 0     obj2 = pobj;
306 0     while (obj2) {
307 0         if (obj2 == obj) {
308             SET_SLOT_DONE(rt);
309 0             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
310                                  JSMSG_CYCLIC_VALUE,
311 #if JS_HAS_OBJ_PROTO_PROP
312                                  object_props[slot].name
313 #else
314                                  (slot == JSSLOT_PROTO) ? js_proto_str
315                                                         : js_parent_str
316 #endif
317                                  );
318 0             return JS_FALSE;
319         }
320 0         obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot));
321     }
322
323 0     if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) {
324         /* Check to see whether obj shares its prototype's scope. */
325 0         JS_LOCK_OBJ(cx, obj);
326 0         scope = OBJ_SCOPE(obj);
327 0         oldproto = JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO));
328 0         if (oldproto && OBJ_SCOPE(oldproto) == scope) {
329             /* Either obj needs a new empty scope, or it should share pobj's. */
330 0             if (!pobj ||
331                 !OBJ_IS_NATIVE(pobj) ||
332                 OBJ_GET_CLASS(cx, pobj) != LOCKED_OBJ_GET_CLASS(oldproto)) {
333                 /*
334                  * With no proto and no scope of its own, obj is truly empty.
335                  *
336                  * If pobj is not native, obj needs its own empty scope -- it
337                  * should not continue to share oldproto's scope once oldproto
338                  * is not on obj's prototype chain.  That would put properties
339                  * from oldproto's scope ahead of properties defined by pobj,
340                  * in lookup order.
341                  *
342                  * If pobj's class differs from oldproto's, we may need a new
343                  * scope to handle differences in private and reserved slots,
344                  * so we suboptimally but safely make one.
345                  */
346 0                 scope = js_GetMutableScope(cx, obj);
347 0                 if (!scope) {
348 0                     JS_UNLOCK_OBJ(cx, obj);
349                     SET_SLOT_DONE(rt);
350 0                     return JS_FALSE;
351                 }
352 0             } else if (OBJ_SCOPE(pobj) != scope) {
353 #ifdef JS_THREADSAFE
354                 /*
355                  * We are about to nest scope locks.  Help jslock.c:ShareScope
356                  * keep scope->u.count balanced for the JS_UNLOCK_SCOPE, while
357                  * avoiding deadlock, by recording scope in rt->setSlotScope.
358                  */
359                 if (scope->ownercx) {
360                     JS_ASSERT(scope->ownercx == cx);
361                     rt->setSlotScope = scope;
362                 }
363 #endif
364
365                 /* We can't deadlock because we checked for cycles above (2). */
366 0                 JS_LOCK_OBJ(cx, pobj);
367 0                 newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map);
368 0                 obj->map = &newscope->map;
369 0                 js_DropObjectMap(cx, &scope->map, obj);
370 0                 JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
371 0                 scope = newscope;
372 #ifdef JS_THREADSAFE
373                 rt->setSlotScope = NULL;
374 #endif
375             }
376         }
377 0         LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj));
378 0         JS_UNLOCK_SCOPE(cx, scope);
379     } else {
380 0         OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj));
381     }
382
383     SET_SLOT_DONE(rt);
384 0     return JS_TRUE;
385
386 #undef SET_SLOT_DONE
387 }
388
389 JS_STATIC_DLL_CALLBACK(JSHashNumber)
390 js_hash_object(const void *key)
391 0 {
392 0     return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS;
393 }
394
395 static JSHashEntry *
396 MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
397 0 {
398 0     JSSharpObjectMap *map;
399 0     JSHashTable *table;
400 0     JSHashNumber hash;
401 0     JSHashEntry **hep, *he;
402 0     jsatomid sharpid;
403 0     JSIdArray *ida;
404 0     JSBool ok;
405 0     jsint i, length;
406 0     jsid id;
407 #if JS_HAS_GETTER_SETTER
408 0     JSObject *obj2;
409 0     JSProperty *prop;
410 0     uintN attrs;
411 #endif
412 0     jsval val;
413 0     int stackDummy;
414
415 0     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
416 0         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
417 0         return NULL;
418     }
419
420 0     map = &cx->sharpObjectMap;
421 0     table = map->table;
422 0     hash = js_hash_object(obj);
423 0     hep = JS_HashTableRawLookup(table, hash, obj);
424 0     he = *hep;
425 0     if (!he) {
426 0         sharpid = 0;
427 0         he = JS_HashTableRawAdd(table, hep, hash, obj,
428                                 JS_UINT32_TO_PTR(sharpid));
429 0         if (!he) {
430 0             JS_ReportOutOfMemory(cx);
431 0             return NULL;
432         }
433
434         /* 
435          * Increment map->depth to protect js_EnterSharpObject from reentering
436          * itself badly.  Without this fix, if we reenter the basis case where
437          * map->depth == 0, when unwinding the inner call we will destroy the
438          * newly-created hash table and crash.
439          */
440 0 ++map->depth;
441 0         ida = JS_Enumerate(cx, obj);
442 0 --map->depth;
443 0         if (!ida)
444 0             return NULL;
445
446 0         ok = JS_TRUE;
447 0         for (i = 0, length = ida->length; i < length; i++) {
448 0             id = ida->vector[i];
449 #if JS_HAS_GETTER_SETTER
450 0             ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
451 0             if (!ok)
452 0                 break;
453 0             if (!prop)
454 0                 continue;
455 0             ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
456 0             if (ok) {
457 0                 if (OBJ_IS_NATIVE(obj2) &&
458                     (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
459 0                     val = JSVAL_NULL;
460 0                     if (attrs & JSPROP_GETTER)
461 0                         val = (jsval) ((JSScopeProperty*)prop)->getter;
462 0                     if (attrs & JSPROP_SETTER) {
463 0                         if (val != JSVAL_NULL) {
464                             /* Mark the getter, then set val to setter. */
465 0                             ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
466                                                    NULL)
467                                   != NULL);
468                         }
469 0                         val = (jsval) ((JSScopeProperty*)prop)->setter;
470                     }
471                 } else {
472 0                     ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
473                 }
474             }
475 0             OBJ_DROP_PROPERTY(cx, obj2, prop);
476 #else
477             ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
478 #endif
479 0             if (!ok)
480 0                 break;
481 0             if (!JSVAL_IS_PRIMITIVE(val) &&
482                 !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {
483 0                 ok = JS_FALSE;
484 0                 break;
485             }
486         }
487 0         if (!ok || !idap)
488 0             JS_DestroyIdArray(cx, ida);
489 0         if (!ok)
490 0             return NULL;
491     } else {
492 0         sharpid = JS_PTR_TO_UINT32(he->value);
493 0         if (sharpid == 0) {
494 0             sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
495 0             he->value = JS_UINT32_TO_PTR(sharpid);
496         }
497 0         ida = NULL;
498     }
499 0     if (idap)
500 0         *idap = ida;
501 0     return he;
502 }
503
504 JSHashEntry *
505 js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
506                     jschar **sp)
507 0 {
508 0     JSSharpObjectMap *map;
509 0     JSHashTable *table;
510 0     JSIdArray *ida;
511 0     JSHashNumber hash;
512 0     JSHashEntry *he, **hep;
513 0     jsatomid sharpid;
514 0     char buf[20];
515 0     size_t len;
516
517 0     if (JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) &&
518         cx->branchCallback &&
519         !cx->branchCallback(cx, NULL)) {
520 0         return NULL;
521     }
522
523     /* Set to null in case we return an early error. */
524 0     *sp = NULL;
525 0     map = &cx->sharpObjectMap;
526 0     table = map->table;
527 0     if (!table) {
528 0         table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
529                                 JS_CompareValues, NULL, NULL);
530 0         if (!table) {
531 0             JS_ReportOutOfMemory(cx);
532 0             return NULL;
533         }
534 0         map->table = table;
535 0         JS_KEEP_ATOMS(cx->runtime);
536     }
537
538     /* From this point the control must flow either through out: or bad:. */
539 0     ida = NULL;
540 0     if (map->depth == 0) {
541 0         he = MarkSharpObjects(cx, obj, &ida);
542 0         if (!he)
543 0             goto bad;
544 0         JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0);
545 0         if (!idap) {
546 0             JS_DestroyIdArray(cx, ida);
547 0             ida = NULL;
548         }
549     } else {
550 0         hash = js_hash_object(obj);
551 0         hep = JS_HashTableRawLookup(table, hash, obj);
552 0         he = *hep;
553
554         /*
555          * It's possible that the value of a property has changed from the
556          * first time the object's properties are traversed (when the property
557          * ids are entered into the hash table) to the second (when they are
558          * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not
559          * idempotent.
560          */
561 0         if (!he) {
562 0             he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
563 0             if (!he) {
564 0                 JS_ReportOutOfMemory(cx);
565 0                 goto bad;
566             }
567 0             sharpid = 0;
568 0             goto out;
569         }
570     }
571
572 0     sharpid = JS_PTR_TO_UINT32(he->value);
573 0     if (sharpid != 0) {
574 0         len = JS_snprintf(buf, sizeof buf, "#%u%c",
575                           sharpid >> SHARP_ID_SHIFT,
576                           (sharpid & SHARP_BIT) ? '#' : '=');
577 0         *sp = js_InflateString(cx, buf, &len);
578 0         if (!*sp) {
579 0             if (ida)
580 0                 JS_DestroyIdArray(cx, ida);
581 0             goto bad;
582         }
583     }
584
585 out:
586 0     JS_ASSERT(he);
587 0     if ((sharpid & SHARP_BIT) == 0) {
588 0         if (idap && !ida) {
589 0             ida = JS_Enumerate(cx, obj);
590 0             if (!ida) {
591 0                 if (*sp) {
592 0                     JS_free(cx, *sp);
593 0                     *sp = NULL;
594                 }
595 0                 goto bad;
596             }
597         }
598 0         map->depth++;
599     }
600
601 0     if (idap)
602 0         *idap = ida;
603 0     return he;
604
605 bad:
606     /* Clean up the sharpObjectMap table on outermost error. */
607 0     if (map->depth == 0) {
608 0         JS_UNKEEP_ATOMS(cx->runtime);
609 0         map->sharpgen = 0;
610 0         JS_HashTableDestroy(map->table);
611 0         map->table = NULL;
612     }
613 0     return NULL;
614 }
615
616 void
617 js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
618 0 {
619 0     JSSharpObjectMap *map;
620 0     JSIdArray *ida;
621
622 0     map = &cx->sharpObjectMap;
623 0     JS_ASSERT(map->depth > 0);
624 0     if (--map->depth == 0) {
625 0         JS_UNKEEP_ATOMS(cx->runtime);
626 0         map->sharpgen = 0;
627 0         JS_HashTableDestroy(map->table);
628 0         map->table = NULL;
629     }
630 0     if (idap) {
631 0         ida = *idap;
632 0         if (ida) {
633 0             JS_DestroyIdArray(cx, ida);
634 0             *idap = NULL;
635         }
636     }
637 }
638
639 JS_STATIC_DLL_CALLBACK(intN)
640 gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg)
641 0 {
642 0     GC_MARK((JSContext *)arg, (JSObject *)he->key, "sharp table entry", NULL);
643 0     return JS_DHASH_NEXT;
644 }
645
646 void
647 js_GCMarkSharpMap(JSContext *cx, JSSharpObjectMap *map)
648 0 {
649 0     JS_ASSERT(map->depth > 0);
650 0     JS_ASSERT(map->table);
651
652     /*
653      * During recursive calls to MarkSharpObjects a non-native object or
654      * object with a custom getProperty method can potentially return an
655      * unrooted value or even cut from the object graph an argument of one of
656      * MarkSharpObjects recursive invocations. So we must protect map->table
657      * entries against GC.
658      *
659      * We can not simply use JSTempValueRooter to mark the obj argument of
660      * MarkSharpObjects during recursion as we have to protect *all* entries
661      * in JSSharpObjectMap including those that contains otherwise unreachable
662      * objects just allocated through custom getProperty. Otherwise newer
663      * allocations can re-use the address of an object stored in the hashtable
664      * confusing js_EnterSharpObject. So to address the problem we simply
665      * mark all objects from map->table.
666      *
667      * An alternative "proper" solution is to use JSTempValueRooter in
668      * MarkSharpObjects with code to remove during finalization entries
669      * with otherwise unreachable objects. But this is way too complex
670      * to justify spending efforts.
671      */
672 0     JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, cx);
673 }
674
675 #define OBJ_TOSTRING_EXTRA      4       /* for 4 local GC roots */
676
677 #if JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE
678 JSBool
679 js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
680                 jsval *rval)
681 0 {
682 0     JSBool ok, outermost;
683 0     JSHashEntry *he;
684 0     JSIdArray *ida;
685 0     jschar *chars, *ochars, *vsharp;
686 0     const jschar *idstrchars, *vchars;
687 0     size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen;
688 0     char *comma;
689 0     jsint i, j, length, valcnt;
690 0     jsid id;
691 #if JS_HAS_GETTER_SETTER
692 0     JSObject *obj2;
693 0     JSProperty *prop;
694 0     uintN attrs;
695 #endif
696 0     jsval *val;
697 0     JSString *gsop[2];
698 0     JSAtom *atom;
699 0     JSString *idstr, *valstr, *str;
700 0     int stackDummy;
701
702 0     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
703 0         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
704 0         return JS_FALSE;
705     }
706
707     /*
708      * obj_toString for 1.2 calls toSource, and doesn't want the extra parens
709      * on the outside.
710      */
711 0     outermost = !JS_VERSION_IS_1_2(cx) && cx->sharpObjectMap.depth == 0;
712 0     he = js_EnterSharpObject(cx, obj, &ida, &chars);
713 0     if (!he)
714 0         return JS_FALSE;
715 0     if (IS_SHARP(he)) {
716         /*
717          * We didn't enter -- obj is already "sharp", meaning we've visited it
718          * already in our depth first search, and therefore chars contains a
719          * string of the form "#n#".
720          */
721 0         JS_ASSERT(!ida);
722 #if JS_HAS_SHARP_VARS
723 0         nchars = js_strlen(chars);
724 #else
725         chars[0] = '{';
726         chars[1] = '}';
727         chars[2] = 0;
728         nchars = 2;
729 #endif
730 0         goto make_string;
731     }
732 0     JS_ASSERT(ida);
733 0     ok = JS_TRUE;
734
735 0     if (!chars) {
736         /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
737 0         chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
738 0         nchars = 0;
739 0         if (!chars)
740 0             goto error;
741 0         if (outermost)
742 0             chars[nchars++] = '(';
743     } else {
744         /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
745 0         MAKE_SHARP(he);
746 0         nchars = js_strlen(chars);
747 0         chars = (jschar *)
748             realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
749 0         if (!chars) {
750 0             free(ochars);
751 0             goto error;
752         }
753 0         if (outermost) {
754             /*
755              * No need for parentheses around the whole shebang, because #n=
756              * unambiguously begins an object initializer, and never a block
757              * statement.
758              */
759 0             outermost = JS_FALSE;
760         }
761     }
762
763 #ifdef DUMP_CALL_TABLE
764     if (cx->options & JSOPTION_LOGCALL_TOSOURCE) {
765         const char *classname = OBJ_GET_CLASS(cx, obj)->name;
766         size_t classnchars = strlen(classname);
767         static const char classpropid[] = "C";
768         const char *cp;
769         size_t onchars = nchars;
770
771         /* 2 for ': ', 2 quotes around classname, 2 for ', ' after. */
772         classnchars += sizeof classpropid - 1 + 2 + 2;
773         if (ida->length)
774             classnchars += 2;
775
776         /* 2 for the braces, 1 for the terminator */
777         chars = (jschar *)
778             realloc((ochars = chars),
779                     (nchars + classnchars + 2 + 1) * sizeof(jschar));
780         if (!chars) {
781             free(ochars);
782             goto error;
783         }
784
785         chars[nchars++] = '{';          /* 1 from the 2 braces */
786         for (cp = classpropid; *cp; cp++)
787             chars[nchars++] = (jschar) *cp;
788         chars[nchars++] = ':';
789         chars[nchars++] = ' ';          /* 2 for ': ' */
790         chars[nchars++] = '"';
791         for (cp = classname; *cp; cp++)
792             chars[nchars++] = (jschar) *cp;
793         chars[nchars++] = '"';          /* 2 quotes */
794         if (ida->length) {
795             chars[nchars++] = ',';
796             chars[nchars++] = ' ';      /* 2 for ', ' */
797         }
798
799         JS_ASSERT(nchars - onchars == 1 + classnchars);
800     } else
801 #endif
802 0     chars[nchars++] = '{';
803
804 0     comma = NULL;
805
806     /*
807      * We have four local roots for cooked and raw value GC safety.  Hoist the
808      * "argv + 2" out of the loop using the val local, which refers to the raw
809      * (unconverted, "uncooked") values.
810      */
811 0     val = argv + 2;
812
813 0     for (i = 0, length = ida->length; i < length; i++) {
814         /* Get strings for id and value and GC-root them via argv. */
815 0         id = ida->vector[i];
816
817 #if JS_HAS_GETTER_SETTER
818
819 0         ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
820 0         if (!ok)
821 0             goto error;
822 0         valcnt = 0;
823 0         if (prop) {
824 0             ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
825 0             if (!ok) {
826 0                 OBJ_DROP_PROPERTY(cx, obj2, prop);
827 0                 goto error;
828             }
829 0             if (OBJ_IS_NATIVE(obj2) &&
830                 (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
831 0                 if (attrs & JSPROP_GETTER) {
832 0                     val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter;
833 #ifdef OLD_GETTER_SETTER
834                     gsop[valcnt] =
835                         ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
836 #else
837 0                     gsop[valcnt] =
838                         ATOM_TO_STRING(cx->runtime->atomState.getAtom);
839 #endif
840 0                     valcnt++;
841                 }
842 0                 if (attrs & JSPROP_SETTER) {
843 0                     val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter;
844 #ifdef OLD_GETTER_SETTER
845                     gsop[valcnt] =
846                         ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
847 #else
848 0                     gsop[valcnt] =
849                         ATOM_TO_STRING(cx->runtime->atomState.setAtom);
850 #endif
851 0                     valcnt++;
852                 }
853             } else {
854 0                 valcnt = 1;
855 0                 gsop[0] = NULL;
856 0                 ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
857             }
858 0             OBJ_DROP_PROPERTY(cx, obj2, prop);
859         }
860
861 #else  /* !JS_HAS_GETTER_SETTER */
862
863         valcnt = 1;
864         gsop[0] = NULL;
865         ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
866
867 #endif /* !JS_HAS_GETTER_SETTER */
868
869 0         if (!ok)
870 0             goto error;
871
872         /* Convert id to a jsval and then to a string. */
873 0         atom = JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL;
874 0         id = ID_TO_VALUE(id);
875 0         idstr = js_ValueToString(cx, id);
876 0         if (!idstr) {
877 0             ok = JS_FALSE;
878 0             goto error;
879         }
880 0         *rval = STRING_TO_JSVAL(idstr);         /* local root */
881
882         /*
883          * If id is a string that's a reserved identifier, or else id is not
884          * an identifier at all, then it needs to be quoted.  Also, negative
885          * integer ids must be quoted.
886          */
887 0         if (atom
888             ? (ATOM_KEYWORD(atom) || !js_IsIdentifier(idstr))
889             : (JSID_IS_OBJECT(id) || JSID_TO_INT(id) < 0)) {
890 0             idstr = js_QuoteString(cx, idstr, (jschar)'\'');
891 0             if (!idstr) {
892 0                 ok = JS_FALSE;
893 0                 goto error;
894             }
895 0             *rval = STRING_TO_JSVAL(idstr);     /* local root */
896         }
897 0         idstrchars = JSSTRING_CHARS(idstr);
898 0         idstrlength = JSSTRING_LENGTH(idstr);
899
900 0         for (j = 0; j < valcnt; j++) {
901             /* Convert val[j] to its canonical source form. */
902 0             valstr = js_ValueToSource(cx, val[j]);
903 0             if (!valstr) {
904 0                 ok = JS_FALSE;
905 0                 goto error;
906             }
907 0             argv[j] = STRING_TO_JSVAL(valstr);  /* local root */
908 0             vchars = JSSTRING_CHARS(valstr);
909 0             vlength = JSSTRING_LENGTH(valstr);
910
911 #ifndef OLD_GETTER_SETTER
912             /*
913              * Remove '(function ' from the beginning of valstr and ')' from the
914              * end so that we can put "get" in front of the function definition.
915              */
916 0             if (gsop[j]) {
917 0                 int n = strlen(js_function_str) + 2;
918 0                 vchars += n;
919 0                 vlength -= n + 1;
920             }
921 #endif
922
923             /* If val[j] is a non-sharp object, consider sharpening it. */
924 0             vsharp = NULL;
925 0             vsharplength = 0;
926 #if JS_HAS_SHARP_VARS
927 0             if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') {
928 0                 he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL,
929                                          &vsharp);
930 0                 if (!he) {
931 0                     ok = JS_FALSE;
932 0                     goto error;
933                 }
934 0                 if (IS_SHARP(he)) {
935 0                     vchars = vsharp;
936 0                     vlength = js_strlen(vchars);
937                 } else {
938 0                     if (vsharp) {
939 0                         vsharplength = js_strlen(vsharp);
940 0                         MAKE_SHARP(he);
941                     }
942 0                     js_LeaveSharpObject(cx, NULL);
943                 }
944             }
945 #endif
946
947 #define SAFE_ADD(n)                                                          \
948     JS_BEGIN_MACRO                                                           \
949         size_t n_ = (n);                                                     \
950         curlen += n_;                                                        \
951         if (curlen < n_)                                                     \
952             goto overflow;                                                   \
953     JS_END_MACRO
954
955 0             curlen = nchars;
956 0             if (comma)
957 0                 SAFE_ADD(2);
958 0             SAFE_ADD(idstrlength + 1);
959 0             if (gsop[j])
960 0                 SAFE_ADD(JSSTRING_LENGTH(gsop[j]) + 1);
961 0             SAFE_ADD(vsharplength);
962 0             SAFE_ADD(vlength);
963 0             SAFE_ADD((outermost ? 2 : 1) + 1);
964 #undef SAFE_ADD
965
966 0             if (curlen > (size_t)-1 / sizeof(jschar))
967 0                 goto overflow;
968
969             /* Allocate 1 + 1 at end for closing brace and terminating 0. */
970 0             chars = (jschar *)
971                 realloc((ochars = chars), curlen * sizeof(jschar));
972 0             if (!chars) {
973                 /* Save code space on error: let JS_free ignore null vsharp. */
974 0                 JS_free(cx, vsharp);
975 0                 free(ochars);
976 0                 goto error;
977             }
978
979 0             if (comma) {
980 0                 chars[nchars++] = comma[0];
981 0                 chars[nchars++] = comma[1];
982             }
983 0             comma = ", ";
984
985 #ifdef OLD_GETTER_SETTER
986             js_strncpy(&chars[nchars], idstrchars, idstrlength);
987             nchars += idstrlength;
988             if (gsop[j]) {
989                 chars[nchars++] = ' ';
990                 gsoplength = JSSTRING_LENGTH(gsop[j]);
991                 js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength);
992                 nchars += gsoplength;
993             }
994             chars[nchars++] = ':';
995 #else
996 0             if (gsop[j]) {
997 0                 gsoplength = JSSTRING_LENGTH(gsop[j]);
998 0                 js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength);
999 0                 nchars += gsoplength;
1000 0                 chars[nchars++] = ' ';
1001             }
1002 0             js_strncpy(&chars[nchars], idstrchars, idstrlength);
1003 0             nchars += idstrlength;
1004 0             if (!gsop[j])
1005 0                 chars[nchars++] = ':';
1006 #endif
1007 0             if (vsharplength) {
1008 0                 js_strncpy(&chars[nchars], vsharp, vsharplength);
1009 0                 nchars += vsharplength;
1010             }
1011 0             js_strncpy(&chars[nchars], vchars, vlength);
1012 0             nchars += vlength;
1013
1014 0             if (vsharp)
1015 0                 JS_free(cx, vsharp);
1016 #ifdef DUMP_CALL_TABLE
1017             if (outermost && nchars >= js_LogCallToSourceLimit)
1018                 break;
1019 #endif
1020         }
1021     }
1022
1023 0     chars[nchars++] = '}';
1024 0     if (outermost)
1025 0         chars[nchars++] = ')';
1026 0     chars[nchars] = 0;
1027
1028   error:
1029 0     js_LeaveSharpObject(cx, &ida);
1030
1031 0     if (!ok) {
1032 0         if (chars)
1033 0             free(chars);
1034 0         return ok;
1035     }
1036
1037 0     if (!chars) {
1038 0         JS_ReportOutOfMemory(cx);
1039 0         return JS_FALSE;
1040     }
1041   make_string:
1042 0     str = js_NewString(cx, chars, nchars, 0);
1043 0     if (!str) {
1044 0         free(chars);
1045 0         return JS_FALSE;
1046     }
1047 0     *rval = STRING_TO_JSVAL(str);
1048 0     return JS_TRUE;
1049
1050   overflow:
1051 0     JS_free(cx, vsharp);
1052 0     free(chars);
1053 0     chars = NULL;
1054 0     goto error;
1055 }
1056 #endif /* JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE */
1057
1058 JSBool
1059 js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1060                 jsval *rval)
1061 0 {
1062 0     jschar *chars;
1063 0     size_t nchars;
1064 0     const char *clazz, *prefix;
1065 0     JSString *str;
1066
1067 #if JS_HAS_INITIALIZERS
1068 0     if (JS_VERSION_IS_1_2(cx))
1069 0         return js_obj_toSource(cx, obj, argc, argv, rval);
1070 #endif
1071
1072 0     clazz = OBJ_GET_CLASS(cx, obj)->name;
1073 0     nchars = 9 + strlen(clazz);         /* 9 for "[object ]" */
1074 0     chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar));
1075 0     if (!chars)
1076 0         return JS_FALSE;
1077
1078 0     prefix = "[object ";
1079 0     nchars = 0;
1080 0     while ((chars[nchars] = (jschar)*prefix) != 0)
1081 0         nchars++, prefix++;
1082 0     while ((chars[nchars] = (jschar)*clazz) != 0)
1083 0         nchars++, clazz++;
1084 0     chars[nchars++] = ']';
1085 0     chars[nchars] = 0;
1086
1087 0     str = js_NewString(cx, chars, nchars, 0);
1088 0     if (!str) {
1089 0         JS_free(cx, chars);
1090 0         return JS_FALSE;
1091     }
1092 0     *rval = STRING_TO_JSVAL(str);
1093 0     return JS_TRUE;
1094 }
1095
1096 static JSBool
1097 js_obj_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1098                       jsval *rval)
1099 0 {
1100 0     JSString *str;
1101
1102 0     str = js_ValueToString(cx, argv[-1]);
1103 0     if (!str)
1104 0         return JS_FALSE;
1105
1106 0     *rval = STRING_TO_JSVAL(str);
1107 0     return JS_TRUE;
1108 }
1109
1110 static JSBool
1111 obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1112 0 {
1113 0     *rval = OBJECT_TO_JSVAL(obj);
1114 0     return JS_TRUE;
1115 }
1116
1117 /*
1118  * Check whether principals subsumes scopeobj's principals, and return true
1119  * if so (or if scopeobj has no principals, for backward compatibility with
1120  * the JS API, which does not require principals), and false otherwise.
1121  */
1122 JSBool
1123 js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj,
1124                          JSPrincipals *principals, const char *caller)
1125 0 {
1126 0     JSRuntime *rt;
1127 0     JSPrincipals *scopePrincipals;
1128
1129 0     rt = cx->runtime;
1130 0     if (rt->findObjectPrincipals) {
1131 0         scopePrincipals = rt->findObjectPrincipals(cx, scopeobj);
1132 0         if (!principals || !scopePrincipals ||
1133             !principals->subsume(principals, scopePrincipals)) {
1134 0             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1135                                  JSMSG_BAD_INDIRECT_CALL, caller);
1136 0             return JS_FALSE;
1137         }
1138     }
1139 0     return JS_TRUE;
1140 }
1141
1142 JSObject *
1143 js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller)
1144 0 {
1145 0     JSClass *clasp;
1146 0     JSExtendedClass *xclasp;
1147 0     JSObject *inner;
1148
1149 0     if (!scopeobj)
1150 0         goto bad;
1151
1152 0     OBJ_TO_INNER_OBJECT(cx, scopeobj);
1153 0     if (!scopeobj)
1154 0         return NULL;
1155
1156 0     inner = scopeobj;
1157
1158     /* XXX This is an awful gross hack. */
1159 0     while (scopeobj) {
1160 0         clasp = OBJ_GET_CLASS(cx, scopeobj);
1161 0         if (clasp->flags & JSCLASS_IS_EXTENDED) {
1162 0             xclasp = (JSExtendedClass*)clasp;
1163 0             if (xclasp->innerObject &&
1164                 xclasp->innerObject(cx, scopeobj) != scopeobj) {
1165 0                 goto bad;
1166             }
1167         }
1168
1169 0         scopeobj = OBJ_GET_PARENT(cx, scopeobj);
1170     }
1171
1172 0     return inner;
1173
1174 bad:
1175 0     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1176                          JSMSG_BAD_INDIRECT_CALL, caller);
1177 0     return NULL;
1178 }
1179
1180 static JSBool
1181 obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1182 0 {
1183 0     JSStackFrame *fp, *caller;
1184 0     JSBool indirectCall;
1185 0     JSObject *scopeobj;
1186 0     JSString *str;
1187 0     const char *file;
1188 0     uintN line;
1189 0     JSPrincipals *principals;
1190 0     JSScript *script;
1191 0     JSBool ok;
1192 #if JS_HAS_EVAL_THIS_SCOPE
1193 0     JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
1194 0     JSObject *setCallerScopeChain = NULL;
1195 0     JSBool setCallerVarObj = JS_FALSE;
1196 #endif
1197
1198 0     fp = cx->fp;
1199 0     caller = JS_GetScriptedCaller(cx, fp);
1200 0     indirectCall = (caller && caller->pc && *caller->pc != JSOP_EVAL);
1201
1202 0     if (JS_VERSION_IS_ECMA(cx) &&
1203         indirectCall &&
1204         !JS_ReportErrorFlagsAndNumber(cx,
1205                                       JSREPORT_WARNING | JSREPORT_STRICT,
1206                                       js_GetErrorMessage, NULL,
1207                                       JSMSG_BAD_INDIRECT_CALL,
1208                                       js_eval_str)) {
1209 0         return JS_FALSE;
1210     }
1211
1212 0     if (!JSVAL_IS_STRING(argv[0])) {
1213 0         *rval = argv[0];
1214 0         return JS_TRUE;
1215     }
1216
1217     /*
1218      * If the caller is a lightweight function and doesn't have a variables
1219      * object, then we need to provide one for the compiler to stick any
1220      * declared (var) variables into.
1221      */
1222 0     if (caller && !caller->varobj && !js_GetCallObject(cx, caller, NULL))
1223 0         return JS_FALSE;
1224
1225 #if JS_HAS_SCRIPT_OBJECT
1226     /*
1227      * Script.prototype.compile/exec and Object.prototype.eval all take an
1228      * optional trailing argument that overrides the scope object.
1229      */
1230 0     scopeobj = NULL;
1231 0     if (argc >= 2) {
1232 0         if (!js_ValueToObject(cx, argv[1], &scopeobj))
1233 0             return JS_FALSE;
1234 0         argv[1] = OBJECT_TO_JSVAL(scopeobj);
1235     }
1236 0     if (!scopeobj)
1237 #endif
1238     {
1239 #if JS_HAS_EVAL_THIS_SCOPE
1240         /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
1241 0         if (indirectCall) {
1242 0             callerScopeChain = caller->scopeChain;
1243 0             if (obj != callerScopeChain) {
1244 0                 if (!js_CheckPrincipalsAccess(cx, obj,
1245                                               caller->script->principals,
1246                                               js_eval_str)) {
1247 0                     return JS_FALSE;
1248                 }
1249
1250 0                 scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1);
1251 0                 if (!scopeobj)
1252 0                     return JS_FALSE;
1253
1254                 /* Set fp->scopeChain too, for the compiler. */
1255 0                 caller->scopeChain = fp->scopeChain = scopeobj;
1256
1257                 /* Remember scopeobj so we can null its private when done. */
1258 0                 setCallerScopeChain = scopeobj;
1259             }
1260
1261 0             callerVarObj = caller->varobj;
1262 0             if (obj != callerVarObj) {
1263                 /* Set fp->varobj too, for the compiler. */
1264 0                 caller->varobj = fp->varobj = obj;
1265 0                 setCallerVarObj = JS_TRUE;
1266             }
1267         }
1268         /* From here on, control must exit through label out with ok set. */
1269 #endif
1270
1271 #if JS_BUG_EVAL_THIS_SCOPE
1272         /* An old version used the object in which eval was found for scope. */
1273         scopeobj = obj;
1274 #else
1275         /* Compile using caller's current scope object. */
1276 0         if (caller)
1277 0             scopeobj = caller->scopeChain;
1278 #endif
1279     }
1280
1281     /* Ensure we compile this eval with the right object in the scope chain. */
1282 0     scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str);
1283 0     if (!scopeobj)
1284 0         return JS_FALSE;
1285
1286 0     str = JSVAL_TO_STRING(argv[0]);
1287 0     if (caller) {
1288 0         file = caller->script->filename;
1289 0         line = js_PCToLineNumber(cx, caller->script, caller->pc);
1290 0         principals = JS_EvalFramePrincipals(cx, fp, caller);
1291     } else {
1292 0         file = NULL;
1293 0         line = 0;
1294 0         principals = NULL;
1295     }
1296
1297     /*
1298      * Set JSFRAME_EVAL on fp and any frames (e.g., fun_call if eval.call was
1299      * invoked) between fp and its scripted caller, to help the compiler easily
1300      * find the same caller whose scope and var obj we've set.
1301      *
1302      * XXX this nonsense could, and perhaps should, go away with a better way
1303      * to pass params to the compiler than via the top-most frame.
1304      */
1305 0     do {
1306 0         fp->flags |= JSFRAME_EVAL;
1307 0     } while ((fp = fp->down) != caller);
1308
1309 0     script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,
1310                                              JSSTRING_CHARS(str),
1311                                              JSSTRING_LENGTH(str),
1312                                              file, line);
1313 0     if (!script) {
1314 0         ok = JS_FALSE;
1315 0         goto out;
1316     }
1317
1318 #if !JS_BUG_EVAL_THIS_SCOPE
1319 #if JS_HAS_SCRIPT_OBJECT
1320 0     if (argc < 2)
1321 #endif
1322     {
1323         /* Execute using caller's new scope object (might be a Call object). */
1324 0         if (caller)
1325 0             scopeobj = caller->scopeChain;
1326     }
1327 #endif
1328
1329     /*
1330      * Belt-and-braces: check that the lesser of eval's principals and the
1331      * caller's principals has access to scopeobj.
1332      */
1333 0     ok = js_CheckPrincipalsAccess(cx, scopeobj, principals, js_eval_str);
1334 0     if (ok)
1335 0         ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
1336
1337 0     JS_DestroyScript(cx, script);
1338
1339 out:
1340 #if JS_HAS_EVAL_THIS_SCOPE
1341     /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
1342 0     if (setCallerScopeChain) {
1343 0         caller->scopeChain = callerScopeChain;
1344 0         JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass);
1345 0         JS_SetPrivate(cx, setCallerScopeChain, NULL);
1346     }
1347 0     if (setCallerVarObj)
1348 0         caller->varobj = callerVarObj;
1349 #endif
1350 0     return ok;
1351 }
1352
1353 #if JS_HAS_OBJ_WATCHPOINT
1354
1355 static JSBool
1356 obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
1357                   void *closure)
1358 0 {
1359 0     JSObject *callable;
1360 0     JSRuntime *rt;
1361 0     JSStackFrame *caller;
1362 0     JSPrincipals *subject, *watcher;
1363 0     JSResolvingKey key;
1364 0     JSResolvingEntry *entry;
1365 0     uint32 generation;
1366 0     jsval argv[3];
1367 0     JSBool ok;
1368
1369 0     callable = (JSObject *) closure;
1370
1371 0     rt = cx->runtime;
1372 0     if (rt->findObjectPrincipals) {
1373         /* Skip over any obj_watch_* frames between us and the real subject. */
1374 0         caller = JS_GetScriptedCaller(cx, cx->fp);
1375 0         if (caller) {
1376             /*
1377              * Only call the watch handler if the watcher is allowed to watch
1378              * the currently executing script.
1379              */
1380 0             watcher = rt->findObjectPrincipals(cx, callable);
1381 0             subject = JS_StackFramePrincipals(cx, caller);
1382
1383 0             if (watcher && subject && !watcher->subsume(watcher, subject)) {
1384                 /* Silently don't call the watch handler. */
1385 0                 return JS_TRUE;
1386             }
1387         }
1388     }
1389
1390     /* Avoid recursion on (obj, id) already being watched on cx. */
1391 0     key.obj = obj;
1392 0     key.id = id;
1393 0     if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
1394 0         return JS_FALSE;
1395 0     if (!entry)
1396 0         return JS_TRUE;
1397 0     generation = cx->resolvingTable->generation;
1398
1399 0     argv[0] = id;
1400 0     argv[1] = old;
1401 0     argv[2] = *nvp;
1402 0     ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp);
1403 0     js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
1404 0     return ok;
1405 }
1406
1407 static JSBool
1408 obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1409 0 {
1410 0     JSObject *callable;
1411 0     jsval userid, value;
1412 0     jsid propid;
1413 0     uintN attrs;
1414
1415 0     callable = js_ValueToCallableObject(cx, &argv[1], 0);
1416 0     if (!callable)
1417 0         return JS_FALSE;
1418
1419     /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1420 0     userid = argv[0];
1421 0     if (!JS_ValueToId(cx, userid, &propid))
1422 0         return JS_FALSE;
1423
1424 0     if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs))
1425 0         return JS_FALSE;
1426 0     if (attrs & JSPROP_READONLY)
1427 0         return JS_TRUE;
1428 0     return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable);
1429 }
1430
1431 static JSBool
1432 obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1433 0 {
1434 0     return JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL);
1435 }
1436
1437 #endif /* JS_HAS_OBJ_WATCHPOINT */
1438
1439 #if JS_HAS_NEW_OBJ_METHODS
1440 /*
1441  * Prototype and property query methods, to complement the 'in' and
1442  * 'instanceof' operators.
1443  */
1444
1445 /* Proposed ECMA 15.2.4.5. */
1446 static JSBool
1447 obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1448                    jsval *rval)
1449 0 {
1450 0     return js_HasOwnPropertyHelper(cx, obj, obj->map->ops->lookupProperty,
1451                                    argc, argv, rval);
1452 }
1453
1454 JSBool
1455 js_HasOwnPropertyHelper(JSContext *cx, JSObject *obj, JSLookupPropOp lookup,
1456                         uintN argc, jsval *argv, jsval *rval)
1457 0 {
1458 0     jsid id;
1459 0     JSObject *obj2;
1460 0     JSProperty *prop;
1461 0     JSScopeProperty *sprop;
1462
1463 0     if (!JS_ValueToId(cx, argv[0], &id))
1464 0         return JS_FALSE;
1465 0     if (!lookup(cx, obj, id, &obj2, &prop))
1466 0         return JS_FALSE;
1467 0     if (!prop) {
1468 0         *rval = JSVAL_FALSE;
1469 0     } else if (obj2 == obj) {
1470 0         *rval = JSVAL_TRUE;
1471     } else {
1472 0         JSClass *clasp;
1473 0         JSExtendedClass *xclasp;
1474
1475 0         clasp = OBJ_GET_CLASS(cx, obj);
1476 0         xclasp = (clasp->flags & JSCLASS_IS_EXTENDED)
1477                  ? (JSExtendedClass *)clasp
1478                  : NULL;
1479 0         if (xclasp && xclasp->outerObject &&
1480             xclasp->outerObject(cx, obj2) == obj) {
1481 0             *rval = JSVAL_TRUE;
1482 0         } else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj2) == clasp) {
1483             /*
1484              * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
1485              * delegated property makes that property appear to be direct in
1486              * all delegating instances of the same native class.  This hack
1487              * avoids bloating every function instance with its own 'length'
1488              * (AKA 'arity') property.  But it must not extend across class
1489              * boundaries, to avoid making hasOwnProperty lie (bug 320854).
1490              *
1491              * It's not really a hack, of course: a permanent property can't
1492              * be deleted, and JSPROP_SHARED means "don't allocate a slot in
1493              * any instance, prototype or delegating".  Without a slot, and
1494              * without the ability to remove and recreate (with differences)
1495              * the property, there is no way to tell whether it is directly
1496              * owned, or indirectly delegated.
1497              */
1498 0             sprop = (JSScopeProperty *)prop;
1499 0             *rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
1500         } else {
1501 0             *rval = JSVAL_FALSE;
1502         }
1503     }
1504 0     if (prop)
1505 0         OBJ_DROP_PROPERTY(cx, obj2, prop);
1506 0     return JS_TRUE;
1507 }
1508
1509 /* Proposed ECMA 15.2.4.6. */
1510 static JSBool
1511 obj_isPrototypeOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1512                   jsval *rval)
1513 0 {
1514 0     JSBool b;
1515
1516 0     if (!js_IsDelegate(cx, obj, *argv, &b))
1517 0         return JS_FALSE;
1518 0     *rval = BOOLEAN_TO_JSVAL(b);
1519 0     return JS_TRUE;
1520 }
1521
1522 /* Proposed ECMA 15.2.4.7. */
1523 static JSBool
1524 obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1525                          jsval *rval)
1526 0 {
1527 0     jsid id;
1528 0     uintN attrs;
1529 0     JSObject *obj2;
1530 0     JSProperty *prop;
1531 0     JSBool ok;
1532
1533 0     if (!JS_ValueToId(cx, argv[0], &id))
1534 0         return JS_FALSE;
1535
1536 0     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1537 0         return JS_FALSE;
1538
1539 0     if (!prop) {
1540 0         *rval = JSVAL_FALSE;
1541 0         return JS_TRUE;
1542     }
1543
1544     /*
1545      * XXX ECMA spec error compatible: return false unless hasOwnProperty.
1546      * The ECMA spec really should be fixed so propertyIsEnumerable and the
1547      * for..in loop agree on whether prototype properties are enumerable,
1548      * obviously by fixing this method (not by breaking the for..in loop!).
1549      *
1550      * We check here for shared permanent prototype properties, which should
1551      * be treated as if they are local to obj.  They are an implementation
1552      * technique used to satisfy ECMA requirements; users should not be able
1553      * to distinguish a shared permanent proto-property from a local one.
1554      */
1555 0     if (obj2 != obj &&
1556         !(OBJ_IS_NATIVE(obj2) &&
1557           SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) {
1558 0         OBJ_DROP_PROPERTY(cx, obj2, prop);
1559 0         *rval = JSVAL_FALSE;
1560 0         return JS_TRUE;
1561     }
1562
1563 0     ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
1564 0     OBJ_DROP_PROPERTY(cx, obj2, prop);
1565 0     if (ok)
1566 0         *rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);
1567 0     return ok;
1568 }
1569 #endif /* JS_HAS_NEW_OBJ_METHODS */
1570
1571 #if JS_HAS_GETTER_SETTER
1572 static JSBool
1573 obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1574                  jsval *rval)
1575 0 {
1576 0     jsval fval, junk;
1577 0     jsid id;
1578 0     uintN attrs;
1579
1580 0     fval = argv[1];
1581 0     if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
1582 0         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1583                              JSMSG_BAD_GETTER_OR_SETTER,
1584                              js_getter_str);
1585 0         return JS_FALSE;
1586     }
1587
1588 0     if (!JS_ValueToId(cx, argv[0], &id))
1589 0         return JS_FALSE;
1590 0     if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
1591 0         return JS_FALSE;
1592     /*
1593      * Getters and setters are just like watchpoints from an access
1594      * control point of view.
1595      */
1596 0     if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1597 0         return JS_FALSE;
1598 0     return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
1599                                (JSPropertyOp) JSVAL_TO_OBJECT(fval), NULL,
1600                                JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED,
1601                                NULL);
1602 }
1603
1604 static JSBool
1605 obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1606                  jsval *rval)
1607 0 {
1608 0     jsval fval, junk;
1609 0     jsid id;
1610 0     uintN attrs;
1611
1612 0     fval = argv[1];
1613 0     if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
1614 0         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1615                              JSMSG_BAD_GETTER_OR_SETTER,
1616                              js_setter_str);
1617 0         return JS_FALSE;
1618     }
1619
1620 0     if (!JS_ValueToId(cx, argv[0], &id))
1621 0         return JS_FALSE;
1622 0     if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
1623 0         return JS_FALSE;
1624     /*
1625      * Getters and setters are just like watchpoints from an access
1626      * control point of view.
1627      */
1628 0     if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1629 0         return JS_FALSE;
1630 0     return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
1631                                NULL, (JSPropertyOp) JSVAL_TO_OBJECT(fval),
1632                                JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED,
1633                                NULL);
1634 }
1635
1636 static JSBool
1637 obj_lookupGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1638                  jsval *rval)
1639 0 {
1640 0     jsid id;
1641 0     JSObject *pobj;
1642 0     JSProperty *prop;
1643 0     JSScopeProperty *sprop;
1644
1645 0     if (!JS_ValueToId(cx, argv[0], &id))
1646 0         return JS_FALSE;
1647 0     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1648 0         return JS_FALSE;
1649 0     if (prop) {
1650 0         if (OBJ_IS_NATIVE(pobj)) {
1651 0             sprop = (JSScopeProperty *) prop;
1652 0             if (sprop->attrs & JSPROP_GETTER)
1653 0                 *rval = OBJECT_TO_JSVAL(sprop->getter);
1654         }
1655 0         OBJ_DROP_PROPERTY(cx, pobj, prop);
1656     }
1657 0     return JS_TRUE;
1658 }
1659
1660 static JSBool
1661 obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1662                  jsval *rval)
1663 0 {
1664 0     jsid id;
1665 0     JSObject *pobj;
1666 0     JSProperty *prop;
1667 0     JSScopeProperty *sprop;
1668
1669 0     if (!JS_ValueToId(cx, argv[0], &id))
1670 0         return JS_FALSE;
1671 0     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1672 0         return JS_FALSE;
1673 0     if (prop) {
1674 0         if (OBJ_IS_NATIVE(pobj)) {
1675 0             sprop = (JSScopeProperty *) prop;
1676 0             if (sprop->attrs & JSPROP_SETTER)
1677 0                 *rval = OBJECT_TO_JSVAL(sprop->setter);
1678         }
1679 0         OBJ_DROP_PROPERTY(cx, pobj, prop);
1680     }
1681 0     return JS_TRUE;
1682 }
1683 #endif /* JS_HAS_GETTER_SETTER */
1684
1685 #if JS_HAS_OBJ_WATCHPOINT
1686 const char js_watch_str[] = "watch";
1687 const char js_unwatch_str[] = "unwatch";
1688 #endif
1689 #if JS_HAS_NEW_OBJ_METHODS
1690 const char js_hasOwnProperty_str[] = "hasOwnProperty";
1691 const char js_isPrototypeOf_str[] = "isPrototypeOf";
1692 const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
1693 #endif
1694 #if JS_HAS_GETTER_SETTER
1695 const char js_defineGetter_str[] = "__defineGetter__";
1696 const char js_defineSetter_str[] = "__defineSetter__";
1697 const char js_lookupGetter_str[] = "__lookupGetter__";
1698 const char js_lookupSetter_str[] = "__lookupSetter__";
1699 #endif
1700
1701 static JSFunctionSpec object_methods[] = {
1702 #if JS_HAS_TOSOURCE
1703     {js_toSource_str,             js_obj_toSource,    0, 0, OBJ_TOSTRING_EXTRA},
1704 #endif
1705     {js_toString_str,             js_obj_toString,    0, 0, OBJ_TOSTRING_EXTRA},
1706     {js_toLocaleString_str,       js_obj_toLocaleString, 0, 0, OBJ_TOSTRING_EXTRA},
1707     {js_valueOf_str,              obj_valueOf,        0,0,0},
1708     {js_eval_str,                 obj_eval,           1,0,0},
1709 #if JS_HAS_OBJ_WATCHPOINT
1710     {js_watch_str,                obj_watch,          2,0,0},
1711     {js_unwatch_str,              obj_unwatch,        1,0,0},
1712 #endif
1713 #if JS_HAS_NEW_OBJ_METHODS
1714     {js_hasOwnProperty_str,       obj_hasOwnProperty, 1,0,0},
1715     {js_isPrototypeOf_str,        obj_isPrototypeOf,  1,0,0},
1716     {js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0},
1717 #endif
1718 #if JS_HAS_GETTER_SETTER
1719     {js_defineGetter_str,         obj_defineGetter,   2,0,0},
1720     {js_defineSetter_str,         obj_defineSetter,   2,0,0},
1721     {js_lookupGetter_str,         obj_lookupGetter,   1,0,0},
1722     {js_lookupSetter_str,         obj_lookupSetter,   1,0,0},
1723 #endif
1724     {0,0,0,0,0}
1725 };
1726
1727 static JSBool
1728 Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1729 0 {
1730 0     if (argc == 0) {
1731         /* Trigger logic below to construct a blank object. */
1732 0         obj = NULL;
1733     } else {
1734         /* If argv[0] is null or undefined, obj comes back null. */
1735 0         if (!js_ValueToObject(cx, argv[0], &obj))
1736 0             return JS_FALSE;
1737     }
1738 0     if (!obj) {
1739 0         JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));
1740 0         if (cx->fp->flags & JSFRAME_CONSTRUCTING)
1741 0             return JS_TRUE;
1742 0         obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL);
1743 0         if (!obj)
1744 0             return JS_FALSE;
1745     }
1746 0     *rval = OBJECT_TO_JSVAL(obj);
1747 0     return JS_TRUE;
1748 }
1749
1750 /*
1751  * ObjectOps and Class for with-statement stack objects.
1752  */
1753 static JSBool
1754 with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
1755                     JSProperty **propp)
1756 0 {
1757 0     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1758 0     if (!proto)
1759 0         return js_LookupProperty(cx, obj, id, objp, propp);
1760 0     return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
1761 }
1762
1763 static JSBool
1764 with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1765 0 {
1766 0     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1767 0     if (!proto)
1768 0         return js_GetProperty(cx, obj, id, vp);
1769 0     return OBJ_GET_PROPERTY(cx, proto, id, vp);
1770 }
1771
1772 static JSBool
1773 with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1774 0 {
1775 0     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1776 0     if (!proto)
1777 0         return js_SetProperty(cx, obj, id, vp);
1778 0     return OBJ_SET_PROPERTY(cx, proto, id, vp);
1779 }
1780
1781 static JSBool
1782 with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
1783                    uintN *attrsp)
1784 0 {
1785 0     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1786 0     if (!proto)
1787 0         return js_GetAttributes(cx, obj, id, prop, attrsp);
1788 0     return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp);
1789 }
1790
1791 static JSBool
1792 with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
1793                    uintN *attrsp)
1794 0 {
1795 0     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1796 0     if (!proto)
1797 0         return js_SetAttributes(cx, obj, id, prop, attrsp);
1798 0     return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp);
1799 }
1800
1801 static JSBool
1802 with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
1803 0 {
1804 0     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1805 0     if (!proto)
1806 0         return js_DeleteProperty(cx, obj, id, rval);
1807 0     return OBJ_DELETE_PROPERTY(cx, proto, id, rval);
1808 }
1809
1810 static JSBool
1811 with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
1812 0 {
1813 0     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1814 0     if (!proto)
1815 0         return js_DefaultValue(cx, obj, hint, vp);
1816 0     return OBJ_DEFAULT_VALUE(cx, proto, hint, vp);
1817 }
1818
1819 static JSBool
1820 with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
1821                jsval *statep, jsid *idp)
1822 0 {
1823 0     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1824 0     if (!proto)
1825 0         return js_Enumerate(cx, obj, enum_op, statep, idp);
1826 0     return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp);
1827 }
1828
1829 static JSBool
1830 with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
1831                  jsval *vp, uintN *attrsp)
1832 0 {
1833 0     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1834 0     if (!proto)
1835 0         return js_CheckAccess(cx, obj, id, mode, vp, attrsp);
1836 0     return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp);
1837 }
1838
1839 static JSObject *
1840 with_ThisObject(JSContext *cx, JSObject *obj)
1841 0 {
1842 0     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1843 0     if (!proto)
1844 0         return obj;
1845 0     return OBJ_THIS_OBJECT(cx, proto);
1846 }
1847
1848 JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = {
1849     js_NewObjectMap,        js_DestroyObjectMap,
1850     with_LookupProperty,    js_DefineProperty,
1851     with_GetProperty,       with_SetProperty,
1852     with_GetAttributes,     with_SetAttributes,
1853     with_DeleteProperty,    with_DefaultValue,
1854     with_Enumerate,         with_CheckAccess,
1855     with_ThisObject,        NATIVE_DROP_PROPERTY,
1856     NULL,                   NULL,
1857     NULL,                   NULL,
1858     js_SetProtoOrParent,    js_SetProtoOrParent,
1859     js_Mark,                js_Clear,
1860     NULL,                   NULL
1861 };
1862
1863 static JSObjectOps *
1864 with_getObjectOps(JSContext *cx, JSClass *clasp)
1865 16 {
1866 16     return &js_WithObjectOps;
1867 }
1868
1869 JSClass js_WithClass = {
1870     "With",
1871     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1),
1872     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
1873     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
1874     with_getObjectOps,
1875     0,0,0,0,0,0,0
1876 };
1877
1878 JSObject *
1879 js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
1880 0 {
1881 0     JSObject *obj;
1882
1883 0     obj = js_NewObject(cx, &js_WithClass, proto, parent);
1884 0     if (!obj)
1885 0         return NULL;
1886 0     obj->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(cx->fp);
1887 0     OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
1888 0     return obj;
1889 }
1890
1891 #if JS_HAS_OBJ_PROTO_PROP
1892 static JSBool
1893 With(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1894 0 {
1895 0     JSObject *parent, *proto;
1896 0     jsval v;
1897
1898 0     if (!JS_ReportErrorFlagsAndNumber(cx,
1899                                       JSREPORT_WARNING | JSREPORT_STRICT,
1900                                       js_GetErrorMessage, NULL,
1901                                       JSMSG_DEPRECATED_USAGE,
1902                                       js_WithClass.name)) {
1903 0         return JS_FALSE;
1904     }
1905
1906 0     if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
1907 0         obj = js_NewWithObject(cx, NULL, NULL, -1);
1908 0         if (!obj)
1909 0             return JS_FALSE;
1910 0         *rval = OBJECT_TO_JSVAL(obj);
1911     }
1912
1913 0     parent = cx->fp->scopeChain;
1914 0     if (argc > 0) {
1915 0         if (!js_ValueToObject(cx, argv[0], &proto))
1916 0             return JS_FALSE;
1917 0         v = OBJECT_TO_JSVAL(proto);
1918 0         if (!obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PROTO), &v))
1919 0             return JS_FALSE;
1920 0         if (argc > 1) {
1921 0             if (!js_ValueToObject(cx, argv[1], &parent))
1922 0                 return JS_FALSE;
1923         }
1924     }
1925 0     v = OBJECT_TO_JSVAL(parent);
1926 0     return obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PARENT), &v);
1927 }
1928 #endif
1929
1930 JSObject *
1931 js_InitObjectClass(JSContext *cx, JSObject *obj)
1932 16 {
1933 16     JSObject *proto;
1934 16     jsval eval;
1935
1936 #if JS_HAS_SHARP_VARS
1937 16     JS_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >= ATOM_INDEX_LIMIT_LOG2 + 1);
1938 #endif
1939
1940 16     proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1,
1941                          object_props, object_methods, NULL, NULL);
1942 16     if (!proto)
1943 0         return NULL;
1944
1945 #if JS_HAS_OBJ_PROTO_PROP
1946 16     if (!JS_InitClass(cx, obj, NULL, &js_WithClass, With, 0,
1947                       NULL, NULL, NULL, NULL)) {
1948 0         return NULL;
1949     }
1950 #endif
1951
1952     /* ECMA (15.1.2.1) says 'eval' is also a property of the global object. */
1953 16     if (!OBJ_GET_PROPERTY(cx, proto,
1954                           ATOM_TO_JSID(cx->runtime->atomState.evalAtom),
1955                           &eval)) {
1956 0         return NULL;
1957     }
1958 16     if (!OBJ_DEFINE_PROPERTY(cx, obj,
1959                              ATOM_TO_JSID(cx->runtime->atomState.evalAtom),
1960                              eval, NULL, NULL, 0, NULL)) {
1961 0         return NULL;
1962     }
1963
1964 16     return proto;
1965 }
1966
1967 void
1968 js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops,
1969                  JSClass *clasp)
1970 144713 {
1971 144713     map->nrefs = nrefs;
1972 144713     map->ops = ops;
1973 144713     map->nslots = JS_INITIAL_NSLOTS;
1974 144713     map->freeslot = JSSLOT_FREE(clasp);
1975 }
1976
1977 JSObjectMap *
1978 js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,
1979                 JSClass *clasp, JSObject *obj)
1980 43508 {
1981 43508     return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj);
1982 }
1983
1984 void
1985 js_DestroyObjectMap(JSContext *cx, JSObjectMap *map)
1986 144713 {
1987 144713     js_DestroyScope(cx, (JSScope *)map);
1988 }
1989
1990 JSObjectMap *
1991 js_HoldObjectMap(JSContext *cx, JSObjectMap *map)
1992 289972 {
1993 289972     JS_ASSERT(map->nrefs >= 0);
1994 289972     JS_ATOMIC_INCREMENT(&map->nrefs);
1995 289972     return map;
1996 }
1997
1998 JSObjectMap *
1999 js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj)
2000 333480 {
2001 333480     JS_ASSERT(map->nrefs > 0);
2002 333480     JS_ATOMIC_DECREMENT(&map->nrefs);
2003 333480     if (map->nrefs == 0) {
2004 144713         map->ops->destroyObjectMap(cx, map);
2005 144713         return NULL;
2006     }
2007 188767     if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj)
2008 50         ((JSScope *)map)->object = NULL;
2009 188767     return map;
2010 }
2011
2012 static JSBool
2013 GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
2014                   JSObject **protop);
2015
2016 static jsval *
2017 AllocSlots(JSContext *cx, jsval *slots, uint32 nslots)
2018 470689 {
2019 470689     size_t nbytes, obytes, minbytes;
2020 470689     uint32 i, oslots;
2021 470689     jsval *newslots;
2022
2023 470689     nbytes = (nslots + 1) * sizeof(jsval);
2024 470689     if (slots) {
2025 238414         oslots = slots[-1];
2026 238414         obytes = (oslots + 1) * sizeof(jsval);
2027     } else {
2028 232275         oslots = 0;
2029 232275         obytes = 0;
2030     }
2031
2032 470689     if (nbytes <= GC_NBYTES_MAX) {
2033 421933         newslots = (jsval *) js_NewGCThing(cx, GCX_PRIVATE, nbytes);
2034     } else {
2035 48756         newslots = (jsval *)
2036                    JS_realloc(cx,
2037                               (obytes <= GC_NBYTES_MAX) ? NULL : slots - 1,
2038                               nbytes);
2039     }
2040 470689     if (!newslots)
2041 0         return NULL;
2042
2043 470689     if (obytes != 0) {
2044         /* If either nbytes or obytes fit in a GC-thing, we must copy. */
2045 238414         minbytes = JS_MIN(nbytes, obytes);
2046 238414         if (minbytes <= GC_NBYTES_MAX)
2047 237816             memcpy(newslots + 1, slots, minbytes - sizeof(jsval));
2048
2049         /* If nbytes are in a GC-thing but obytes aren't, free obytes. */
2050 238414         if (nbytes <= GC_NBYTES_MAX && obytes > GC_NBYTES_MAX)
2051 0             JS_free(cx, slots - 1);
2052
2053         /* If we're extending an allocation, initialize free slots. */
2054 238414         if (nslots > oslots) {
2055 1467997             for (i = 1 + oslots; i <= nslots; i++)
2056 1229583                 newslots[i] = JSVAL_VOID;
2057         }
2058     }
2059
2060 470689     newslots[0] = nslots;
2061 470689     return ++newslots;
2062 }
2063
2064 static void
2065 FreeSlots(JSContext *cx, jsval *slots)
2066 232275 {
2067 232275     size_t nbytes;
2068
2069     /*
2070      * NB: We count on smaller GC-things being finalized before larger things
2071      * that become garbage during the same GC.  Without this assumption, we
2072      * couldn't load slots[-1] here without possibly loading a gcFreeList link
2073      * (see struct JSGCThing in jsgc.h).
2074      */
2075 232275     nbytes = (slots[-1] + 1) * sizeof(jsval);
2076 232275     if (nbytes > GC_NBYTES_MAX)
2077 48158         JS_free(cx, slots - 1);
2078 }
2079
2080 JSObject *
2081 js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
2082 232275 {
2083 232275     JSObject *obj;
2084 232275     JSObjectOps *ops;
2085 232275     JSObjectMap *map;
2086 232275     JSClass *protoclasp;
2087 232275     uint32 nslots, i;
2088 232275     jsval *newslots;
2089 232275     JSTempValueRooter tvr;
2090
2091     /* Bootstrap the ur-object, and make it the default prototype object. */
2092 232275     if (!proto) {
2093 121471         if (!GetClassPrototype(cx, parent, clasp->name, &proto))
2094 0             return NULL;
2095 121471         if (!proto && !GetClassPrototype(cx, parent, js_Object_str, &proto))
2096 0             return NULL;
2097     }
2098
2099     /* Always call the class's getObjectOps hook if it has one. */
2100 232275     ops = clasp->getObjectOps
2101           ? clasp->getObjectOps(cx, clasp)
2102           : &js_ObjectOps;
2103
2104     /*
2105      * Allocate a zeroed object from the GC heap.  Do this *after* any other
2106      * GC-thing allocations under GetClassPrototype or clasp->getObjectOps,
2107      * to avoid displacing the newborn root for obj.
2108      */
2109 232275     obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject));
2110 232275     if (!obj)
2111 0         return NULL;
2112
2113     /*
2114      * Root obj to prevent it from being killed.
2115      * AllocSlots can trigger a finalizer from a last-ditch GC calling
2116      * JS_ClearNewbornRoots. There's also the possibilty of things
2117      * happening under the objectHook call-out below.    
2118      */
2119 232275     JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(obj), &tvr);
2120
2121     /*
2122      * Share proto's map only if it has the same JSObjectOps, and only if
2123      * proto's class has the same private and reserved slots as obj's map
2124      * and class have.  We assume that if prototype and object are of the
2125      * same class, they always have the same number of computed reserved
2126      * slots (returned via clasp->reserveSlots); otherwise, prototype and
2127      * object classes must have the same (null or not) reserveSlots hook.
2128      */
2129 232275     if (proto &&
2130         (map = proto->map)->ops == ops &&
2131         ((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp ||
2132          (!((protoclasp->flags ^ clasp->flags) &
2133             (JSCLASS_HAS_PRIVATE |
2134              (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) &&
2135           protoclasp->reserveSlots == clasp->reserveSlots)))
2136     {
2137         /*
2138          * Default parent to the parent of the prototype, which was set from
2139          * the parent of the prototype's constructor.
2140          */
2141 188767         if (!parent)
2142 69311             parent = OBJ_GET_PARENT(cx, proto);
2143
2144         /* Share the given prototype's map. */
2145 188767         obj->map = js_HoldObjectMap(cx, map);
2146
2147         /* Ensure that obj starts with the minimum slots for clasp. */
2148 188767         nslots = JS_INITIAL_NSLOTS;
2149     } else {
2150         /* Leave parent alone.  Allocate a new map for obj. */
2151 43508         map = ops->newObjectMap(cx, 1, ops, clasp, obj);
2152 43508         if (!map)
2153 0             goto bad;
2154 43508         obj->map = map;
2155
2156         /* Let ops->newObjectMap set nslots so as to reserve slots. */
2157 43508         nslots = map->nslots;
2158     }
2159
2160     /* Allocate a slots vector, with a -1'st element telling its length. */
2161 232275     newslots = AllocSlots(cx, NULL, nslots);
2162 232275     if (!newslots) {
2163 0         js_DropObjectMap(cx, obj->map, obj);
2164 0         obj->map = NULL;
2165 0         goto bad;
2166     }
2167
2168     /* Set the proto, parent, and class properties. */
2169 232275     newslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
2170 232275     newslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);
2171 232275     newslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp);
2172
2173     /* Clear above JSSLOT_CLASS so the GC doesn't load uninitialized memory. */
2174 696825     for (i = JSSLOT_CLASS + 1; i < nslots; i++)
2175 464550         newslots[i] = JSVAL_VOID;
2176
2177     /* Store newslots after initializing all of 'em, just in case. */
2178 232275     obj->slots = newslots;
2179
2180 232275     if (cx->runtime->objectHook) {
2181 0         JS_KEEP_ATOMS(cx->runtime);
2182 0         cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData);
2183 0         JS_UNKEEP_ATOMS(cx->runtime);
2184     }
2185
2186 out:
2187 232275     JS_POP_TEMP_ROOT(cx, &tvr);
2188 232275     cx->newborn[GCX_OBJECT] = (JSGCThing *) obj;
2189 232275     return obj;
2190
2191 bad:
2192 0     obj = NULL;
2193 0     goto out;
2194 }
2195
2196 JSBool
2197 js_FindConstructor(JSContext *cx, JSObject *start, const char *name, jsval *vp)
2198 187268 {
2199 187268     JSAtom *atom;
2200 187268     JSObject *obj, *pobj;
2201 187268     JSProperty *prop;
2202 187268     JSScopeProperty *sprop;
2203
2204 187268     atom = js_Atomize(cx, name, strlen(name), 0);
2205 187268     if (!atom)
2206 0         return JS_FALSE;
2207
2208 187268     if (start || (cx->fp && (start = cx->fp->scopeChain) != NULL)) {
2209         /* Find the topmost object in the scope chain. */
2210 619880         do {
2211 619880             obj = start;
2212 619880             start = OBJ_GET_PARENT(cx, obj);
2213 619880         } while (start);
2214     } else {
2215 96         obj = cx->globalObject;
2216 96         if (!obj) {
2217 32             *vp = JSVAL_VOID;
2218 32             return JS_TRUE;
2219         }
2220     }
2221
2222 187236     JS_ASSERT(OBJ_IS_NATIVE(obj));
2223 187236     if (!js_LookupPropertyWithFlags(cx, obj, ATOM_TO_JSID(atom),
2224                                     JSRESOLVE_CLASSNAME, &pobj, &prop)) {
2225 0         return JS_FALSE;
2226     }
2227 187236     if (!prop)  {
2228 65740         *vp = JSVAL_VOID;
2229 65740         return JS_TRUE;
2230     }
2231
2232 121496     JS_ASSERT(OBJ_IS_NATIVE(pobj));
2233 121496     sprop = (JSScopeProperty *) prop;
2234 121496     JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
2235 121496     *vp = OBJ_GET_SLOT(cx, pobj, sprop->slot);
2236 121496     OBJ_DROP_PROPERTY(cx, pobj, prop);
2237 121496     return JS_TRUE;
2238 }
2239
2240 JSObject *
2241 js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
2242                    JSObject *parent, uintN argc, jsval *argv)
2243 86 {
2244 86     jsval cval, rval;
2245 86     JSTempValueRooter argtvr, tvr;
2246 86     JSObject *obj, *ctor;
2247
2248 86     JS_PUSH_TEMP_ROOT(cx, argc, argv, &argtvr);
2249
2250 86     if (!js_FindConstructor(cx, parent, clasp->name, &cval)) {
2251 0         JS_POP_TEMP_ROOT(cx, &argtvr);
2252 0         return NULL;
2253     }
2254 86     if (JSVAL_IS_PRIMITIVE(cval)) {
2255 0         js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
2256 0         JS_POP_TEMP_ROOT(cx, &argtvr);
2257 0         return NULL;
2258     }
2259
2260     /*
2261      * Protect cval in case a crazy getter for .prototype uproots it.  After
2262      * this point, all control flow must exit through label out with obj set.
2263      */
2264 86     JS_PUSH_SINGLE_TEMP_ROOT(cx, cval, &tvr);
2265
2266     /*
2267      * If proto or parent are NULL, set them to Constructor.prototype and/or
2268      * Constructor.__parent__, just like JSOP_NEW does.
2269      */
2270 86     ctor = JSVAL_TO_OBJECT(cval);
2271 86     if (!parent)
2272 86         parent = OBJ_GET_PARENT(cx, ctor);
2273 86     if (!proto) {
2274 86         if (!OBJ_GET_PROPERTY(cx, ctor,
2275                               ATOM_TO_JSID(cx->runtime->atomState
2276                                            .classPrototypeAtom),
2277                               &rval)) {
2278 0             obj = NULL;
2279 0             goto out;
2280         }
2281 86         if (JSVAL_IS_OBJECT(rval))
2282 86             proto = JSVAL_TO_OBJECT(rval);
2283     }
2284
2285 86     obj = js_NewObject(cx, clasp, proto, parent);
2286 86     if (!obj)
2287 0         goto out;
2288
2289 86     if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval))
2290 0         goto bad;
2291
2292 86     if (JSVAL_IS_PRIMITIVE(rval))
2293 0         goto out;
2294 86     obj = JSVAL_TO_OBJECT(rval);
2295
2296     /*
2297      * If the given class has both the JSCLASS_HAS_PRIVATE and the
2298      * JSCLASS_CONSTRUCT_PROTOTYPE flags, then the class should have its private
2299      * data set. If it doesn't, then it means the constructor was replaced, and
2300      * we should throw a typerr.
2301      */
2302 86     if (OBJ_GET_CLASS(cx, obj) != clasp ||
2303         (!(~clasp->flags & (JSCLASS_HAS_PRIVATE |
2304                             JSCLASS_CONSTRUCT_PROTOTYPE)) &&
2305          !JS_GetPrivate(cx, obj))) {
2306 0         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2307                              JSMSG_WRONG_CONSTRUCTOR, clasp->name);
2308 0         goto bad;
2309     }
2310
2311 out:
2312 86     JS_POP_TEMP_ROOT(cx, &tvr);
2313 86     JS_POP_TEMP_ROOT(cx, &argtvr);
2314 86     return obj;
2315
2316 bad:
2317 0     cx->newborn[GCX_OBJECT] = NULL;
2318 0     obj = NULL;
2319 0     goto out;
2320 }
2321
2322 void
2323 js_FinalizeObject(JSContext *cx, JSObject *obj)
2324 232275 {
2325 232275     JSObjectMap *map;
2326
2327     /* Cope with stillborn objects that have no map. */
2328 232275     map = obj->map;
2329 232275     if (!map)
2330 0         return;
2331 232275     JS_ASSERT(obj->slots);
2332
2333 232275     if (cx->runtime->objectHook)
2334 0         cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData);
2335
2336     /* Remove all watchpoints with weak links to obj. */
2337 232275     JS_ClearWatchPointsForObject(cx, obj);
2338
2339     /*
2340      * Finalize obj first, in case it needs map and slots.  Optimized to use
2341      * LOCKED_OBJ_GET_CLASS instead of OBJ_GET_CLASS, so we avoid "promoting"
2342      * obj's scope from lock-free to lock-full (see jslock.c:ClaimScope) when
2343      * we're called from the GC.  Only the GC should call js_FinalizeObject,
2344      * and no other threads run JS (and possibly racing to update obj->slots)
2345      * while the GC is running.
2346      */
2347 232275     LOCKED_OBJ_GET_CLASS(obj)->finalize(cx, obj);
2348
2349     /* Drop map and free slots. */
2350 232275     js_DropObjectMap(cx, map, obj);
2351 232275     obj->map = NULL;
2352 232275     FreeSlots(cx, obj->slots);
2353 232275     obj->slots = NULL;
2354 }
2355
2356 /* XXXbe if one adds props, deletes earlier props, adds more, the last added
2357          won't recycle the deleted props' slots. */
2358 JSBool
2359 js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)
2360 1201023 {
2361 1201023     JSObjectMap *map;
2362 1201023     JSClass *clasp;
2363 1201023     uint32 nslots;
2364 1201023     jsval *newslots;
2365
2366 1201023     map = obj->map;
2367 1201023     JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
2368 1201023     clasp = LOCKED_OBJ_GET_CLASS(obj);
2369 1201023     if (map->freeslot == JSSLOT_FREE(clasp)) {
2370         /* Adjust map->freeslot to include computed reserved slots, if any. */
2371 138992         if (clasp->reserveSlots)
2372 384             map->freeslot += clasp->reserveSlots(cx, obj);
2373     }
2374 1201023     nslots = map->nslots;
2375 1201023     if (map->freeslot >= nslots) {
2376 238414         nslots = map->freeslot;
2377 238414         JS_ASSERT(nslots >= JS_INITIAL_NSLOTS);
2378 238414         nslots += (nslots + 1) / 2;
2379
2380 238414         newslots = AllocSlots(cx, obj->slots, nslots);
2381 238414         if (!newslots)
2382 0             return JS_FALSE;
2383 238414         map->nslots = nslots;
2384 238414         obj->slots = newslots;
2385     }
2386
2387 #ifdef TOO_MUCH_GC
2388     obj->slots[map->freeslot] = JSVAL_VOID;
2389 #endif
2390 1201023     *slotp = map->freeslot++;
2391 1201023     return JS_TRUE;
2392 }
2393
2394 void
2395 js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
2396 0 {
2397 0     JSObjectMap *map;
2398 0     uint32 nslots;
2399 0     jsval *newslots;
2400
2401 0     OBJ_CHECK_SLOT(obj, slot);
2402 0     obj->slots[slot] = JSVAL_VOID;
2403 0     map = obj->map;
2404 0     JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
2405 0     if (map->freeslot == slot + 1)
2406 0         map->freeslot = slot;
2407 0     nslots = map->nslots;
2408 0     if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) {
2409 0         nslots = map->freeslot;
2410 0         nslots += nslots / 2;
2411 0         if (nslots < JS_INITIAL_NSLOTS)
2412 0             nslots = JS_INITIAL_NSLOTS;
2413
2414 0         newslots = AllocSlots(cx, obj->slots, nslots);
2415 0         if (!newslots)
2416 0             return;
2417 0         map->nslots = nslots;
2418 0         obj->slots = newslots;
2419     }
2420 }
2421
2422 #if JS_BUG_EMPTY_INDEX_ZERO
2423 #define CHECK_FOR_EMPTY_INDEX(id)                                             \
2424     JS_BEGIN_MACRO                                                            \
2425         if (JSSTRING_LENGTH(str_) == 0)                                       \
2426             id = JSVAL_ZERO;                                                  \
2427     JS_END_MACRO
2428 #else
2429 #define CHECK_FOR_EMPTY_INDEX(id) /* nothing */
2430 #endif
2431
2432 /* JSVAL_INT_MAX as a string */
2433 #define JSVAL_INT_MAX_STRING "1073741823"
2434
2435 #define CHECK_FOR_STRING_INDEX(id)                                            \
2436     JS_BEGIN_MACRO                                                            \
2437         if (JSID_IS_ATOM(id)) {                                               \
2438             JSAtom *atom_ = JSID_TO_ATOM(id);                                 \
2439             JSString *str_ = ATOM_TO_STRING(atom_);                           \
2440             const jschar *cp_ = str_->chars;                                  \
2441             JSBool negative_ = (*cp_ == '-');                                 \
2442             if (negative_) cp_++;                                             \
2443             if (JS7_ISDEC(*cp_)) {                                            \
2444                 size_t n_ = str_->length - negative_;                         \
2445                 if (n_ <= sizeof(JSVAL_INT_MAX_STRING) - 1)                   \
2446                     id = CheckForStringIndex(id, cp_, cp_ + n_, negative_);   \
2447             } else {                                                          \
2448                 CHECK_FOR_EMPTY_INDEX(id);                                    \
2449             }                                                                 \
2450         }                                                                     \
2451     JS_END_MACRO
2452
2453 static jsid
2454 CheckForStringIndex(jsid id, const jschar *cp, const jschar *end,
2455                     JSBool negative)
2456 35475 {
2457 35475     jsuint index = JS7_UNDEC(*cp++);
2458 35475     jsuint oldIndex = 0;
2459 35475     jsuint c = 0;
2460
2461 35475     if (index != 0) {
2462 9643         while (JS7_ISDEC(*cp)) {
2463 0             oldIndex = index;
2464 0             c = JS7_UNDEC(*cp);
2465 0             index = 10 * index + c;
2466 0             cp++;
2467         }
2468     }
2469 35475     if (cp == end &&
2470         (oldIndex < (JSVAL_INT_MAX / 10) ||
2471          (oldIndex == (JSVAL_INT_MAX / 10) &&
2472           c <= (JSVAL_INT_MAX % 10)))) {
2473 35475         if (negative)
2474 0             index = 0 - index;
2475 35475         id = INT_TO_JSID((jsint)index);
2476     }
2477 35475     return id;
2478 }
2479
2480 static JSBool
2481 HidePropertyName(JSContext *cx, jsid *idp)
2482 156343 {
2483 156343     jsid id;
2484 156343     JSAtom *atom, *hidden;
2485
2486 156343     id = *idp;
2487 156343     JS_ASSERT(JSID_IS_ATOM(id));
2488
2489 156343     atom = JSID_TO_ATOM(id);
2490 156343     JS_ASSERT(!(atom->flags & ATOM_HIDDEN));
2491 156343     JS_ASSERT(ATOM_IS_STRING(atom));
2492
2493 156343     hidden = js_AtomizeString(cx, ATOM_TO_STRING(atom), ATOM_HIDDEN);
2494 156343     if (!hidden)
2495 0         return JS_FALSE;
2496
2497     /*
2498      * Link hidden to unhidden atom to optimize call_enumerate -- this means
2499      * the GC must mark a hidden atom's unhidden counterpart (see js_MarkAtom
2500      * in jsgc.c).  It overloads the entry.value member, which for unhidden
2501      * atoms may point to keyword information.
2502      */
2503 156343     hidden->entry.value = atom;
2504 156343     *idp = ATOM_TO_JSID(hidden);
2505 156343     return JS_TRUE;
2506 }
2507
2508 JSScopeProperty *
2509 js_AddHiddenProperty(JSContext *cx, JSObject *obj, jsid id,
2510                      JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
2511                      uintN attrs, uintN flags, intN shortid)
2512 21696 {
2513 21696     if (!HidePropertyName(cx, &id))
2514 0         return NULL;
2515
2516 21696     flags |= SPROP_IS_HIDDEN;
2517 21696     return js_AddNativeProperty(cx, obj, id, getter, setter, slot, attrs,
2518                                 flags, shortid);
2519 }
2520
2521 JSBool
2522 js_LookupHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
2523                         JSProperty **propp)
2524 134647 {
2525 134647     return HidePropertyName(cx, &id) &&
2526            js_LookupProperty(cx, obj, id, objp, propp);
2527 }
2528
2529 JSScopeProperty *
2530 js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
2531                      JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
2532                      uintN attrs, uintN flags, intN shortid)
2533 21808 {
2534 21808     JSScope *scope;
2535 21808     JSScopeProperty *sprop;
2536
2537 21808     JS_LOCK_OBJ(cx, obj);
2538 21808     scope = js_GetMutableScope(cx, obj);
2539 21808     if (!scope) {
2540 0         sprop = NULL;
2541     } else {
2542         /*
2543          * Handle old bug that took empty string as zero index.  Also convert
2544          * string indices to integers if appropriate.
2545          */
2546 21808         CHECK_FOR_STRING_INDEX(id);
2547 21808         sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs,
2548                                     flags, shortid);
2549     }
2550 21808     JS_UNLOCK_OBJ(cx, obj);
2551 21808     return sprop;
2552 }
2553
2554 JSScopeProperty *
2555 js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
2556                              JSScopeProperty *sprop, uintN attrs, uintN mask,
2557                              JSPropertyOp getter, JSPropertyOp setter)
2558 1184 {
2559 1184     JSScope *scope;
2560
2561 1184     JS_LOCK_OBJ(cx, obj);
2562 1184     scope = js_GetMutableScope(cx, obj);
2563 1184     if (!scope) {
2564 0         sprop = NULL;
2565     } else {
2566 1184         sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, attrs, mask,
2567                                             getter, setter);
2568 1184         if (sprop) {
2569 1184             PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, sprop->id,
2570                                 sprop);
2571         }
2572     }
2573 1184     JS_UNLOCK_OBJ(cx, obj);
2574 1184     return sprop;
2575 }
2576
2577 JSBool
2578 js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
2579                   JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
2580                   JSProperty **propp)
2581 112356 {
2582 112356     return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs,
2583                                    0, 0, propp);
2584 }
2585
2586 /*
2587  * Backward compatibility requires allowing addProperty hooks to mutate the
2588  * nominal initial value of a slot-full property, while GC safety wants that
2589  * value to be stored before the call-out through the hook.  Optimize to do
2590  * both while saving cycles for classes that stub their addProperty hook.
2591  */
2592 #define ADD_PROPERTY_HELPER(cx,clasp,obj,scope,sprop,vp,cleanup)              \
2593     JS_BEGIN_MACRO                                                            \
2594         if ((clasp)->addProperty != JS_PropertyStub) {                        \
2595             jsval nominal_ = *(vp);                                           \
2596             if (!(clasp)->addProperty(cx, obj, SPROP_USERID(sprop), vp)) {    \
2597                 cleanup;                                                      \
2598             }                                                                 \
2599             if (*(vp) != nominal_) {                                          \
2600                 if (SPROP_HAS_VALID_SLOT(sprop, scope))                       \
2601                     LOCKED_OBJ_SET_SLOT(obj, (sprop)->slot, *(vp));           \
2602             }                                                                 \
2603         }                                                                     \
2604     JS_END_MACRO
2605
2606 JSBool
2607 js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
2608                         JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
2609                         uintN flags, intN shortid, JSProperty **propp)
2610 1084886 {
2611 1084886     JSClass *clasp;
2612 1084886     JSScope *scope;
2613 1084886     JSProperty *prop;
2614 1084886     JSScopeProperty *sprop;
2615
2616     /*
2617      * Handle old bug that took empty string as zero index.  Also convert
2618      * string indices to integers if appropriate.
2619      */
2620 1084886     CHECK_FOR_STRING_INDEX(id);
2621
2622 #if JS_HAS_GETTER_SETTER
2623     /*
2624      * If defining a getter or setter, we must check for its counterpart and
2625      * update the attributes and property ops.  A getter or setter is really
2626      * only half of a property.
2627      */
2628 1084886     if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
2629 0         JSObject *pobj;
2630
2631         /*
2632          * If JS_THREADSAFE and id is found, js_LookupProperty returns with
2633          * sprop non-null and pobj locked.  If pobj == obj, the property is
2634          * already in obj and obj has its own (mutable) scope.  So if we are
2635          * defining a getter whose setter was already defined, or vice versa,
2636          * finish the job via js_ChangeScopePropertyAttributes, and refresh
2637          * the property cache line for (obj, id) to map sprop.
2638          */
2639 0         if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
2640 0             return JS_FALSE;
2641 0         sprop = (JSScopeProperty *) prop;
2642 0         if (sprop &&
2643             pobj == obj &&
2644             (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
2645 0             sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(obj), sprop,
2646                                                 attrs, sprop->attrs,
2647                                                 (attrs & JSPROP_GETTER)
2648                                                 ? getter
2649                                                 : sprop->getter,
2650                                                 (attrs & JSPROP_SETTER)
2651                                                 ? setter
2652                                                 : sprop->setter);
2653
2654             /* NB: obj == pobj, so we can share unlock code at the bottom. */
2655 0             if (!sprop)
2656 0                 goto bad;
2657 0             goto out;
2658         }
2659
2660 0         if (prop) {
2661             /* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */
2662 0             OBJ_DROP_PROPERTY(cx, pobj, prop);
2663 0             prop = NULL;
2664         }
2665     }
2666 #endif /* JS_HAS_GETTER_SETTER */
2667
2668     /* Lock if object locking is required by this implementation. */
2669 1084886     JS_LOCK_OBJ(cx, obj);
2670
2671     /* Use the object's class getter and setter by default. */
2672 1084886     clasp = LOCKED_OBJ_GET_CLASS(obj);
2673 1084886     if (!getter)
2674 1038162         getter = clasp->getProperty;
2675 1084886     if (!setter)
2676 1038178         setter = clasp->setProperty;
2677
2678     /* Get obj's own scope if it has one, or create a new one for obj. */
2679 1084886     scope = js_GetMutableScope(cx, obj);
2680 1084886     if (!scope)
2681 0         goto bad;
2682
2683     /* Add the property to scope, or replace an existing one of the same id. */
2684 1084886     if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
2685 0         attrs |= JSPROP_SHARED;
2686 1084886     sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
2687                                 SPROP_INVALID_SLOT, attrs, flags, shortid);
2688 1084886     if (!sprop)
2689 0         goto bad;
2690
2691     /* Store value before calling addProperty, in case the latter GC's. */
2692 1084886     if (SPROP_HAS_VALID_SLOT(sprop, scope))
2693 1084406         LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value);
2694
2695     /* XXXbe called with lock held */
2696 1084886     ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value,
2697                         js_RemoveScopeProperty(cx, scope, id);
2698                         goto bad);
2699
2700 #if JS_HAS_GETTER_SETTER
2701 out:
2702 #endif
2703 1084886     PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
2704 1084886     if (propp)
2705 4581         *propp = (JSProperty *) sprop;
2706     else
2707 1084886         JS_UNLOCK_OBJ(cx, obj);
2708 1084886     return JS_TRUE;
2709
2710 bad:
2711 0     JS_UNLOCK_OBJ(cx, obj);
2712 0     return JS_FALSE;
2713 }
2714
2715 /*
2716  * Given pc pointing after a property accessing bytecode, return true if the
2717  * access is a "object-detecting" in the sense used by web pages, e.g., when
2718  * checking whether document.all is defined.
2719  */
2720 static JSBool
2721 Detecting(JSContext *cx, jsbytecode *pc)
2722 46525 {
2723 46525     JSScript *script;
2724 46525     jsbytecode *endpc;
2725 46525     JSOp op;
2726 46525     JSAtom *atom;
2727
2728 46525     if (!cx->fp)
2729 0         return JS_FALSE;
2730 46525     script = cx->fp->script;
2731 46525     for (endpc = script->code + script->length; pc < endpc; pc++) {
2732         /* General case: a branch or equality op follows the access. */
2733 46525         op = (JSOp) *pc;
2734 46525         if (js_CodeSpec[op].format & JOF_DETECTING)
2735 0             return JS_TRUE;
2736
2737         /*
2738          * Special case #1: handle (document.all == null).  Don't sweat about
2739          * JS1.2's revision of the equality operators here.
2740          */
2741 46525         if (op == JSOP_NULL) {
2742 0             if (++pc < endpc)
2743 0                 return *pc == JSOP_EQ || *pc == JSOP_NE;
2744 46525             break;
2745         }
2746
2747         /*
2748          * Special case #2: handle (document.all == undefined).  Don't worry
2749          * about someone redefining undefined, which was added by Edition 3,
2750          * so was read/write for backward compatibility.
2751          */
2752 46525         if (op == JSOP_NAME) {
2753 635             atom = GET_ATOM(cx, script, pc);
2754 635             if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] &&
2755                 (pc += js_CodeSpec[op].length) < endpc) {
2756 635                 op = (JSOp) *pc;
2757 635                 return op == JSOP_EQ || op == JSOP_NE ||
2758                        op == JSOP_NEW_EQ || op == JSOP_NEW_NE;
2759             }
2760 45890             break;
2761         }
2762
2763         /* At this point, anything but grouping means we're not detecting. */
2764 45890         if (op != JSOP_GROUP)
2765 45890             break;
2766     }
2767 45890     return JS_FALSE;
2768 }
2769
2770 JS_FRIEND_API(JSBool)
2771 js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
2772                   JSProperty **propp)
2773 1312195 {
2774 1312195     return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp);
2775 }
2776
2777 JSBool
2778 js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
2779                            JSObject **objp, JSProperty **propp)
2780 1499431 {
2781 1499431     JSObject *start, *obj2, *proto;
2782 1499431     JSScope *scope;
2783 1499431     JSScopeProperty *sprop;
2784 1499431     JSClass *clasp;
2785 1499431     JSResolveOp resolve;
2786 1499431     JSResolvingKey key;
2787 1499431     JSResolvingEntry *entry;
2788 1499431     uint32 generation;
2789 1499431     JSNewResolveOp newresolve;
2790 1499431     jsbytecode *pc;
2791 1499431     const JSCodeSpec *cs;
2792 1499431     uint32 format;
2793 1499431     JSBool ok;
2794
2795     /*
2796      * Handle old bug that took empty string as zero index.  Also convert
2797      * string indices to integers if appropriate.
2798      */
2799 1499431     CHECK_FOR_STRING_INDEX(id);
2800
2801     /* Search scopes starting with obj and following the prototype link. */
2802 1499431     start = obj;
2803 2724447     for (;;) {
2804 2111939         JS_LOCK_OBJ(cx, obj);
2805 2111939         scope = OBJ_SCOPE(obj);
2806 2111939         if (scope->object == obj) {
2807 2014571             sprop = SCOPE_GET_PROPERTY(scope, id);
2808         } else {
2809             /* Shared prototype scope: try resolve before lookup. */
2810 97368             sprop = NULL;
2811         }
2812
2813         /* Try obj's class resolve hook if id was not found in obj's scope. */
2814 2111939         if (!sprop) {
2815 950861             clasp = LOCKED_OBJ_GET_CLASS(obj);
2816 950861             resolve = clasp->resolve;
2817 950861             if (resolve != JS_ResolveStub) {
2818                 /* Avoid recursion on (obj, id) already being resolved on cx. */
2819 280512                 key.obj = obj;
2820 280512                 key.id = id;
2821
2822                 /*
2823                  * Once we have successfully added an entry for (obj, key) to
2824                  * cx->resolvingTable, control must go through cleanup: before
2825                  * returning.  But note that JS_DHASH_ADD may find an existing
2826                  * entry, in which case we bail to suppress runaway recursion.
2827                  */
2828 280512                 if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) {
2829 0                     JS_UNLOCK_OBJ(cx, obj);
2830 0                     return JS_FALSE;
2831                 }
2832 280512                 if (!entry) {
2833                     /* Already resolving id in obj -- dampen recursion. */
2834 0                     JS_UNLOCK_OBJ(cx, obj);
2835 0                     goto out;
2836                 }
2837 280512                 generation = cx->resolvingTable->generation;
2838
2839                 /* Null *propp here so we can test it at cleanup: safely. */
2840 280512                 *propp = NULL;
2841
2842 280512                 if (clasp->flags & JSCLASS_NEW_RESOLVE) {
2843 236243                     newresolve = (JSNewResolveOp)resolve;
2844 236243                     if (!(flags & JSRESOLVE_CLASSNAME) &&
2845                         cx->fp &&
2846                         (pc = cx->fp->pc)) {
2847 46567                         cs = &js_CodeSpec[*pc];
2848 46567                         format = cs->format;
2849 46567                         if ((format & JOF_MODEMASK) != JOF_NAME)
2850 46567                             flags |= JSRESOLVE_QUALIFIED;
2851 46567                         if ((format & JOF_ASSIGNING) ||
2852                             (cx->fp->flags & JSFRAME_ASSIGNING)) {
2853 42                             flags |= JSRESOLVE_ASSIGNING;
2854                         } else {
2855 46525                             pc += cs->length;
2856 46525                             if (Detecting(cx, pc))
2857 635                                 flags |= JSRESOLVE_DETECTING;
2858                         }
2859 46567                         if (format & JOF_DECLARING)
2860 0                             flags |= JSRESOLVE_DECLARING;
2861                     }
2862 236243                     obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START)
2863                            ? start
2864                            : NULL;
2865 236243                     JS_UNLOCK_OBJ(cx, obj);
2866
2867                     /* Protect id and all atoms from a GC nested in resolve. */
2868 236243                     JS_KEEP_ATOMS(cx->runtime);
2869 236243                     ok = newresolve(cx, obj, ID_TO_VALUE(id), flags, &obj2);
2870 236243                     JS_UNKEEP_ATOMS(cx->runtime);
2871 236243                     if (!ok)
2872 0                         goto cleanup;
2873
2874 236243                     JS_LOCK_OBJ(cx, obj);
2875 236243                     if (obj2) {
2876                         /* Resolved: juggle locks and lookup id again. */
2877 255                         if (obj2 != obj) {
2878 255                             JS_UNLOCK_OBJ(cx, obj);
2879 255                             JS_LOCK_OBJ(cx, obj2);
2880                         }
2881 255                         scope = OBJ_SCOPE(obj2);
2882 255                         if (!MAP_IS_NATIVE(&scope->map)) {
2883                             /* Whoops, newresolve handed back a foreign obj2. */
2884 0                             JS_ASSERT(obj2 != obj);
2885 0                             JS_UNLOCK_OBJ(cx, obj2);
2886 0                             ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp);
2887 0                             if (!ok || *propp)
2888 255                                 goto cleanup;
2889 255                             JS_LOCK_OBJ(cx, obj2);
2890                         } else {
2891                             /*
2892                              * Require that obj2 have its own scope now, as we
2893                              * do for old-style resolve.  If it doesn't, then
2894                              * id was not truly resolved, and we'll find it in
2895                              * the proto chain, or miss it if obj2's proto is
2896                              * not on obj's proto chain.  That last case is a
2897                              * "too bad!" case.
2898                              */
2899 255                             if (scope->object == obj2)
2900 255                                 sprop = SCOPE_GET_PROPERTY(scope, id);
2901                         }
2902 255                         if (sprop) {
2903 255                             JS_ASSERT(obj2 == scope->object);
2904 255                             obj = obj2;
2905 0                         } else if (obj2 != obj) {
2906 0                             JS_UNLOCK_OBJ(cx, obj2);
2907 0                             JS_LOCK_OBJ(cx, obj);
2908                         }
2909                     }
2910                 } else {
2911                     /*
2912                      * Old resolve always requires id re-lookup if obj owns
2913                      * its scope after resolve returns.
2914                      */
2915 44269                     JS_UNLOCK_OBJ(cx, obj);
2916 44269                     ok = resolve(cx, obj, ID_TO_VALUE(id));
2917 44269                     if (!ok)
2918 0                         goto cleanup;
2919 44269                     JS_LOCK_OBJ(cx, obj);
2920 44269                     scope = OBJ_SCOPE(obj);
2921 44269                     JS_ASSERT(MAP_IS_NATIVE(&scope->map));
2922 44269                     if (scope->object == obj)
2923 44269                         sprop = SCOPE_GET_PROPERTY(scope, id);
2924                 }
2925
2926             cleanup:
2927 280512                 js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
2928 280512                 if (!ok || *propp)
2929 0                     return ok;
2930             }
2931         }
2932
2933 2111939         if (sprop) {
2934 1205602             JS_ASSERT(OBJ_SCOPE(obj) == scope);
2935 1205602             *objp = scope->object;      /* XXXbe hide in jsscope.[ch] */
2936
2937 1205602             *propp = (JSProperty *) sprop;
2938 1205602             return JS_TRUE;
2939         }
2940
2941 906337         proto = LOCKED_OBJ_GET_PROTO(obj);
2942 906337         JS_UNLOCK_OBJ(cx, obj);
2943 906337         if (!proto)
2944 293829             break;
2945 612508         if (!OBJ_IS_NATIVE(proto))
2946 0             return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
2947 612508         obj = proto;
2948     }
2949
2950 out:
2951 293829     *objp = NULL;
2952 293829     *propp = NULL;
2953 293829     return JS_TRUE;
2954 }
2955
2956 JS_FRIEND_API(JSBool)
2957 js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
2958                 JSProperty **propp)
2959 1390545 {
2960 1390545     JSRuntime *rt;
2961 1390545     JSObject *obj, *pobj, *lastobj;
2962 1390545     JSScopeProperty *sprop;
2963 1390545     JSProperty *prop;
2964
2965 1390545     rt = cx->runtime;
2966 1390545     obj = cx->fp->scopeChain;
2967 1390545     do {
2968         /* Try the property cache and return immediately on cache hit. */
2969 1390545         if (OBJ_IS_NATIVE(obj)) {
2970 1390545             JS_LOCK_OBJ(cx, obj);
2971 1390545             PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop);
2972 1390545             if (sprop) {
2973 1305748                 JS_ASSERT(OBJ_IS_NATIVE(obj));
2974 1305748                 *objp = obj;
2975 1305748                 *pobjp = obj;
2976 1305748                 *propp = (JSProperty *) sprop;
2977 1305748                 return JS_TRUE;
2978             }
2979 84797             JS_UNLOCK_OBJ(cx, obj);
2980         }
2981
2982         /* If cache miss, take the slow path. */
2983 84797         if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
2984 0             return JS_FALSE;
2985 84797         if (prop) {
2986 84700             if (OBJ_IS_NATIVE(pobj)) {
2987 84700                 sprop = (JSScopeProperty *) prop;
2988 84700                 PROPERTY_CACHE_FILL(&rt->propertyCache, pobj, id, sprop);
2989             }
2990 84700             *objp = obj;
2991 84700             *pobjp = pobj;
2992 84700             *propp = prop;
2993 84700             return JS_TRUE;
2994         }
2995 97         lastobj = obj;
2996 97     } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);
2997
2998 97     *objp = lastobj;
2999 97     *pobjp = NULL;
3000 97     *propp = NULL;
3001 97     return JS_TRUE;
3002 }
3003
3004 JSObject *
3005 js_FindIdentifierBase(JSContext *cx, jsid id)
3006 90223 {
3007 90223     JSObject *obj, *pobj;
3008 90223     JSProperty *prop;
3009
3010     /*
3011      * Look for id's property along the "with" statement chain and the
3012      * statically-linked scope chain.
3013      */
3014 90223     if (!js_FindProperty(cx, id, &obj, &pobj, &prop))
3015 0         return NULL;
3016 90223     if (prop) {
3017 90126         OBJ_DROP_PROPERTY(cx, pobj, prop);
3018 90126         return obj;
3019     }
3020
3021     /*
3022      * Use the top-level scope from the scope chain, which won't end in the
3023      * same scope as cx->globalObject for cross-context function calls.
3024      */
3025 97     JS_ASSERT(obj);
3026
3027     /*
3028      * Property not found.  Give a strict warning if binding an undeclared
3029      * top-level variable.
3030      */
3031 97     if (JS_HAS_STRICT_OPTION(cx)) {
3032 0         JSString *str = JSVAL_TO_STRING(ID_TO_VALUE(id));
3033 0         if (!JS_ReportErrorFlagsAndNumber(cx,
3034                                           JSREPORT_WARNING | JSREPORT_STRICT,
3035                                           js_GetErrorMessage, NULL,
3036                                           JSMSG_UNDECLARED_VAR,
3037                                           JS_GetStringBytes(str))) {
3038 0             return NULL;
3039         }
3040     }
3041 97     return obj;
3042 }
3043
3044 JSBool
3045 js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
3046 799313 {
3047 799313     JSObject *obj2;
3048 799313     JSProperty *prop;
3049 799313     JSScope *scope;
3050 799313     JSScopeProperty *sprop;
3051 799313     uint32 slot;
3052
3053     /*
3054      * Handle old bug that took empty string as zero index.  Also convert
3055      * string indices to integers if appropriate.
3056      */
3057 799313     CHECK_FOR_STRING_INDEX(id);
3058
3059 799313     if (!js_LookupProperty(cx, obj, id, &obj2, &prop))
3060 0         return JS_FALSE;
3061 799313     if (!prop) {
3062 23771         jsval default_val;
3063
3064 #if JS_BUG_NULL_INDEX_PROPS
3065         /* Indexed properties defaulted to null in old versions. */
3066         default_val = (JSID_IS_INT(id) && JSID_TO_INT(id) >= 0)
3067                       ? JSVAL_NULL
3068                       : JSVAL_VOID;
3069 #else
3070 23771         default_val = JSVAL_VOID;
3071 #endif
3072 23771         *vp = default_val;
3073
3074 23771         if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp))
3075 0             return JS_FALSE;
3076
3077         /*
3078          * Give a strict warning if foo.bar is evaluated by a script for an
3079          * object foo with no property named 'bar'.
3080          */
3081 23771         if (JS_HAS_STRICT_OPTION(cx) &&
3082             *vp == default_val &&
3083             cx->fp && cx->fp->pc &&
3084             (*cx->fp->pc == JSOP_GETPROP || *cx->fp->pc == JSOP_GETELEM))
3085         {
3086 0             jsbytecode *pc;
3087 0             JSString *str;
3088
3089             /* Kludge to allow (typeof foo == "undefined") tests. */
3090 0             JS_ASSERT(cx->fp->script);
3091 0             pc = cx->fp->pc;
3092 0             pc += js_CodeSpec[*pc].length;
3093 0             if (Detecting(cx, pc))
3094 0                 return JS_TRUE;
3095
3096             /* Ok, bad undefined property reference: whine about it. */
3097 0             str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
3098                                              ID_TO_VALUE(id), NULL);
3099 0             if (!str ||
3100                 !JS_ReportErrorFlagsAndNumber(cx,
3101                                               JSREPORT_WARNING|JSREPORT_STRICT,
3102                                               js_GetErrorMessage, NULL,
3103                                               JSMSG_UNDEFINED_PROP,
3104                                               JS_GetStringBytes(str))) {
3105 0                 return JS_FALSE;
3106             }
3107         }
3108 23771         return JS_TRUE;
3109     }
3110
3111 775542     if (!OBJ_IS_NATIVE(obj2)) {
3112 0         OBJ_DROP_PROPERTY(cx, obj2, prop);
3113 0         return OBJ_GET_PROPERTY(cx, obj2, id, vp);
3114     }
3115
3116     /* Unlock obj2 before calling getter, relock after to avoid deadlock. */
3117 775542     scope = OBJ_SCOPE(obj2);
3118 775542     sprop = (JSScopeProperty *) prop;
3119 775542     slot = sprop->slot;
3120 775542     if (slot != SPROP_INVALID_SLOT) {
3121 775498         JS_ASSERT(slot < obj2->map->freeslot);
3122 775498         *vp = LOCKED_OBJ_GET_SLOT(obj2, slot);
3123
3124         /* If sprop has a stub getter, we're done. */
3125 775498         if (!sprop->getter)
3126 369707             goto out;
3127     } else {
3128 44         *vp = JSVAL_VOID;
3129     }
3130
3131 405835     JS_UNLOCK_SCOPE(cx, scope);
3132 405835     if (!SPROP_GET(cx, sprop, obj, obj2, vp))
3133 0         return JS_FALSE;
3134 405835     JS_LOCK_SCOPE(cx, scope);
3135
3136 405835     if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
3137 405791         LOCKED_OBJ_SET_SLOT(obj2, slot, *vp);
3138 405791         PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj2, id, sprop);
3139     }
3140
3141 out:
3142 775542     JS_UNLOCK_SCOPE(cx, scope);
3143 775542     return JS_TRUE;
3144 }
3145
3146 JSBool
3147 js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
3148 249340 {
3149 249340     JSObject *pobj;
3150 249340     JSProperty *prop;
3151 249340     JSScopeProperty *sprop;
3152 249340     JSScope *scope;
3153 249340     uintN attrs, flags;
3154 249340     intN shortid;
3155 249340     JSClass *clasp;
3156 249340     JSPropertyOp getter, setter;
3157 249340     jsval pval;
3158 249340     uint32 slot;
3159
3160     /*
3161      * Handle old bug that took empty string as zero index.  Also convert
3162      * string indices to integers if appropriate.
3163      */
3164 249340     CHECK_FOR_STRING_INDEX(id);
3165
3166 249340     if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
3167 0         return JS_FALSE;
3168
3169 249340     if (prop && !OBJ_IS_NATIVE(pobj)) {
3170 0         OBJ_DROP_PROPERTY(cx, pobj, prop);
3171 0         prop = NULL;
3172     }
3173 249340     sprop = (JSScopeProperty *) prop;
3174
3175     /*
3176      * Now either sprop is null, meaning id was not found in obj or one of its
3177      * prototypes; or sprop is non-null, meaning id was found in pobj's scope.
3178      * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop
3179      * is held: we must OBJ_DROP_PROPERTY or JS_UNLOCK_SCOPE before we return
3180      * (the two are equivalent for native objects, but we use JS_UNLOCK_SCOPE
3181      * because it is cheaper).
3182      */
3183 249340     attrs = JSPROP_ENUMERATE;
3184 249340     flags = 0;
3185 249340     shortid = 0;
3186 249340     clasp = OBJ_GET_CLASS(cx, obj);
3187 249340     getter = clasp->getProperty;
3188 249340     setter = clasp->setProperty;
3189
3190 249340     if (sprop) {
3191         /*
3192          * Set scope for use below.  It was locked by js_LookupProperty, and
3193          * we know pobj owns it (i.e., scope->object == pobj).  Therefore we
3194          * optimize JS_UNLOCK_OBJ(cx, pobj) into JS_UNLOCK_SCOPE(cx, scope).
3195          */
3196 124011         scope = OBJ_SCOPE(pobj);
3197
3198 124011         attrs = sprop->attrs;
3199 124011         if ((attrs & JSPROP_READONLY) ||
3200             (SCOPE_IS_SEALED(scope) && pobj == obj)) {
3201 0             JS_UNLOCK_SCOPE(cx, scope);
3202 0             if ((attrs & JSPROP_READONLY) && JS_VERSION_IS_ECMA(cx))
3203 0                 return JS_TRUE;
3204 124011             goto read_only_error;
3205         }
3206
3207 124011         if (pobj != obj) {
3208             /*
3209              * We found id in a prototype object: prepare to share or shadow.
3210              * NB: Thanks to the immutable, garbage-collected property tree
3211              * maintained by jsscope.c in cx->runtime, we needn't worry about
3212              * sprop going away behind our back after we've unlocked scope.
3213              */
3214 0             JS_UNLOCK_SCOPE(cx, scope);
3215
3216             /* Don't clone a shared prototype property. */
3217 0             if (attrs & JSPROP_SHARED)
3218 0                 return SPROP_SET(cx, sprop, obj, pobj, vp);
3219
3220             /* Restore attrs to the ECMA default for new properties. */
3221 0             attrs = JSPROP_ENUMERATE;
3222
3223             /*
3224              * Preserve the shortid, getter, and setter when shadowing any
3225              * property that has a shortid.  An old API convention requires
3226              * that the property's getter and setter functions receive the
3227              * shortid, not id, when they are called on the shadow we are
3228              * about to create in obj's scope.
3229              */
3230 0             if (sprop->flags & SPROP_HAS_SHORTID) {
3231 0                 flags = SPROP_HAS_SHORTID;
3232 0                 shortid = sprop->shortid;
3233 0                 getter = sprop->getter;
3234 0                 setter = sprop->setter;
3235             }
3236
3237             /*
3238              * Forget we found the proto-property now that we've copied any
3239              * needed member values.
3240              */
3241 0             sprop = NULL;
3242         }
3243 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3244     } else {
3245 125329         scope = NULL;
3246 #endif
3247     }
3248
3249 249340     if (!sprop) {
3250 125329         if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj)
3251 0             goto read_only_error;
3252
3253         /* Find or make a property descriptor with the right heritage. */
3254 125329         JS_LOCK_OBJ(cx, obj);
3255 125329         scope = js_GetMutableScope(cx, obj);
3256 125329         if (!scope) {
3257 0             JS_UNLOCK_OBJ(cx, obj);
3258 0             return JS_FALSE;
3259         }
3260 125329         if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
3261 0             attrs |= JSPROP_SHARED;
3262 125329         sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
3263                                     SPROP_INVALID_SLOT, attrs, flags, shortid);
3264 125329         if (!sprop) {
3265 0             JS_UNLOCK_SCOPE(cx, scope);
3266 0             return JS_FALSE;
3267         }
3268
3269         /*
3270          * Initialize the new property value (passed to setter) to undefined.
3271          * Note that we store before calling addProperty, to match the order
3272          * in js_DefineNativeProperty.
3273          */
3274 125329         if (SPROP_HAS_VALID_SLOT(sprop, scope))
3275 125329             LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID);
3276
3277         /* XXXbe called with obj locked */
3278 125329         ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, vp,
3279                             js_RemoveScopeProperty(cx, scope, id);
3280                             JS_UNLOCK_SCOPE(cx, scope);
3281                             return JS_FALSE);
3282
3283 125329         PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
3284     }
3285
3286     /* Get the current property value from its slot. */
3287 249340     slot = sprop->slot;
3288 249340     if (slot != SPROP_INVALID_SLOT) {
3289 249340         JS_ASSERT(slot < obj->map->freeslot);
3290 249340         pval = LOCKED_OBJ_GET_SLOT(obj, slot);
3291
3292         /* If sprop has a stub setter, keep scope locked and just store *vp. */
3293 249340         if (!sprop->setter)
3294 137436             goto set_slot;
3295     }
3296
3297     /* Avoid deadlock by unlocking obj's scope while calling sprop's setter. */
3298 111904     JS_UNLOCK_SCOPE(cx, scope);
3299
3300     /* Let the setter modify vp before copying from it to obj->slots[slot]. */
3301 111904     if (!SPROP_SET(cx, sprop, obj, obj, vp))
3302 0         return JS_FALSE;
3303
3304     /* Relock obj's scope until we are done with sprop. */
3305 111904     JS_LOCK_SCOPE(cx, scope);
3306
3307     /*
3308      * Check whether sprop is still around (was not deleted), and whether it
3309      * has a slot (it may never have had one, or we may have lost a race with
3310      * someone who cleared scope).
3311      */
3312 111904     if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
3313   set_slot:
3314 249340         GC_POKE(cx, pval);
3315 249340         LOCKED_OBJ_SET_SLOT(obj, slot, *vp);
3316     }
3317 249340     JS_UNLOCK_SCOPE(cx, scope);
3318 249340     return JS_TRUE;
3319
3320   read_only_error: {
3321 0     JSString *str = js_DecompileValueGenerator(cx,
3322                                                JSDVG_IGNORE_STACK,
3323                                                ID_TO_VALUE(id),
3324 0                                                NULL);
3325 0     if (str) {
3326 0         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3327                              JSMSG_READ_ONLY,
3328                              JS_GetStringBytes(str));
3329     }
3330 0     return JS_FALSE;
3331   }
3332 }
3333
3334 JSBool
3335 js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
3336                  uintN *attrsp)
3337 4866 {
3338 4866     JSBool noprop, ok;
3339 4866     JSScopeProperty *sprop;
3340
3341 4866     noprop = !prop;
3342 4866     if (noprop) {
3343 0         if (!js_LookupProperty(cx, obj, id, &obj, &prop))
3344 0             return JS_FALSE;
3345 0         if (!prop) {
3346 0             *attrsp = 0;
3347 0             return JS_TRUE;
3348         }
3349 0         if (!OBJ_IS_NATIVE(obj)) {
3350 0             ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp);
3351 0             OBJ_DROP_PROPERTY(cx, obj, prop);
3352 0             return ok;
3353         }
3354     }
3355 4866     sprop = (JSScopeProperty *)prop;
3356 4866     *attrsp = sprop->attrs;
3357 4866     if (noprop)
3358 0         OBJ_DROP_PROPERTY(cx, obj, prop);
3359 4866     return JS_TRUE;
3360 }
3361
3362 JSBool
3363 js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
3364                  uintN *attrsp)
3365 0 {
3366 0     JSBool noprop, ok;
3367 0     JSScopeProperty *sprop;
3368
3369 0     noprop = !prop;
3370 0     if (noprop) {
3371 0         if (!js_LookupProperty(cx, obj, id, &obj, &prop))
3372 0             return JS_FALSE;
3373 0         if (!prop)
3374 0             return JS_TRUE;
3375 0         if (!OBJ_IS_NATIVE(obj)) {
3376 0             ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, attrsp);
3377 0             OBJ_DROP_PROPERTY(cx, obj, prop);
3378 0             return ok;
3379         }
3380     }
3381 0     sprop = (JSScopeProperty *)prop;
3382 0     sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, *attrsp, 0,
3383                                          sprop->getter, sprop->setter);
3384 0     if (noprop)
3385 0         OBJ_DROP_PROPERTY(cx, obj, prop);
3386 0     return (sprop != NULL);
3387 }
3388
3389 JSBool
3390 js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
3391 0 {
3392 #if JS_HAS_PROP_DELETE
3393
3394 0     JSObject *proto;
3395 0     JSProperty *prop;
3396 0     JSScopeProperty *sprop;
3397 0     JSString *str;
3398 0     JSScope *scope;
3399 0     JSBool ok;
3400
3401 0     *rval = JS_VERSION_IS_ECMA(cx) ? JSVAL_TRUE : JSVAL_VOID;
3402
3403     /*
3404      * Handle old bug that took empty string as zero index.  Also convert
3405      * string indices to integers if appropriate.
3406      */
3407 0     CHECK_FOR_STRING_INDEX(id);
3408
3409 0     if (!js_LookupProperty(cx, obj, id, &proto, &prop))
3410 0         return JS_FALSE;
3411 0     if (!prop || proto != obj) {
3412         /*
3413          * If the property was found in a native prototype, check whether it's
3414          * shared and permanent.  Such a property stands for direct properties
3415          * in all delegating objects, matching ECMA semantics without bloating
3416          * each delegating object.
3417          */
3418 0         if (prop) {
3419 0             if (OBJ_IS_NATIVE(proto)) {
3420 0                 sprop = (JSScopeProperty *)prop;
3421 0                 if (SPROP_IS_SHARED_PERMANENT(sprop))
3422 0                     *rval = JSVAL_FALSE;
3423             }
3424 0             OBJ_DROP_PROPERTY(cx, proto, prop);
3425 0             if (*rval == JSVAL_FALSE)
3426 0                 return JS_TRUE;
3427         }
3428
3429         /*
3430          * If no property, or the property comes unshared or impermanent from
3431          * a prototype, call the class's delProperty hook, passing rval as the
3432          * result parameter.
3433          */
3434 0         return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, ID_TO_VALUE(id),
3435                                                    rval);
3436     }
3437
3438 0     sprop = (JSScopeProperty *)prop;
3439 0     if (sprop->attrs & JSPROP_PERMANENT) {
3440 0         OBJ_DROP_PROPERTY(cx, obj, prop);
3441 0         if (JS_VERSION_IS_ECMA(cx)) {
3442 0             *rval = JSVAL_FALSE;
3443 0             return JS_TRUE;
3444         }
3445 0         str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
3446                                          ID_TO_VALUE(id), NULL);
3447 0         if (str) {
3448 0             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3449                                  JSMSG_PERMANENT, JS_GetStringBytes(str));
3450         }
3451 0         return JS_FALSE;
3452     }
3453
3454     /* XXXbe called with obj locked */
3455 0     if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, SPROP_USERID(sprop),
3456                                                 rval)) {
3457 0         OBJ_DROP_PROPERTY(cx, obj, prop);
3458 0         return JS_FALSE;
3459     }
3460
3461 0     scope = OBJ_SCOPE(obj);
3462 0     if (SPROP_HAS_VALID_SLOT(sprop, scope))
3463 0         GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot));
3464
3465 0     PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, NULL);
3466 0     ok = js_RemoveScopeProperty(cx, scope, id);
3467 0     OBJ_DROP_PROPERTY(cx, obj, prop);
3468 0     return ok;
3469
3470 #else  /* !JS_HAS_PROP_DELETE */
3471
3472     jsval null = JSVAL_NULL;
3473
3474     *rval = JSVAL_VOID;
3475     return js_SetProperty(cx, obj, id, &null);
3476
3477 #endif /* !JS_HAS_PROP_DELETE */
3478 }
3479
3480 JSBool
3481 js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
3482 45846 {
3483 45846     jsval v;
3484 45846     JSString *str;
3485
3486 45846     v = OBJECT_TO_JSVAL(obj);
3487 45846     switch (hint) {
3488       case JSTYPE_STRING:
3489         /*
3490          * Propagate the exception if js_TryMethod finds an appropriate
3491          * method, and calling that method returned failure.
3492          */
3493 45846         if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL,
3494                           &v)) {
3495 0             return JS_FALSE;
3496         }
3497
3498 45846         if (!JSVAL_IS_PRIMITIVE(v)) {
3499 0             if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
3500 0                 return JS_FALSE;
3501
3502             /*
3503              * JS1.2 never failed (except for malloc failure) to convert an
3504              * object to a string.  ECMA requires an error if both toString
3505              * and valueOf fail to produce a primitive value.
3506              */
3507 0             if (!JSVAL_IS_PRIMITIVE(v) && JS_VERSION_IS_1_2(cx)) {
3508 0                 char *bytes = JS_smprintf("[object %s]",
3509 0                                           OBJ_GET_CLASS(cx, obj)->name);
3510 0                 if (!bytes)
3511 0                     return JS_FALSE;
3512 0                 str = JS_NewString(cx, bytes, strlen(bytes));
3513 0                 if (!str) {
3514 0                     free(bytes);
3515 0                     return JS_FALSE;
3516                 }
3517 0                 v = STRING_TO_JSVAL(str);
3518 0                 goto out;
3519             }
3520         }
3521 0         break;
3522
3523       default:
3524 0         if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
3525 0             return JS_FALSE;
3526 0         if (!JSVAL_IS_PRIMITIVE(v)) {
3527 0             JSType type = JS_TypeOfValue(cx, v);
3528 0             if (type == hint ||
3529                 (type == JSTYPE_FUNCTION && hint == JSTYPE_OBJECT)) {
3530 0                 goto out;
3531             }
3532             /* Don't convert to string (source object literal) for JS1.2. */
3533 0             if (JS_VERSION_IS_1_2(cx) && hint == JSTYPE_BOOLEAN)
3534 0                 goto out;
3535 0             if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0,
3536                               NULL, &v))
3537 0                 return JS_FALSE;
3538         }
3539 45846         break;
3540     }
3541 45846     if (!JSVAL_IS_PRIMITIVE(v)) {
3542         /* Avoid recursive death through js_DecompileValueGenerator. */
3543 0         if (hint == JSTYPE_STRING) {
3544 0             str = JS_InternString(cx, OBJ_GET_CLASS(cx, obj)->name);
3545 0             if (!str)
3546 0                 return JS_FALSE;
3547         } else {
3548 0             str = NULL;
3549         }
3550 0         *vp = OBJECT_TO_JSVAL(obj);
3551 0         str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, str);
3552 0         if (str) {
3553 0             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3554                                  JSMSG_CANT_CONVERT_TO,
3555                                  JS_GetStringBytes(str),
3556                                  (hint == JSTYPE_VOID)
3557                                  ? "primitive type"
3558                                  : js_type_str[hint]);
3559         }
3560 0         return JS_FALSE;
3561     }
3562 out:
3563 45846     *vp = v;
3564 45846     return JS_TRUE;
3565 }
3566
3567 JSIdArray *
3568 js_NewIdArray(JSContext *cx, jsint length)
3569 45523 {
3570 45523     JSIdArray *ida;
3571
3572 45523     ida = (JSIdArray *)
3573           JS_malloc(cx, sizeof(JSIdArray) + (length-1) * sizeof(jsval));
3574 45523     if (ida)
3575 45523         ida->length = length;
3576 45523     return ida;
3577 }
3578
3579 JSIdArray *
3580 js_SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length)
3581 0 {
3582 0     JSIdArray *rida;
3583
3584 0     rida = (JSIdArray *)
3585            JS_realloc(cx, ida, sizeof(JSIdArray) + (length-1) * sizeof(jsval));
3586 0     if (!rida)
3587 0         JS_DestroyIdArray(cx, ida);
3588     else
3589 0         rida->length = length;
3590 0     return rida;
3591 }
3592
3593 /* Private type used to iterate over all properties of a native JS object */
3594 struct JSNativeIteratorState {
3595     jsint                   next_index; /* index into jsid array */
3596     JSIdArray               *ida;       /* all property ids in enumeration */
3597     JSNativeIteratorState   *next;      /* double-linked list support */
3598     JSNativeIteratorState   **prevp;
3599 };
3600
3601 /*
3602  * This function is used to enumerate the properties of native JSObjects
3603  * and those host objects that do not define a JSNewEnumerateOp-style iterator
3604  * function.
3605  */
3606 JSBool
3607 js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
3608              jsval *statep, jsid *idp)
3609 146615 {
3610 146615     JSRuntime *rt;
3611 146615     JSObject *proto;
3612 146615     JSClass *clasp;
3613 146615     JSEnumerateOp enumerate;
3614 146615     JSScopeProperty *sprop, *lastProp;
3615 146615     jsint i, length;
3616 146615     JSScope *scope;
3617 146615     JSIdArray *ida;
3618 146615     JSNativeIteratorState *state;
3619
3620 146615     rt = cx->runtime;
3621 146615     clasp = OBJ_GET_CLASS(cx, obj);
3622 146615     enumerate = clasp->enumerate;
3623 146615     if (clasp->flags & JSCLASS_NEW_ENUMERATE)
3624 35050         return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
3625
3626 111565     switch (enum_op) {
3627       case JSENUMERATE_INIT:
3628 45523         if (!enumerate(cx, obj))
3629 0             return JS_FALSE;
3630 45523         length = 0;
3631
3632         /*
3633          * The set of all property ids is pre-computed when the iterator
3634          * is initialized so as to avoid problems with properties being
3635          * deleted during the iteration.
3636          */
3637 45523         JS_LOCK_OBJ(cx, obj);
3638 45523         scope = OBJ_SCOPE(obj);
3639
3640         /*
3641          * If this object shares a scope with its prototype, don't enumerate
3642          * its properties.  Otherwise they will be enumerated a second time
3643          * when the prototype object is enumerated.
3644          */
3645 45523         proto = OBJ_GET_PROTO(cx, obj);
3646 45523         if (proto && scope == OBJ_SCOPE(proto)) {
3647 11             ida = js_NewIdArray(cx, 0);
3648 11             if (!ida) {
3649 0                 JS_UNLOCK_OBJ(cx, obj);
3650 0                 return JS_FALSE;
3651             }
3652         } else {
3653             /* Object has a private scope; Enumerate all props in scope. */
3654 731028             for (sprop = lastProp = SCOPE_LAST_PROP(scope); sprop;
3655                  sprop = sprop->parent) {
3656 685516                 if ((
3657 #ifdef DUMP_CALL_TABLE
3658                      (cx->options & JSOPTION_LOGCALL_TOSOURCE) ||
3659 #endif
3660                      (sprop->attrs & JSPROP_ENUMERATE)) &&
3661                     !(sprop->flags & SPROP_IS_ALIAS) &&
3662                     (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
3663                      SCOPE_HAS_PROPERTY(scope, sprop))) {
3664 20519                     length++;
3665                 }
3666             }
3667 45512             ida = js_NewIdArray(cx, length);
3668 45512             if (!ida) {
3669 0                 JS_UNLOCK_OBJ(cx, obj);
3670 0                 return JS_FALSE;
3671             }
3672 45512             i = length;
3673 731028             for (sprop = lastProp; sprop; sprop = sprop->parent) {
3674 685516                 if ((
3675 #ifdef DUMP_CALL_TABLE
3676                      (cx->options & JSOPTION_LOGCALL_TOSOURCE) ||
3677 #endif
3678                      (sprop->attrs & JSPROP_ENUMERATE)) &&
3679                     !(sprop->flags & SPROP_IS_ALIAS) &&
3680                     (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
3681                      SCOPE_HAS_PROPERTY(scope, sprop))) {
3682 20519                     JS_ASSERT(i > 0);
3683 20519                     ida->vector[--i] = sprop->id;
3684                 }
3685             }
3686         }
3687 45523         JS_UNLOCK_OBJ(cx, obj);
3688
3689 45523         state = (JSNativeIteratorState *)
3690             JS_malloc(cx, sizeof(JSNativeIteratorState));
3691 45523         if (!state) {
3692 0             JS_DestroyIdArray(cx, ida);
3693 0             return JS_FALSE;
3694         }
3695 45523         state->ida = ida;
3696 45523         state->next_index = 0;
3697
3698 45523         JS_LOCK_RUNTIME(rt);
3699 45523         state->next = rt->nativeIteratorStates;
3700 45523         if (state->next)
3701 45267             state->next->prevp = &state->next;
3702 45523         state->prevp = &rt->nativeIteratorStates;
3703 45523         *state->prevp = state;
3704 45523         JS_UNLOCK_RUNTIME(rt);
3705
3706 45523         *statep = PRIVATE_TO_JSVAL(state);
3707 45523         if (idp)
3708 0             *idp = INT_TO_JSVAL(length);
3709 0         break;
3710
3711       case JSENUMERATE_NEXT:
3712 66042         state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
3713 66042         ida = state->ida;
3714 66042         length = ida->length;
3715 66042         if (state->next_index != length) {
3716 20519             *idp = ida->vector[state->next_index++];
3717 20519             break;
3718         }
3719         /* FALL THROUGH */
3720
3721       case JSENUMERATE_DESTROY:
3722 45523         state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
3723
3724 45523         JS_LOCK_RUNTIME(rt);
3725 45523         JS_ASSERT(rt->nativeIteratorStates);
3726 45523         JS_ASSERT(*state->prevp == state);
3727 45523         if (state->next) {
3728 45267             JS_ASSERT(state->next->prevp == &state->next);
3729 45267             state->next->prevp = state->prevp;
3730         }
3731 45523         *state->prevp = state->next;
3732 45523         JS_UNLOCK_RUNTIME(rt);
3733
3734 45523         JS_DestroyIdArray(cx, state->ida);
3735 45523         JS_free(cx, state);
3736 45523         *statep = JSVAL_NULL;
3737         break;
3738     }
3739 111565     return JS_TRUE;
3740 }
3741
3742 void
3743 js_MarkNativeIteratorStates(JSContext *cx)
3744 32 {
3745 32     JSNativeIteratorState *state;
3746 32     jsid *cursor, *end, id;
3747
3748 32     state = cx->runtime->nativeIteratorStates;
3749 32     if (!state)
3750 32         return;
3751
3752 0     do {
3753 0         JS_ASSERT(*state->prevp == state);
3754 0         cursor = state->ida->vector;
3755 0         end = cursor + state->ida->length;
3756 0         for (; cursor != end; ++cursor) {
3757 0             id = *cursor;
3758 0             if (JSID_IS_ATOM(id)) {
3759 0                 GC_MARK_ATOM(cx, JSID_TO_ATOM(id), NULL);
3760 0             } else if (JSID_IS_OBJECT(id)) {
3761 0                 GC_MARK(cx, JSID_TO_OBJECT(id), "ida->vector[i]", NULL);
3762             }
3763         }
3764 0     } while ((state = state->next) != NULL);
3765 }
3766   
3767
3768 JSBool
3769 js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
3770                jsval *vp, uintN *attrsp)
3771 2708 {
3772 2708     JSBool writing;
3773 2708     JSObject *pobj;
3774 2708     JSProperty *prop;
3775 2708     JSClass *clasp;
3776 2708     JSScopeProperty *sprop;
3777 2708     JSCheckAccessOp check;
3778
3779 2708     writing = (mode & JSACC_WRITE) != 0;
3780 2708     switch (mode & JSACC_TYPEMASK) {
3781       case JSACC_PROTO:
3782 0         pobj = obj;
3783 0         if (!writing)
3784 0             *vp = OBJ_GET_SLOT(cx, obj, JSSLOT_PROTO);
3785 0         *attrsp = JSPROP_PERMANENT;
3786 0         break;
3787
3788       case JSACC_PARENT:
3789 2644         JS_ASSERT(!writing);
3790 2644         pobj = obj;
3791 2644         *vp = OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT);
3792 2644         *attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
3793 2644         break;
3794
3795       default:
3796 64         if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
3797 0             return JS_FALSE;
3798 64         if (!prop) {
3799 0             if (!writing)
3800 0                 *vp = JSVAL_VOID;
3801 0             *attrsp = 0;
3802 0             clasp = OBJ_GET_CLASS(cx, obj);
3803 0             return !clasp->checkAccess ||
3804                    clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp);
3805         }
3806 64         if (!OBJ_IS_NATIVE(pobj)) {
3807 0             OBJ_DROP_PROPERTY(cx, pobj, prop);
3808 0             return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp);
3809         }
3810
3811 64         sprop = (JSScopeProperty *)prop;
3812 64         *attrsp = sprop->attrs;
3813 64         if (!writing) {
3814 64             *vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)))
3815                   ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
3816                   : JSVAL_VOID;
3817         }
3818 64         OBJ_DROP_PROPERTY(cx, pobj, prop);
3819     }
3820
3821     /*
3822      * If obj's class has a stub (null) checkAccess hook, use the per-runtime
3823      * checkObjectAccess callback, if configured.
3824      *
3825      * We don't want to require all classes to supply a checkAccess hook; we
3826      * need that hook only for certain classes used when precompiling scripts
3827      * and functions ("brutal sharing").  But for general safety of built-in
3828      * magic properties such as __proto__ and __parent__, we route all access
3829      * checks, even for classes that stub out checkAccess, through the global
3830      * checkObjectAccess hook.  This covers precompilation-based sharing and
3831      * (possibly unintended) runtime sharing across trust boundaries.
3832      */
3833 2708     clasp = OBJ_GET_CLASS(cx, pobj);
3834 2708     check = clasp->checkAccess;
3835 2708     if (!check)
3836 2708         check = cx->runtime->checkObjectAccess;
3837 2708     return !check || check(cx, pobj, ID_TO_VALUE(id), mode, vp);
3838 }
3839
3840 #ifdef JS_THREADSAFE
3841 void
3842 js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
3843 {
3844     JS_UNLOCK_OBJ(cx, obj);
3845 }
3846 #endif
3847
3848 static void
3849 ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
3850 0 {
3851     /*
3852      * The decompiler may need to access the args of the function in
3853      * progress rather than the one we had hoped to call.
3854      * So we switch the cx->fp to the frame below us. We stick the
3855      * current frame in the dormantFrameChain to protect it from gc.
3856      */
3857
3858 0     JSStackFrame *fp = cx->fp;
3859 0     if (fp->down) {
3860 0         JS_ASSERT(!fp->dormantNext);
3861 0         fp->dormantNext = cx->dormantFrameChain;
3862 0         cx->dormantFrameChain = fp;
3863 0         cx->fp = fp->down;
3864     }
3865
3866 0     js_ReportIsNotFunction(cx, vp, flags);
3867
3868 0     if (fp->down) {
3869 0         JS_ASSERT(cx->dormantFrameChain == fp);
3870 0         cx->dormantFrameChain = fp->dormantNext;
3871 0         fp->dormantNext = NULL;
3872 0         cx->fp = fp;
3873     }
3874 }
3875
3876 #ifdef NARCISSUS
3877 static JSBool
3878 GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval)
3879 {
3880     JSObject *tmp;
3881     jsval xcval;
3882
3883     while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
3884         obj = tmp;
3885     if (!OBJ_GET_PROPERTY(cx, obj,
3886                           ATOM_TO_JSID(cx->runtime->atomState
3887                                        .ExecutionContextAtom),
3888                           &xcval)) {
3889         return JS_FALSE;
3890     }
3891     if (JSVAL_IS_PRIMITIVE(xcval)) {
3892         JS_ReportError(cx, "invalid ExecutionContext in global object");
3893         return JS_FALSE;
3894     }
3895     if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(xcval),
3896                           ATOM_TO_JSID(cx->runtime->atomState.currentAtom),
3897                           rval)) {
3898         return JS_FALSE;
3899     }
3900     return JS_TRUE;
3901 }
3902 #endif
3903
3904 JSBool
3905 js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
3906 0 {
3907 0     JSClass *clasp;
3908
3909 0     clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
3910 0     if (!clasp->call) {
3911 #ifdef NARCISSUS
3912         JSObject *callee, *args;
3913         jsval fval, nargv[3];
3914         JSBool ok;
3915
3916         callee = JSVAL_TO_OBJECT(argv[-2]);
3917         if (!OBJ_GET_PROPERTY(cx, callee,
3918                               ATOM_TO_JSID(cx->runtime->atomState.callAtom),
3919                               &fval)) {
3920             return JS_FALSE;
3921         }
3922         if (JSVAL_IS_FUNCTION(cx, fval)) {
3923             if (!GetCurrentExecutionContext(cx, obj, &nargv[2]))
3924                 return JS_FALSE;
3925             args = js_GetArgsObject(cx, cx->fp);
3926             if (!args)
3927                 return JS_FALSE;
3928             nargv[0] = OBJECT_TO_JSVAL(obj);
3929             nargv[1] = OBJECT_TO_JSVAL(args);
3930             return js_InternalCall(cx, callee, fval, 3, nargv, rval);
3931         }
3932         if (JSVAL_IS_OBJECT(fval) && JSVAL_TO_OBJECT(fval) != callee) {
3933             argv[-2] = fval;
3934             ok = js_Call(cx, obj, argc, argv, rval);
3935             argv[-2] = OBJECT_TO_JSVAL(callee);
3936             return ok;
3937         }
3938 #endif
3939 0         ReportIsNotFunction(cx, &argv[-2], 0);
3940 0         return JS_FALSE;
3941     }
3942 0     return clasp->call(cx, obj, argc, argv, rval);
3943 }
3944
3945 JSBool
3946 js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
3947              jsval *rval)
3948 0 {
3949 0     JSClass *clasp;
3950
3951 0     clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
3952 0     if (!clasp->construct) {
3953 #ifdef NARCISSUS
3954         JSObject *callee, *args;
3955         jsval cval, nargv[2];
3956         JSBool ok;
3957
3958         callee = JSVAL_TO_OBJECT(argv[-2]);
3959         if (!OBJ_GET_PROPERTY(cx, callee,
3960                               ATOM_TO_JSID(cx->runtime->atomState
3961                                            .constructAtom),
3962                               &cval)) {
3963             return JS_FALSE;
3964         }
3965         if (JSVAL_IS_FUNCTION(cx, cval)) {
3966             if (!GetCurrentExecutionContext(cx, obj, &nargv[1]))
3967                 return JS_FALSE;
3968             args = js_GetArgsObject(cx, cx->fp);
3969             if (!args)
3970                 return JS_FALSE;
3971             nargv[0] = OBJECT_TO_JSVAL(args);
3972             return js_InternalCall(cx, callee, cval, 2, nargv, rval);
3973         }
3974         if (JSVAL_IS_OBJECT(cval) && JSVAL_TO_OBJECT(cval) != callee) {
3975             argv[-2] = cval;
3976             ok = js_Call(cx, obj, argc, argv, rval);
3977             argv[-2] = OBJECT_TO_JSVAL(callee);
3978             return ok;
3979         }
3980 #endif
3981 0         ReportIsNotFunction(cx, &argv[-2], JSV2F_CONSTRUCT);
3982 0         return JS_FALSE;
3983     }
3984 0     return clasp->construct(cx, obj, argc, argv, rval);
3985 }
3986
3987 JSBool
3988 js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
3989 183 {
3990 183     JSClass *clasp;
3991 183     JSString *str;
3992
3993 183     clasp = OBJ_GET_CLASS(cx, obj);
3994 183     if (clasp->hasInstance)
3995 183         return clasp->hasInstance(cx, obj, v, bp);
3996 #ifdef NARCISSUS
3997     {
3998         jsval fval, rval;
3999
4000         if (!OBJ_GET_PROPERTY(cx, obj,
4001                               ATOM_TO_JSID(cx->runtime->atomState
4002                                            .hasInstanceAtom),
4003                               &fval)) {
4004             return JS_FALSE;
4005         }
4006         if (JSVAL_IS_FUNCTION(cx, fval)) {
4007             return js_InternalCall(cx, obj, fval, 1, &v, &rval) &&
4008                    js_ValueToBoolean(cx, rval, bp);
4009         }
4010     }
4011 #endif
4012 0     str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
4013                                      OBJECT_TO_JSVAL(obj), NULL);
4014 0     if (str) {
4015 0         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4016                              JSMSG_BAD_INSTANCEOF_RHS,
4017                              JS_GetStringBytes(str));
4018     }
4019 0     return JS_FALSE;
4020 }
4021
4022 JSBool
4023 js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
4024 183 {
4025 183     JSObject *obj2;
4026
4027 183     *bp = JS_FALSE;
4028 183     if (JSVAL_IS_PRIMITIVE(v))
4029 61         return JS_TRUE;
4030 122     obj2 = JSVAL_TO_OBJECT(v);
4031 122     while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL) {
4032 122         if (obj2 == obj) {
4033 122             *bp = JS_TRUE;
4034             break;
4035         }
4036     }
4037 122     return JS_TRUE;
4038 }
4039
4040 JSBool
4041 js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop)
4042 0 {
4043 0     return GetClassPrototype(cx, NULL, name, protop);
4044 }
4045
4046 static JSBool
4047 GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
4048                   JSObject **protop)
4049 187182 {
4050 187182     jsval v;
4051 187182     JSObject *ctor;
4052
4053 187182     if (!js_FindConstructor(cx, scope, name, &v))
4054 0         return JS_FALSE;
4055 187182     if (JSVAL_IS_FUNCTION(cx, v)) {
4056 121075         ctor = JSVAL_TO_OBJECT(v);
4057 121075         if (!OBJ_GET_PROPERTY(cx, ctor,
4058                               ATOM_TO_JSID(cx->runtime->atomState
4059                                            .classPrototypeAtom),
4060                               &v)) {
4061 0             return JS_FALSE;
4062         }
4063 121075         if (!JSVAL_IS_PRIMITIVE(v)) {
4064             /*
4065              * Set the newborn root in case v is otherwise unreferenced.
4066              * It's ok to overwrite newborn roots here, since the getter
4067              * called just above could have.  Unlike the common GC rooting
4068              * model, our callers do not have to protect protop thanks to
4069              * this newborn root, since they all immediately create a new
4070              * instance that delegates to this object, or just query the
4071              * prototype for its class.
4072              */
4073 121075             cx->newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(v);
4074         }
4075     }
4076 187182     *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;
4077 187182     return JS_TRUE;
4078 }
4079
4080 /*
4081  * For shared precompilation of function objects, we support cloning on entry
4082  * to an execution context in which the function declaration or expression
4083  * should be processed as if it were not precompiled, where the precompiled
4084  * function's scope chain does not match the execution context's.  The cloned
4085  * function object carries its execution-context scope in its parent slot; it
4086  * links to the precompiled function (the "clone-parent") via its proto slot.
4087  *
4088  * Note that this prototype-based delegation leaves an unchecked access path
4089  * from the clone to the clone-parent's 'constructor' property.  If the clone
4090  * lives in a less privileged or shared scope than the clone-parent, this is
4091  * a security hole, a sharing hazard, or both.  Therefore we check all such
4092  * accesses with the following getter/setter pair, which we use when defining
4093  * 'constructor' in f.prototype for all function objects f.
4094  */
4095 static JSBool
4096 CheckCtorGetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
4097 64 {
4098 64     JSAtom *atom;
4099 64     uintN attrs;
4100
4101 64     atom = cx->runtime->atomState.constructorAtom;
4102 64     JS_ASSERT(id == ATOM_KEY(atom));
4103 64     return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_READ,
4104                             vp, &attrs);
4105 }
4106
4107 static JSBool
4108 CheckCtorSetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
4109 0 {
4110 0     JSAtom *atom;
4111 0     uintN attrs;
4112
4113 0     atom = cx->runtime->atomState.constructorAtom;
4114 0     JS_ASSERT(id == ATOM_KEY(atom));
4115 0     return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_WRITE,
4116                             vp, &attrs);
4117 }
4118
4119 JSBool
4120 js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,
4121                      uintN attrs)
4122 368 {
4123     /*
4124      * Use the given attributes for the prototype property of the constructor,
4125      * as user-defined constructors have a DontDelete prototype (which may be
4126      * reset), while native or "system" constructors have DontEnum | ReadOnly |
4127      * DontDelete.
4128      */
4129 368     if (!OBJ_DEFINE_PROPERTY(cx, ctor,
4130                              ATOM_TO_JSID(cx->runtime->atomState
4131                                           .classPrototypeAtom),
4132                              OBJECT_TO_JSVAL(proto),
4133                              JS_PropertyStub, JS_PropertyStub,
4134                              attrs, NULL)) {
4135 0         return JS_FALSE;
4136     }
4137
4138     /*
4139      * ECMA says that Object.prototype.constructor, or f.prototype.constructor
4140      * for a user-defined function f, is DontEnum.
4141      */
4142 368     return OBJ_DEFINE_PROPERTY(cx, proto,
4143                                ATOM_TO_JSID(cx->runtime->atomState
4144                                             .constructorAtom),
4145                                OBJECT_TO_JSVAL(ctor),
4146                                CheckCtorGetAccess, CheckCtorSetAccess,
4147                                0, NULL);
4148 }
4149
4150 JSBool
4151 js_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
4152 47840 {
4153 47840     JSObject *obj;
4154
4155 47840     if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
4156 2038         obj = NULL;
4157 45802     } else if (JSVAL_IS_OBJECT(v)) {
4158 0         obj = JSVAL_TO_OBJECT(v);
4159 0         if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v))
4160 0             return JS_FALSE;
4161 0         if (JSVAL_IS_OBJECT(v))
4162 0             obj = JSVAL_TO_OBJECT(v);
4163     } else {
4164 45802         if (JSVAL_IS_STRING(v)) {
4165 45802             obj = js_StringToObject(cx, JSVAL_TO_STRING(v));
4166 0         } else if (JSVAL_IS_INT(v)) {
4167 0             obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v));
4168 0         } else if (JSVAL_IS_DOUBLE(v)) {
4169 0             obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v));
4170         } else {
4171 0             JS_ASSERT(JSVAL_IS_BOOLEAN(v));
4172 0             obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v));
4173         }
4174 45802         if (!obj)
4175 0             return JS_FALSE;
4176     }
4177 47840     *objp = obj;
4178 47840     return JS_TRUE;
4179 }
4180
4181 JSObject *
4182 js_ValueToNonNullObject(JSContext *cx, jsval v)
4183 45802 {
4184 45802     JSObject *obj;
4185 45802     JSString *str;
4186
4187 45802     if (!js_ValueToObject(cx, v, &obj))
4188 0         return NULL;
4189 45802     if (!obj) {
4190 0         str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
4191 0         if (str) {
4192 0             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4193                                  JSMSG_NO_PROPERTIES, JS_GetStringBytes(str));
4194         }
4195     }
4196 45802     return obj;
4197 }
4198
4199 JSBool
4200 js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval)
4201 0 {
4202 #if JS_HAS_VALUEOF_HINT
4203 0     jsval argv[1];
4204
4205 0     argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]);
4206 0     return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv,
4207                         rval);
4208 #else
4209     return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 0, NULL,
4210                         rval);
4211 #endif
4212 }
4213
4214 JSBool
4215 js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
4216              uintN argc, jsval *argv, jsval *rval)
4217 45846 {
4218 45846     JSErrorReporter older;
4219 45846     jsid id;
4220 45846     jsval fval;
4221 45846     JSBool ok;
4222 45846     int stackDummy;
4223
4224 45846     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
4225 0         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
4226 0         return JS_FALSE;
4227     }
4228
4229     /*
4230      * Report failure only if an appropriate method was found, and calling it
4231      * returned failure.  We propagate failure in this case to make exceptions
4232      * behave properly.
4233      */
4234 45846     older = JS_SetErrorReporter(cx, NULL);
4235 45846     id = ATOM_TO_JSID(atom);
4236 45846     fval = JSVAL_VOID;
4237 #if JS_HAS_XML_SUPPORT
4238 45846     if (OBJECT_IS_XML(cx, obj)) {
4239 0         JSXMLObjectOps *ops;
4240
4241 0         ops = (JSXMLObjectOps *) obj->map->ops;
4242 0         obj = ops->getMethod(cx, obj, id, &fval);
4243 0         ok = (obj != NULL);
4244     } else
4245 #endif
4246     {
4247 45846         ok = OBJ_GET_PROPERTY(cx, obj, id, &fval);
4248     }
4249 45846     if (!ok)
4250 0         JS_ClearPendingException(cx);
4251 45846     ok = JSVAL_IS_PRIMITIVE(fval) ||
4252          js_InternalCall(cx, obj, fval, argc, argv, rval);
4253 45846     JS_SetErrorReporter(cx, older);
4254 45846     return ok;
4255 }
4256
4257 #if JS_HAS_XDR
4258
4259 #include "jsxdrapi.h"
4260
4261 JSBool
4262 js_XDRObject(JSXDRState *xdr, JSObject **objp)
4263 0 {
4264 0     JSContext *cx;
4265 0     JSClass *clasp;
4266 0     const char *className;
4267 0     uint32 classId, classDef;
4268 0     JSBool ok;
4269 0     JSObject *proto;
4270
4271 0     cx = xdr->cx;
4272 0     if (xdr->mode == JSXDR_ENCODE) {
4273 0         clasp = OBJ_GET_CLASS(cx, *objp);
4274 0         className = clasp->name;
4275 0         classId = JS_XDRFindClassIdByName(xdr, className);
4276 0         classDef = !classId;
4277 0         if (classDef && !JS_XDRRegisterClass(xdr, clasp, &classId))
4278 0             return JS_FALSE;
4279     } else {
4280 0         classDef = 0;
4281 0         className = NULL;
4282 0         clasp = NULL;           /* quell GCC overwarning */
4283     }
4284
4285     /* XDR a flag word followed (if true) by the class name. */
4286 0     if (!JS_XDRUint32(xdr, &classDef))
4287 0         return JS_FALSE;
4288 0     if (classDef && !JS_XDRCString(xdr, (char **) &className))
4289 0         return JS_FALSE;
4290
4291     /* From here on, return through out: to free className if it was set. */
4292 0     ok = JS_XDRUint32(xdr, &classId);
4293 0     if (!ok)
4294 0         goto out;
4295
4296 0     if (xdr->mode != JSXDR_ENCODE) {
4297 0         if (classDef) {
4298 0             ok = GetClassPrototype(cx, NULL, className, &proto);
4299 0             if (!ok)
4300 0                 goto out;
4301 0             clasp = OBJ_GET_CLASS(cx, proto);
4302 0             ok = JS_XDRRegisterClass(xdr, clasp, &classId);
4303 0             if (!ok)
4304 0                 goto out;
4305         } else {
4306 0             clasp = JS_XDRFindClassById(xdr, classId);
4307 0             if (!clasp) {
4308 0                 char numBuf[12];
4309 0                 JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId);
4310 0                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4311                                      JSMSG_CANT_FIND_CLASS, numBuf);
4312 0                 ok = JS_FALSE;
4313 0                 goto out;
4314             }
4315         }
4316     }
4317
4318 0     if (!clasp->xdrObject) {
4319 0         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4320                              JSMSG_CANT_XDR_CLASS, clasp->name);
4321 0         ok = JS_FALSE;
4322     } else {
4323 0         ok = clasp->xdrObject(xdr, objp);
4324     }
4325 out:
4326 0     if (xdr->mode != JSXDR_ENCODE && className)
4327 0         JS_free(cx, (void *)className);
4328 0     return ok;
4329 }
4330
4331 #endif /* JS_HAS_XDR */
4332
4333 #ifdef DEBUG_brendan
4334
4335 #include <stdio.h>
4336 #include <math.h>
4337
4338 uint32 js_entry_count_max;
4339 uint32 js_entry_count_sum;
4340 double js_entry_count_sqsum;
4341 uint32 js_entry_count_hist[11];
4342
4343 static void
4344 MeterEntryCount(uintN count)
4345 {
4346     if (count) {
4347         js_entry_count_sum += count;
4348         js_entry_count_sqsum += (double)count * count;
4349         if (count > js_entry_count_max)
4350             js_entry_count_max = count;
4351     }
4352     js_entry_count_hist[JS_MIN(count, 10)]++;
4353 }
4354
4355 void
4356 js_DumpScopeMeters(JSRuntime *rt)
4357 {
4358     static FILE *logfp;
4359     if (!logfp)
4360         logfp = fopen("/tmp/scope.stats", "a");
4361
4362     {
4363         double mean = 0., var = 0., sigma = 0.;
4364         double nscopes = rt->liveScopes;
4365         double nentrys = js_entry_count_sum;
4366         if (nscopes > 0 && nentrys >= 0) {
4367             mean = nentrys / nscopes;
4368             var = nscopes * js_entry_count_sqsum - nentrys * nentrys;
4369             if (var < 0.0 || nscopes <= 1)
4370                 var = 0.0;
4371             else
4372                 var /= nscopes * (nscopes - 1);
4373
4374             /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
4375             sigma = (var != 0.) ? sqrt(var) : 0.;
4376         }
4377
4378         fprintf(logfp,
4379                 "scopes %g entries %g mean %g sigma %g max %u",
4380                 nscopes, nentrys, mean, sigma, js_entry_count_max);
4381     }
4382
4383     fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u\n",
4384             js_entry_count_hist[0], js_entry_count_hist[1],
4385             js_entry_count_hist[2], js_entry_count_hist[3],
4386             js_entry_count_hist[4], js_entry_count_hist[5],
4387             js_entry_count_hist[6], js_entry_count_hist[7],
4388             js_entry_count_hist[8], js_entry_count_hist[9],
4389             js_entry_count_hist[10]);
4390     js_entry_count_sum = js_entry_count_max = 0;
4391     js_entry_count_sqsum = 0;
4392     memset(js_entry_count_hist, 0, sizeof js_entry_count_hist);
4393     fflush(logfp);
4394 }
4395
4396 #endif /* DEBUG_brendan */
4397
4398 uint32
4399 js_Mark(JSContext *cx, JSObject *obj, void *arg)
4400 17415 {
4401 17415     JSScope *scope;
4402 17415     JSScopeProperty *sprop;
4403 17415     JSClass *clasp;
4404
4405 17415     JS_ASSERT(OBJ_IS_NATIVE(obj));
4406 17415     scope = OBJ_SCOPE(obj);
4407 #ifdef DEBUG_brendan
4408     if (scope->object == obj)
4409         MeterEntryCount(scope->entryCount);
4410 #endif
4411
4412 17415     JS_ASSERT(!SCOPE_LAST_PROP(scope) ||
4413               SCOPE_HAS_PROPERTY(scope, SCOPE_LAST_PROP(scope)));
4414
4415 217976     for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
4416 200561         if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
4417 0             continue;
4418 200561         MARK_SCOPE_PROPERTY(sprop);
4419 200561         if (JSID_IS_ATOM(sprop->id))
4420 197953             GC_MARK_ATOM(cx, JSID_TO_ATOM(sprop->id), arg);
4421 2608         else if (JSID_IS_OBJECT(sprop->id))
4422 0             GC_MARK(cx, JSID_TO_OBJECT(sprop->id), "id", arg);
4423
4424 #if JS_HAS_GETTER_SETTER
4425 200561         if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
4426 #ifdef GC_MARK_DEBUG
4427             char buf[64];
4428             JSAtom *atom = JSID_TO_ATOM(sprop->id);
4429             const char *id = (atom && ATOM_IS_STRING(atom))
4430                              ? JS_GetStringBytes(ATOM_TO_STRING(atom))
4431                              : "unknown";
4432 #endif
4433
4434 0             if (sprop->attrs & JSPROP_GETTER) {
4435 #ifdef GC_MARK_DEBUG
4436                 JS_snprintf(buf, sizeof buf, "%s %s",
4437                             id, js_getter_str);
4438 #endif
4439 0                 GC_MARK(cx,
4440                         JSVAL_TO_GCTHING((jsval) sprop->getter),
4441                         buf,
4442                         arg);
4443             }
4444 0             if (sprop->attrs & JSPROP_SETTER) {
4445 #ifdef GC_MARK_DEBUG
4446                 JS_snprintf(buf, sizeof buf, "%s %s",
4447                             id, js_setter_str);
4448 #endif
4449 0                 GC_MARK(cx,
4450                         JSVAL_TO_GCTHING((jsval) sprop->setter),
4451                         buf,
4452                         arg);
4453             }
4454         }
4455 #endif /* JS_HAS_GETTER_SETTER */
4456     }
4457
4458     /* No one runs while the GC is running, so we can use LOCKED_... here. */
4459 17415     clasp = LOCKED_OBJ_GET_CLASS(obj);
4460 17415     if (clasp->mark)
4461 6624         (void) clasp->mark(cx, obj, arg);
4462
4463 17415     if (scope->object != obj) {
4464         /*
4465          * An unmutated object that shares a prototype's scope.  We can't tell
4466          * how many slots are allocated and in use at obj->slots by looking at
4467          * scope, so we get obj->slots' length from its -1'st element.
4468          */
4469 4463         return (uint32) obj->slots[-1];
4470     }
4471 12952     return JS_MIN(scope->map.freeslot, scope->map.nslots);
4472 }
4473
4474 void
4475 js_Clear(JSContext *cx, JSObject *obj)
4476 0 {
4477 0     JSScope *scope;
4478 0     JSRuntime *rt;
4479 0     JSScopeProperty *sprop;
4480 0     uint32 i, n;
4481
4482     /*
4483      * Clear our scope and the property cache of all obj's properties only if
4484      * obj owns the scope (i.e., not if obj is unmutated and therefore sharing
4485      * its prototype's scope).  NB: we do not clear any reserved slots lying
4486      * below JSSLOT_FREE(clasp).
4487      */
4488 0     JS_LOCK_OBJ(cx, obj);
4489 0     scope = OBJ_SCOPE(obj);
4490 0     if (scope->object == obj) {
4491         /* Clear the property cache before we clear the scope. */
4492 0         rt = cx->runtime;
4493 0         for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
4494 0             if (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
4495                 SCOPE_HAS_PROPERTY(scope, sprop)) {
4496 0                 PROPERTY_CACHE_FILL(&rt->propertyCache, obj, sprop->id, NULL);
4497             }
4498         }
4499
4500         /* Now that we're done using scope->lastProp/table, clear scope. */
4501 0         js_ClearScope(cx, scope);
4502
4503         /* Clear slot values and reset freeslot so we're consistent. */
4504 0         i = scope->map.nslots;
4505 0         n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj));
4506 0         while (--i >= n)
4507 0             obj->slots[i] = JSVAL_VOID;
4508 0         scope->map.freeslot = n;
4509     }
4510 0     JS_UNLOCK_OBJ(cx, obj);
4511 }
4512
4513 jsval
4514 js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot)
4515 0 {
4516 0     jsval v;
4517
4518 0     JS_LOCK_OBJ(cx, obj);
4519 0     v = (slot < (uint32) obj->slots[-1]) ? obj->slots[slot] : JSVAL_VOID;
4520 0     JS_UNLOCK_OBJ(cx, obj);
4521 0     return v;
4522 }
4523
4524 JSBool
4525 js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
4526 16314 {
4527 16314     JSScope *scope;
4528 16314     uint32 nslots;
4529 16314     JSClass *clasp;
4530 16314     jsval *newslots;
4531
4532 16314     JS_LOCK_OBJ(cx, obj);
4533 16314     scope = OBJ_SCOPE(obj);
4534 16314     nslots = (uint32) obj->slots[-1];
4535 16314     if (slot >= nslots) {
4536         /*
4537          * At this point, obj may or may not own scope.  If some path calls
4538          * js_GetMutableScope but does not add a slot-owning property, then
4539          * scope->object == obj but nslots will be nominal.  If obj shares a
4540          * prototype's scope, then we cannot update scope->map here, but we
4541          * must update obj->slots[-1] when we grow obj->slots.
4542          *
4543          * See js_Mark, before the last return, where we make a special case
4544          * for unmutated (scope->object != obj) objects.
4545          */
4546 0         JS_ASSERT(nslots == JS_INITIAL_NSLOTS);
4547 0         clasp = LOCKED_OBJ_GET_CLASS(obj);
4548 0         nslots = JSSLOT_FREE(clasp);
4549 0         if (clasp->reserveSlots)
4550 0             nslots += clasp->reserveSlots(cx, obj);
4551 0         JS_ASSERT(slot < nslots);
4552
4553 0         newslots = AllocSlots(cx, obj->slots, nslots);
4554 0         if (!newslots) {
4555 0             JS_UNLOCK_SCOPE(cx, scope);
4556 0             return JS_FALSE;
4557         }
4558 0         if (scope->object == obj)
4559 0             scope->map.nslots = nslots;
4560 0         obj->slots = newslots;
4561     }
4562
4563     /* Whether or not we grew nslots, we may need to advance freeslot. */
4564 16314     if (scope->object == obj && slot >= scope->map.freeslot)
4565 0         scope->map.freeslot = slot + 1;
4566
4567 16314     obj->slots[slot] = v;
4568 16314     JS_UNLOCK_SCOPE(cx, scope);
4569 16314     return JS_TRUE;
4570 }
4571
4572 #ifdef DEBUG
4573
4574 /* Routines to print out values during debugging. */
4575
4576 void printChar(jschar *cp) {
4577     fprintf(stderr, "jschar* (0x%p) \"", (void *)cp);
4578     while (*cp)
4579         fputc(*cp++, stderr);
4580     fputc('"', stderr);
4581     fputc('\n', stderr);
4582 }
4583
4584 void printString(JSString *str) {
4585     size_t i, n;
4586     jschar *s;
4587     fprintf(stderr, "string (0x%p) \"", (void *)str);
4588     s = JSSTRING_CHARS(str);
4589     for (i=0, n=JSSTRING_LENGTH(str); i < n; i++)
4590         fputc(s[i], stderr);
4591     fputc('"', stderr);
4592     fputc('\n', stderr);
4593 }
4594
4595 void printVal(JSContext *cx, jsval val);
4596
4597 void printObj(JSContext *cx, JSObject *jsobj) {
4598     jsuint i;
4599     jsval val;
4600     JSClass *clasp;
4601
4602     fprintf(stderr, "object 0x%p\n", (void *)jsobj);
4603     clasp = OBJ_GET_CLASS(cx, jsobj);
4604     fprintf(stderr, "class 0x%p %s\n", (void *)clasp, clasp->name);
4605     for (i=0; i < jsobj->map->nslots; i++) {
4606         fprintf(stderr, "slot %3d ", i);
4607         val = jsobj->slots[i];
4608         if (JSVAL_IS_OBJECT(val))
4609             fprintf(stderr, "object 0x%p\n", (void *)JSVAL_TO_OBJECT(val));
4610         else
4611             printVal(cx, val);
4612     }
4613 }
4614
4615 void printVal(JSContext *cx, jsval val) {
4616     fprintf(stderr, "val %d (0x%p) = ", (int)val, (void *)val);
4617     if (JSVAL_IS_NULL(val)) {
4618         fprintf(stderr, "null\n");
4619     } else if (JSVAL_IS_VOID(val)) {
4620         fprintf(stderr, "undefined\n");
4621     } else if (JSVAL_IS_OBJECT(val)) {
4622         printObj(cx, JSVAL_TO_OBJECT(val));
4623     } else if (JSVAL_IS_INT(val)) {
4624         fprintf(stderr, "(int) %d\n", JSVAL_TO_INT(val));
4625     } else if (JSVAL_IS_STRING(val)) {
4626         printString(JSVAL_TO_STRING(val));
4627     } else if (JSVAL_IS_DOUBLE(val)) {
4628         fprintf(stderr, "(double) %g\n", *JSVAL_TO_DOUBLE(val));
4629     } else {
4630         JS_ASSERT(JSVAL_IS_BOOLEAN(val));
4631         fprintf(stderr, "(boolean) %s\n",
4632                 JSVAL_TO_BOOLEAN(val) ? "true" : "false");
4633     }
4634     fflush(stderr);
4635 }
4636
4637 void printId(JSContext *cx, jsid id) {
4638     fprintf(stderr, "id %d (0x%p) is ", (int)id, (void *)id);
4639     printVal(cx, ID_TO_VALUE(id));
4640 }
4641
4642 void printAtom(JSAtom *atom) {
4643     printString(ATOM_TO_STRING(atom));
4644 }
4645