Code

fix 1243587 and misc fixes
[inkscape.git] / src / dom / js / jsobj.c
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 ***** */
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"
70 #include "jsdbgapi.h"   /* whether or not JS_HAS_OBJ_WATCHPOINT */
72 #if JS_HAS_XML_SUPPORT
73 #include "jsxml.h"
74 #endif
76 #ifdef JS_THREADSAFE
77 #define NATIVE_DROP_PROPERTY js_DropProperty
79 extern void
80 js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);
81 #else
82 #define NATIVE_DROP_PROPERTY NULL
83 #endif
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 };
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 };
108 #if JS_HAS_OBJ_PROTO_PROP
110 static JSBool
111 obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
113 static JSBool
114 obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
116 static JSBool
117 obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
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 };
129 /* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */
130 #define JSSLOT_COUNT 2
132 static JSBool
133 ReportStrictSlot(JSContext *cx, uint32 slot)
135     if (slot == JSSLOT_PROTO)
136         return JS_TRUE;
137     return JS_ReportErrorFlagsAndNumber(cx,
138                                         JSREPORT_WARNING | JSREPORT_STRICT,
139                                         js_GetErrorMessage, NULL,
140                                         JSMSG_DEPRECATED_USAGE,
141                                         object_props[slot].name);
144 static JSBool
145 obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
147     uint32 slot;
148     jsid propid;
149     JSAccessMode mode;
150     uintN attrs;
151     JSObject *pobj;
152     JSClass *clasp;
153     JSExtendedClass *xclasp;
155     slot = (uint32) JSVAL_TO_INT(id);
156     if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
157         propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
158         mode = JSACC_PROTO;
159     } else {
160         propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
161         mode = JSACC_PARENT;
162     }
164     /* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */
165     if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs))
166         return JS_FALSE;
168     pobj = JSVAL_TO_OBJECT(*vp);
169     if (pobj) {
170         clasp = OBJ_GET_CLASS(cx, pobj);
171         if (clasp->flags & JSCLASS_IS_EXTENDED) {
172             xclasp = (JSExtendedClass *) clasp;
173             if (xclasp->outerObject) {
174                 pobj = xclasp->outerObject(cx, pobj);
175                 if (!pobj)
176                     return JS_FALSE;
177                 *vp = OBJECT_TO_JSVAL(pobj);
178             }
179         }
180     }
181     return JS_TRUE;
184 static JSBool
185 obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
187     JSObject *pobj;
188     uint32 slot;
189     jsid propid;
190     uintN attrs;
192     if (!JSVAL_IS_OBJECT(*vp))
193         return JS_TRUE;
194     pobj = JSVAL_TO_OBJECT(*vp);
195     slot = (uint32) JSVAL_TO_INT(id);
196     if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
197         return JS_FALSE;
199     /* __parent__ is readonly and permanent, only __proto__ may be set. */
200     propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
201     if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_PROTO|JSACC_WRITE, vp, &attrs))
202         return JS_FALSE;
204     return js_SetProtoOrParent(cx, obj, slot, pobj);
207 static JSBool
208 obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
210     jsval iter_state;
211     jsid num_properties;
212     JSBool ok;
214     if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))
215         return JS_FALSE;
217     /* Get the number of properties to enumerate. */
218     iter_state = JSVAL_NULL;
219     ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties);
220     if (!ok)
221         goto out;
223     if (!JSVAL_IS_INT(num_properties)) {
224         JS_ASSERT(0);
225         *vp = JSVAL_ZERO;
226         goto out;
227     }
228     *vp = num_properties;
230 out:
231     if (iter_state != JSVAL_NULL)
232         ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
233     return ok;
236 #else  /* !JS_HAS_OBJ_PROTO_PROP */
238 #define object_props NULL
240 #endif /* !JS_HAS_OBJ_PROTO_PROP */
242 JSBool
243 js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)
245     JSRuntime *rt;
246     JSObject *obj2, *oldproto;
247     JSScope *scope, *newscope;
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     rt = cx->runtime;
272 #ifdef JS_THREADSAFE
274     JS_ACQUIRE_LOCK(rt->setSlotLock);
275     while (rt->setSlotBusy) {
276         jsrefcount saveDepth;
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);
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
299 #else
301 #define SET_SLOT_DONE(rt)       /* nothing */
303 #endif
305     obj2 = pobj;
306     while (obj2) {
307         if (obj2 == obj) {
308             SET_SLOT_DONE(rt);
309             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             return JS_FALSE;
319         }
320         obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot));
321     }
323     if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) {
324         /* Check to see whether obj shares its prototype's scope. */
325         JS_LOCK_OBJ(cx, obj);
326         scope = OBJ_SCOPE(obj);
327         oldproto = JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO));
328         if (oldproto && OBJ_SCOPE(oldproto) == scope) {
329             /* Either obj needs a new empty scope, or it should share pobj's. */
330             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                 scope = js_GetMutableScope(cx, obj);
347                 if (!scope) {
348                     JS_UNLOCK_OBJ(cx, obj);
349                     SET_SLOT_DONE(rt);
350                     return JS_FALSE;
351                 }
352             } 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
365                 /* We can't deadlock because we checked for cycles above (2). */
366                 JS_LOCK_OBJ(cx, pobj);
367                 newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map);
368                 obj->map = &newscope->map;
369                 js_DropObjectMap(cx, &scope->map, obj);
370                 JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
371                 scope = newscope;
372 #ifdef JS_THREADSAFE
373                 rt->setSlotScope = NULL;
374 #endif
375             }
376         }
377         LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj));
378         JS_UNLOCK_SCOPE(cx, scope);
379     } else {
380         OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj));
381     }
383     SET_SLOT_DONE(rt);
384     return JS_TRUE;
386 #undef SET_SLOT_DONE
389 JS_STATIC_DLL_CALLBACK(JSHashNumber)
390 js_hash_object(const void *key)
392     return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS;
395 static JSHashEntry *
396 MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
398     JSSharpObjectMap *map;
399     JSHashTable *table;
400     JSHashNumber hash;
401     JSHashEntry **hep, *he;
402     jsatomid sharpid;
403     JSIdArray *ida;
404     JSBool ok;
405     jsint i, length;
406     jsid id;
407 #if JS_HAS_GETTER_SETTER
408     JSObject *obj2;
409     JSProperty *prop;
410     uintN attrs;
411 #endif
412     jsval val;
413     int stackDummy;
415     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
416         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
417         return NULL;
418     }
420     map = &cx->sharpObjectMap;
421     table = map->table;
422     hash = js_hash_object(obj);
423     hep = JS_HashTableRawLookup(table, hash, obj);
424     he = *hep;
425     if (!he) {
426         sharpid = 0;
427         he = JS_HashTableRawAdd(table, hep, hash, obj,
428                                 JS_UINT32_TO_PTR(sharpid));
429         if (!he) {
430             JS_ReportOutOfMemory(cx);
431             return NULL;
432         }
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         ++map->depth;
441         ida = JS_Enumerate(cx, obj);
442         --map->depth;
443         if (!ida)
444             return NULL;
446         ok = JS_TRUE;
447         for (i = 0, length = ida->length; i < length; i++) {
448             id = ida->vector[i];
449 #if JS_HAS_GETTER_SETTER
450             ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
451             if (!ok)
452                 break;
453             if (!prop)
454                 continue;
455             ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
456             if (ok) {
457                 if (OBJ_IS_NATIVE(obj2) &&
458                     (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
459                     val = JSVAL_NULL;
460                     if (attrs & JSPROP_GETTER)
461                         val = (jsval) ((JSScopeProperty*)prop)->getter;
462                     if (attrs & JSPROP_SETTER) {
463                         if (val != JSVAL_NULL) {
464                             /* Mark the getter, then set val to setter. */
465                             ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
466                                                    NULL)
467                                   != NULL);
468                         }
469                         val = (jsval) ((JSScopeProperty*)prop)->setter;
470                     }
471                 } else {
472                     ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
473                 }
474             }
475             OBJ_DROP_PROPERTY(cx, obj2, prop);
476 #else
477             ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
478 #endif
479             if (!ok)
480                 break;
481             if (!JSVAL_IS_PRIMITIVE(val) &&
482                 !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {
483                 ok = JS_FALSE;
484                 break;
485             }
486         }
487         if (!ok || !idap)
488             JS_DestroyIdArray(cx, ida);
489         if (!ok)
490             return NULL;
491     } else {
492         sharpid = JS_PTR_TO_UINT32(he->value);
493         if (sharpid == 0) {
494             sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
495             he->value = JS_UINT32_TO_PTR(sharpid);
496         }
497         ida = NULL;
498     }
499     if (idap)
500         *idap = ida;
501     return he;
504 JSHashEntry *
505 js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
506                     jschar **sp)
508     JSSharpObjectMap *map;
509     JSHashTable *table;
510     JSIdArray *ida;
511     JSHashNumber hash;
512     JSHashEntry *he, **hep;
513     jsatomid sharpid;
514     char buf[20];
515     size_t len;
517     if (JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) &&
518         cx->branchCallback &&
519         !cx->branchCallback(cx, NULL)) {
520         return NULL;
521     }
523     /* Set to null in case we return an early error. */
524     *sp = NULL;
525     map = &cx->sharpObjectMap;
526     table = map->table;
527     if (!table) {
528         table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
529                                 JS_CompareValues, NULL, NULL);
530         if (!table) {
531             JS_ReportOutOfMemory(cx);
532             return NULL;
533         }
534         map->table = table;
535         JS_KEEP_ATOMS(cx->runtime);
536     }
538     /* From this point the control must flow either through out: or bad:. */
539     ida = NULL;
540     if (map->depth == 0) {
541         he = MarkSharpObjects(cx, obj, &ida);
542         if (!he)
543             goto bad;
544         JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0);
545         if (!idap) {
546             JS_DestroyIdArray(cx, ida);
547             ida = NULL;
548         }
549     } else {
550         hash = js_hash_object(obj);
551         hep = JS_HashTableRawLookup(table, hash, obj);
552         he = *hep;
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         if (!he) {
562             he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
563             if (!he) {
564                 JS_ReportOutOfMemory(cx);
565                 goto bad;
566             }
567             sharpid = 0;
568             goto out;
569         }
570     }
572     sharpid = JS_PTR_TO_UINT32(he->value);
573     if (sharpid != 0) {
574         len = JS_snprintf(buf, sizeof buf, "#%u%c",
575                           sharpid >> SHARP_ID_SHIFT,
576                           (sharpid & SHARP_BIT) ? '#' : '=');
577         *sp = js_InflateString(cx, buf, &len);
578         if (!*sp) {
579             if (ida)
580                 JS_DestroyIdArray(cx, ida);
581             goto bad;
582         }
583     }
585 out:
586     JS_ASSERT(he);
587     if ((sharpid & SHARP_BIT) == 0) {
588         if (idap && !ida) {
589             ida = JS_Enumerate(cx, obj);
590             if (!ida) {
591                 if (*sp) {
592                     JS_free(cx, *sp);
593                     *sp = NULL;
594                 }
595                 goto bad;
596             }
597         }
598         map->depth++;
599     }
601     if (idap)
602         *idap = ida;
603     return he;
605 bad:
606     /* Clean up the sharpObjectMap table on outermost error. */
607     if (map->depth == 0) {
608         JS_UNKEEP_ATOMS(cx->runtime);
609         map->sharpgen = 0;
610         JS_HashTableDestroy(map->table);
611         map->table = NULL;
612     }
613     return NULL;
616 void
617 js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
619     JSSharpObjectMap *map;
620     JSIdArray *ida;
622     map = &cx->sharpObjectMap;
623     JS_ASSERT(map->depth > 0);
624     if (--map->depth == 0) {
625         JS_UNKEEP_ATOMS(cx->runtime);
626         map->sharpgen = 0;
627         JS_HashTableDestroy(map->table);
628         map->table = NULL;
629     }
630     if (idap) {
631         ida = *idap;
632         if (ida) {
633             JS_DestroyIdArray(cx, ida);
634             *idap = NULL;
635         }
636     }
639 JS_STATIC_DLL_CALLBACK(intN)
640 gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg)
642     GC_MARK((JSContext *)arg, (JSObject *)he->key, "sharp table entry", NULL);
643     return JS_DHASH_NEXT;
646 void
647 js_GCMarkSharpMap(JSContext *cx, JSSharpObjectMap *map)
649     JS_ASSERT(map->depth > 0);
650     JS_ASSERT(map->table);
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     JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, cx);
675 #define OBJ_TOSTRING_EXTRA      4       /* for 4 local GC roots */
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)
682     JSBool ok, outermost;
683     JSHashEntry *he;
684     JSIdArray *ida;
685     jschar *chars, *ochars, *vsharp;
686     const jschar *idstrchars, *vchars;
687     size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen;
688     char *comma;
689     jsint i, j, length, valcnt;
690     jsid id;
691 #if JS_HAS_GETTER_SETTER
692     JSObject *obj2;
693     JSProperty *prop;
694     uintN attrs;
695 #endif
696     jsval *val;
697     JSString *gsop[2];
698     JSAtom *atom;
699     JSString *idstr, *valstr, *str;
700     int stackDummy;
702     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
703         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
704         return JS_FALSE;
705     }
707     /*
708      * obj_toString for 1.2 calls toSource, and doesn't want the extra parens
709      * on the outside.
710      */
711     outermost = !JS_VERSION_IS_1_2(cx) && cx->sharpObjectMap.depth == 0;
712     he = js_EnterSharpObject(cx, obj, &ida, &chars);
713     if (!he)
714         return JS_FALSE;
715     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         JS_ASSERT(!ida);
722 #if JS_HAS_SHARP_VARS
723         nchars = js_strlen(chars);
724 #else
725         chars[0] = '{';
726         chars[1] = '}';
727         chars[2] = 0;
728         nchars = 2;
729 #endif
730         goto make_string;
731     }
732     JS_ASSERT(ida);
733     ok = JS_TRUE;
735     if (!chars) {
736         /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
737         chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
738         nchars = 0;
739         if (!chars)
740             goto error;
741         if (outermost)
742             chars[nchars++] = '(';
743     } else {
744         /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
745         MAKE_SHARP(he);
746         nchars = js_strlen(chars);
747         chars = (jschar *)
748             realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
749         if (!chars) {
750             free(ochars);
751             goto error;
752         }
753         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             outermost = JS_FALSE;
760         }
761     }
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;
771         /* 2 for ': ', 2 quotes around classname, 2 for ', ' after. */
772         classnchars += sizeof classpropid - 1 + 2 + 2;
773         if (ida->length)
774             classnchars += 2;
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         }
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         }
799         JS_ASSERT(nchars - onchars == 1 + classnchars);
800     } else
801 #endif
802     chars[nchars++] = '{';
804     comma = NULL;
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     val = argv + 2;
813     for (i = 0, length = ida->length; i < length; i++) {
814         /* Get strings for id and value and GC-root them via argv. */
815         id = ida->vector[i];
817 #if JS_HAS_GETTER_SETTER
819         ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
820         if (!ok)
821             goto error;
822         valcnt = 0;
823         if (prop) {
824             ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
825             if (!ok) {
826                 OBJ_DROP_PROPERTY(cx, obj2, prop);
827                 goto error;
828             }
829             if (OBJ_IS_NATIVE(obj2) &&
830                 (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
831                 if (attrs & JSPROP_GETTER) {
832                     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                     gsop[valcnt] =
838                         ATOM_TO_STRING(cx->runtime->atomState.getAtom);
839 #endif
840                     valcnt++;
841                 }
842                 if (attrs & JSPROP_SETTER) {
843                     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                     gsop[valcnt] =
849                         ATOM_TO_STRING(cx->runtime->atomState.setAtom);
850 #endif
851                     valcnt++;
852                 }
853             } else {
854                 valcnt = 1;
855                 gsop[0] = NULL;
856                 ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
857             }
858             OBJ_DROP_PROPERTY(cx, obj2, prop);
859         }
861 #else  /* !JS_HAS_GETTER_SETTER */
863         valcnt = 1;
864         gsop[0] = NULL;
865         ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
867 #endif /* !JS_HAS_GETTER_SETTER */
869         if (!ok)
870             goto error;
872         /* Convert id to a jsval and then to a string. */
873         atom = JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL;
874         id = ID_TO_VALUE(id);
875         idstr = js_ValueToString(cx, id);
876         if (!idstr) {
877             ok = JS_FALSE;
878             goto error;
879         }
880         *rval = STRING_TO_JSVAL(idstr);         /* local root */
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         if (atom
888             ? (ATOM_KEYWORD(atom) || !js_IsIdentifier(idstr))
889             : (JSID_IS_OBJECT(id) || JSID_TO_INT(id) < 0)) {
890             idstr = js_QuoteString(cx, idstr, (jschar)'\'');
891             if (!idstr) {
892                 ok = JS_FALSE;
893                 goto error;
894             }
895             *rval = STRING_TO_JSVAL(idstr);     /* local root */
896         }
897         idstrchars = JSSTRING_CHARS(idstr);
898         idstrlength = JSSTRING_LENGTH(idstr);
900         for (j = 0; j < valcnt; j++) {
901             /* Convert val[j] to its canonical source form. */
902             valstr = js_ValueToSource(cx, val[j]);
903             if (!valstr) {
904                 ok = JS_FALSE;
905                 goto error;
906             }
907             argv[j] = STRING_TO_JSVAL(valstr);  /* local root */
908             vchars = JSSTRING_CHARS(valstr);
909             vlength = JSSTRING_LENGTH(valstr);
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             if (gsop[j]) {
917                 int n = strlen(js_function_str) + 2;
918                 vchars += n;
919                 vlength -= n + 1;
920             }
921 #endif
923             /* If val[j] is a non-sharp object, consider sharpening it. */
924             vsharp = NULL;
925             vsharplength = 0;
926 #if JS_HAS_SHARP_VARS
927             if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') {
928                 he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL,
929                                          &vsharp);
930                 if (!he) {
931                     ok = JS_FALSE;
932                     goto error;
933                 }
934                 if (IS_SHARP(he)) {
935                     vchars = vsharp;
936                     vlength = js_strlen(vchars);
937                 } else {
938                     if (vsharp) {
939                         vsharplength = js_strlen(vsharp);
940                         MAKE_SHARP(he);
941                     }
942                     js_LeaveSharpObject(cx, NULL);
943                 }
944             }
945 #endif
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
955             curlen = nchars;
956             if (comma)
957                 SAFE_ADD(2);
958             SAFE_ADD(idstrlength + 1);
959             if (gsop[j])
960                 SAFE_ADD(JSSTRING_LENGTH(gsop[j]) + 1);
961             SAFE_ADD(vsharplength);
962             SAFE_ADD(vlength);
963             SAFE_ADD((outermost ? 2 : 1) + 1);
964 #undef SAFE_ADD
966             if (curlen > (size_t)-1 / sizeof(jschar))
967                 goto overflow;
969             /* Allocate 1 + 1 at end for closing brace and terminating 0. */
970             chars = (jschar *)
971                 realloc((ochars = chars), curlen * sizeof(jschar));
972             if (!chars) {
973                 /* Save code space on error: let JS_free ignore null vsharp. */
974                 JS_free(cx, vsharp);
975                 free(ochars);
976                 goto error;
977             }
979             if (comma) {
980                 chars[nchars++] = comma[0];
981                 chars[nchars++] = comma[1];
982             }
983             comma = ", ";
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             if (gsop[j]) {
997                 gsoplength = JSSTRING_LENGTH(gsop[j]);
998                 js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength);
999                 nchars += gsoplength;
1000                 chars[nchars++] = ' ';
1001             }
1002             js_strncpy(&chars[nchars], idstrchars, idstrlength);
1003             nchars += idstrlength;
1004             if (!gsop[j])
1005                 chars[nchars++] = ':';
1006 #endif
1007             if (vsharplength) {
1008                 js_strncpy(&chars[nchars], vsharp, vsharplength);
1009                 nchars += vsharplength;
1010             }
1011             js_strncpy(&chars[nchars], vchars, vlength);
1012             nchars += vlength;
1014             if (vsharp)
1015                 JS_free(cx, vsharp);
1016 #ifdef DUMP_CALL_TABLE
1017             if (outermost && nchars >= js_LogCallToSourceLimit)
1018                 break;
1019 #endif
1020         }
1021     }
1023     chars[nchars++] = '}';
1024     if (outermost)
1025         chars[nchars++] = ')';
1026     chars[nchars] = 0;
1028   error:
1029     js_LeaveSharpObject(cx, &ida);
1031     if (!ok) {
1032         if (chars)
1033             free(chars);
1034         return ok;
1035     }
1037     if (!chars) {
1038         JS_ReportOutOfMemory(cx);
1039         return JS_FALSE;
1040     }
1041   make_string:
1042     str = js_NewString(cx, chars, nchars, 0);
1043     if (!str) {
1044         free(chars);
1045         return JS_FALSE;
1046     }
1047     *rval = STRING_TO_JSVAL(str);
1048     return JS_TRUE;
1050   overflow:
1051     JS_free(cx, vsharp);
1052     free(chars);
1053     chars = NULL;
1054     goto error;
1056 #endif /* JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE */
1058 JSBool
1059 js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1060                 jsval *rval)
1062     jschar *chars;
1063     size_t nchars;
1064     const char *clazz, *prefix;
1065     JSString *str;
1067 #if JS_HAS_INITIALIZERS
1068     if (JS_VERSION_IS_1_2(cx))
1069         return js_obj_toSource(cx, obj, argc, argv, rval);
1070 #endif
1072     clazz = OBJ_GET_CLASS(cx, obj)->name;
1073     nchars = 9 + strlen(clazz);         /* 9 for "[object ]" */
1074     chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar));
1075     if (!chars)
1076         return JS_FALSE;
1078     prefix = "[object ";
1079     nchars = 0;
1080     while ((chars[nchars] = (jschar)*prefix) != 0)
1081         nchars++, prefix++;
1082     while ((chars[nchars] = (jschar)*clazz) != 0)
1083         nchars++, clazz++;
1084     chars[nchars++] = ']';
1085     chars[nchars] = 0;
1087     str = js_NewString(cx, chars, nchars, 0);
1088     if (!str) {
1089         JS_free(cx, chars);
1090         return JS_FALSE;
1091     }
1092     *rval = STRING_TO_JSVAL(str);
1093     return JS_TRUE;
1096 static JSBool
1097 js_obj_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1098                       jsval *rval)
1100     JSString *str;
1102     str = js_ValueToString(cx, argv[-1]);
1103     if (!str)
1104         return JS_FALSE;
1106     *rval = STRING_TO_JSVAL(str);
1107     return JS_TRUE;
1110 static JSBool
1111 obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1113     *rval = OBJECT_TO_JSVAL(obj);
1114     return JS_TRUE;
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)
1126     JSRuntime *rt;
1127     JSPrincipals *scopePrincipals;
1129     rt = cx->runtime;
1130     if (rt->findObjectPrincipals) {
1131         scopePrincipals = rt->findObjectPrincipals(cx, scopeobj);
1132         if (!principals || !scopePrincipals ||
1133             !principals->subsume(principals, scopePrincipals)) {
1134             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1135                                  JSMSG_BAD_INDIRECT_CALL, caller);
1136             return JS_FALSE;
1137         }
1138     }
1139     return JS_TRUE;
1142 JSObject *
1143 js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller)
1145     JSClass *clasp;
1146     JSExtendedClass *xclasp;
1147     JSObject *inner;
1149     if (!scopeobj)
1150         goto bad;
1152     OBJ_TO_INNER_OBJECT(cx, scopeobj);
1153     if (!scopeobj)
1154         return NULL;
1156     inner = scopeobj;
1158     /* XXX This is an awful gross hack. */
1159     while (scopeobj) {
1160         clasp = OBJ_GET_CLASS(cx, scopeobj);
1161         if (clasp->flags & JSCLASS_IS_EXTENDED) {
1162             xclasp = (JSExtendedClass*)clasp;
1163             if (xclasp->innerObject &&
1164                 xclasp->innerObject(cx, scopeobj) != scopeobj) {
1165                 goto bad;
1166             }
1167         }
1169         scopeobj = OBJ_GET_PARENT(cx, scopeobj);
1170     }
1172     return inner;
1174 bad:
1175     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1176                          JSMSG_BAD_INDIRECT_CALL, caller);
1177     return NULL;
1180 static JSBool
1181 obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1183     JSStackFrame *fp, *caller;
1184     JSBool indirectCall;
1185     JSObject *scopeobj;
1186     JSString *str;
1187     const char *file;
1188     uintN line;
1189     JSPrincipals *principals;
1190     JSScript *script;
1191     JSBool ok;
1192 #if JS_HAS_EVAL_THIS_SCOPE
1193     JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
1194     JSObject *setCallerScopeChain = NULL;
1195     JSBool setCallerVarObj = JS_FALSE;
1196 #endif
1198     fp = cx->fp;
1199     caller = JS_GetScriptedCaller(cx, fp);
1200     indirectCall = (caller && caller->pc && *caller->pc != JSOP_EVAL);
1202     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         return JS_FALSE;
1210     }
1212     if (!JSVAL_IS_STRING(argv[0])) {
1213         *rval = argv[0];
1214         return JS_TRUE;
1215     }
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     if (caller && !caller->varobj && !js_GetCallObject(cx, caller, NULL))
1223         return JS_FALSE;
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     scopeobj = NULL;
1231     if (argc >= 2) {
1232         if (!js_ValueToObject(cx, argv[1], &scopeobj))
1233             return JS_FALSE;
1234         argv[1] = OBJECT_TO_JSVAL(scopeobj);
1235     }
1236     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         if (indirectCall) {
1242             callerScopeChain = caller->scopeChain;
1243             if (obj != callerScopeChain) {
1244                 if (!js_CheckPrincipalsAccess(cx, obj,
1245                                               caller->script->principals,
1246                                               js_eval_str)) {
1247                     return JS_FALSE;
1248                 }
1250                 scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1);
1251                 if (!scopeobj)
1252                     return JS_FALSE;
1254                 /* Set fp->scopeChain too, for the compiler. */
1255                 caller->scopeChain = fp->scopeChain = scopeobj;
1257                 /* Remember scopeobj so we can null its private when done. */
1258                 setCallerScopeChain = scopeobj;
1259             }
1261             callerVarObj = caller->varobj;
1262             if (obj != callerVarObj) {
1263                 /* Set fp->varobj too, for the compiler. */
1264                 caller->varobj = fp->varobj = obj;
1265                 setCallerVarObj = JS_TRUE;
1266             }
1267         }
1268         /* From here on, control must exit through label out with ok set. */
1269 #endif
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         if (caller)
1277             scopeobj = caller->scopeChain;
1278 #endif
1279     }
1281     /* Ensure we compile this eval with the right object in the scope chain. */
1282     scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str);
1283     if (!scopeobj)
1284         return JS_FALSE;
1286     str = JSVAL_TO_STRING(argv[0]);
1287     if (caller) {
1288         file = caller->script->filename;
1289         line = js_PCToLineNumber(cx, caller->script, caller->pc);
1290         principals = JS_EvalFramePrincipals(cx, fp, caller);
1291     } else {
1292         file = NULL;
1293         line = 0;
1294         principals = NULL;
1295     }
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     do {
1306         fp->flags |= JSFRAME_EVAL;
1307     } while ((fp = fp->down) != caller);
1309     script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,
1310                                              JSSTRING_CHARS(str),
1311                                              JSSTRING_LENGTH(str),
1312                                              file, line);
1313     if (!script) {
1314         ok = JS_FALSE;
1315         goto out;
1316     }
1318 #if !JS_BUG_EVAL_THIS_SCOPE
1319 #if JS_HAS_SCRIPT_OBJECT
1320     if (argc < 2)
1321 #endif
1322     {
1323         /* Execute using caller's new scope object (might be a Call object). */
1324         if (caller)
1325             scopeobj = caller->scopeChain;
1326     }
1327 #endif
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     ok = js_CheckPrincipalsAccess(cx, scopeobj, principals, js_eval_str);
1334     if (ok)
1335         ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
1337     JS_DestroyScript(cx, script);
1339 out:
1340 #if JS_HAS_EVAL_THIS_SCOPE
1341     /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
1342     if (setCallerScopeChain) {
1343         caller->scopeChain = callerScopeChain;
1344         JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass);
1345         JS_SetPrivate(cx, setCallerScopeChain, NULL);
1346     }
1347     if (setCallerVarObj)
1348         caller->varobj = callerVarObj;
1349 #endif
1350     return ok;
1353 #if JS_HAS_OBJ_WATCHPOINT
1355 static JSBool
1356 obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
1357                   void *closure)
1359     JSObject *callable;
1360     JSRuntime *rt;
1361     JSStackFrame *caller;
1362     JSPrincipals *subject, *watcher;
1363     JSResolvingKey key;
1364     JSResolvingEntry *entry;
1365     uint32 generation;
1366     jsval argv[3];
1367     JSBool ok;
1369     callable = (JSObject *) closure;
1371     rt = cx->runtime;
1372     if (rt->findObjectPrincipals) {
1373         /* Skip over any obj_watch_* frames between us and the real subject. */
1374         caller = JS_GetScriptedCaller(cx, cx->fp);
1375         if (caller) {
1376             /*
1377              * Only call the watch handler if the watcher is allowed to watch
1378              * the currently executing script.
1379              */
1380             watcher = rt->findObjectPrincipals(cx, callable);
1381             subject = JS_StackFramePrincipals(cx, caller);
1383             if (watcher && subject && !watcher->subsume(watcher, subject)) {
1384                 /* Silently don't call the watch handler. */
1385                 return JS_TRUE;
1386             }
1387         }
1388     }
1390     /* Avoid recursion on (obj, id) already being watched on cx. */
1391     key.obj = obj;
1392     key.id = id;
1393     if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
1394         return JS_FALSE;
1395     if (!entry)
1396         return JS_TRUE;
1397     generation = cx->resolvingTable->generation;
1399     argv[0] = id;
1400     argv[1] = old;
1401     argv[2] = *nvp;
1402     ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp);
1403     js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
1404     return ok;
1407 static JSBool
1408 obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1410     JSObject *callable;
1411     jsval userid, value;
1412     jsid propid;
1413     uintN attrs;
1415     callable = js_ValueToCallableObject(cx, &argv[1], 0);
1416     if (!callable)
1417         return JS_FALSE;
1419     /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1420     userid = argv[0];
1421     if (!JS_ValueToId(cx, userid, &propid))
1422         return JS_FALSE;
1424     if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs))
1425         return JS_FALSE;
1426     if (attrs & JSPROP_READONLY)
1427         return JS_TRUE;
1428     return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable);
1431 static JSBool
1432 obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1434     return JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL);
1437 #endif /* JS_HAS_OBJ_WATCHPOINT */
1439 #if JS_HAS_NEW_OBJ_METHODS
1440 /*
1441  * Prototype and property query methods, to complement the 'in' and
1442  * 'instanceof' operators.
1443  */
1445 /* Proposed ECMA 15.2.4.5. */
1446 static JSBool
1447 obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1448                    jsval *rval)
1450     return js_HasOwnPropertyHelper(cx, obj, obj->map->ops->lookupProperty,
1451                                    argc, argv, rval);
1454 JSBool
1455 js_HasOwnPropertyHelper(JSContext *cx, JSObject *obj, JSLookupPropOp lookup,
1456                         uintN argc, jsval *argv, jsval *rval)
1458     jsid id;
1459     JSObject *obj2;
1460     JSProperty *prop;
1461     JSScopeProperty *sprop;
1463     if (!JS_ValueToId(cx, argv[0], &id))
1464         return JS_FALSE;
1465     if (!lookup(cx, obj, id, &obj2, &prop))
1466         return JS_FALSE;
1467     if (!prop) {
1468         *rval = JSVAL_FALSE;
1469     } else if (obj2 == obj) {
1470         *rval = JSVAL_TRUE;
1471     } else {
1472         JSClass *clasp;
1473         JSExtendedClass *xclasp;
1475         clasp = OBJ_GET_CLASS(cx, obj);
1476         xclasp = (clasp->flags & JSCLASS_IS_EXTENDED)
1477                  ? (JSExtendedClass *)clasp
1478                  : NULL;
1479         if (xclasp && xclasp->outerObject &&
1480             xclasp->outerObject(cx, obj2) == obj) {
1481             *rval = JSVAL_TRUE;
1482         } 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             sprop = (JSScopeProperty *)prop;
1499             *rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
1500         } else {
1501             *rval = JSVAL_FALSE;
1502         }
1503     }
1504     if (prop)
1505         OBJ_DROP_PROPERTY(cx, obj2, prop);
1506     return JS_TRUE;
1509 /* Proposed ECMA 15.2.4.6. */
1510 static JSBool
1511 obj_isPrototypeOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1512                   jsval *rval)
1514     JSBool b;
1516     if (!js_IsDelegate(cx, obj, *argv, &b))
1517         return JS_FALSE;
1518     *rval = BOOLEAN_TO_JSVAL(b);
1519     return JS_TRUE;
1522 /* Proposed ECMA 15.2.4.7. */
1523 static JSBool
1524 obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1525                          jsval *rval)
1527     jsid id;
1528     uintN attrs;
1529     JSObject *obj2;
1530     JSProperty *prop;
1531     JSBool ok;
1533     if (!JS_ValueToId(cx, argv[0], &id))
1534         return JS_FALSE;
1536     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1537         return JS_FALSE;
1539     if (!prop) {
1540         *rval = JSVAL_FALSE;
1541         return JS_TRUE;
1542     }
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     if (obj2 != obj &&
1556         !(OBJ_IS_NATIVE(obj2) &&
1557           SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) {
1558         OBJ_DROP_PROPERTY(cx, obj2, prop);
1559         *rval = JSVAL_FALSE;
1560         return JS_TRUE;
1561     }
1563     ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
1564     OBJ_DROP_PROPERTY(cx, obj2, prop);
1565     if (ok)
1566         *rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);
1567     return ok;
1569 #endif /* JS_HAS_NEW_OBJ_METHODS */
1571 #if JS_HAS_GETTER_SETTER
1572 static JSBool
1573 obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1574                  jsval *rval)
1576     jsval fval, junk;
1577     jsid id;
1578     uintN attrs;
1580     fval = argv[1];
1581     if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
1582         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1583                              JSMSG_BAD_GETTER_OR_SETTER,
1584                              js_getter_str);
1585         return JS_FALSE;
1586     }
1588     if (!JS_ValueToId(cx, argv[0], &id))
1589         return JS_FALSE;
1590     if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
1591         return JS_FALSE;
1592     /*
1593      * Getters and setters are just like watchpoints from an access
1594      * control point of view.
1595      */
1596     if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1597         return JS_FALSE;
1598     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);
1604 static JSBool
1605 obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1606                  jsval *rval)
1608     jsval fval, junk;
1609     jsid id;
1610     uintN attrs;
1612     fval = argv[1];
1613     if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
1614         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1615                              JSMSG_BAD_GETTER_OR_SETTER,
1616                              js_setter_str);
1617         return JS_FALSE;
1618     }
1620     if (!JS_ValueToId(cx, argv[0], &id))
1621         return JS_FALSE;
1622     if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
1623         return JS_FALSE;
1624     /*
1625      * Getters and setters are just like watchpoints from an access
1626      * control point of view.
1627      */
1628     if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1629         return JS_FALSE;
1630     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);
1636 static JSBool
1637 obj_lookupGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1638                  jsval *rval)
1640     jsid id;
1641     JSObject *pobj;
1642     JSProperty *prop;
1643     JSScopeProperty *sprop;
1645     if (!JS_ValueToId(cx, argv[0], &id))
1646         return JS_FALSE;
1647     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1648         return JS_FALSE;
1649     if (prop) {
1650         if (OBJ_IS_NATIVE(pobj)) {
1651             sprop = (JSScopeProperty *) prop;
1652             if (sprop->attrs & JSPROP_GETTER)
1653                 *rval = OBJECT_TO_JSVAL(sprop->getter);
1654         }
1655         OBJ_DROP_PROPERTY(cx, pobj, prop);
1656     }
1657     return JS_TRUE;
1660 static JSBool
1661 obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1662                  jsval *rval)
1664     jsid id;
1665     JSObject *pobj;
1666     JSProperty *prop;
1667     JSScopeProperty *sprop;
1669     if (!JS_ValueToId(cx, argv[0], &id))
1670         return JS_FALSE;
1671     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1672         return JS_FALSE;
1673     if (prop) {
1674         if (OBJ_IS_NATIVE(pobj)) {
1675             sprop = (JSScopeProperty *) prop;
1676             if (sprop->attrs & JSPROP_SETTER)
1677                 *rval = OBJECT_TO_JSVAL(sprop->setter);
1678         }
1679         OBJ_DROP_PROPERTY(cx, pobj, prop);
1680     }
1681     return JS_TRUE;
1683 #endif /* JS_HAS_GETTER_SETTER */
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
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 };
1727 static JSBool
1728 Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1730     if (argc == 0) {
1731         /* Trigger logic below to construct a blank object. */
1732         obj = NULL;
1733     } else {
1734         /* If argv[0] is null or undefined, obj comes back null. */
1735         if (!js_ValueToObject(cx, argv[0], &obj))
1736             return JS_FALSE;
1737     }
1738     if (!obj) {
1739         JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));
1740         if (cx->fp->flags & JSFRAME_CONSTRUCTING)
1741             return JS_TRUE;
1742         obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL);
1743         if (!obj)
1744             return JS_FALSE;
1745     }
1746     *rval = OBJECT_TO_JSVAL(obj);
1747     return JS_TRUE;
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)
1757     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1758     if (!proto)
1759         return js_LookupProperty(cx, obj, id, objp, propp);
1760     return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
1763 static JSBool
1764 with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1766     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1767     if (!proto)
1768         return js_GetProperty(cx, obj, id, vp);
1769     return OBJ_GET_PROPERTY(cx, proto, id, vp);
1772 static JSBool
1773 with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1775     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1776     if (!proto)
1777         return js_SetProperty(cx, obj, id, vp);
1778     return OBJ_SET_PROPERTY(cx, proto, id, vp);
1781 static JSBool
1782 with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
1783                    uintN *attrsp)
1785     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1786     if (!proto)
1787         return js_GetAttributes(cx, obj, id, prop, attrsp);
1788     return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp);
1791 static JSBool
1792 with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
1793                    uintN *attrsp)
1795     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1796     if (!proto)
1797         return js_SetAttributes(cx, obj, id, prop, attrsp);
1798     return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp);
1801 static JSBool
1802 with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
1804     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1805     if (!proto)
1806         return js_DeleteProperty(cx, obj, id, rval);
1807     return OBJ_DELETE_PROPERTY(cx, proto, id, rval);
1810 static JSBool
1811 with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
1813     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1814     if (!proto)
1815         return js_DefaultValue(cx, obj, hint, vp);
1816     return OBJ_DEFAULT_VALUE(cx, proto, hint, vp);
1819 static JSBool
1820 with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
1821                jsval *statep, jsid *idp)
1823     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1824     if (!proto)
1825         return js_Enumerate(cx, obj, enum_op, statep, idp);
1826     return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp);
1829 static JSBool
1830 with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
1831                  jsval *vp, uintN *attrsp)
1833     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1834     if (!proto)
1835         return js_CheckAccess(cx, obj, id, mode, vp, attrsp);
1836     return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp);
1839 static JSObject *
1840 with_ThisObject(JSContext *cx, JSObject *obj)
1842     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1843     if (!proto)
1844         return obj;
1845     return OBJ_THIS_OBJECT(cx, proto);
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 };
1863 static JSObjectOps *
1864 with_getObjectOps(JSContext *cx, JSClass *clasp)
1866     return &js_WithObjectOps;
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 };
1878 JSObject *
1879 js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
1881     JSObject *obj;
1883     obj = js_NewObject(cx, &js_WithClass, proto, parent);
1884     if (!obj)
1885         return NULL;
1886     obj->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(cx->fp);
1887     OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
1888     return obj;
1891 #if JS_HAS_OBJ_PROTO_PROP
1892 static JSBool
1893 With(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1895     JSObject *parent, *proto;
1896     jsval v;
1898     if (!JS_ReportErrorFlagsAndNumber(cx,
1899                                       JSREPORT_WARNING | JSREPORT_STRICT,
1900                                       js_GetErrorMessage, NULL,
1901                                       JSMSG_DEPRECATED_USAGE,
1902                                       js_WithClass.name)) {
1903         return JS_FALSE;
1904     }
1906     if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
1907         obj = js_NewWithObject(cx, NULL, NULL, -1);
1908         if (!obj)
1909             return JS_FALSE;
1910         *rval = OBJECT_TO_JSVAL(obj);
1911     }
1913     parent = cx->fp->scopeChain;
1914     if (argc > 0) {
1915         if (!js_ValueToObject(cx, argv[0], &proto))
1916             return JS_FALSE;
1917         v = OBJECT_TO_JSVAL(proto);
1918         if (!obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PROTO), &v))
1919             return JS_FALSE;
1920         if (argc > 1) {
1921             if (!js_ValueToObject(cx, argv[1], &parent))
1922                 return JS_FALSE;
1923         }
1924     }
1925     v = OBJECT_TO_JSVAL(parent);
1926     return obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PARENT), &v);
1928 #endif
1930 JSObject *
1931 js_InitObjectClass(JSContext *cx, JSObject *obj)
1933     JSObject *proto;
1934     jsval eval;
1936 #if JS_HAS_SHARP_VARS
1937     JS_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >= ATOM_INDEX_LIMIT_LOG2 + 1);
1938 #endif
1940     proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1,
1941                          object_props, object_methods, NULL, NULL);
1942     if (!proto)
1943         return NULL;
1945 #if JS_HAS_OBJ_PROTO_PROP
1946     if (!JS_InitClass(cx, obj, NULL, &js_WithClass, With, 0,
1947                       NULL, NULL, NULL, NULL)) {
1948         return NULL;
1949     }
1950 #endif
1952     /* ECMA (15.1.2.1) says 'eval' is also a property of the global object. */
1953     if (!OBJ_GET_PROPERTY(cx, proto,
1954                           ATOM_TO_JSID(cx->runtime->atomState.evalAtom),
1955                           &eval)) {
1956         return NULL;
1957     }
1958     if (!OBJ_DEFINE_PROPERTY(cx, obj,
1959                              ATOM_TO_JSID(cx->runtime->atomState.evalAtom),
1960                              eval, NULL, NULL, 0, NULL)) {
1961         return NULL;
1962     }
1964     return proto;
1967 void
1968 js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops,
1969                  JSClass *clasp)
1971     map->nrefs = nrefs;
1972     map->ops = ops;
1973     map->nslots = JS_INITIAL_NSLOTS;
1974     map->freeslot = JSSLOT_FREE(clasp);
1977 JSObjectMap *
1978 js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,
1979                 JSClass *clasp, JSObject *obj)
1981     return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj);
1984 void
1985 js_DestroyObjectMap(JSContext *cx, JSObjectMap *map)
1987     js_DestroyScope(cx, (JSScope *)map);
1990 JSObjectMap *
1991 js_HoldObjectMap(JSContext *cx, JSObjectMap *map)
1993     JS_ASSERT(map->nrefs >= 0);
1994     JS_ATOMIC_INCREMENT(&map->nrefs);
1995     return map;
1998 JSObjectMap *
1999 js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj)
2001     JS_ASSERT(map->nrefs > 0);
2002     JS_ATOMIC_DECREMENT(&map->nrefs);
2003     if (map->nrefs == 0) {
2004         map->ops->destroyObjectMap(cx, map);
2005         return NULL;
2006     }
2007     if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj)
2008         ((JSScope *)map)->object = NULL;
2009     return map;
2012 static JSBool
2013 GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
2014                   JSObject **protop);
2016 static jsval *
2017 AllocSlots(JSContext *cx, jsval *slots, uint32 nslots)
2019     size_t nbytes, obytes, minbytes;
2020     uint32 i, oslots;
2021     jsval *newslots;
2023     nbytes = (nslots + 1) * sizeof(jsval);
2024     if (slots) {
2025         oslots = slots[-1];
2026         obytes = (oslots + 1) * sizeof(jsval);
2027     } else {
2028         oslots = 0;
2029         obytes = 0;
2030     }
2032     if (nbytes <= GC_NBYTES_MAX) {
2033         newslots = (jsval *) js_NewGCThing(cx, GCX_PRIVATE, nbytes);
2034     } else {
2035         newslots = (jsval *)
2036                    JS_realloc(cx,
2037                               (obytes <= GC_NBYTES_MAX) ? NULL : slots - 1,
2038                               nbytes);
2039     }
2040     if (!newslots)
2041         return NULL;
2043     if (obytes != 0) {
2044         /* If either nbytes or obytes fit in a GC-thing, we must copy. */
2045         minbytes = JS_MIN(nbytes, obytes);
2046         if (minbytes <= GC_NBYTES_MAX)
2047             memcpy(newslots + 1, slots, minbytes - sizeof(jsval));
2049         /* If nbytes are in a GC-thing but obytes aren't, free obytes. */
2050         if (nbytes <= GC_NBYTES_MAX && obytes > GC_NBYTES_MAX)
2051             JS_free(cx, slots - 1);
2053         /* If we're extending an allocation, initialize free slots. */
2054         if (nslots > oslots) {
2055             for (i = 1 + oslots; i <= nslots; i++)
2056                 newslots[i] = JSVAL_VOID;
2057         }
2058     }
2060     newslots[0] = nslots;
2061     return ++newslots;
2064 static void
2065 FreeSlots(JSContext *cx, jsval *slots)
2067     size_t nbytes;
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     nbytes = (slots[-1] + 1) * sizeof(jsval);
2076     if (nbytes > GC_NBYTES_MAX)
2077         JS_free(cx, slots - 1);
2080 JSObject *
2081 js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
2083     JSObject *obj;
2084     JSObjectOps *ops;
2085     JSObjectMap *map;
2086     JSClass *protoclasp;
2087     uint32 nslots, i;
2088     jsval *newslots;
2089     JSTempValueRooter tvr;
2091     /* Bootstrap the ur-object, and make it the default prototype object. */
2092     if (!proto) {
2093         if (!GetClassPrototype(cx, parent, clasp->name, &proto))
2094             return NULL;
2095         if (!proto && !GetClassPrototype(cx, parent, js_Object_str, &proto))
2096             return NULL;
2097     }
2099     /* Always call the class's getObjectOps hook if it has one. */
2100     ops = clasp->getObjectOps
2101           ? clasp->getObjectOps(cx, clasp)
2102           : &js_ObjectOps;
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     obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject));
2110     if (!obj)
2111         return NULL;
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     JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(obj), &tvr);
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     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         if (!parent)
2142             parent = OBJ_GET_PARENT(cx, proto);
2144         /* Share the given prototype's map. */
2145         obj->map = js_HoldObjectMap(cx, map);
2147         /* Ensure that obj starts with the minimum slots for clasp. */
2148         nslots = JS_INITIAL_NSLOTS;
2149     } else {
2150         /* Leave parent alone.  Allocate a new map for obj. */
2151         map = ops->newObjectMap(cx, 1, ops, clasp, obj);
2152         if (!map)
2153             goto bad;
2154         obj->map = map;
2156         /* Let ops->newObjectMap set nslots so as to reserve slots. */
2157         nslots = map->nslots;
2158     }
2160     /* Allocate a slots vector, with a -1'st element telling its length. */
2161     newslots = AllocSlots(cx, NULL, nslots);
2162     if (!newslots) {
2163         js_DropObjectMap(cx, obj->map, obj);
2164         obj->map = NULL;
2165         goto bad;
2166     }
2168     /* Set the proto, parent, and class properties. */
2169     newslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
2170     newslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);
2171     newslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp);
2173     /* Clear above JSSLOT_CLASS so the GC doesn't load uninitialized memory. */
2174     for (i = JSSLOT_CLASS + 1; i < nslots; i++)
2175         newslots[i] = JSVAL_VOID;
2177     /* Store newslots after initializing all of 'em, just in case. */
2178     obj->slots = newslots;
2180     if (cx->runtime->objectHook) {
2181         JS_KEEP_ATOMS(cx->runtime);
2182         cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData);
2183         JS_UNKEEP_ATOMS(cx->runtime);
2184     }
2186 out:
2187     JS_POP_TEMP_ROOT(cx, &tvr);
2188     cx->newborn[GCX_OBJECT] = (JSGCThing *) obj;
2189     return obj;
2191 bad:
2192     obj = NULL;
2193     goto out;
2196 JSBool
2197 js_FindConstructor(JSContext *cx, JSObject *start, const char *name, jsval *vp)
2199     JSAtom *atom;
2200     JSObject *obj, *pobj;
2201     JSProperty *prop;
2202     JSScopeProperty *sprop;
2204     atom = js_Atomize(cx, name, strlen(name), 0);
2205     if (!atom)
2206         return JS_FALSE;
2208     if (start || (cx->fp && (start = cx->fp->scopeChain) != NULL)) {
2209         /* Find the topmost object in the scope chain. */
2210         do {
2211             obj = start;
2212             start = OBJ_GET_PARENT(cx, obj);
2213         } while (start);
2214     } else {
2215         obj = cx->globalObject;
2216         if (!obj) {
2217             *vp = JSVAL_VOID;
2218             return JS_TRUE;
2219         }
2220     }
2222     JS_ASSERT(OBJ_IS_NATIVE(obj));
2223     if (!js_LookupPropertyWithFlags(cx, obj, ATOM_TO_JSID(atom),
2224                                     JSRESOLVE_CLASSNAME, &pobj, &prop)) {
2225         return JS_FALSE;
2226     }
2227     if (!prop)  {
2228         *vp = JSVAL_VOID;
2229         return JS_TRUE;
2230     }
2232     JS_ASSERT(OBJ_IS_NATIVE(pobj));
2233     sprop = (JSScopeProperty *) prop;
2234     JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
2235     *vp = OBJ_GET_SLOT(cx, pobj, sprop->slot);
2236     OBJ_DROP_PROPERTY(cx, pobj, prop);
2237     return JS_TRUE;
2240 JSObject *
2241 js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
2242                    JSObject *parent, uintN argc, jsval *argv)
2244     jsval cval, rval;
2245     JSTempValueRooter argtvr, tvr;
2246     JSObject *obj, *ctor;
2248     JS_PUSH_TEMP_ROOT(cx, argc, argv, &argtvr);
2250     if (!js_FindConstructor(cx, parent, clasp->name, &cval)) {
2251         JS_POP_TEMP_ROOT(cx, &argtvr);
2252         return NULL;
2253     }
2254     if (JSVAL_IS_PRIMITIVE(cval)) {
2255         js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
2256         JS_POP_TEMP_ROOT(cx, &argtvr);
2257         return NULL;
2258     }
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     JS_PUSH_SINGLE_TEMP_ROOT(cx, cval, &tvr);
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     ctor = JSVAL_TO_OBJECT(cval);
2271     if (!parent)
2272         parent = OBJ_GET_PARENT(cx, ctor);
2273     if (!proto) {
2274         if (!OBJ_GET_PROPERTY(cx, ctor,
2275                               ATOM_TO_JSID(cx->runtime->atomState
2276                                            .classPrototypeAtom),
2277                               &rval)) {
2278             obj = NULL;
2279             goto out;
2280         }
2281         if (JSVAL_IS_OBJECT(rval))
2282             proto = JSVAL_TO_OBJECT(rval);
2283     }
2285     obj = js_NewObject(cx, clasp, proto, parent);
2286     if (!obj)
2287         goto out;
2289     if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval))
2290         goto bad;
2292     if (JSVAL_IS_PRIMITIVE(rval))
2293         goto out;
2294     obj = JSVAL_TO_OBJECT(rval);
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     if (OBJ_GET_CLASS(cx, obj) != clasp ||
2303         (!(~clasp->flags & (JSCLASS_HAS_PRIVATE |
2304                             JSCLASS_CONSTRUCT_PROTOTYPE)) &&
2305          !JS_GetPrivate(cx, obj))) {
2306         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2307                              JSMSG_WRONG_CONSTRUCTOR, clasp->name);
2308         goto bad;
2309     }
2311 out:
2312     JS_POP_TEMP_ROOT(cx, &tvr);
2313     JS_POP_TEMP_ROOT(cx, &argtvr);
2314     return obj;
2316 bad:
2317     cx->newborn[GCX_OBJECT] = NULL;
2318     obj = NULL;
2319     goto out;
2322 void
2323 js_FinalizeObject(JSContext *cx, JSObject *obj)
2325     JSObjectMap *map;
2327     /* Cope with stillborn objects that have no map. */
2328     map = obj->map;
2329     if (!map)
2330         return;
2331     JS_ASSERT(obj->slots);
2333     if (cx->runtime->objectHook)
2334         cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData);
2336     /* Remove all watchpoints with weak links to obj. */
2337     JS_ClearWatchPointsForObject(cx, obj);
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     LOCKED_OBJ_GET_CLASS(obj)->finalize(cx, obj);
2349     /* Drop map and free slots. */
2350     js_DropObjectMap(cx, map, obj);
2351     obj->map = NULL;
2352     FreeSlots(cx, obj->slots);
2353     obj->slots = NULL;
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)
2361     JSObjectMap *map;
2362     JSClass *clasp;
2363     uint32 nslots;
2364     jsval *newslots;
2366     map = obj->map;
2367     JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
2368     clasp = LOCKED_OBJ_GET_CLASS(obj);
2369     if (map->freeslot == JSSLOT_FREE(clasp)) {
2370         /* Adjust map->freeslot to include computed reserved slots, if any. */
2371         if (clasp->reserveSlots)
2372             map->freeslot += clasp->reserveSlots(cx, obj);
2373     }
2374     nslots = map->nslots;
2375     if (map->freeslot >= nslots) {
2376         nslots = map->freeslot;
2377         JS_ASSERT(nslots >= JS_INITIAL_NSLOTS);
2378         nslots += (nslots + 1) / 2;
2380         newslots = AllocSlots(cx, obj->slots, nslots);
2381         if (!newslots)
2382             return JS_FALSE;
2383         map->nslots = nslots;
2384         obj->slots = newslots;
2385     }
2387 #ifdef TOO_MUCH_GC
2388     obj->slots[map->freeslot] = JSVAL_VOID;
2389 #endif
2390     *slotp = map->freeslot++;
2391     return JS_TRUE;
2394 void
2395 js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
2397     JSObjectMap *map;
2398     uint32 nslots;
2399     jsval *newslots;
2401     OBJ_CHECK_SLOT(obj, slot);
2402     obj->slots[slot] = JSVAL_VOID;
2403     map = obj->map;
2404     JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
2405     if (map->freeslot == slot + 1)
2406         map->freeslot = slot;
2407     nslots = map->nslots;
2408     if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) {
2409         nslots = map->freeslot;
2410         nslots += nslots / 2;
2411         if (nslots < JS_INITIAL_NSLOTS)
2412             nslots = JS_INITIAL_NSLOTS;
2414         newslots = AllocSlots(cx, obj->slots, nslots);
2415         if (!newslots)
2416             return;
2417         map->nslots = nslots;
2418         obj->slots = newslots;
2419     }
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
2432 /* JSVAL_INT_MAX as a string */
2433 #define JSVAL_INT_MAX_STRING "1073741823"
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
2453 static jsid
2454 CheckForStringIndex(jsid id, const jschar *cp, const jschar *end,
2455                     JSBool negative)
2457     jsuint index = JS7_UNDEC(*cp++);
2458     jsuint oldIndex = 0;
2459     jsuint c = 0;
2461     if (index != 0) {
2462         while (JS7_ISDEC(*cp)) {
2463             oldIndex = index;
2464             c = JS7_UNDEC(*cp);
2465             index = 10 * index + c;
2466             cp++;
2467         }
2468     }
2469     if (cp == end &&
2470         (oldIndex < (JSVAL_INT_MAX / 10) ||
2471          (oldIndex == (JSVAL_INT_MAX / 10) &&
2472           c <= (JSVAL_INT_MAX % 10)))) {
2473         if (negative)
2474             index = 0 - index;
2475         id = INT_TO_JSID((jsint)index);
2476     }
2477     return id;
2480 static JSBool
2481 HidePropertyName(JSContext *cx, jsid *idp)
2483     jsid id;
2484     JSAtom *atom, *hidden;
2486     id = *idp;
2487     JS_ASSERT(JSID_IS_ATOM(id));
2489     atom = JSID_TO_ATOM(id);
2490     JS_ASSERT(!(atom->flags & ATOM_HIDDEN));
2491     JS_ASSERT(ATOM_IS_STRING(atom));
2493     hidden = js_AtomizeString(cx, ATOM_TO_STRING(atom), ATOM_HIDDEN);
2494     if (!hidden)
2495         return JS_FALSE;
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     hidden->entry.value = atom;
2504     *idp = ATOM_TO_JSID(hidden);
2505     return JS_TRUE;
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)
2513     if (!HidePropertyName(cx, &id))
2514         return NULL;
2516     flags |= SPROP_IS_HIDDEN;
2517     return js_AddNativeProperty(cx, obj, id, getter, setter, slot, attrs,
2518                                 flags, shortid);
2521 JSBool
2522 js_LookupHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
2523                         JSProperty **propp)
2525     return HidePropertyName(cx, &id) &&
2526            js_LookupProperty(cx, obj, id, objp, propp);
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)
2534     JSScope *scope;
2535     JSScopeProperty *sprop;
2537     JS_LOCK_OBJ(cx, obj);
2538     scope = js_GetMutableScope(cx, obj);
2539     if (!scope) {
2540         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         CHECK_FOR_STRING_INDEX(id);
2547         sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs,
2548                                     flags, shortid);
2549     }
2550     JS_UNLOCK_OBJ(cx, obj);
2551     return sprop;
2554 JSScopeProperty *
2555 js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
2556                              JSScopeProperty *sprop, uintN attrs, uintN mask,
2557                              JSPropertyOp getter, JSPropertyOp setter)
2559     JSScope *scope;
2561     JS_LOCK_OBJ(cx, obj);
2562     scope = js_GetMutableScope(cx, obj);
2563     if (!scope) {
2564         sprop = NULL;
2565     } else {
2566         sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, attrs, mask,
2567                                             getter, setter);
2568         if (sprop) {
2569             PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, sprop->id,
2570                                 sprop);
2571         }
2572     }
2573     JS_UNLOCK_OBJ(cx, obj);
2574     return sprop;
2577 JSBool
2578 js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
2579                   JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
2580                   JSProperty **propp)
2582     return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs,
2583                                    0, 0, propp);
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
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)
2611     JSClass *clasp;
2612     JSScope *scope;
2613     JSProperty *prop;
2614     JSScopeProperty *sprop;
2616     /*
2617      * Handle old bug that took empty string as zero index.  Also convert
2618      * string indices to integers if appropriate.
2619      */
2620     CHECK_FOR_STRING_INDEX(id);
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     if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
2629         JSObject *pobj;
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         if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
2640             return JS_FALSE;
2641         sprop = (JSScopeProperty *) prop;
2642         if (sprop &&
2643             pobj == obj &&
2644             (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
2645             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);
2654             /* NB: obj == pobj, so we can share unlock code at the bottom. */
2655             if (!sprop)
2656                 goto bad;
2657             goto out;
2658         }
2660         if (prop) {
2661             /* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */
2662             OBJ_DROP_PROPERTY(cx, pobj, prop);
2663             prop = NULL;
2664         }
2665     }
2666 #endif /* JS_HAS_GETTER_SETTER */
2668     /* Lock if object locking is required by this implementation. */
2669     JS_LOCK_OBJ(cx, obj);
2671     /* Use the object's class getter and setter by default. */
2672     clasp = LOCKED_OBJ_GET_CLASS(obj);
2673     if (!getter)
2674         getter = clasp->getProperty;
2675     if (!setter)
2676         setter = clasp->setProperty;
2678     /* Get obj's own scope if it has one, or create a new one for obj. */
2679     scope = js_GetMutableScope(cx, obj);
2680     if (!scope)
2681         goto bad;
2683     /* Add the property to scope, or replace an existing one of the same id. */
2684     if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
2685         attrs |= JSPROP_SHARED;
2686     sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
2687                                 SPROP_INVALID_SLOT, attrs, flags, shortid);
2688     if (!sprop)
2689         goto bad;
2691     /* Store value before calling addProperty, in case the latter GC's. */
2692     if (SPROP_HAS_VALID_SLOT(sprop, scope))
2693         LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value);
2695     /* XXXbe called with lock held */
2696     ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value,
2697                         js_RemoveScopeProperty(cx, scope, id);
2698                         goto bad);
2700 #if JS_HAS_GETTER_SETTER
2701 out:
2702 #endif
2703     PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
2704     if (propp)
2705         *propp = (JSProperty *) sprop;
2706     else
2707         JS_UNLOCK_OBJ(cx, obj);
2708     return JS_TRUE;
2710 bad:
2711     JS_UNLOCK_OBJ(cx, obj);
2712     return JS_FALSE;
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)
2723     JSScript *script;
2724     jsbytecode *endpc;
2725     JSOp op;
2726     JSAtom *atom;
2728     if (!cx->fp)
2729         return JS_FALSE;
2730     script = cx->fp->script;
2731     for (endpc = script->code + script->length; pc < endpc; pc++) {
2732         /* General case: a branch or equality op follows the access. */
2733         op = (JSOp) *pc;
2734         if (js_CodeSpec[op].format & JOF_DETECTING)
2735             return JS_TRUE;
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         if (op == JSOP_NULL) {
2742             if (++pc < endpc)
2743                 return *pc == JSOP_EQ || *pc == JSOP_NE;
2744             break;
2745         }
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         if (op == JSOP_NAME) {
2753             atom = GET_ATOM(cx, script, pc);
2754             if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] &&
2755                 (pc += js_CodeSpec[op].length) < endpc) {
2756                 op = (JSOp) *pc;
2757                 return op == JSOP_EQ || op == JSOP_NE ||
2758                        op == JSOP_NEW_EQ || op == JSOP_NEW_NE;
2759             }
2760             break;
2761         }
2763         /* At this point, anything but grouping means we're not detecting. */
2764         if (op != JSOP_GROUP)
2765             break;
2766     }
2767     return JS_FALSE;
2770 JS_FRIEND_API(JSBool)
2771 js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
2772                   JSProperty **propp)
2774     return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp);
2777 JSBool
2778 js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
2779                            JSObject **objp, JSProperty **propp)
2781     JSObject *start, *obj2, *proto;
2782     JSScope *scope;
2783     JSScopeProperty *sprop;
2784     JSClass *clasp;
2785     JSResolveOp resolve;
2786     JSResolvingKey key;
2787     JSResolvingEntry *entry;
2788     uint32 generation;
2789     JSNewResolveOp newresolve;
2790     jsbytecode *pc;
2791     const JSCodeSpec *cs;
2792     uint32 format;
2793     JSBool ok;
2795     /*
2796      * Handle old bug that took empty string as zero index.  Also convert
2797      * string indices to integers if appropriate.
2798      */
2799     CHECK_FOR_STRING_INDEX(id);
2801     /* Search scopes starting with obj and following the prototype link. */
2802     start = obj;
2803     for (;;) {
2804         JS_LOCK_OBJ(cx, obj);
2805         scope = OBJ_SCOPE(obj);
2806         if (scope->object == obj) {
2807             sprop = SCOPE_GET_PROPERTY(scope, id);
2808         } else {
2809             /* Shared prototype scope: try resolve before lookup. */
2810             sprop = NULL;
2811         }
2813         /* Try obj's class resolve hook if id was not found in obj's scope. */
2814         if (!sprop) {
2815             clasp = LOCKED_OBJ_GET_CLASS(obj);
2816             resolve = clasp->resolve;
2817             if (resolve != JS_ResolveStub) {
2818                 /* Avoid recursion on (obj, id) already being resolved on cx. */
2819                 key.obj = obj;
2820                 key.id = id;
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                 if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) {
2829                     JS_UNLOCK_OBJ(cx, obj);
2830                     return JS_FALSE;
2831                 }
2832                 if (!entry) {
2833                     /* Already resolving id in obj -- dampen recursion. */
2834                     JS_UNLOCK_OBJ(cx, obj);
2835                     goto out;
2836                 }
2837                 generation = cx->resolvingTable->generation;
2839                 /* Null *propp here so we can test it at cleanup: safely. */
2840                 *propp = NULL;
2842                 if (clasp->flags & JSCLASS_NEW_RESOLVE) {
2843                     newresolve = (JSNewResolveOp)resolve;
2844                     if (!(flags & JSRESOLVE_CLASSNAME) &&
2845                         cx->fp &&
2846                         (pc = cx->fp->pc)) {
2847                         cs = &js_CodeSpec[*pc];
2848                         format = cs->format;
2849                         if ((format & JOF_MODEMASK) != JOF_NAME)
2850                             flags |= JSRESOLVE_QUALIFIED;
2851                         if ((format & JOF_ASSIGNING) ||
2852                             (cx->fp->flags & JSFRAME_ASSIGNING)) {
2853                             flags |= JSRESOLVE_ASSIGNING;
2854                         } else {
2855                             pc += cs->length;
2856                             if (Detecting(cx, pc))
2857                                 flags |= JSRESOLVE_DETECTING;
2858                         }
2859                         if (format & JOF_DECLARING)
2860                             flags |= JSRESOLVE_DECLARING;
2861                     }
2862                     obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START)
2863                            ? start
2864                            : NULL;
2865                     JS_UNLOCK_OBJ(cx, obj);
2867                     /* Protect id and all atoms from a GC nested in resolve. */
2868                     JS_KEEP_ATOMS(cx->runtime);
2869                     ok = newresolve(cx, obj, ID_TO_VALUE(id), flags, &obj2);
2870                     JS_UNKEEP_ATOMS(cx->runtime);
2871                     if (!ok)
2872                         goto cleanup;
2874                     JS_LOCK_OBJ(cx, obj);
2875                     if (obj2) {
2876                         /* Resolved: juggle locks and lookup id again. */
2877                         if (obj2 != obj) {
2878                             JS_UNLOCK_OBJ(cx, obj);
2879                             JS_LOCK_OBJ(cx, obj2);
2880                         }
2881                         scope = OBJ_SCOPE(obj2);
2882                         if (!MAP_IS_NATIVE(&scope->map)) {
2883                             /* Whoops, newresolve handed back a foreign obj2. */
2884                             JS_ASSERT(obj2 != obj);
2885                             JS_UNLOCK_OBJ(cx, obj2);
2886                             ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp);
2887                             if (!ok || *propp)
2888                                 goto cleanup;
2889                             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                             if (scope->object == obj2)
2900                                 sprop = SCOPE_GET_PROPERTY(scope, id);
2901                         }
2902                         if (sprop) {
2903                             JS_ASSERT(obj2 == scope->object);
2904                             obj = obj2;
2905                         } else if (obj2 != obj) {
2906                             JS_UNLOCK_OBJ(cx, obj2);
2907                             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                     JS_UNLOCK_OBJ(cx, obj);
2916                     ok = resolve(cx, obj, ID_TO_VALUE(id));
2917                     if (!ok)
2918                         goto cleanup;
2919                     JS_LOCK_OBJ(cx, obj);
2920                     scope = OBJ_SCOPE(obj);
2921                     JS_ASSERT(MAP_IS_NATIVE(&scope->map));
2922                     if (scope->object == obj)
2923                         sprop = SCOPE_GET_PROPERTY(scope, id);
2924                 }
2926             cleanup:
2927                 js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
2928                 if (!ok || *propp)
2929                     return ok;
2930             }
2931         }
2933         if (sprop) {
2934             JS_ASSERT(OBJ_SCOPE(obj) == scope);
2935             *objp = scope->object;      /* XXXbe hide in jsscope.[ch] */
2937             *propp = (JSProperty *) sprop;
2938             return JS_TRUE;
2939         }
2941         proto = LOCKED_OBJ_GET_PROTO(obj);
2942         JS_UNLOCK_OBJ(cx, obj);
2943         if (!proto)
2944             break;
2945         if (!OBJ_IS_NATIVE(proto))
2946             return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
2947         obj = proto;
2948     }
2950 out:
2951     *objp = NULL;
2952     *propp = NULL;
2953     return JS_TRUE;
2956 JS_FRIEND_API(JSBool)
2957 js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
2958                 JSProperty **propp)
2960     JSRuntime *rt;
2961     JSObject *obj, *pobj, *lastobj;
2962     JSScopeProperty *sprop;
2963     JSProperty *prop;
2965     rt = cx->runtime;
2966     obj = cx->fp->scopeChain;
2967     do {
2968         /* Try the property cache and return immediately on cache hit. */
2969         if (OBJ_IS_NATIVE(obj)) {
2970             JS_LOCK_OBJ(cx, obj);
2971             PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop);
2972             if (sprop) {
2973                 JS_ASSERT(OBJ_IS_NATIVE(obj));
2974                 *objp = obj;
2975                 *pobjp = obj;
2976                 *propp = (JSProperty *) sprop;
2977                 return JS_TRUE;
2978             }
2979             JS_UNLOCK_OBJ(cx, obj);
2980         }
2982         /* If cache miss, take the slow path. */
2983         if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
2984             return JS_FALSE;
2985         if (prop) {
2986             if (OBJ_IS_NATIVE(pobj)) {
2987                 sprop = (JSScopeProperty *) prop;
2988                 PROPERTY_CACHE_FILL(&rt->propertyCache, pobj, id, sprop);
2989             }
2990             *objp = obj;
2991             *pobjp = pobj;
2992             *propp = prop;
2993             return JS_TRUE;
2994         }
2995         lastobj = obj;
2996     } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);
2998     *objp = lastobj;
2999     *pobjp = NULL;
3000     *propp = NULL;
3001     return JS_TRUE;
3004 JSObject *
3005 js_FindIdentifierBase(JSContext *cx, jsid id)
3007     JSObject *obj, *pobj;
3008     JSProperty *prop;
3010     /*
3011      * Look for id's property along the "with" statement chain and the
3012      * statically-linked scope chain.
3013      */
3014     if (!js_FindProperty(cx, id, &obj, &pobj, &prop))
3015         return NULL;
3016     if (prop) {
3017         OBJ_DROP_PROPERTY(cx, pobj, prop);
3018         return obj;
3019     }
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     JS_ASSERT(obj);
3027     /*
3028      * Property not found.  Give a strict warning if binding an undeclared
3029      * top-level variable.
3030      */
3031     if (JS_HAS_STRICT_OPTION(cx)) {
3032         JSString *str = JSVAL_TO_STRING(ID_TO_VALUE(id));
3033         if (!JS_ReportErrorFlagsAndNumber(cx,
3034                                           JSREPORT_WARNING | JSREPORT_STRICT,
3035                                           js_GetErrorMessage, NULL,
3036                                           JSMSG_UNDECLARED_VAR,
3037                                           JS_GetStringBytes(str))) {
3038             return NULL;
3039         }
3040     }
3041     return obj;
3044 JSBool
3045 js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
3047     JSObject *obj2;
3048     JSProperty *prop;
3049     JSScope *scope;
3050     JSScopeProperty *sprop;
3051     uint32 slot;
3053     /*
3054      * Handle old bug that took empty string as zero index.  Also convert
3055      * string indices to integers if appropriate.
3056      */
3057     CHECK_FOR_STRING_INDEX(id);
3059     if (!js_LookupProperty(cx, obj, id, &obj2, &prop))
3060         return JS_FALSE;
3061     if (!prop) {
3062         jsval default_val;
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         default_val = JSVAL_VOID;
3071 #endif
3072         *vp = default_val;
3074         if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp))
3075             return JS_FALSE;
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         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             jsbytecode *pc;
3087             JSString *str;
3089             /* Kludge to allow (typeof foo == "undefined") tests. */
3090             JS_ASSERT(cx->fp->script);
3091             pc = cx->fp->pc;
3092             pc += js_CodeSpec[*pc].length;
3093             if (Detecting(cx, pc))
3094                 return JS_TRUE;
3096             /* Ok, bad undefined property reference: whine about it. */
3097             str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
3098                                              ID_TO_VALUE(id), NULL);
3099             if (!str ||
3100                 !JS_ReportErrorFlagsAndNumber(cx,
3101                                               JSREPORT_WARNING|JSREPORT_STRICT,
3102                                               js_GetErrorMessage, NULL,
3103                                               JSMSG_UNDEFINED_PROP,
3104                                               JS_GetStringBytes(str))) {
3105                 return JS_FALSE;
3106             }
3107         }
3108         return JS_TRUE;
3109     }
3111     if (!OBJ_IS_NATIVE(obj2)) {
3112         OBJ_DROP_PROPERTY(cx, obj2, prop);
3113         return OBJ_GET_PROPERTY(cx, obj2, id, vp);
3114     }
3116     /* Unlock obj2 before calling getter, relock after to avoid deadlock. */
3117     scope = OBJ_SCOPE(obj2);
3118     sprop = (JSScopeProperty *) prop;
3119     slot = sprop->slot;
3120     if (slot != SPROP_INVALID_SLOT) {
3121         JS_ASSERT(slot < obj2->map->freeslot);
3122         *vp = LOCKED_OBJ_GET_SLOT(obj2, slot);
3124         /* If sprop has a stub getter, we're done. */
3125         if (!sprop->getter)
3126             goto out;
3127     } else {
3128         *vp = JSVAL_VOID;
3129     }
3131     JS_UNLOCK_SCOPE(cx, scope);
3132     if (!SPROP_GET(cx, sprop, obj, obj2, vp))
3133         return JS_FALSE;
3134     JS_LOCK_SCOPE(cx, scope);
3136     if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
3137         LOCKED_OBJ_SET_SLOT(obj2, slot, *vp);
3138         PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj2, id, sprop);
3139     }
3141 out:
3142     JS_UNLOCK_SCOPE(cx, scope);
3143     return JS_TRUE;
3146 JSBool
3147 js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
3149     JSObject *pobj;
3150     JSProperty *prop;
3151     JSScopeProperty *sprop;
3152     JSScope *scope;
3153     uintN attrs, flags;
3154     intN shortid;
3155     JSClass *clasp;
3156     JSPropertyOp getter, setter;
3157     jsval pval;
3158     uint32 slot;
3160     /*
3161      * Handle old bug that took empty string as zero index.  Also convert
3162      * string indices to integers if appropriate.
3163      */
3164     CHECK_FOR_STRING_INDEX(id);
3166     if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
3167         return JS_FALSE;
3169     if (prop && !OBJ_IS_NATIVE(pobj)) {
3170         OBJ_DROP_PROPERTY(cx, pobj, prop);
3171         prop = NULL;
3172     }
3173     sprop = (JSScopeProperty *) prop;
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     attrs = JSPROP_ENUMERATE;
3184     flags = 0;
3185     shortid = 0;
3186     clasp = OBJ_GET_CLASS(cx, obj);
3187     getter = clasp->getProperty;
3188     setter = clasp->setProperty;
3190     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         scope = OBJ_SCOPE(pobj);
3198         attrs = sprop->attrs;
3199         if ((attrs & JSPROP_READONLY) ||
3200             (SCOPE_IS_SEALED(scope) && pobj == obj)) {
3201             JS_UNLOCK_SCOPE(cx, scope);
3202             if ((attrs & JSPROP_READONLY) && JS_VERSION_IS_ECMA(cx))
3203                 return JS_TRUE;
3204             goto read_only_error;
3205         }
3207         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             JS_UNLOCK_SCOPE(cx, scope);
3216             /* Don't clone a shared prototype property. */
3217             if (attrs & JSPROP_SHARED)
3218                 return SPROP_SET(cx, sprop, obj, pobj, vp);
3220             /* Restore attrs to the ECMA default for new properties. */
3221             attrs = JSPROP_ENUMERATE;
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             if (sprop->flags & SPROP_HAS_SHORTID) {
3231                 flags = SPROP_HAS_SHORTID;
3232                 shortid = sprop->shortid;
3233                 getter = sprop->getter;
3234                 setter = sprop->setter;
3235             }
3237             /*
3238              * Forget we found the proto-property now that we've copied any
3239              * needed member values.
3240              */
3241             sprop = NULL;
3242         }
3243 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3244     } else {
3245         scope = NULL;
3246 #endif
3247     }
3249     if (!sprop) {
3250         if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj)
3251             goto read_only_error;
3253         /* Find or make a property descriptor with the right heritage. */
3254         JS_LOCK_OBJ(cx, obj);
3255         scope = js_GetMutableScope(cx, obj);
3256         if (!scope) {
3257             JS_UNLOCK_OBJ(cx, obj);
3258             return JS_FALSE;
3259         }
3260         if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
3261             attrs |= JSPROP_SHARED;
3262         sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
3263                                     SPROP_INVALID_SLOT, attrs, flags, shortid);
3264         if (!sprop) {
3265             JS_UNLOCK_SCOPE(cx, scope);
3266             return JS_FALSE;
3267         }
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         if (SPROP_HAS_VALID_SLOT(sprop, scope))
3275             LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID);
3277         /* XXXbe called with obj locked */
3278         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);
3283         PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
3284     }
3286     /* Get the current property value from its slot. */
3287     slot = sprop->slot;
3288     if (slot != SPROP_INVALID_SLOT) {
3289         JS_ASSERT(slot < obj->map->freeslot);
3290         pval = LOCKED_OBJ_GET_SLOT(obj, slot);
3292         /* If sprop has a stub setter, keep scope locked and just store *vp. */
3293         if (!sprop->setter)
3294             goto set_slot;
3295     }
3297     /* Avoid deadlock by unlocking obj's scope while calling sprop's setter. */
3298     JS_UNLOCK_SCOPE(cx, scope);
3300     /* Let the setter modify vp before copying from it to obj->slots[slot]. */
3301     if (!SPROP_SET(cx, sprop, obj, obj, vp))
3302         return JS_FALSE;
3304     /* Relock obj's scope until we are done with sprop. */
3305     JS_LOCK_SCOPE(cx, scope);
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     if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
3313   set_slot:
3314         GC_POKE(cx, pval);
3315         LOCKED_OBJ_SET_SLOT(obj, slot, *vp);
3316     }
3317     JS_UNLOCK_SCOPE(cx, scope);
3318     return JS_TRUE;
3320   read_only_error: {
3321     JSString *str = js_DecompileValueGenerator(cx,
3322                                                JSDVG_IGNORE_STACK,
3323                                                ID_TO_VALUE(id),
3324                                                NULL);
3325     if (str) {
3326         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3327                              JSMSG_READ_ONLY,
3328                              JS_GetStringBytes(str));
3329     }
3330     return JS_FALSE;
3331   }
3334 JSBool
3335 js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
3336                  uintN *attrsp)
3338     JSBool noprop, ok;
3339     JSScopeProperty *sprop;
3341     noprop = !prop;
3342     if (noprop) {
3343         if (!js_LookupProperty(cx, obj, id, &obj, &prop))
3344             return JS_FALSE;
3345         if (!prop) {
3346             *attrsp = 0;
3347             return JS_TRUE;
3348         }
3349         if (!OBJ_IS_NATIVE(obj)) {
3350             ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp);
3351             OBJ_DROP_PROPERTY(cx, obj, prop);
3352             return ok;
3353         }
3354     }
3355     sprop = (JSScopeProperty *)prop;
3356     *attrsp = sprop->attrs;
3357     if (noprop)
3358         OBJ_DROP_PROPERTY(cx, obj, prop);
3359     return JS_TRUE;
3362 JSBool
3363 js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
3364                  uintN *attrsp)
3366     JSBool noprop, ok;
3367     JSScopeProperty *sprop;
3369     noprop = !prop;
3370     if (noprop) {
3371         if (!js_LookupProperty(cx, obj, id, &obj, &prop))
3372             return JS_FALSE;
3373         if (!prop)
3374             return JS_TRUE;
3375         if (!OBJ_IS_NATIVE(obj)) {
3376             ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, attrsp);
3377             OBJ_DROP_PROPERTY(cx, obj, prop);
3378             return ok;
3379         }
3380     }
3381     sprop = (JSScopeProperty *)prop;
3382     sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, *attrsp, 0,
3383                                          sprop->getter, sprop->setter);
3384     if (noprop)
3385         OBJ_DROP_PROPERTY(cx, obj, prop);
3386     return (sprop != NULL);
3389 JSBool
3390 js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
3392 #if JS_HAS_PROP_DELETE
3394     JSObject *proto;
3395     JSProperty *prop;
3396     JSScopeProperty *sprop;
3397     JSString *str;
3398     JSScope *scope;
3399     JSBool ok;
3401     *rval = JS_VERSION_IS_ECMA(cx) ? JSVAL_TRUE : JSVAL_VOID;
3403     /*
3404      * Handle old bug that took empty string as zero index.  Also convert
3405      * string indices to integers if appropriate.
3406      */
3407     CHECK_FOR_STRING_INDEX(id);
3409     if (!js_LookupProperty(cx, obj, id, &proto, &prop))
3410         return JS_FALSE;
3411     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         if (prop) {
3419             if (OBJ_IS_NATIVE(proto)) {
3420                 sprop = (JSScopeProperty *)prop;
3421                 if (SPROP_IS_SHARED_PERMANENT(sprop))
3422                     *rval = JSVAL_FALSE;
3423             }
3424             OBJ_DROP_PROPERTY(cx, proto, prop);
3425             if (*rval == JSVAL_FALSE)
3426                 return JS_TRUE;
3427         }
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         return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, ID_TO_VALUE(id),
3435                                                    rval);
3436     }
3438     sprop = (JSScopeProperty *)prop;
3439     if (sprop->attrs & JSPROP_PERMANENT) {
3440         OBJ_DROP_PROPERTY(cx, obj, prop);
3441         if (JS_VERSION_IS_ECMA(cx)) {
3442             *rval = JSVAL_FALSE;
3443             return JS_TRUE;
3444         }
3445         str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
3446                                          ID_TO_VALUE(id), NULL);
3447         if (str) {
3448             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3449                                  JSMSG_PERMANENT, JS_GetStringBytes(str));
3450         }
3451         return JS_FALSE;
3452     }
3454     /* XXXbe called with obj locked */
3455     if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, SPROP_USERID(sprop),
3456                                                 rval)) {
3457         OBJ_DROP_PROPERTY(cx, obj, prop);
3458         return JS_FALSE;
3459     }
3461     scope = OBJ_SCOPE(obj);
3462     if (SPROP_HAS_VALID_SLOT(sprop, scope))
3463         GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot));
3465     PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, NULL);
3466     ok = js_RemoveScopeProperty(cx, scope, id);
3467     OBJ_DROP_PROPERTY(cx, obj, prop);
3468     return ok;
3470 #else  /* !JS_HAS_PROP_DELETE */
3472     jsval null = JSVAL_NULL;
3474     *rval = JSVAL_VOID;
3475     return js_SetProperty(cx, obj, id, &null);
3477 #endif /* !JS_HAS_PROP_DELETE */
3480 JSBool
3481 js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
3483     jsval v;
3484     JSString *str;
3486     v = OBJECT_TO_JSVAL(obj);
3487     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         if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL,
3494                           &v)) {
3495             return JS_FALSE;
3496         }
3498         if (!JSVAL_IS_PRIMITIVE(v)) {
3499             if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
3500                 return JS_FALSE;
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             if (!JSVAL_IS_PRIMITIVE(v) && JS_VERSION_IS_1_2(cx)) {
3508                 char *bytes = JS_smprintf("[object %s]",
3509                                           OBJ_GET_CLASS(cx, obj)->name);
3510                 if (!bytes)
3511                     return JS_FALSE;
3512                 str = JS_NewString(cx, bytes, strlen(bytes));
3513                 if (!str) {
3514                     free(bytes);
3515                     return JS_FALSE;
3516                 }
3517                 v = STRING_TO_JSVAL(str);
3518                 goto out;
3519             }
3520         }
3521         break;
3523       default:
3524         if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
3525             return JS_FALSE;
3526         if (!JSVAL_IS_PRIMITIVE(v)) {
3527             JSType type = JS_TypeOfValue(cx, v);
3528             if (type == hint ||
3529                 (type == JSTYPE_FUNCTION && hint == JSTYPE_OBJECT)) {
3530                 goto out;
3531             }
3532             /* Don't convert to string (source object literal) for JS1.2. */
3533             if (JS_VERSION_IS_1_2(cx) && hint == JSTYPE_BOOLEAN)
3534                 goto out;
3535             if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0,
3536                               NULL, &v))
3537                 return JS_FALSE;
3538         }
3539         break;
3540     }
3541     if (!JSVAL_IS_PRIMITIVE(v)) {
3542         /* Avoid recursive death through js_DecompileValueGenerator. */
3543         if (hint == JSTYPE_STRING) {
3544             str = JS_InternString(cx, OBJ_GET_CLASS(cx, obj)->name);
3545             if (!str)
3546                 return JS_FALSE;
3547         } else {
3548             str = NULL;
3549         }
3550         *vp = OBJECT_TO_JSVAL(obj);
3551         str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, str);
3552         if (str) {
3553             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         return JS_FALSE;
3561     }
3562 out:
3563     *vp = v;
3564     return JS_TRUE;
3567 JSIdArray *
3568 js_NewIdArray(JSContext *cx, jsint length)
3570     JSIdArray *ida;
3572     ida = (JSIdArray *)
3573           JS_malloc(cx, sizeof(JSIdArray) + (length-1) * sizeof(jsval));
3574     if (ida)
3575         ida->length = length;
3576     return ida;
3579 JSIdArray *
3580 js_SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length)
3582     JSIdArray *rida;
3584     rida = (JSIdArray *)
3585            JS_realloc(cx, ida, sizeof(JSIdArray) + (length-1) * sizeof(jsval));
3586     if (!rida)
3587         JS_DestroyIdArray(cx, ida);
3588     else
3589         rida->length = length;
3590     return rida;
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 };
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)
3610     JSRuntime *rt;
3611     JSObject *proto;
3612     JSClass *clasp;
3613     JSEnumerateOp enumerate;
3614     JSScopeProperty *sprop, *lastProp;
3615     jsint i, length;
3616     JSScope *scope;
3617     JSIdArray *ida;
3618     JSNativeIteratorState *state;
3620     rt = cx->runtime;
3621     clasp = OBJ_GET_CLASS(cx, obj);
3622     enumerate = clasp->enumerate;
3623     if (clasp->flags & JSCLASS_NEW_ENUMERATE)
3624         return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
3626     switch (enum_op) {
3627       case JSENUMERATE_INIT:
3628         if (!enumerate(cx, obj))
3629             return JS_FALSE;
3630         length = 0;
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         JS_LOCK_OBJ(cx, obj);
3638         scope = OBJ_SCOPE(obj);
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         proto = OBJ_GET_PROTO(cx, obj);
3646         if (proto && scope == OBJ_SCOPE(proto)) {
3647             ida = js_NewIdArray(cx, 0);
3648             if (!ida) {
3649                 JS_UNLOCK_OBJ(cx, obj);
3650                 return JS_FALSE;
3651             }
3652         } else {
3653             /* Object has a private scope; Enumerate all props in scope. */
3654             for (sprop = lastProp = SCOPE_LAST_PROP(scope); sprop;
3655                  sprop = sprop->parent) {
3656                 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                     length++;
3665                 }
3666             }
3667             ida = js_NewIdArray(cx, length);
3668             if (!ida) {
3669                 JS_UNLOCK_OBJ(cx, obj);
3670                 return JS_FALSE;
3671             }
3672             i = length;
3673             for (sprop = lastProp; sprop; sprop = sprop->parent) {
3674                 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                     JS_ASSERT(i > 0);
3683                     ida->vector[--i] = sprop->id;
3684                 }
3685             }
3686         }
3687         JS_UNLOCK_OBJ(cx, obj);
3689         state = (JSNativeIteratorState *)
3690             JS_malloc(cx, sizeof(JSNativeIteratorState));
3691         if (!state) {
3692             JS_DestroyIdArray(cx, ida);
3693             return JS_FALSE;
3694         }
3695         state->ida = ida;
3696         state->next_index = 0;
3698         JS_LOCK_RUNTIME(rt);
3699         state->next = rt->nativeIteratorStates;
3700         if (state->next)
3701             state->next->prevp = &state->next;
3702         state->prevp = &rt->nativeIteratorStates;
3703         *state->prevp = state;
3704         JS_UNLOCK_RUNTIME(rt);
3706         *statep = PRIVATE_TO_JSVAL(state);
3707         if (idp)
3708             *idp = INT_TO_JSVAL(length);
3709         break;
3711       case JSENUMERATE_NEXT:
3712         state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
3713         ida = state->ida;
3714         length = ida->length;
3715         if (state->next_index != length) {
3716             *idp = ida->vector[state->next_index++];
3717             break;
3718         }
3719         /* FALL THROUGH */
3721       case JSENUMERATE_DESTROY:
3722         state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
3724         JS_LOCK_RUNTIME(rt);
3725         JS_ASSERT(rt->nativeIteratorStates);
3726         JS_ASSERT(*state->prevp == state);
3727         if (state->next) {
3728             JS_ASSERT(state->next->prevp == &state->next);
3729             state->next->prevp = state->prevp;
3730         }
3731         *state->prevp = state->next;
3732         JS_UNLOCK_RUNTIME(rt);
3734         JS_DestroyIdArray(cx, state->ida);
3735         JS_free(cx, state);
3736         *statep = JSVAL_NULL;
3737         break;
3738     }
3739     return JS_TRUE;
3742 void
3743 js_MarkNativeIteratorStates(JSContext *cx)
3745     JSNativeIteratorState *state;
3746     jsid *cursor, *end, id;
3748     state = cx->runtime->nativeIteratorStates;
3749     if (!state)
3750         return;
3752     do {
3753         JS_ASSERT(*state->prevp == state);
3754         cursor = state->ida->vector;
3755         end = cursor + state->ida->length;
3756         for (; cursor != end; ++cursor) {
3757             id = *cursor;
3758             if (JSID_IS_ATOM(id)) {
3759                 GC_MARK_ATOM(cx, JSID_TO_ATOM(id), NULL);
3760             } else if (JSID_IS_OBJECT(id)) {
3761                 GC_MARK(cx, JSID_TO_OBJECT(id), "ida->vector[i]", NULL);
3762             }
3763         }
3764     } while ((state = state->next) != NULL);
3766   
3768 JSBool
3769 js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
3770                jsval *vp, uintN *attrsp)
3772     JSBool writing;
3773     JSObject *pobj;
3774     JSProperty *prop;
3775     JSClass *clasp;
3776     JSScopeProperty *sprop;
3777     JSCheckAccessOp check;
3779     writing = (mode & JSACC_WRITE) != 0;
3780     switch (mode & JSACC_TYPEMASK) {
3781       case JSACC_PROTO:
3782         pobj = obj;
3783         if (!writing)
3784             *vp = OBJ_GET_SLOT(cx, obj, JSSLOT_PROTO);
3785         *attrsp = JSPROP_PERMANENT;
3786         break;
3788       case JSACC_PARENT:
3789         JS_ASSERT(!writing);
3790         pobj = obj;
3791         *vp = OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT);
3792         *attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
3793         break;
3795       default:
3796         if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
3797             return JS_FALSE;
3798         if (!prop) {
3799             if (!writing)
3800                 *vp = JSVAL_VOID;
3801             *attrsp = 0;
3802             clasp = OBJ_GET_CLASS(cx, obj);
3803             return !clasp->checkAccess ||
3804                    clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp);
3805         }
3806         if (!OBJ_IS_NATIVE(pobj)) {
3807             OBJ_DROP_PROPERTY(cx, pobj, prop);
3808             return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp);
3809         }
3811         sprop = (JSScopeProperty *)prop;
3812         *attrsp = sprop->attrs;
3813         if (!writing) {
3814             *vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)))
3815                   ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
3816                   : JSVAL_VOID;
3817         }
3818         OBJ_DROP_PROPERTY(cx, pobj, prop);
3819     }
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     clasp = OBJ_GET_CLASS(cx, pobj);
3834     check = clasp->checkAccess;
3835     if (!check)
3836         check = cx->runtime->checkObjectAccess;
3837     return !check || check(cx, pobj, ID_TO_VALUE(id), mode, vp);
3840 #ifdef JS_THREADSAFE
3841 void
3842 js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
3844     JS_UNLOCK_OBJ(cx, obj);
3846 #endif
3848 static void
3849 ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
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      */
3858     JSStackFrame *fp = cx->fp;
3859     if (fp->down) {
3860         JS_ASSERT(!fp->dormantNext);
3861         fp->dormantNext = cx->dormantFrameChain;
3862         cx->dormantFrameChain = fp;
3863         cx->fp = fp->down;
3864     }
3866     js_ReportIsNotFunction(cx, vp, flags);
3868     if (fp->down) {
3869         JS_ASSERT(cx->dormantFrameChain == fp);
3870         cx->dormantFrameChain = fp->dormantNext;
3871         fp->dormantNext = NULL;
3872         cx->fp = fp;
3873     }
3876 #ifdef NARCISSUS
3877 static JSBool
3878 GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval)
3880     JSObject *tmp;
3881     jsval xcval;
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;
3902 #endif
3904 JSBool
3905 js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
3907     JSClass *clasp;
3909     clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
3910     if (!clasp->call) {
3911 #ifdef NARCISSUS
3912         JSObject *callee, *args;
3913         jsval fval, nargv[3];
3914         JSBool ok;
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         ReportIsNotFunction(cx, &argv[-2], 0);
3940         return JS_FALSE;
3941     }
3942     return clasp->call(cx, obj, argc, argv, rval);
3945 JSBool
3946 js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
3947              jsval *rval)
3949     JSClass *clasp;
3951     clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
3952     if (!clasp->construct) {
3953 #ifdef NARCISSUS
3954         JSObject *callee, *args;
3955         jsval cval, nargv[2];
3956         JSBool ok;
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         ReportIsNotFunction(cx, &argv[-2], JSV2F_CONSTRUCT);
3982         return JS_FALSE;
3983     }
3984     return clasp->construct(cx, obj, argc, argv, rval);
3987 JSBool
3988 js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
3990     JSClass *clasp;
3991     JSString *str;
3993     clasp = OBJ_GET_CLASS(cx, obj);
3994     if (clasp->hasInstance)
3995         return clasp->hasInstance(cx, obj, v, bp);
3996 #ifdef NARCISSUS
3997     {
3998         jsval fval, rval;
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     str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
4013                                      OBJECT_TO_JSVAL(obj), NULL);
4014     if (str) {
4015         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4016                              JSMSG_BAD_INSTANCEOF_RHS,
4017                              JS_GetStringBytes(str));
4018     }
4019     return JS_FALSE;
4022 JSBool
4023 js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
4025     JSObject *obj2;
4027     *bp = JS_FALSE;
4028     if (JSVAL_IS_PRIMITIVE(v))
4029         return JS_TRUE;
4030     obj2 = JSVAL_TO_OBJECT(v);
4031     while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL) {
4032         if (obj2 == obj) {
4033             *bp = JS_TRUE;
4034             break;
4035         }
4036     }
4037     return JS_TRUE;
4040 JSBool
4041 js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop)
4043     return GetClassPrototype(cx, NULL, name, protop);
4046 static JSBool
4047 GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
4048                   JSObject **protop)
4050     jsval v;
4051     JSObject *ctor;
4053     if (!js_FindConstructor(cx, scope, name, &v))
4054         return JS_FALSE;
4055     if (JSVAL_IS_FUNCTION(cx, v)) {
4056         ctor = JSVAL_TO_OBJECT(v);
4057         if (!OBJ_GET_PROPERTY(cx, ctor,
4058                               ATOM_TO_JSID(cx->runtime->atomState
4059                                            .classPrototypeAtom),
4060                               &v)) {
4061             return JS_FALSE;
4062         }
4063         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             cx->newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(v);
4074         }
4075     }
4076     *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;
4077     return JS_TRUE;
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)
4098     JSAtom *atom;
4099     uintN attrs;
4101     atom = cx->runtime->atomState.constructorAtom;
4102     JS_ASSERT(id == ATOM_KEY(atom));
4103     return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_READ,
4104                             vp, &attrs);
4107 static JSBool
4108 CheckCtorSetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
4110     JSAtom *atom;
4111     uintN attrs;
4113     atom = cx->runtime->atomState.constructorAtom;
4114     JS_ASSERT(id == ATOM_KEY(atom));
4115     return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_WRITE,
4116                             vp, &attrs);
4119 JSBool
4120 js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,
4121                      uintN attrs)
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     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         return JS_FALSE;
4136     }
4138     /*
4139      * ECMA says that Object.prototype.constructor, or f.prototype.constructor
4140      * for a user-defined function f, is DontEnum.
4141      */
4142     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);
4150 JSBool
4151 js_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
4153     JSObject *obj;
4155     if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
4156         obj = NULL;
4157     } else if (JSVAL_IS_OBJECT(v)) {
4158         obj = JSVAL_TO_OBJECT(v);
4159         if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v))
4160             return JS_FALSE;
4161         if (JSVAL_IS_OBJECT(v))
4162             obj = JSVAL_TO_OBJECT(v);
4163     } else {
4164         if (JSVAL_IS_STRING(v)) {
4165             obj = js_StringToObject(cx, JSVAL_TO_STRING(v));
4166         } else if (JSVAL_IS_INT(v)) {
4167             obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v));
4168         } else if (JSVAL_IS_DOUBLE(v)) {
4169             obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v));
4170         } else {
4171             JS_ASSERT(JSVAL_IS_BOOLEAN(v));
4172             obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v));
4173         }
4174         if (!obj)
4175             return JS_FALSE;
4176     }
4177     *objp = obj;
4178     return JS_TRUE;
4181 JSObject *
4182 js_ValueToNonNullObject(JSContext *cx, jsval v)
4184     JSObject *obj;
4185     JSString *str;
4187     if (!js_ValueToObject(cx, v, &obj))
4188         return NULL;
4189     if (!obj) {
4190         str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
4191         if (str) {
4192             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4193                                  JSMSG_NO_PROPERTIES, JS_GetStringBytes(str));
4194         }
4195     }
4196     return obj;
4199 JSBool
4200 js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval)
4202 #if JS_HAS_VALUEOF_HINT
4203     jsval argv[1];
4205     argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]);
4206     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
4214 JSBool
4215 js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
4216              uintN argc, jsval *argv, jsval *rval)
4218     JSErrorReporter older;
4219     jsid id;
4220     jsval fval;
4221     JSBool ok;
4222     int stackDummy;
4224     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
4225         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
4226         return JS_FALSE;
4227     }
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     older = JS_SetErrorReporter(cx, NULL);
4235     id = ATOM_TO_JSID(atom);
4236     fval = JSVAL_VOID;
4237 #if JS_HAS_XML_SUPPORT
4238     if (OBJECT_IS_XML(cx, obj)) {
4239         JSXMLObjectOps *ops;
4241         ops = (JSXMLObjectOps *) obj->map->ops;
4242         obj = ops->getMethod(cx, obj, id, &fval);
4243         ok = (obj != NULL);
4244     } else
4245 #endif
4246     {
4247         ok = OBJ_GET_PROPERTY(cx, obj, id, &fval);
4248     }
4249     if (!ok)
4250         JS_ClearPendingException(cx);
4251     ok = JSVAL_IS_PRIMITIVE(fval) ||
4252          js_InternalCall(cx, obj, fval, argc, argv, rval);
4253     JS_SetErrorReporter(cx, older);
4254     return ok;
4257 #if JS_HAS_XDR
4259 #include "jsxdrapi.h"
4261 JSBool
4262 js_XDRObject(JSXDRState *xdr, JSObject **objp)
4264     JSContext *cx;
4265     JSClass *clasp;
4266     const char *className;
4267     uint32 classId, classDef;
4268     JSBool ok;
4269     JSObject *proto;
4271     cx = xdr->cx;
4272     if (xdr->mode == JSXDR_ENCODE) {
4273         clasp = OBJ_GET_CLASS(cx, *objp);
4274         className = clasp->name;
4275         classId = JS_XDRFindClassIdByName(xdr, className);
4276         classDef = !classId;
4277         if (classDef && !JS_XDRRegisterClass(xdr, clasp, &classId))
4278             return JS_FALSE;
4279     } else {
4280         classDef = 0;
4281         className = NULL;
4282         clasp = NULL;           /* quell GCC overwarning */
4283     }
4285     /* XDR a flag word followed (if true) by the class name. */
4286     if (!JS_XDRUint32(xdr, &classDef))
4287         return JS_FALSE;
4288     if (classDef && !JS_XDRCString(xdr, (char **) &className))
4289         return JS_FALSE;
4291     /* From here on, return through out: to free className if it was set. */
4292     ok = JS_XDRUint32(xdr, &classId);
4293     if (!ok)
4294         goto out;
4296     if (xdr->mode != JSXDR_ENCODE) {
4297         if (classDef) {
4298             ok = GetClassPrototype(cx, NULL, className, &proto);
4299             if (!ok)
4300                 goto out;
4301             clasp = OBJ_GET_CLASS(cx, proto);
4302             ok = JS_XDRRegisterClass(xdr, clasp, &classId);
4303             if (!ok)
4304                 goto out;
4305         } else {
4306             clasp = JS_XDRFindClassById(xdr, classId);
4307             if (!clasp) {
4308                 char numBuf[12];
4309                 JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId);
4310                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4311                                      JSMSG_CANT_FIND_CLASS, numBuf);
4312                 ok = JS_FALSE;
4313                 goto out;
4314             }
4315         }
4316     }
4318     if (!clasp->xdrObject) {
4319         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4320                              JSMSG_CANT_XDR_CLASS, clasp->name);
4321         ok = JS_FALSE;
4322     } else {
4323         ok = clasp->xdrObject(xdr, objp);
4324     }
4325 out:
4326     if (xdr->mode != JSXDR_ENCODE && className)
4327         JS_free(cx, (void *)className);
4328     return ok;
4331 #endif /* JS_HAS_XDR */
4333 #ifdef DEBUG_brendan
4335 #include <stdio.h>
4336 #include <math.h>
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];
4343 static void
4344 MeterEntryCount(uintN count)
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)]++;
4355 void
4356 js_DumpScopeMeters(JSRuntime *rt)
4358     static FILE *logfp;
4359     if (!logfp)
4360         logfp = fopen("/tmp/scope.stats", "a");
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);
4374             /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
4375             sigma = (var != 0.) ? sqrt(var) : 0.;
4376         }
4378         fprintf(logfp,
4379                 "scopes %g entries %g mean %g sigma %g max %u",
4380                 nscopes, nentrys, mean, sigma, js_entry_count_max);
4381     }
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);
4396 #endif /* DEBUG_brendan */
4398 uint32
4399 js_Mark(JSContext *cx, JSObject *obj, void *arg)
4401     JSScope *scope;
4402     JSScopeProperty *sprop;
4403     JSClass *clasp;
4405     JS_ASSERT(OBJ_IS_NATIVE(obj));
4406     scope = OBJ_SCOPE(obj);
4407 #ifdef DEBUG_brendan
4408     if (scope->object == obj)
4409         MeterEntryCount(scope->entryCount);
4410 #endif
4412     JS_ASSERT(!SCOPE_LAST_PROP(scope) ||
4413               SCOPE_HAS_PROPERTY(scope, SCOPE_LAST_PROP(scope)));
4415     for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
4416         if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
4417             continue;
4418         MARK_SCOPE_PROPERTY(sprop);
4419         if (JSID_IS_ATOM(sprop->id))
4420             GC_MARK_ATOM(cx, JSID_TO_ATOM(sprop->id), arg);
4421         else if (JSID_IS_OBJECT(sprop->id))
4422             GC_MARK(cx, JSID_TO_OBJECT(sprop->id), "id", arg);
4424 #if JS_HAS_GETTER_SETTER
4425         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
4434             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                 GC_MARK(cx,
4440                         JSVAL_TO_GCTHING((jsval) sprop->getter),
4441                         buf,
4442                         arg);
4443             }
4444             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                 GC_MARK(cx,
4450                         JSVAL_TO_GCTHING((jsval) sprop->setter),
4451                         buf,
4452                         arg);
4453             }
4454         }
4455 #endif /* JS_HAS_GETTER_SETTER */
4456     }
4458     /* No one runs while the GC is running, so we can use LOCKED_... here. */
4459     clasp = LOCKED_OBJ_GET_CLASS(obj);
4460     if (clasp->mark)
4461         (void) clasp->mark(cx, obj, arg);
4463     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         return (uint32) obj->slots[-1];
4470     }
4471     return JS_MIN(scope->map.freeslot, scope->map.nslots);
4474 void
4475 js_Clear(JSContext *cx, JSObject *obj)
4477     JSScope *scope;
4478     JSRuntime *rt;
4479     JSScopeProperty *sprop;
4480     uint32 i, n;
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     JS_LOCK_OBJ(cx, obj);
4489     scope = OBJ_SCOPE(obj);
4490     if (scope->object == obj) {
4491         /* Clear the property cache before we clear the scope. */
4492         rt = cx->runtime;
4493         for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
4494             if (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
4495                 SCOPE_HAS_PROPERTY(scope, sprop)) {
4496                 PROPERTY_CACHE_FILL(&rt->propertyCache, obj, sprop->id, NULL);
4497             }
4498         }
4500         /* Now that we're done using scope->lastProp/table, clear scope. */
4501         js_ClearScope(cx, scope);
4503         /* Clear slot values and reset freeslot so we're consistent. */
4504         i = scope->map.nslots;
4505         n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj));
4506         while (--i >= n)
4507             obj->slots[i] = JSVAL_VOID;
4508         scope->map.freeslot = n;
4509     }
4510     JS_UNLOCK_OBJ(cx, obj);
4513 jsval
4514 js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot)
4516     jsval v;
4518     JS_LOCK_OBJ(cx, obj);
4519     v = (slot < (uint32) obj->slots[-1]) ? obj->slots[slot] : JSVAL_VOID;
4520     JS_UNLOCK_OBJ(cx, obj);
4521     return v;
4524 JSBool
4525 js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
4527     JSScope *scope;
4528     uint32 nslots;
4529     JSClass *clasp;
4530     jsval *newslots;
4532     JS_LOCK_OBJ(cx, obj);
4533     scope = OBJ_SCOPE(obj);
4534     nslots = (uint32) obj->slots[-1];
4535     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         JS_ASSERT(nslots == JS_INITIAL_NSLOTS);
4547         clasp = LOCKED_OBJ_GET_CLASS(obj);
4548         nslots = JSSLOT_FREE(clasp);
4549         if (clasp->reserveSlots)
4550             nslots += clasp->reserveSlots(cx, obj);
4551         JS_ASSERT(slot < nslots);
4553         newslots = AllocSlots(cx, obj->slots, nslots);
4554         if (!newslots) {
4555             JS_UNLOCK_SCOPE(cx, scope);
4556             return JS_FALSE;
4557         }
4558         if (scope->object == obj)
4559             scope->map.nslots = nslots;
4560         obj->slots = newslots;
4561     }
4563     /* Whether or not we grew nslots, we may need to advance freeslot. */
4564     if (scope->object == obj && slot >= scope->map.freeslot)
4565         scope->map.freeslot = slot + 1;
4567     obj->slots[slot] = v;
4568     JS_UNLOCK_SCOPE(cx, scope);
4569     return JS_TRUE;
4572 #ifdef DEBUG
4574 /* Routines to print out values during debugging. */
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);
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);
4595 void printVal(JSContext *cx, jsval val);
4597 void printObj(JSContext *cx, JSObject *jsobj) {
4598     jsuint i;
4599     jsval val;
4600     JSClass *clasp;
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     }
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);
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));
4642 void printAtom(JSAtom *atom) {
4643     printString(ATOM_TO_STRING(atom));
4646 #endif