Code

r11451@tres: ted | 2006-04-17 22:21:33 -0700
[inkscape.git] / src / dom / js / jsobj.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is Mozilla Communicator client code, released
17  * March 31, 1998.
18  *
19  * The Initial Developer of the Original Code is
20  * Netscape Communications Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 1998
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either of the GNU General Public License Version 2 or later (the "GPL"),
28  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
40 /*
41  * JS object implementation.
42  */
43 #include "jsstddef.h"
44 #include <stdlib.h>
45 #include <string.h>
46 #include "jstypes.h"
47 #include "jsarena.h" /* Added by JSIFY */
48 #include "jsutil.h" /* Added by JSIFY */
49 #include "jshash.h" /* Added by JSIFY */
50 #include "jsdhash.h"
51 #include "jsprf.h"
52 #include "jsapi.h"
53 #include "jsarray.h"
54 #include "jsatom.h"
55 #include "jsbool.h"
56 #include "jscntxt.h"
57 #include "jsconfig.h"
58 #include "jsfun.h"
59 #include "jsgc.h"
60 #include "jsinterp.h"
61 #include "jslock.h"
62 #include "jsnum.h"
63 #include "jsobj.h"
64 #include "jsscope.h"
65 #include "jsscript.h"
66 #include "jsstr.h"
67 #include "jsopcode.h"
69 #include "jsdbgapi.h"   /* whether or not JS_HAS_OBJ_WATCHPOINT */
71 #ifdef JS_THREADSAFE
72 #define NATIVE_DROP_PROPERTY js_DropProperty
74 extern void
75 js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);
76 #else
77 #define NATIVE_DROP_PROPERTY NULL
78 #endif
80 #ifdef XP_MAC
81 #pragma export on
82 #endif
84 JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
85     js_NewObjectMap,        js_DestroyObjectMap,
86 #if defined JS_THREADSAFE && defined DEBUG
87     _js_LookupProperty,     js_DefineProperty,
88 #else
89     js_LookupProperty,      js_DefineProperty,
90 #endif
91     js_GetProperty,         js_SetProperty,
92     js_GetAttributes,       js_SetAttributes,
93     js_DeleteProperty,      js_DefaultValue,
94     js_Enumerate,           js_CheckAccess,
95     NULL,                   NATIVE_DROP_PROPERTY,
96     js_Call,                js_Construct,
97     NULL,                   js_HasInstance,
98     js_SetProtoOrParent,    js_SetProtoOrParent,
99     js_Mark,                js_Clear,
100     js_GetRequiredSlot,     js_SetRequiredSlot
101 };
103 #ifdef XP_MAC
104 #pragma export off
105 #endif
107 JSClass js_ObjectClass = {
108     js_Object_str,
109     0,
110     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
111     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
112     JSCLASS_NO_OPTIONAL_MEMBERS
113 };
115 #if JS_HAS_OBJ_PROTO_PROP
117 static JSBool
118 obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
120 static JSBool
121 obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
123 static JSBool
124 obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
126 static JSPropertySpec object_props[] = {
127     /* These two must come first; see object_props[slot].name usage below. */
128     {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED,
129                                                   obj_getSlot,  obj_setSlot},
130     {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
131                                                   obj_getSlot,  obj_setSlot},
132     {js_count_str, 0,            JSPROP_PERMANENT,obj_getCount, obj_getCount},
133     {0,0,0,0,0}
134 };
136 /* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */
137 #define JSSLOT_COUNT 2
139 static JSBool
140 ReportStrictSlot(JSContext *cx, uint32 slot)
142     if (slot == JSSLOT_PROTO)
143         return JS_TRUE;
144     return JS_ReportErrorFlagsAndNumber(cx,
145                                         JSREPORT_WARNING | JSREPORT_STRICT,
146                                         js_GetErrorMessage, NULL,
147                                         JSMSG_DEPRECATED_USAGE,
148                                         object_props[slot].name);
151 static JSBool
152 obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
154     uint32 slot;
155     JSAccessMode mode;
156     uintN attrs;
158     slot = (uint32) JSVAL_TO_INT(id);
159     if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
160         id = (jsid)cx->runtime->atomState.protoAtom;
161         mode = JSACC_PROTO;
162     } else {
163         id = (jsid)cx->runtime->atomState.parentAtom;
164         mode = JSACC_PARENT;
165     }
166     if (!OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, &attrs))
167         return JS_FALSE;
168     *vp = OBJ_GET_SLOT(cx, obj, slot);
169     return JS_TRUE;
172 static JSBool
173 obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
175     JSObject *pobj;
176     uint32 slot;
177     uintN attrs;
179     if (!JSVAL_IS_OBJECT(*vp))
180         return JS_TRUE;
181     pobj = JSVAL_TO_OBJECT(*vp);
182     slot = (uint32) JSVAL_TO_INT(id);
183     if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
184         return JS_FALSE;
186     /* __parent__ is readonly and permanent, only __proto__ may be set. */
187     if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_PROTO | JSACC_WRITE, vp, &attrs))
188         return JS_FALSE;
190     return js_SetProtoOrParent(cx, obj, slot, pobj);
193 static JSBool
194 obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
196     jsval iter_state;
197     jsid num_properties;
198     JSBool ok;
200     if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))
201         return JS_FALSE;
203     /* Get the number of properties to enumerate. */
204     iter_state = JSVAL_NULL;
205     ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties);
206     if (!ok)
207         goto out;
209     if (!JSVAL_IS_INT(num_properties)) {
210         JS_ASSERT(0);
211         *vp = JSVAL_ZERO;
212         goto out;
213     }
214     *vp = num_properties;
216 out:
217     if (iter_state != JSVAL_NULL)
218         ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
219     return ok;
222 #else  /* !JS_HAS_OBJ_PROTO_PROP */
224 #define object_props NULL
226 #endif /* !JS_HAS_OBJ_PROTO_PROP */
228 JSBool
229 js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)
231     JSRuntime *rt;
232     JSObject *obj2, *oldproto;
233     JSScope *scope, *newscope;
235     /*
236      * Serialize all proto and parent setting in order to detect cycles.
237      * We nest locks in this function, and only here, in the following orders:
238      *
239      * (1)  rt->setSlotLock < pobj's scope lock;
240      *      rt->setSlotLock < pobj's proto-or-parent's scope lock;
241      *      rt->setSlotLock < pobj's grand-proto-or-parent's scope lock;
242      *      etc...
243      * (2)  rt->setSlotLock < obj's scope lock < pobj's scope lock.
244      *
245      * We avoid AB-BA deadlock by restricting obj from being on pobj's parent
246      * or proto chain (pobj may already be on obj's parent or proto chain; it
247      * could be moving up or down).  We finally order obj with respect to pobj
248      * at the bottom of this routine (just before releasing rt->setSlotLock),
249      * by making pobj be obj's prototype or parent.
250      *
251      * After we have set the slot and released rt->setSlotLock, another call
252      * to js_SetProtoOrParent could nest locks according to the first order
253      * list above, but it cannot deadlock with any other thread.  For there
254      * to be a deadlock, other parts of the engine would have to nest scope
255      * locks in the opposite order.  XXXbe ensure they don't!
256      */
257     rt = cx->runtime;
258 #ifdef JS_THREADSAFE
260     JS_ACQUIRE_LOCK(rt->setSlotLock);
261     while (rt->setSlotBusy) {
262         jsrefcount saveDepth;
264         /* Take pains to avoid nesting rt->gcLock inside rt->setSlotLock! */
265         JS_RELEASE_LOCK(rt->setSlotLock);
266         saveDepth = JS_SuspendRequest(cx);
267         JS_ACQUIRE_LOCK(rt->setSlotLock);
268         if (rt->setSlotBusy)
269             JS_WAIT_CONDVAR(rt->setSlotDone, JS_NO_TIMEOUT);
270         JS_RELEASE_LOCK(rt->setSlotLock);
271         JS_ResumeRequest(cx, saveDepth);
272         JS_ACQUIRE_LOCK(rt->setSlotLock);
273     }
274     rt->setSlotBusy = JS_TRUE;
275     JS_RELEASE_LOCK(rt->setSlotLock);
277 #define SET_SLOT_DONE(rt)                                                     \
278     JS_BEGIN_MACRO                                                            \
279         JS_ACQUIRE_LOCK((rt)->setSlotLock);                                   \
280         (rt)->setSlotBusy = JS_FALSE;                                         \
281         JS_NOTIFY_ALL_CONDVAR((rt)->setSlotDone);                             \
282         JS_RELEASE_LOCK((rt)->setSlotLock);                                   \
283     JS_END_MACRO
285 #else
287 #define SET_SLOT_DONE(rt)       /* nothing */
289 #endif
291     obj2 = pobj;
292     while (obj2) {
293         if (obj2 == obj) {
294             SET_SLOT_DONE(rt);
295             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
296                                  JSMSG_CYCLIC_VALUE,
297 #if JS_HAS_OBJ_PROTO_PROP
298                                  object_props[slot].name
299 #else
300                                  (slot == JSSLOT_PROTO) ? js_proto_str
301                                                         : js_parent_str
302 #endif
303                                  );
304             return JS_FALSE;
305         }
306         obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot));
307     }
309     if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) {
310         /* Check to see whether obj shares its prototype's scope. */
311         JS_LOCK_OBJ(cx, obj);
312         scope = OBJ_SCOPE(obj);
313         oldproto = JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO));
314         if (oldproto && OBJ_SCOPE(oldproto) == scope) {
315             /* Either obj needs a new empty scope, or it should share pobj's. */
316             if (!pobj ||
317                 !OBJ_IS_NATIVE(pobj) ||
318                 OBJ_GET_CLASS(cx, pobj) != LOCKED_OBJ_GET_CLASS(oldproto)) {
319                 /*
320                  * With no proto and no scope of its own, obj is truly empty.
321                  *
322                  * If pobj is not native, obj needs its own empty scope -- it
323                  * should not continue to share oldproto's scope once oldproto
324                  * is not on obj's prototype chain.  That would put properties
325                  * from oldproto's scope ahead of properties defined by pobj,
326                  * in lookup order.
327                  *
328                  * If pobj's class differs from oldproto's, we may need a new
329                  * scope to handle differences in private and reserved slots,
330                  * so we suboptimally but safely make one.
331                  */
332                 scope = js_GetMutableScope(cx, obj);
333                 if (!scope) {
334                     JS_UNLOCK_OBJ(cx, obj);
335                     SET_SLOT_DONE(rt);
336                     return JS_FALSE;
337                 }
338             } else if (OBJ_SCOPE(pobj) != scope) {
339 #ifdef JS_THREADSAFE
340                 /*
341                  * We are about to nest scope locks.  Help jslock.c:ShareScope
342                  * keep scope->u.count balanced for the JS_UNLOCK_SCOPE, while
343                  * avoiding deadlock, by recording scope in rt->setSlotScope.
344                  */
345                 if (scope->ownercx) {
346                     JS_ASSERT(scope->ownercx == cx);
347                     rt->setSlotScope = scope;
348                 }
349 #endif
351                 /* We can't deadlock because we checked for cycles above (2). */
352                 JS_LOCK_OBJ(cx, pobj);
353                 newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map);
354                 obj->map = &newscope->map;
355                 js_DropObjectMap(cx, &scope->map, obj);
356                 JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
357                 scope = newscope;
358 #ifdef JS_THREADSAFE
359                 rt->setSlotScope = NULL;
360 #endif
361             }
362         }
363         LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj));
364         JS_UNLOCK_SCOPE(cx, scope);
365     } else {
366         OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj));
367     }
369     SET_SLOT_DONE(rt);
370     return JS_TRUE;
372 #undef SET_SLOT_DONE
375 JS_STATIC_DLL_CALLBACK(JSHashNumber)
376 js_hash_object(const void *key)
378     return (JSHashNumber)key >> JSVAL_TAGBITS;
381 static JSHashEntry *
382 MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
384     JSSharpObjectMap *map;
385     JSHashTable *table;
386     JSHashNumber hash;
387     JSHashEntry **hep, *he;
388     jsatomid sharpid;
389     JSIdArray *ida;
390     JSBool ok;
391     jsint i, length;
392     jsid id;
393 #if JS_HAS_GETTER_SETTER
394     JSObject *obj2;
395     JSProperty *prop;
396     uintN attrs;
397 #endif
398     jsval val;
400     map = &cx->sharpObjectMap;
401     table = map->table;
402     hash = js_hash_object(obj);
403     hep = JS_HashTableRawLookup(table, hash, obj);
404     he = *hep;
405     if (!he) {
406         sharpid = 0;
407         he = JS_HashTableRawAdd(table, hep, hash, obj, (void *)sharpid);
408         if (!he) {
409             JS_ReportOutOfMemory(cx);
410             return NULL;
411         }
412         ida = JS_Enumerate(cx, obj);
413         if (!ida)
414             return NULL;
415         ok = JS_TRUE;
416         for (i = 0, length = ida->length; i < length; i++) {
417             id = ida->vector[i];
418 #if JS_HAS_GETTER_SETTER
419             ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
420             if (!ok)
421                 break;
422             if (prop) {
423                 ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
424                 if (ok) {
425                     if (OBJ_IS_NATIVE(obj2) &&
426                         (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
427                         val = JSVAL_NULL;
428                         if (attrs & JSPROP_GETTER)
429                             val = (jsval) ((JSScopeProperty*)prop)->getter;
430                         if (attrs & JSPROP_SETTER) {
431                             if (val != JSVAL_NULL) {
432                                 /* Mark the getter, then set val to setter. */
433                                 ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
434                                                        NULL)
435                                       != NULL);
436                             }
437                             val = (jsval) ((JSScopeProperty*)prop)->setter;
438                         }
439                     } else {
440                         ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
441                     }
442                 }
443                 OBJ_DROP_PROPERTY(cx, obj2, prop);
444             }
445 #else
446             ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
447 #endif
448             if (!ok)
449                 break;
450             if (!JSVAL_IS_PRIMITIVE(val) &&
451                 !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {
452                 ok = JS_FALSE;
453                 break;
454             }
455         }
456         if (!ok || !idap)
457             JS_DestroyIdArray(cx, ida);
458         if (!ok)
459             return NULL;
460     } else {
461         sharpid = (jsatomid) he->value;
462         if (sharpid == 0) {
463             sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
464             he->value = (void *) sharpid;
465         }
466         ida = NULL;
467     }
468     if (idap)
469         *idap = ida;
470     return he;
473 JSHashEntry *
474 js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
475                     jschar **sp)
477     JSSharpObjectMap *map;
478     JSHashTable *table;
479     JSIdArray *ida;
480     JSHashNumber hash;
481     JSHashEntry *he, **hep;
482     jsatomid sharpid;
483     char buf[20];
484     size_t len;
486     /* Set to null in case we return an early error. */
487     *sp = NULL;
488     map = &cx->sharpObjectMap;
489     table = map->table;
490     if (!table) {
491         table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
492                                 JS_CompareValues, NULL, NULL);
493         if (!table) {
494             JS_ReportOutOfMemory(cx);
495             return NULL;
496         }
497         map->table = table;
498     }
500     ida = NULL;
501     if (map->depth == 0) {
502         he = MarkSharpObjects(cx, obj, &ida);
503         if (!he)
504             goto bad;
505         JS_ASSERT((((jsatomid) he->value) & SHARP_BIT) == 0);
506         if (!idap) {
507             JS_DestroyIdArray(cx, ida);
508             ida = NULL;
509         }
510     } else {
511         hash = js_hash_object(obj);
512         hep = JS_HashTableRawLookup(table, hash, obj);
513         he = *hep;
515         /*
516          * It's possible that the value of a property has changed from the
517          * first time the object's properties are traversed (when the property
518          * ids are entered into the hash table) to the second (when they are
519          * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not
520          * idempotent.
521          */
522         if (!he) {
523             he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
524             if (!he) {
525                 JS_ReportOutOfMemory(cx);
526                 goto bad;
527             }
528             *sp = NULL;
529             sharpid = 0;
530             goto out;
531         }
532     }
534     sharpid = (jsatomid) he->value;
535     if (sharpid == 0) {
536         *sp = NULL;
537     } else {
538         len = JS_snprintf(buf, sizeof buf, "#%u%c",
539                           sharpid >> SHARP_ID_SHIFT,
540                           (sharpid & SHARP_BIT) ? '#' : '=');
541         *sp = js_InflateString(cx, buf, len);
542         if (!*sp) {
543             if (ida)
544                 JS_DestroyIdArray(cx, ida);
545             goto bad;
546         }
547     }
549 out:
550     JS_ASSERT(he);
551     if ((sharpid & SHARP_BIT) == 0) {
552         if (idap && !ida) {
553             ida = JS_Enumerate(cx, obj);
554             if (!ida) {
555                 if (*sp) {
556                     JS_free(cx, *sp);
557                     *sp = NULL;
558                 }
559                 goto bad;
560             }
561         }
562         map->depth++;
563     }
565     if (idap)
566         *idap = ida;
567     return he;
569 bad:
570     /* Clean up the sharpObjectMap table on outermost error. */
571     if (map->depth == 0) {
572         map->sharpgen = 0;
573         JS_HashTableDestroy(map->table);
574         map->table = NULL;
575     }
576     return NULL;
579 void
580 js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
582     JSSharpObjectMap *map;
583     JSIdArray *ida;
585     map = &cx->sharpObjectMap;
586     JS_ASSERT(map->depth > 0);
587     if (--map->depth == 0) {
588         map->sharpgen = 0;
589         JS_HashTableDestroy(map->table);
590         map->table = NULL;
591     }
592     if (idap) {
593         ida = *idap;
594         if (ida) {
595             JS_DestroyIdArray(cx, ida);
596             *idap = NULL;
597         }
598     }
601 #define OBJ_TOSTRING_EXTRA      3       /* for 3 local GC roots */
603 #if JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE
604 JSBool
605 js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
606                 jsval *rval)
608     JSBool ok, outermost;
609     JSHashEntry *he;
610     JSIdArray *ida;
611     jschar *chars, *ochars, *vsharp;
612     const jschar *idstrchars, *vchars;
613     size_t nchars, idstrlength, gsoplength, vlength, vsharplength;
614     char *comma;
615     jsint i, j, length, valcnt;
616     jsid id;
617 #if JS_HAS_GETTER_SETTER
618     JSObject *obj2;
619     JSProperty *prop;
620     uintN attrs;
621 #endif
622     jsval val[2];
623     JSString *gsop[2];
624     JSAtom *atom;
625     JSString *idstr, *valstr, *str;
626     int stackDummy;
628     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
629         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
630         return JS_FALSE;
631     }
633     /*
634      * obj_toString for 1.2 calls toSource, and doesn't want the extra parens
635      * on the outside.
636      */
637     outermost = (cx->version != JSVERSION_1_2 && cx->sharpObjectMap.depth == 0);
638     he = js_EnterSharpObject(cx, obj, &ida, &chars);
639     if (!he)
640         return JS_FALSE;
641     if (IS_SHARP(he)) {
642         /*
643          * We didn't enter -- obj is already "sharp", meaning we've visited it
644          * already in our depth first search, and therefore chars contains a
645          * string of the form "#n#".
646          */
647         JS_ASSERT(!ida);
648 #if JS_HAS_SHARP_VARS
649         nchars = js_strlen(chars);
650 #else
651         chars[0] = '{';
652         chars[1] = '}';
653         chars[2] = 0;
654         nchars = 2;
655 #endif
656         goto make_string;
657     }
658     JS_ASSERT(ida);
659     ok = JS_TRUE;
661     if (!chars) {
662         /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
663         chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
664         nchars = 0;
665         if (!chars)
666             goto error;
667         if (outermost)
668             chars[nchars++] = '(';
669     } else {
670         /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
671         MAKE_SHARP(he);
672         nchars = js_strlen(chars);
673         chars = (jschar *)
674             realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
675         if (!chars) {
676             free(ochars);
677             goto error;
678         }
679         if (outermost) {
680             /*
681              * No need for parentheses around the whole shebang, because #n=
682              * unambiguously begins an object initializer, and never a block
683              * statement.
684              */
685             outermost = JS_FALSE;
686         }
687     }
689 #ifdef DUMP_CALL_TABLE
690     if (cx->options & JSOPTION_LOGCALL_TOSOURCE) {
691         const char *classname = OBJ_GET_CLASS(cx, obj)->name;
692         size_t classnchars = strlen(classname);
693         static const char classpropid[] = "C";
694         const char *cp;
695         size_t onchars = nchars;
697         /* 2 for ': ', 2 quotes around classname, 2 for ', ' after. */
698         classnchars += sizeof classpropid - 1 + 2 + 2;
699         if (ida->length)
700             classnchars += 2;
702         /* 2 for the braces, 1 for the terminator */
703         chars = (jschar *)
704             realloc((ochars = chars),
705                     (nchars + classnchars + 2 + 1) * sizeof(jschar));
706         if (!chars) {
707             free(ochars);
708             goto error;
709         }
711         chars[nchars++] = '{';          /* 1 from the 2 braces */
712         for (cp = classpropid; *cp; cp++)
713             chars[nchars++] = (jschar) *cp;
714         chars[nchars++] = ':';
715         chars[nchars++] = ' ';          /* 2 for ': ' */
716         chars[nchars++] = '"';
717         for (cp = classname; *cp; cp++)
718             chars[nchars++] = (jschar) *cp;
719         chars[nchars++] = '"';          /* 2 quotes */
720         if (ida->length) {
721             chars[nchars++] = ',';
722             chars[nchars++] = ' ';      /* 2 for ', ' */
723         }
725         JS_ASSERT(nchars - onchars == 1 + classnchars);
726     } else
727 #endif
728     chars[nchars++] = '{';
730     comma = NULL;
732     for (i = 0, length = ida->length; i < length; i++) {
733         /* Get strings for id and value and GC-root them via argv. */
734         id = ida->vector[i];
736 #if JS_HAS_GETTER_SETTER
738         ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
739         if (!ok)
740             goto error;
741         valcnt = 0;
742         if (prop) {
743             ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
744             if (!ok) {
745                 OBJ_DROP_PROPERTY(cx, obj2, prop);
746                 goto error;
747             }
748             if (OBJ_IS_NATIVE(obj2) &&
749                 (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
750                 if (attrs & JSPROP_GETTER) {
751                     val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter;
752 #ifdef OLD_GETTER_SETTER
753                     gsop[valcnt] =
754                         ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
755 #else
756                     gsop[valcnt] =
757                         ATOM_TO_STRING(cx->runtime->atomState.getAtom);
758 #endif
759                     valcnt++;
760                 }
761                 if (attrs & JSPROP_SETTER) {
762                     val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter;
763 #ifdef OLD_GETTER_SETTER
764                     gsop[valcnt] =
765                         ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
766 #else
767                     gsop[valcnt] =
768                         ATOM_TO_STRING(cx->runtime->atomState.setAtom);
769 #endif
770                     valcnt++;
771                 }
772             } else {
773                 valcnt = 1;
774                 gsop[0] = NULL;
775                 ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
776             }
777             OBJ_DROP_PROPERTY(cx, obj2, prop);
778         }
780 #else  /* !JS_HAS_GETTER_SETTER */
782         valcnt = 1;
783         gsop[0] = NULL;
784         ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
786 #endif /* !JS_HAS_GETTER_SETTER */
788         if (!ok)
789             goto error;
791         /* Convert id to a jsval and then to a string. */
792         atom = JSVAL_IS_INT(id) ? NULL : (JSAtom *)id;
793         id = ID_TO_VALUE(id);
794         idstr = js_ValueToString(cx, id);
795         if (!idstr) {
796             ok = JS_FALSE;
797             goto error;
798         }
799         argv[0] = STRING_TO_JSVAL(idstr);
801         /*
802          * If id is a string that's a reserved identifier, or else id is not
803          * an identifier at all, then it needs to be quoted.  Also, negative
804          * integer ids must be quoted.
805          */
806         if (atom
807             ? (ATOM_KEYWORD(atom) || !js_IsIdentifier(idstr))
808             : JSVAL_TO_INT(id) < 0) {
809             idstr = js_QuoteString(cx, idstr, (jschar)'\'');
810             if (!idstr) {
811                 ok = JS_FALSE;
812                 goto error;
813             }
814             argv[0] = STRING_TO_JSVAL(idstr);
815         }
816         idstrchars = JSSTRING_CHARS(idstr);
817         idstrlength = JSSTRING_LENGTH(idstr);
819         for (j = 0; j < valcnt; j++) {
820             /* Convert val[j] to its canonical source form. */
821             valstr = js_ValueToSource(cx, val[j]);
822             if (!valstr) {
823                 ok = JS_FALSE;
824                 goto error;
825             }
826             argv[1+j] = STRING_TO_JSVAL(valstr);
827             vchars = JSSTRING_CHARS(valstr);
828             vlength = JSSTRING_LENGTH(valstr);
830 #ifndef OLD_GETTER_SETTER
831             /* Remove 'function ' from beginning of valstr. */
832             if (gsop[j]) {
833                 int n = strlen(js_function_str) + 1;
834                 vchars += n;
835                 vlength -= n;
836             }
837 #endif
839             /* If val[j] is a non-sharp object, consider sharpening it. */
840             vsharp = NULL;
841             vsharplength = 0;
842 #if JS_HAS_SHARP_VARS
843             if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') {
844                 he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL,
845                                          &vsharp);
846                 if (!he) {
847                     ok = JS_FALSE;
848                     goto error;
849                 }
850                 if (IS_SHARP(he)) {
851                     vchars = vsharp;
852                     vlength = js_strlen(vchars);
853                 } else {
854                     if (vsharp) {
855                         vsharplength = js_strlen(vsharp);
856                         MAKE_SHARP(he);
857                     }
858                     js_LeaveSharpObject(cx, NULL);
859                 }
860             }
861 #endif
863             /* Allocate 1 + 1 at end for closing brace and terminating 0. */
864             chars = (jschar *)
865                 realloc((ochars = chars),
866                         (nchars + (comma ? 2 : 0) +
867                          idstrlength + 1 +
868                          (gsop[j] ? 1 + JSSTRING_LENGTH(gsop[j]) : 0) +
869                          vsharplength + vlength +
870                          (outermost ? 2 : 1) + 1) * sizeof(jschar));
871             if (!chars) {
872                 /* Save code space on error: let JS_free ignore null vsharp. */
873                 JS_free(cx, vsharp);
874                 free(ochars);
875                 goto error;
876             }
878             if (comma) {
879                 chars[nchars++] = comma[0];
880                 chars[nchars++] = comma[1];
881             }
882             comma = ", ";
884 #ifdef OLD_GETTER_SETTER
885             js_strncpy(&chars[nchars], idstrchars, idstrlength);
886             nchars += idstrlength;
887             if (gsop[j]) {
888                 chars[nchars++] = ' ';
889                 gsoplength = JSSTRING_LENGTH(gsop[j]);
890                 js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength);
891                 nchars += gsoplength;
892             }
893             chars[nchars++] = ':';
894 #else
895             if (gsop[j]) {
896                 gsoplength = JSSTRING_LENGTH(gsop[j]);
897                 js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength);
898                 nchars += gsoplength;
899                 chars[nchars++] = ' ';
900             }
901             js_strncpy(&chars[nchars], idstrchars, idstrlength);
902             nchars += idstrlength;
903             if (!gsop[j])
904                 chars[nchars++] = ':';
905 #endif
906             if (vsharplength) {
907                 js_strncpy(&chars[nchars], vsharp, vsharplength);
908                 nchars += vsharplength;
909             }
910             js_strncpy(&chars[nchars], vchars, vlength);
911             nchars += vlength;
913             if (vsharp)
914                 JS_free(cx, vsharp);
915 #ifdef DUMP_CALL_TABLE
916             if (outermost && nchars >= js_LogCallToSourceLimit)
917                 break;
918 #endif
919         }
920     }
922     chars[nchars++] = '}';
923     if (outermost)
924         chars[nchars++] = ')';
925     chars[nchars] = 0;
927   error:
928     js_LeaveSharpObject(cx, &ida);
930     if (!ok) {
931         if (chars)
932             free(chars);
933         return ok;
934     }
936     if (!chars) {
937         JS_ReportOutOfMemory(cx);
938         return JS_FALSE;
939     }
940   make_string:
941     str = js_NewString(cx, chars, nchars, 0);
942     if (!str) {
943         free(chars);
944         return JS_FALSE;
945     }
946     *rval = STRING_TO_JSVAL(str);
947     return JS_TRUE;
949 #endif /* JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE */
951 JSBool
952 js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
953                 jsval *rval)
955     jschar *chars;
956     size_t nchars;
957     const char *clazz, *prefix;
958     JSString *str;
960 #if JS_HAS_INITIALIZERS
961     if (cx->version == JSVERSION_1_2)
962         return js_obj_toSource(cx, obj, argc, argv, rval);
963 #endif
965     clazz = OBJ_GET_CLASS(cx, obj)->name;
966     nchars = 9 + strlen(clazz);         /* 9 for "[object ]" */
967     chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar));
968     if (!chars)
969         return JS_FALSE;
971     prefix = "[object ";
972     nchars = 0;
973     while ((chars[nchars] = (jschar)*prefix) != 0)
974         nchars++, prefix++;
975     while ((chars[nchars] = (jschar)*clazz) != 0)
976         nchars++, clazz++;
977     chars[nchars++] = ']';
978     chars[nchars] = 0;
980     str = js_NewString(cx, chars, nchars, 0);
981     if (!str) {
982         JS_free(cx, chars);
983         return JS_FALSE;
984     }
985     *rval = STRING_TO_JSVAL(str);
986     return JS_TRUE;
989 static JSBool
990 obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
992     *rval = OBJECT_TO_JSVAL(obj);
993     return JS_TRUE;
996 static JSBool
997 obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
999     JSStackFrame *fp, *caller;
1000     JSBool indirectCall;
1001     JSObject *scopeobj;
1002     JSString *str;
1003     const char *file;
1004     uintN line;
1005     JSPrincipals *principals;
1006     JSScript *script;
1007     JSBool ok;
1008 #if JS_HAS_EVAL_THIS_SCOPE
1009     JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
1010     JSBool setCallerScopeChain = JS_FALSE, setCallerVarObj = JS_FALSE;
1011 #endif
1013     fp = cx->fp;
1014     caller = JS_GetScriptedCaller(cx, fp);
1015     indirectCall = (caller && caller->pc && *caller->pc != JSOP_EVAL);
1017     if (JSVERSION_IS_ECMA(cx->version) &&
1018         indirectCall &&
1019         !JS_ReportErrorFlagsAndNumber(cx,
1020                                       JSREPORT_WARNING | JSREPORT_STRICT,
1021                                       js_GetErrorMessage, NULL,
1022                                       JSMSG_BAD_INDIRECT_CALL,
1023                                       js_eval_str)) {
1024         return JS_FALSE;
1025     }
1027     if (!JSVAL_IS_STRING(argv[0])) {
1028         *rval = argv[0];
1029         return JS_TRUE;
1030     }
1032 #if JS_HAS_SCRIPT_OBJECT
1033     /*
1034      * Script.prototype.compile/exec and Object.prototype.eval all take an
1035      * optional trailing argument that overrides the scope object.
1036      */
1037     scopeobj = NULL;
1038     if (argc >= 2) {
1039         if (!js_ValueToObject(cx, argv[1], &scopeobj))
1040             return JS_FALSE;
1041         argv[1] = OBJECT_TO_JSVAL(scopeobj);
1042     }
1043     if (!scopeobj)
1044 #endif
1045     {
1046 #if JS_HAS_EVAL_THIS_SCOPE
1047         /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
1048         if (indirectCall) {
1049             callerScopeChain = caller->scopeChain;
1050             if (obj != callerScopeChain) {
1051                 scopeobj = js_NewObject(cx, &js_WithClass, obj,
1052                                         callerScopeChain);
1053                 if (!scopeobj)
1054                     return JS_FALSE;
1056                 /* Set fp->scopeChain too, for the compiler. */
1057                 caller->scopeChain = fp->scopeChain = scopeobj;
1058                 setCallerScopeChain = JS_TRUE;
1059             }
1061             callerVarObj = caller->varobj;
1062             if (obj != callerVarObj) {
1063                 /* Set fp->varobj too, for the compiler. */
1064                 caller->varobj = fp->varobj = obj;
1065                 setCallerVarObj = JS_TRUE;
1066             }
1067         }
1068         /* From here on, control must exit through label out with ok set. */
1069 #endif
1071 #if JS_BUG_EVAL_THIS_SCOPE
1072         /* An old version used the object in which eval was found for scope. */
1073         scopeobj = obj;
1074 #else
1075         /* Compile using caller's current scope object. */
1076         if (caller)
1077             scopeobj = caller->scopeChain;
1078 #endif
1079     }
1081     str = JSVAL_TO_STRING(argv[0]);
1082     if (caller) {
1083         file = caller->script->filename;
1084         line = js_PCToLineNumber(cx, caller->script, caller->pc);
1085         principals = JS_EvalFramePrincipals(cx, fp, caller);
1086     } else {
1087         file = NULL;
1088         line = 0;
1089         principals = NULL;
1090     }
1092     fp->flags |= JSFRAME_EVAL;
1093     script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,
1094                                              JSSTRING_CHARS(str),
1095                                              JSSTRING_LENGTH(str),
1096                                              file, line);
1097     if (!script) {
1098         ok = JS_FALSE;
1099         goto out;
1100     }
1102 #if !JS_BUG_EVAL_THIS_SCOPE
1103 #if JS_HAS_SCRIPT_OBJECT
1104     if (argc < 2)
1105 #endif
1106     {
1107         /* Execute using caller's new scope object (might be a Call object). */
1108         if (caller)
1109             scopeobj = caller->scopeChain;
1110     }
1111 #endif
1112     ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
1113     JS_DestroyScript(cx, script);
1115 out:
1116 #if JS_HAS_EVAL_THIS_SCOPE
1117     /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
1118     if (setCallerScopeChain)
1119         caller->scopeChain = callerScopeChain;
1120     if (setCallerVarObj)
1121         caller->varobj = callerVarObj;
1122 #endif
1123     return ok;
1126 #if JS_HAS_OBJ_WATCHPOINT
1128 static JSBool
1129 obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
1130                   void *closure)
1132     JSResolvingKey key;
1133     JSResolvingEntry *entry;
1134     uint32 generation;
1135     JSObject *funobj;
1136     jsval argv[3];
1137     JSBool ok;
1139     /* Avoid recursion on (obj, id) already being watched on cx. */
1140     key.obj = obj;
1141     key.id = id;
1142     if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
1143         return JS_FALSE;
1144     if (!entry)
1145         return JS_TRUE;
1146     generation = cx->resolvingTable->generation;
1148     funobj = (JSObject *) closure;
1149     argv[0] = id;
1150     argv[1] = old;
1151     argv[2] = *nvp;
1152     ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(funobj), 3, argv, nvp);
1153     js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
1154     return ok;
1157 static JSBool
1158 obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1160     JSObject *funobj;
1161     JSFunction *fun;
1162     jsval userid, value;
1163     jsid propid;
1164     uintN attrs;
1166     if (JSVAL_IS_FUNCTION(cx, argv[1])) {
1167         funobj = JSVAL_TO_OBJECT(argv[1]);
1168     } else {
1169         fun = js_ValueToFunction(cx, &argv[1], 0);
1170         if (!fun)
1171             return JS_FALSE;
1172         funobj = fun->object;
1173     }
1174     argv[1] = OBJECT_TO_JSVAL(funobj);
1176     /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1177     userid = argv[0];
1178     if (!JS_ValueToId(cx, userid, &propid))
1179         return JS_FALSE;
1181     if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs))
1182         return JS_FALSE;
1183     if (attrs & JSPROP_READONLY)
1184         return JS_TRUE;
1185     return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, funobj);
1188 static JSBool
1189 obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1191     return JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL);
1194 #endif /* JS_HAS_OBJ_WATCHPOINT */
1196 #if JS_HAS_NEW_OBJ_METHODS
1197 /*
1198  * Prototype and property query methods, to complement the 'in' and
1199  * 'instanceof' operators.
1200  */
1202 /* Proposed ECMA 15.2.4.5. */
1203 static JSBool
1204 obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1205                    jsval *rval)
1207     jsid id;
1208     JSObject *obj2;
1209     JSProperty *prop;
1210     JSScopeProperty *sprop;
1212     if (!JS_ValueToId(cx, argv[0], &id))
1213         return JS_FALSE;
1214     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1215         return JS_FALSE;
1216     if (!prop) {
1217         *rval = JSVAL_FALSE;
1218     } else if (obj2 == obj) {
1219         *rval = JSVAL_TRUE;
1220     } else if (OBJ_IS_NATIVE(obj2)) {
1221         sprop = (JSScopeProperty *)prop;
1222         *rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
1223     } else {
1224         *rval = JSVAL_FALSE;
1225     }
1226     if (prop)
1227         OBJ_DROP_PROPERTY(cx, obj2, prop);
1228     return JS_TRUE;
1231 /* Proposed ECMA 15.2.4.6. */
1232 static JSBool
1233 obj_isPrototypeOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1234                   jsval *rval)
1236     JSBool b;
1238     if (!js_IsDelegate(cx, obj, *argv, &b))
1239         return JS_FALSE;
1240     *rval = BOOLEAN_TO_JSVAL(b);
1241     return JS_TRUE;
1244 /* Proposed ECMA 15.2.4.7. */
1245 static JSBool
1246 obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1247                          jsval *rval)
1249     jsid id;
1250     uintN attrs;
1251     JSObject *obj2;
1252     JSProperty *prop;
1253     JSBool ok;
1255     if (!JS_ValueToId(cx, argv[0], &id))
1256         return JS_FALSE;
1258     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1259         return JS_FALSE;
1261     if (!prop) {
1262         *rval = JSVAL_FALSE;
1263         return JS_TRUE;
1264     }
1266     /*
1267      * XXX ECMA spec error compatible: return false unless hasOwnProperty.
1268      * The ECMA spec really should be fixed so propertyIsEnumerable and the
1269      * for..in loop agree on whether prototype properties are enumerable,
1270      * obviously by fixing this method (not by breaking the for..in loop!).
1271      *
1272      * We check here for shared permanent prototype properties, which should
1273      * be treated as if they are local to obj.  They are an implementation
1274      * technique used to satisfy ECMA requirements; users should not be able
1275      * to distinguish a shared permanent proto-property from a local one.
1276      */
1277     if (obj2 != obj &&
1278         !(OBJ_IS_NATIVE(obj2) &&
1279           SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) {
1280         OBJ_DROP_PROPERTY(cx, obj2, prop);
1281         *rval = JSVAL_FALSE;
1282         return JS_TRUE;
1283     }
1285     ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
1286     OBJ_DROP_PROPERTY(cx, obj2, prop);
1287     if (ok)
1288         *rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);
1289     return ok;
1291 #endif /* JS_HAS_NEW_OBJ_METHODS */
1293 #if JS_HAS_GETTER_SETTER
1294 static JSBool
1295 obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1296                  jsval *rval)
1298     jsval fval, junk;
1299     jsid id;
1300     uintN attrs;
1302     fval = argv[1];
1303     if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
1304         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1305                              JSMSG_BAD_GETTER_OR_SETTER,
1306                              js_getter_str);
1307         return JS_FALSE;
1308     }
1310     if (!JS_ValueToId(cx, argv[0], &id))
1311         return JS_FALSE;
1312     if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
1313         return JS_FALSE;
1314     /*
1315      * Getters and setters are just like watchpoints from an access
1316      * control point of view.
1317      */
1318     if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1319         return JS_FALSE;
1320     return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
1321                                (JSPropertyOp) JSVAL_TO_OBJECT(fval), NULL,
1322                                JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED,
1323                                NULL);
1326 static JSBool
1327 obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1328                  jsval *rval)
1330     jsval fval, junk;
1331     jsid id;
1332     uintN attrs;
1334     fval = argv[1];
1335     if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
1336         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1337                              JSMSG_BAD_GETTER_OR_SETTER,
1338                              js_setter_str);
1339         return JS_FALSE;
1340     }
1342     if (!JS_ValueToId(cx, argv[0], &id))
1343         return JS_FALSE;
1344     if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
1345         return JS_FALSE;
1346     /*
1347      * Getters and setters are just like watchpoints from an access
1348      * control point of view.
1349      */
1350     if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1351         return JS_FALSE;
1352     return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
1353                                NULL, (JSPropertyOp) JSVAL_TO_OBJECT(fval),
1354                                JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED,
1355                                NULL);
1358 static JSBool
1359 obj_lookupGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1360                  jsval *rval)
1362     jsid id;
1363     JSObject *pobj;
1364     JSProperty *prop;
1365     JSScopeProperty *sprop;
1367     if (!JS_ValueToId(cx, argv[0], &id))
1368         return JS_FALSE;
1369     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1370         return JS_FALSE;
1371     if (prop) {
1372         if (OBJ_IS_NATIVE(pobj)) {
1373             sprop = (JSScopeProperty *) prop;
1374             if (sprop->attrs & JSPROP_GETTER)
1375                 *rval = OBJECT_TO_JSVAL(sprop->getter);
1376         }
1377         OBJ_DROP_PROPERTY(cx, pobj, prop);
1378     }
1379     return JS_TRUE;
1382 static JSBool
1383 obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1384                  jsval *rval)
1386     jsid id;
1387     JSObject *pobj;
1388     JSProperty *prop;
1389     JSScopeProperty *sprop;
1391     if (!JS_ValueToId(cx, argv[0], &id))
1392         return JS_FALSE;
1393     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1394         return JS_FALSE;
1395     if (prop) {
1396         if (OBJ_IS_NATIVE(pobj)) {
1397             sprop = (JSScopeProperty *) prop;
1398             if (sprop->attrs & JSPROP_SETTER)
1399                 *rval = OBJECT_TO_JSVAL(sprop->setter);
1400         }
1401         OBJ_DROP_PROPERTY(cx, pobj, prop);
1402     }
1403     return JS_TRUE;
1405 #endif /* JS_HAS_GETTER_SETTER */
1407 #if JS_HAS_OBJ_WATCHPOINT
1408 const char js_watch_str[] = "watch";
1409 const char js_unwatch_str[] = "unwatch";
1410 #endif
1411 #if JS_HAS_NEW_OBJ_METHODS
1412 const char js_hasOwnProperty_str[] = "hasOwnProperty";
1413 const char js_isPrototypeOf_str[] = "isPrototypeOf";
1414 const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
1415 #endif
1416 #if JS_HAS_GETTER_SETTER
1417 const char js_defineGetter_str[] = "__defineGetter__";
1418 const char js_defineSetter_str[] = "__defineSetter__";
1419 const char js_lookupGetter_str[] = "__lookupGetter__";
1420 const char js_lookupSetter_str[] = "__lookupSetter__";
1421 #endif
1423 static JSFunctionSpec object_methods[] = {
1424 #if JS_HAS_TOSOURCE
1425     {js_toSource_str,             js_obj_toSource,    0, 0, OBJ_TOSTRING_EXTRA},
1426 #endif
1427     {js_toString_str,             js_obj_toString,    0, 0, OBJ_TOSTRING_EXTRA},
1428     {js_toLocaleString_str,       js_obj_toString,    0, 0, OBJ_TOSTRING_EXTRA},
1429     {js_valueOf_str,              obj_valueOf,        0,0,0},
1430     {js_eval_str,                 obj_eval,           1,0,0},
1431 #if JS_HAS_OBJ_WATCHPOINT
1432     {js_watch_str,                obj_watch,          2,0,0},
1433     {js_unwatch_str,              obj_unwatch,        1,0,0},
1434 #endif
1435 #if JS_HAS_NEW_OBJ_METHODS
1436     {js_hasOwnProperty_str,       obj_hasOwnProperty, 1,0,0},
1437     {js_isPrototypeOf_str,        obj_isPrototypeOf,  1,0,0},
1438     {js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0},
1439 #endif
1440 #if JS_HAS_GETTER_SETTER
1441     {js_defineGetter_str,         obj_defineGetter,   2,0,0},
1442     {js_defineSetter_str,         obj_defineSetter,   2,0,0},
1443     {js_lookupGetter_str,         obj_lookupGetter,   1,0,0},
1444     {js_lookupSetter_str,         obj_lookupSetter,   1,0,0},
1445 #endif
1446     {0,0,0,0,0}
1447 };
1449 static JSBool
1450 Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1452     if (argc == 0) {
1453         /* Trigger logic below to construct a blank object. */
1454         obj = NULL;
1455     } else {
1456         /* If argv[0] is null or undefined, obj comes back null. */
1457         if (!js_ValueToObject(cx, argv[0], &obj))
1458             return JS_FALSE;
1459     }
1460     if (!obj) {
1461         JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));
1462         if (cx->fp->flags & JSFRAME_CONSTRUCTING)
1463             return JS_TRUE;
1464         obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL);
1465         if (!obj)
1466             return JS_FALSE;
1467     }
1468     *rval = OBJECT_TO_JSVAL(obj);
1469     return JS_TRUE;
1472 /*
1473  * ObjectOps and Class for with-statement stack objects.
1474  */
1475 static JSBool
1476 with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
1477                     JSProperty **propp
1478 #if defined JS_THREADSAFE && defined DEBUG
1479                     , const char *file, uintN line
1480 #endif
1481                     )
1483     JSObject *proto;
1484     JSScopeProperty *sprop;
1485     JSStackFrame *fp;
1487     proto = OBJ_GET_PROTO(cx, obj);
1488     if (!proto)
1489         return js_LookupProperty(cx, obj, id, objp, propp);
1490     if (!OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp))
1491         return JS_FALSE;
1493     /*
1494      * Check whether id names an argument or local variable in an active
1495      * function.  If so, pretend we didn't find it, so that the real arg or
1496      * var property can be found in the function's call object, later on in
1497      * the scope chain.  But skip unshared arg and var properties -- those
1498      * result when a script explicitly sets a function "static" property of
1499      * the same name.  See jsinterp.c:SetFunctionSlot.
1500      *
1501      * XXX blame pre-ECMA reflection of function args and vars as properties
1502      */
1503     if ((sprop = (JSScopeProperty *) *propp) &&
1504         (proto = *objp, OBJ_IS_NATIVE(proto)) &&
1505         (sprop->getter == js_GetArgument ||
1506          sprop->getter == js_GetLocalVariable) &&
1507         (sprop->attrs & JSPROP_SHARED)) {
1508         JS_ASSERT(OBJ_GET_CLASS(cx, proto) == &js_FunctionClass);
1509         for (fp = cx->fp; fp && (!fp->fun || !fp->fun->interpreted);
1510              fp = fp->down) {
1511             continue;
1512         }
1513         if (fp && fp->fun == (JSFunction *) JS_GetPrivate(cx, proto)) {
1514             OBJ_DROP_PROPERTY(cx, proto, *propp);
1515             *objp = NULL;
1516             *propp = NULL;
1517         }
1518     }
1519     return JS_TRUE;
1522 static JSBool
1523 with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1525     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1526     if (!proto)
1527         return js_GetProperty(cx, obj, id, vp);
1528     return OBJ_GET_PROPERTY(cx, proto, id, vp);
1531 static JSBool
1532 with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1534     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1535     if (!proto)
1536         return js_SetProperty(cx, obj, id, vp);
1537     return OBJ_SET_PROPERTY(cx, proto, id, vp);
1540 static JSBool
1541 with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
1542                    uintN *attrsp)
1544     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1545     if (!proto)
1546         return js_GetAttributes(cx, obj, id, prop, attrsp);
1547     return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp);
1550 static JSBool
1551 with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
1552                    uintN *attrsp)
1554     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1555     if (!proto)
1556         return js_SetAttributes(cx, obj, id, prop, attrsp);
1557     return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp);
1560 static JSBool
1561 with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
1563     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1564     if (!proto)
1565         return js_DeleteProperty(cx, obj, id, rval);
1566     return OBJ_DELETE_PROPERTY(cx, proto, id, rval);
1569 static JSBool
1570 with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
1572     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1573     if (!proto)
1574         return js_DefaultValue(cx, obj, hint, vp);
1575     return OBJ_DEFAULT_VALUE(cx, proto, hint, vp);
1578 static JSBool
1579 with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
1580                jsval *statep, jsid *idp)
1582     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1583     if (!proto)
1584         return js_Enumerate(cx, obj, enum_op, statep, idp);
1585     return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp);
1588 static JSBool
1589 with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
1590                  jsval *vp, uintN *attrsp)
1592     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1593     if (!proto)
1594         return js_CheckAccess(cx, obj, id, mode, vp, attrsp);
1595     return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp);
1598 static JSObject *
1599 with_ThisObject(JSContext *cx, JSObject *obj)
1601     JSObject *proto = OBJ_GET_PROTO(cx, obj);
1602     if (!proto)
1603         return obj;
1604     return OBJ_THIS_OBJECT(cx, proto);
1607 JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = {
1608     js_NewObjectMap,        js_DestroyObjectMap,
1609     with_LookupProperty,    js_DefineProperty,
1610     with_GetProperty,       with_SetProperty,
1611     with_GetAttributes,     with_SetAttributes,
1612     with_DeleteProperty,    with_DefaultValue,
1613     with_Enumerate,         with_CheckAccess,
1614     with_ThisObject,        NATIVE_DROP_PROPERTY,
1615     NULL,                   NULL,
1616     NULL,                   NULL,
1617     js_SetProtoOrParent,    js_SetProtoOrParent,
1618     js_Mark,                js_Clear,
1619     NULL,                   NULL
1620 };
1622 static JSObjectOps *
1623 with_getObjectOps(JSContext *cx, JSClass *clasp)
1625     return &js_WithObjectOps;
1628 JSClass js_WithClass = {
1629     "With",
1630     0,
1631     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
1632     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
1633     with_getObjectOps,
1634     0,0,0,0,0,0,0
1635 };
1637 #if JS_HAS_OBJ_PROTO_PROP
1638 static JSBool
1639 With(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1641     JSObject *parent, *proto;
1642     jsval v;
1644     if (!JS_ReportErrorFlagsAndNumber(cx,
1645                                       JSREPORT_WARNING | JSREPORT_STRICT,
1646                                       js_GetErrorMessage, NULL,
1647                                       JSMSG_DEPRECATED_USAGE,
1648                                       js_WithClass.name)) {
1649         return JS_FALSE;
1650     }
1652     if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
1653         obj = js_NewObject(cx, &js_WithClass, NULL, NULL);
1654         if (!obj)
1655             return JS_FALSE;
1656         *rval = OBJECT_TO_JSVAL(obj);
1657     }
1659     parent = cx->fp->scopeChain;
1660     if (argc > 0) {
1661         if (!js_ValueToObject(cx, argv[0], &proto))
1662             return JS_FALSE;
1663         v = OBJECT_TO_JSVAL(proto);
1664         if (!obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PROTO), &v))
1665             return JS_FALSE;
1666         if (argc > 1) {
1667             if (!js_ValueToObject(cx, argv[1], &parent))
1668                 return JS_FALSE;
1669         }
1670     }
1671     v = OBJECT_TO_JSVAL(parent);
1672     return obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PARENT), &v);
1674 #endif
1676 JSObject *
1677 js_InitObjectClass(JSContext *cx, JSObject *obj)
1679     JSObject *proto;
1680     jsval eval;
1682 #if JS_HAS_SHARP_VARS
1683     JS_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >= ATOM_INDEX_LIMIT_LOG2 + 1);
1684 #endif
1686     proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1,
1687                          object_props, object_methods, NULL, NULL);
1688     if (!proto)
1689         return NULL;
1691 #if JS_HAS_OBJ_PROTO_PROP
1692     if (!JS_InitClass(cx, obj, NULL, &js_WithClass, With, 0,
1693                       NULL, NULL, NULL, NULL)) {
1694         return NULL;
1695     }
1696 #endif
1698     /* ECMA (15.1.2.1) says 'eval' is also a property of the global object. */
1699     if (!OBJ_GET_PROPERTY(cx, proto, (jsid)cx->runtime->atomState.evalAtom,
1700                           &eval)) {
1701         return NULL;
1702     }
1703     if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.evalAtom,
1704                              eval, NULL, NULL, 0, NULL)) {
1705         return NULL;
1706     }
1708     return proto;
1711 void
1712 js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops,
1713                  JSClass *clasp)
1715     map->nrefs = nrefs;
1716     map->ops = ops;
1717     map->nslots = JS_INITIAL_NSLOTS;
1718     map->freeslot = JSSLOT_FREE(clasp);
1721 JSObjectMap *
1722 js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,
1723                 JSClass *clasp, JSObject *obj)
1725     return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj);
1728 void
1729 js_DestroyObjectMap(JSContext *cx, JSObjectMap *map)
1731     js_DestroyScope(cx, (JSScope *)map);
1734 JSObjectMap *
1735 js_HoldObjectMap(JSContext *cx, JSObjectMap *map)
1737     JS_ASSERT(map->nrefs >= 0);
1738     JS_ATOMIC_INCREMENT(&map->nrefs);
1739     return map;
1742 JSObjectMap *
1743 js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj)
1745     JS_ASSERT(map->nrefs > 0);
1746     JS_ATOMIC_DECREMENT(&map->nrefs);
1747     if (map->nrefs == 0) {
1748         map->ops->destroyObjectMap(cx, map);
1749         return NULL;
1750     }
1751     if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj)
1752         ((JSScope *)map)->object = NULL;
1753     return map;
1756 static JSBool
1757 GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
1758                   JSObject **protop);
1760 JSObject *
1761 js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
1763     JSObject *obj, *ctor;
1764     JSObjectOps *ops;
1765     JSObjectMap *map;
1766     JSClass *protoclasp;
1767     jsval cval;
1768     uint32 nslots, i;
1769     jsval *newslots;
1771     /* Allocate an object from the GC heap and zero it. */
1772     obj = (JSObject *) js_AllocGCThing(cx, GCX_OBJECT);
1773     if (!obj)
1774         return NULL;
1776     /* Bootstrap the ur-object, and make it the default prototype object. */
1777     if (!proto) {
1778         if (!GetClassPrototype(cx, parent, clasp->name, &proto))
1779             goto bad;
1780         if (!proto && !GetClassPrototype(cx, parent, js_Object_str, &proto))
1781             goto bad;
1782     }
1784     /* Always call the class's getObjectOps hook if it has one. */
1785     ops = clasp->getObjectOps
1786           ? clasp->getObjectOps(cx, clasp)
1787           : &js_ObjectOps;
1789     /*
1790      * Share proto's map only if it has the same JSObjectOps, and only if
1791      * proto's class has the same private and reserved slots as obj's map
1792      * and class have.  We assume that if prototype and object are of the
1793      * same class, they always have the same number of computed reserved
1794      * slots (returned via clasp->reserveSlots); otherwise, prototype and
1795      * object classes must have the same (null or not) reserveSlots hook.
1796      */
1797     if (proto &&
1798         (map = proto->map)->ops == ops &&
1799         ((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp ||
1800          (!((protoclasp->flags ^ clasp->flags) &
1801             (JSCLASS_HAS_PRIVATE |
1802              (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) &&
1803           protoclasp->reserveSlots == clasp->reserveSlots)))
1804     {
1805         /* Default parent to the parent of the prototype's constructor. */
1806         if (!parent) {
1807             if (!OBJ_GET_PROPERTY(cx, proto,
1808                                   (jsid)cx->runtime->atomState.constructorAtom,
1809                                   &cval)) {
1810                 goto bad;
1811             }
1812             if (JSVAL_IS_OBJECT(cval) && (ctor = JSVAL_TO_OBJECT(cval)) != NULL)
1813                 parent = OBJ_GET_PARENT(cx, ctor);
1814         }
1816         /* Share the given prototype's map. */
1817         obj->map = js_HoldObjectMap(cx, map);
1819         /* Ensure that obj starts with the minimum slots for clasp. */
1820         nslots = JS_INITIAL_NSLOTS;
1821     } else {
1822         /* Leave parent alone.  Allocate a new map for obj. */
1823         map = ops->newObjectMap(cx, 1, ops, clasp, obj);
1824         if (!map)
1825             goto bad;
1826         obj->map = map;
1828         /* Let ops->newObjectMap set nslots so as to reserve slots. */
1829         nslots = map->nslots;
1830     }
1832     /* Allocate a slots vector, with a -1'st element telling its length. */
1833     newslots = (jsval *) JS_malloc(cx, (nslots + 1) * sizeof(jsval));
1834     if (!newslots) {
1835         js_DropObjectMap(cx, obj->map, obj);
1836         obj->map = NULL;
1837         goto bad;
1838     }
1839     newslots[0] = nslots;
1840     newslots++;
1842     /* Set the proto, parent, and class properties. */
1843     newslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
1844     newslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);
1845     newslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp);
1847     /* Clear above JSSLOT_CLASS so the GC doesn't load uninitialized memory. */
1848     for (i = JSSLOT_CLASS + 1; i < nslots; i++)
1849         newslots[i] = JSVAL_VOID;
1851     /* Store newslots after initializing all of 'em, just in case. */
1852     obj->slots = newslots;
1854     if (cx->runtime->objectHook) {
1855         JS_KEEP_ATOMS(cx->runtime);
1856         cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData);
1857         JS_UNKEEP_ATOMS(cx->runtime);
1858     }
1860     return obj;
1862 bad:
1863     cx->newborn[GCX_OBJECT] = NULL;
1864     return NULL;
1867 static JSBool
1868 FindConstructor(JSContext *cx, JSObject *start, const char *name, jsval *vp)
1870     JSAtom *atom;
1871     JSObject *obj, *pobj;
1872     JSProperty *prop;
1873     JSScopeProperty *sprop;
1875     atom = js_Atomize(cx, name, strlen(name), 0);
1876     if (!atom)
1877         return JS_FALSE;
1879     if (start || (cx->fp && (start = cx->fp->scopeChain) != NULL)) {
1880         /* Find the topmost object in the scope chain. */
1881         do {
1882             obj = start;
1883             start = OBJ_GET_PARENT(cx, obj);
1884         } while (start);
1885     } else {
1886         obj = cx->globalObject;
1887         if (!obj) {
1888             *vp = JSVAL_VOID;
1889             return JS_TRUE;
1890         }
1891     }
1893     JS_ASSERT(OBJ_IS_NATIVE(obj));
1894     if (!js_LookupPropertyWithFlags(cx, obj, (jsid)atom, JSRESOLVE_CLASSNAME,
1895                                     &pobj, &prop
1896 #if defined JS_THREADSAFE && defined DEBUG
1897                                     , __FILE__, __LINE__
1898 #endif
1899                                     )) {
1900         return JS_FALSE;
1901     }
1902     if (!prop)  {
1903         *vp = JSVAL_VOID;
1904         return JS_TRUE;
1905     }
1907     JS_ASSERT(OBJ_IS_NATIVE(pobj));
1908     sprop = (JSScopeProperty *) prop;
1909     JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
1910     *vp = OBJ_GET_SLOT(cx, pobj, sprop->slot);
1911     OBJ_DROP_PROPERTY(cx, pobj, prop);
1912     return JS_TRUE;
1915 JSObject *
1916 js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
1917                    JSObject *parent, uintN argc, jsval *argv)
1919     jsval cval, rval;
1920     JSObject *obj, *ctor;
1922     if (!FindConstructor(cx, parent, clasp->name, &cval))
1923         return NULL;
1924     if (JSVAL_IS_PRIMITIVE(cval)) {
1925         js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
1926         return NULL;
1927     }
1929     /*
1930      * If proto or parent are NULL, set them to Constructor.prototype and/or
1931      * Constructor.__parent__, just like JSOP_NEW does.
1932      */
1933     ctor = JSVAL_TO_OBJECT(cval);
1934     if (!parent)
1935         parent = OBJ_GET_PARENT(cx, ctor);
1936     if (!proto) {
1937         if (!OBJ_GET_PROPERTY(cx, ctor,
1938                               (jsid)cx->runtime->atomState.classPrototypeAtom,
1939                               &rval)) {
1940             return NULL;
1941         }
1942         if (JSVAL_IS_OBJECT(rval))
1943             proto = JSVAL_TO_OBJECT(rval);
1944     }
1946     obj = js_NewObject(cx, clasp, proto, parent);
1947     if (!obj)
1948         return NULL;
1950     if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval))
1951         goto bad;
1952     return JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : obj;
1953 bad:
1954     cx->newborn[GCX_OBJECT] = NULL;
1955     return NULL;
1958 void
1959 js_FinalizeObject(JSContext *cx, JSObject *obj)
1961     JSObjectMap *map;
1963     /* Cope with stillborn objects that have no map. */
1964     map = obj->map;
1965     if (!map)
1966         return;
1967     JS_ASSERT(obj->slots);
1969     if (cx->runtime->objectHook)
1970         cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData);
1972     /* Remove all watchpoints with weak links to obj. */
1973     JS_ClearWatchPointsForObject(cx, obj);
1975     /*
1976      * Finalize obj first, in case it needs map and slots.  Optimized to use
1977      * LOCKED_OBJ_GET_CLASS instead of OBJ_GET_CLASS, so we avoid "promoting"
1978      * obj's scope from lock-free to lock-full (see jslock.c:ClaimScope) when
1979      * we're called from the GC.  Only the GC should call js_FinalizeObject,
1980      * and no other threads run JS (and possibly racing to update obj->slots)
1981      * while the GC is running.
1982      */
1983     LOCKED_OBJ_GET_CLASS(obj)->finalize(cx, obj);
1985     /* Drop map and free slots. */
1986     js_DropObjectMap(cx, map, obj);
1987     obj->map = NULL;
1988     JS_free(cx, obj->slots - 1);
1989     obj->slots = NULL;
1992 /* XXXbe if one adds props, deletes earlier props, adds more, the last added
1993          won't recycle the deleted props' slots. */
1994 JSBool
1995 js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)
1997     JSObjectMap *map;
1998     JSClass *clasp;
1999     uint32 nslots, i;
2000     jsval *newslots;
2002     map = obj->map;
2003     JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
2004     clasp = LOCKED_OBJ_GET_CLASS(obj);
2005     if (map->freeslot == JSSLOT_FREE(clasp)) {
2006         /* Adjust map->freeslot to include computed reserved slots, if any. */
2007         if (clasp->reserveSlots)
2008             map->freeslot += clasp->reserveSlots(cx, obj);
2009     }
2010     nslots = map->nslots;
2011     if (map->freeslot >= nslots) {
2012         nslots = map->freeslot;
2013         JS_ASSERT(nslots >= JS_INITIAL_NSLOTS);
2014         nslots += (nslots + 1) / 2;
2016         newslots = (jsval *)
2017             JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval));
2018         if (!newslots)
2019             return JS_FALSE;
2020         for (i = 1 + newslots[0]; i <= nslots; i++)
2021             newslots[i] = JSVAL_VOID;
2022         newslots[0] = map->nslots = nslots;
2023         obj->slots = newslots + 1;
2024     }
2026 #ifdef TOO_MUCH_GC
2027     obj->slots[map->freeslot] = JSVAL_VOID;
2028 #endif
2029     *slotp = map->freeslot++;
2030     return JS_TRUE;
2033 void
2034 js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
2036     JSObjectMap *map;
2037     uint32 nslots;
2038     jsval *newslots;
2040     OBJ_CHECK_SLOT(obj, slot);
2041     obj->slots[slot] = JSVAL_VOID;
2042     map = obj->map;
2043     JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
2044     if (map->freeslot == slot + 1)
2045         map->freeslot = slot;
2046     nslots = map->nslots;
2047     if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) {
2048         nslots = map->freeslot;
2049         nslots += nslots / 2;
2050         if (nslots < JS_INITIAL_NSLOTS)
2051             nslots = JS_INITIAL_NSLOTS;
2053         newslots = (jsval *)
2054             JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval));
2055         if (!newslots)
2056             return;
2057         newslots[0] = map->nslots = nslots;
2058         obj->slots = newslots + 1;
2059     }
2062 #if JS_BUG_EMPTY_INDEX_ZERO
2063 #define CHECK_FOR_EMPTY_INDEX(id)                                             \
2064     JS_BEGIN_MACRO                                                            \
2065         if (JSSTRING_LENGTH(_str) == 0)                                       \
2066             id = JSVAL_ZERO;                                                  \
2067     JS_END_MACRO
2068 #else
2069 #define CHECK_FOR_EMPTY_INDEX(id) /* nothing */
2070 #endif
2072 /* JSVAL_INT_MAX as a string */
2073 #define JSVAL_INT_MAX_STRING "1073741823"
2075 #define CHECK_FOR_FUNNY_INDEX(id)                                             \
2076     JS_BEGIN_MACRO                                                            \
2077         if (!JSVAL_IS_INT(id)) {                                              \
2078             JSAtom *atom_ = (JSAtom *)id;                                     \
2079             JSString *str_ = ATOM_TO_STRING(atom_);                           \
2080             const jschar *cp_ = str_->chars;                                  \
2081             JSBool negative_ = (*cp_ == '-');                                 \
2082             if (negative_) cp_++;                                             \
2083             if (JS7_ISDEC(*cp_) &&                                            \
2084                 str_->length - negative_ <= sizeof(JSVAL_INT_MAX_STRING)-1) { \
2085                 id = CheckForFunnyIndex(id, cp_, negative_);                  \
2086             } else {                                                          \
2087                 CHECK_FOR_EMPTY_INDEX(id);                                    \
2088             }                                                                 \
2089         }                                                                     \
2090     JS_END_MACRO
2092 static jsid
2093 CheckForFunnyIndex(jsid id, const jschar *cp, JSBool negative)
2095     jsuint index = JS7_UNDEC(*cp++);
2096     jsuint oldIndex = 0;
2097     jsuint c = 0;
2099     if (index != 0) {
2100         while (JS7_ISDEC(*cp)) {
2101             oldIndex = index;
2102             c = JS7_UNDEC(*cp);
2103             index = 10 * index + c;
2104             cp++;
2105         }
2106     }
2107     if (*cp == 0 &&
2108         (oldIndex < (JSVAL_INT_MAX / 10) ||
2109          (oldIndex == (JSVAL_INT_MAX / 10) &&
2110           c <= (JSVAL_INT_MAX % 10)))) {
2111         if (negative)
2112             index = 0 - index;
2113         id = INT_TO_JSVAL((jsint)index);
2114     }
2115     return id;
2118 JSScopeProperty *
2119 js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
2120                      JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
2121                      uintN attrs, uintN flags, intN shortid)
2123     JSScope *scope;
2124     JSScopeProperty *sprop;
2126     JS_LOCK_OBJ(cx, obj);
2127     scope = js_GetMutableScope(cx, obj);
2128     if (!scope) {
2129         sprop = NULL;
2130     } else {
2131         /*
2132          * Handle old bug that took empty string as zero index.  Also convert
2133          * string indices to integers if appropriate.
2134          */
2135         CHECK_FOR_FUNNY_INDEX(id);
2136         sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs,
2137                                     flags, shortid);
2138     }
2139     JS_UNLOCK_OBJ(cx, obj);
2140     return sprop;
2143 JSScopeProperty *
2144 js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
2145                              JSScopeProperty *sprop, uintN attrs, uintN mask,
2146                              JSPropertyOp getter, JSPropertyOp setter)
2148     JSScope *scope;
2150     JS_LOCK_OBJ(cx, obj);
2151     scope = js_GetMutableScope(cx, obj);
2152     if (!scope) {
2153         sprop = NULL;
2154     } else {
2155         sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, attrs, mask,
2156                                             getter, setter);
2157         if (sprop) {
2158             PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, sprop->id,
2159                                 sprop);
2160         }
2161     }
2162     JS_UNLOCK_OBJ(cx, obj);
2163     return sprop;
2166 JSBool
2167 js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
2168                   JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
2169                   JSProperty **propp)
2171     return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs,
2172                                    0, 0, propp);
2175 JSBool
2176 js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
2177                         JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
2178                         uintN flags, intN shortid, JSProperty **propp)
2180     JSClass *clasp;
2181     JSScope *scope;
2182     JSProperty *prop;
2183     JSScopeProperty *sprop;
2185     /*
2186      * Handle old bug that took empty string as zero index.  Also convert
2187      * string indices to integers if appropriate.
2188      */
2189     CHECK_FOR_FUNNY_INDEX(id);
2191 #if JS_HAS_GETTER_SETTER
2192     /*
2193      * If defining a getter or setter, we must check for its counterpart and
2194      * update the attributes and property ops.  A getter or setter is really
2195      * only half of a property.
2196      */
2197     if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
2198         JSObject *pobj;
2200         /*
2201          * If JS_THREADSAFE and id is found, js_LookupProperty returns with
2202          * sprop non-null and pobj locked.  If pobj == obj, the property is
2203          * already in obj and obj has its own (mutable) scope.  So if we are
2204          * defining a getter whose setter was already defined, or vice versa,
2205          * finish the job via js_ChangeScopePropertyAttributes, and refresh
2206          * the property cache line for (obj, id) to map sprop.
2207          */
2208         if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
2209             return JS_FALSE;
2210         sprop = (JSScopeProperty *) prop;
2211         if (sprop &&
2212             pobj == obj &&
2213             (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
2214             sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(obj), sprop,
2215                                                 attrs, sprop->attrs,
2216                                                 (attrs & JSPROP_GETTER)
2217                                                 ? getter
2218                                                 : sprop->getter,
2219                                                 (attrs & JSPROP_SETTER)
2220                                                 ? setter
2221                                                 : sprop->setter);
2223             /* NB: obj == pobj, so we can share unlock code at the bottom. */
2224             if (!sprop)
2225                 goto bad;
2226             goto out;
2227         }
2229         if (prop) {
2230             /* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */
2231             OBJ_DROP_PROPERTY(cx, pobj, prop);
2232             prop = NULL;
2233         }
2234     }
2235 #endif /* JS_HAS_GETTER_SETTER */
2237     /* Lock if object locking is required by this implementation. */
2238     JS_LOCK_OBJ(cx, obj);
2240     /* Use the object's class getter and setter by default. */
2241     clasp = LOCKED_OBJ_GET_CLASS(obj);
2242     if (!getter)
2243         getter = clasp->getProperty;
2244     if (!setter)
2245         setter = clasp->setProperty;
2247     /* Get obj's own scope if it has one, or create a new one for obj. */
2248     scope = js_GetMutableScope(cx, obj);
2249     if (!scope)
2250         goto bad;
2252     /* Add the property to scope, or replace an existing one of the same id. */
2253     if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
2254         attrs |= JSPROP_SHARED;
2255     sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
2256                                 SPROP_INVALID_SLOT, attrs, flags, shortid);
2257     if (!sprop)
2258         goto bad;
2260     /* XXXbe called with lock held */
2261     if (!clasp->addProperty(cx, obj, SPROP_USERID(sprop), &value)) {
2262         (void) js_RemoveScopeProperty(cx, scope, id);
2263         goto bad;
2264     }
2266     if (SPROP_HAS_VALID_SLOT(sprop, scope))
2267         LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value);
2269 #if JS_HAS_GETTER_SETTER
2270 out:
2271 #endif
2272     PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
2273     if (propp)
2274         *propp = (JSProperty *) sprop;
2275     else
2276         JS_UNLOCK_OBJ(cx, obj);
2277     return JS_TRUE;
2279 bad:
2280     JS_UNLOCK_OBJ(cx, obj);
2281     return JS_FALSE;
2284 /*
2285  * Given pc pointing after a property accessing bytecode, return true if the
2286  * access is a "object-detecting" in the sense used by web pages, e.g., when
2287  * checking whether document.all is defined.
2288  */
2289 static JSBool
2290 Detecting(JSContext *cx, jsbytecode *pc)
2292     JSScript *script;
2293     jsbytecode *endpc;
2294     JSOp op;
2295     JSAtom *atom;
2297     script = cx->fp->script;
2298     for (endpc = script->code + script->length; pc < endpc; pc++) {
2299         /* General case: a branch or equality op follows the access. */
2300         op = (JSOp) *pc;
2301         if (js_CodeSpec[op].format & JOF_DETECTING)
2302             return JS_TRUE;
2304         /*
2305          * Special case #1: handle (document.all == null).  Don't sweat about
2306          * JS1.2's revision of the equality operators here.
2307          */
2308         if (op == JSOP_NULL) {
2309             if (++pc < endpc)
2310                 return *pc == JSOP_EQ || *pc == JSOP_NE;
2311             break;
2312         }
2314         /*
2315          * Special case #2: handle (document.all == undefined).  Don't worry
2316          * about someone redefining undefined, which was added by Edition 3,
2317          * so was read/write for backward compatibility.
2318          */
2319         if (op == JSOP_NAME) {
2320             atom = GET_ATOM(cx, script, pc);
2321             if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] &&
2322                 (pc += js_CodeSpec[op].length) < endpc) {
2323                 op = (JSOp) *pc;
2324                 return op == JSOP_EQ || op == JSOP_NE ||
2325                        op == JSOP_NEW_EQ || op == JSOP_NEW_NE;
2326             }
2327             break;
2328         }
2330         /* At this point, anything but grouping means we're not detecting. */
2331         if (op != JSOP_GROUP)
2332             break;
2333     }
2334     return JS_FALSE;
2337 JSBool
2338 js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
2339                            JSObject **objp, JSProperty **propp
2340 #if defined JS_THREADSAFE && defined DEBUG
2341                            , const char *file, uintN line
2342 #endif
2343                            )
2345     JSObject *start, *obj2, *proto;
2346     JSScope *scope;
2347     JSScopeProperty *sprop;
2348     JSClass *clasp;
2349     JSResolveOp resolve;
2350     JSResolvingKey key;
2351     JSResolvingEntry *entry;
2352     uint32 generation;
2353     JSNewResolveOp newresolve;
2354     jsbytecode *pc;
2355     const JSCodeSpec *cs;
2356     uint32 format;
2357     JSBool ok;
2359     /*
2360      * Handle old bug that took empty string as zero index.  Also convert
2361      * string indices to integers if appropriate.
2362      */
2363     CHECK_FOR_FUNNY_INDEX(id);
2365     /* Search scopes starting with obj and following the prototype link. */
2366     start = obj;
2367     for (;;) {
2368         JS_LOCK_OBJ(cx, obj);
2369         SET_OBJ_INFO(obj, file, line);
2370         scope = OBJ_SCOPE(obj);
2371         if (scope->object == obj) {
2372             sprop = SCOPE_GET_PROPERTY(scope, id);
2373         } else {
2374             /* Shared prototype scope: try resolve before lookup. */
2375             sprop = NULL;
2376         }
2378         /* Try obj's class resolve hook if id was not found in obj's scope. */
2379         if (!sprop) {
2380             clasp = LOCKED_OBJ_GET_CLASS(obj);
2381             resolve = clasp->resolve;
2382             if (resolve != JS_ResolveStub) {
2383                 /* Avoid recursion on (obj, id) already being resolved on cx. */
2384                 key.obj = obj;
2385                 key.id = id;
2387                 /*
2388                  * Once we have successfully added an entry for (obj, key) to
2389                  * cx->resolvingTable, control must go through cleanup: before
2390                  * returning.  But note that JS_DHASH_ADD may find an existing
2391                  * entry, in which case we bail to suppress runaway recursion.
2392                  */
2393                 if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) {
2394                     JS_UNLOCK_OBJ(cx, obj);
2395                     return JS_FALSE;
2396                 }
2397                 if (!entry) {
2398                     /* Already resolving id in obj -- dampen recursion. */
2399                     JS_UNLOCK_OBJ(cx, obj);
2400                     goto out;
2401                 }
2402                 generation = cx->resolvingTable->generation;
2404                 /* Null *propp here so we can test it at cleanup: safely. */
2405                 *propp = NULL;
2407                 if (clasp->flags & JSCLASS_NEW_RESOLVE) {
2408                     newresolve = (JSNewResolveOp)resolve;
2409                     if (cx->fp && (pc = cx->fp->pc)) {
2410                         cs = &js_CodeSpec[*pc];
2411                         format = cs->format;
2412                         if ((format & JOF_MODEMASK) != JOF_NAME)
2413                             flags |= JSRESOLVE_QUALIFIED;
2414                         if ((format & JOF_ASSIGNING) ||
2415                             (cx->fp->flags & JSFRAME_ASSIGNING)) {
2416                             flags |= JSRESOLVE_ASSIGNING;
2417                         } else {
2418                             pc += cs->length;
2419                             if (Detecting(cx, pc))
2420                                 flags |= JSRESOLVE_DETECTING;
2421                         }
2422                         if (format & JOF_DECLARING)
2423                             flags |= JSRESOLVE_DECLARING;
2424                     }
2425                     obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START)
2426                            ? start
2427                            : NULL;
2428                     JS_UNLOCK_OBJ(cx, obj);
2430                     /* Protect id and all atoms from a GC nested in resolve. */
2431                     JS_KEEP_ATOMS(cx->runtime);
2432                     ok = newresolve(cx, obj, ID_TO_VALUE(id), flags, &obj2);
2433                     JS_UNKEEP_ATOMS(cx->runtime);
2434                     if (!ok)
2435                         goto cleanup;
2437                     JS_LOCK_OBJ(cx, obj);
2438                     SET_OBJ_INFO(obj, file, line);
2439                     if (obj2) {
2440                         /* Resolved: juggle locks and lookup id again. */
2441                         if (obj2 != obj) {
2442                             JS_UNLOCK_OBJ(cx, obj);
2443                             JS_LOCK_OBJ(cx, obj2);
2444                         }
2445                         scope = OBJ_SCOPE(obj2);
2446                         if (!MAP_IS_NATIVE(&scope->map)) {
2447                             /* Whoops, newresolve handed back a foreign obj2. */
2448                             JS_ASSERT(obj2 != obj);
2449                             JS_UNLOCK_OBJ(cx, obj2);
2450                             ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp);
2451                             if (!ok || *propp)
2452                                 goto cleanup;
2453                             JS_LOCK_OBJ(cx, obj2);
2454                         } else {
2455                             /*
2456                              * Require that obj2 have its own scope now, as we
2457                              * do for old-style resolve.  If it doesn't, then
2458                              * id was not truly resolved, and we'll find it in
2459                              * the proto chain, or miss it if obj2's proto is
2460                              * not on obj's proto chain.  That last case is a
2461                              * "too bad!" case.
2462                              */
2463                             if (scope->object == obj2)
2464                                 sprop = SCOPE_GET_PROPERTY(scope, id);
2465                         }
2466                         if (obj2 != obj && !sprop) {
2467                             JS_UNLOCK_OBJ(cx, obj2);
2468                             JS_LOCK_OBJ(cx, obj);
2469                         }
2470                     }
2471                 } else {
2472                     /*
2473                      * Old resolve always requires id re-lookup if obj owns
2474                      * its scope after resolve returns.
2475                      */
2476                     JS_UNLOCK_OBJ(cx, obj);
2477                     ok = resolve(cx, obj, ID_TO_VALUE(id));
2478                     if (!ok)
2479                         goto cleanup;
2480                     JS_LOCK_OBJ(cx, obj);
2481                     SET_OBJ_INFO(obj, file, line);
2482                     scope = OBJ_SCOPE(obj);
2483                     JS_ASSERT(MAP_IS_NATIVE(&scope->map));
2484                     if (scope->object == obj)
2485                         sprop = SCOPE_GET_PROPERTY(scope, id);
2486                 }
2488             cleanup:
2489                 js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
2490                 if (!ok || *propp)
2491                     return ok;
2492             }
2493         }
2495         if (sprop) {
2496             JS_ASSERT(OBJ_SCOPE(obj) == scope);
2497             *objp = scope->object;      /* XXXbe hide in jsscope.[ch] */
2499             *propp = (JSProperty *) sprop;
2500             return JS_TRUE;
2501         }
2503         proto = LOCKED_OBJ_GET_PROTO(obj);
2504         JS_UNLOCK_OBJ(cx, obj);
2505         if (!proto)
2506             break;
2507         if (!OBJ_IS_NATIVE(proto))
2508             return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
2509         obj = proto;
2510     }
2512 out:
2513     *objp = NULL;
2514     *propp = NULL;
2515     return JS_TRUE;
2518 #if defined JS_THREADSAFE && defined DEBUG
2519 JS_FRIEND_API(JSBool)
2520 _js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
2521                    JSProperty **propp, const char *file, uintN line)
2523     return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp, file, line);
2525 #else
2526 JS_FRIEND_API(JSBool)
2527 js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
2528                   JSProperty **propp)
2530     return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp);
2532 #endif
2534 JS_FRIEND_API(JSBool)
2535 js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
2536                 JSProperty **propp)
2538     JSRuntime *rt;
2539     JSObject *obj, *pobj, *lastobj;
2540     JSScopeProperty *sprop;
2541     JSProperty *prop;
2543     rt = cx->runtime;
2544     obj = cx->fp->scopeChain;
2545     do {
2546         /* Try the property cache and return immediately on cache hit. */
2547         if (OBJ_IS_NATIVE(obj)) {
2548             JS_LOCK_OBJ(cx, obj);
2549             PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop);
2550             if (sprop) {
2551                 JS_ASSERT(OBJ_IS_NATIVE(obj));
2552                 *objp = obj;
2553                 *pobjp = obj;
2554                 *propp = (JSProperty *) sprop;
2555                 return JS_TRUE;
2556             }
2557             JS_UNLOCK_OBJ(cx, obj);
2558         }
2560         /* If cache miss, take the slow path. */
2561         if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
2562             return JS_FALSE;
2563         if (prop) {
2564             if (OBJ_IS_NATIVE(pobj)) {
2565                 sprop = (JSScopeProperty *) prop;
2566                 PROPERTY_CACHE_FILL(&rt->propertyCache, pobj, id, sprop);
2567             }
2568             *objp = obj;
2569             *pobjp = pobj;
2570             *propp = prop;
2571             return JS_TRUE;
2572         }
2573         lastobj = obj;
2574     } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);
2576     *objp = lastobj;
2577     *pobjp = NULL;
2578     *propp = NULL;
2579     return JS_TRUE;
2582 JSObject *
2583 js_FindIdentifierBase(JSContext *cx, jsid id)
2585     JSObject *obj, *pobj;
2586     JSProperty *prop;
2588     /*
2589      * Look for id's property along the "with" statement chain and the
2590      * statically-linked scope chain.
2591      */
2592     if (!js_FindProperty(cx, id, &obj, &pobj, &prop))
2593         return NULL;
2594     if (prop) {
2595         OBJ_DROP_PROPERTY(cx, pobj, prop);
2596         return obj;
2597     }
2599     /*
2600      * Use the top-level scope from the scope chain, which won't end in the
2601      * same scope as cx->globalObject for cross-context function calls.
2602      */
2603     JS_ASSERT(obj);
2605     /*
2606      * Property not found.  Give a strict warning if binding an undeclared
2607      * top-level variable.
2608      */
2609     if (JS_HAS_STRICT_OPTION(cx)) {
2610         JSString *str = JSVAL_TO_STRING(ID_TO_VALUE(id));
2611         if (!JS_ReportErrorFlagsAndNumber(cx,
2612                                           JSREPORT_WARNING | JSREPORT_STRICT,
2613                                           js_GetErrorMessage, NULL,
2614                                           JSMSG_UNDECLARED_VAR,
2615                                           JS_GetStringBytes(str))) {
2616             return NULL;
2617         }
2618     }
2619     return obj;
2622 JSBool
2623 js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2625     JSObject *obj2;
2626     JSProperty *prop;
2627     JSScope *scope;
2628     JSScopeProperty *sprop;
2629     uint32 slot;
2631     /*
2632      * Handle old bug that took empty string as zero index.  Also convert
2633      * string indices to integers if appropriate.
2634      */
2635     CHECK_FOR_FUNNY_INDEX(id);
2637     if (!js_LookupProperty(cx, obj, id, &obj2, &prop))
2638         return JS_FALSE;
2639     if (!prop) {
2640         jsval default_val;
2642 #if JS_BUG_NULL_INDEX_PROPS
2643         /* Indexed properties defaulted to null in old versions. */
2644         default_val = (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0)
2645                       ? JSVAL_NULL
2646                       : JSVAL_VOID;
2647 #else
2648         default_val = JSVAL_VOID;
2649 #endif
2650         *vp = default_val;
2652         if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp))
2653             return JS_FALSE;
2655         /*
2656          * Give a strict warning if foo.bar is evaluated by a script for an
2657          * object foo with no property named 'bar'.
2658          */
2659         if (JS_HAS_STRICT_OPTION(cx) &&
2660             *vp == default_val &&
2661             cx->fp && cx->fp->pc &&
2662             (*cx->fp->pc == JSOP_GETPROP || *cx->fp->pc == JSOP_GETELEM))
2663         {
2664             jsbytecode *pc;
2665             JSString *str;
2667             /* Kludge to allow (typeof foo == "undefined") tests. */
2668             JS_ASSERT(cx->fp->script);
2669             pc = cx->fp->pc;
2670             pc += js_CodeSpec[*pc].length;
2671             if (Detecting(cx, pc))
2672                 return JS_TRUE;
2674             /* Ok, bad undefined property reference: whine about it. */
2675             str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
2676                                              ID_TO_VALUE(id), NULL);
2677             if (!str ||
2678                 !JS_ReportErrorFlagsAndNumber(cx,
2679                                               JSREPORT_WARNING|JSREPORT_STRICT,
2680                                               js_GetErrorMessage, NULL,
2681                                               JSMSG_UNDEFINED_PROP,
2682                                               JS_GetStringBytes(str))) {
2683                 return JS_FALSE;
2684             }
2685         }
2686         return JS_TRUE;
2687     }
2689     if (!OBJ_IS_NATIVE(obj2)) {
2690         OBJ_DROP_PROPERTY(cx, obj2, prop);
2691         return OBJ_GET_PROPERTY(cx, obj2, id, vp);
2692     }
2694     /* Unlock obj2 before calling getter, relock after to avoid deadlock. */
2695     scope = OBJ_SCOPE(obj2);
2696     sprop = (JSScopeProperty *) prop;
2697     slot = sprop->slot;
2698     if (slot != SPROP_INVALID_SLOT) {
2699         JS_ASSERT(slot < obj2->map->freeslot);
2700         *vp = LOCKED_OBJ_GET_SLOT(obj2, slot);
2702         /* If sprop has a stub getter, we're done. */
2703         if (!sprop->getter)
2704             goto out;
2705     } else {
2706         *vp = JSVAL_VOID;
2707     }
2709     JS_UNLOCK_SCOPE(cx, scope);
2710     if (!SPROP_GET(cx, sprop, obj, obj2, vp))
2711         return JS_FALSE;
2712     JS_LOCK_SCOPE(cx, scope);
2714     if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
2715         LOCKED_OBJ_SET_SLOT(obj2, slot, *vp);
2716         PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj2, id, sprop);
2717     }
2719 out:
2720     JS_UNLOCK_SCOPE(cx, scope);
2721     return JS_TRUE;
2724 JSBool
2725 js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2727     JSObject *pobj;
2728     JSProperty *prop;
2729     JSScopeProperty *sprop;
2730     JSScope *scope;
2731     uintN attrs, flags;
2732     intN shortid;
2733     JSClass *clasp;
2734     JSPropertyOp getter, setter;
2735     jsval pval;
2736     uint32 slot;
2738     /*
2739      * Handle old bug that took empty string as zero index.  Also convert
2740      * string indices to integers if appropriate.
2741      */
2742     CHECK_FOR_FUNNY_INDEX(id);
2744     if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
2745         return JS_FALSE;
2747     if (prop && !OBJ_IS_NATIVE(pobj)) {
2748         OBJ_DROP_PROPERTY(cx, pobj, prop);
2749         prop = NULL;
2750     }
2751     sprop = (JSScopeProperty *) prop;
2753     /*
2754      * Now either sprop is null, meaning id was not found in obj or one of its
2755      * prototypes; or sprop is non-null, meaning id was found in pobj's scope.
2756      * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop
2757      * is held: we must OBJ_DROP_PROPERTY or JS_UNLOCK_SCOPE before we return
2758      * (the two are equivalent for native objects, but we use JS_UNLOCK_SCOPE
2759      * because it is cheaper).
2760      */
2761     attrs = JSPROP_ENUMERATE;
2762     flags = 0;
2763     shortid = 0;
2764     clasp = OBJ_GET_CLASS(cx, obj);
2765     getter = clasp->getProperty;
2766     setter = clasp->setProperty;
2768     if (sprop) {
2769         /*
2770          * Set scope for use below.  It was locked by js_LookupProperty, and
2771          * we know pobj owns it (i.e., scope->object == pobj).  Therefore we
2772          * optimize JS_UNLOCK_OBJ(cx, pobj) into JS_UNLOCK_SCOPE(cx, scope).
2773          */
2774         scope = OBJ_SCOPE(pobj);
2776         attrs = sprop->attrs;
2777         if ((attrs & JSPROP_READONLY) ||
2778             (SCOPE_IS_SEALED(scope) && pobj == obj)) {
2779             JS_UNLOCK_SCOPE(cx, scope);
2780             if ((attrs & JSPROP_READONLY) && JSVERSION_IS_ECMA(cx->version))
2781                 return JS_TRUE;
2782             goto read_only_error;
2783         }
2785         if (pobj != obj) {
2786             /*
2787              * We found id in a prototype object: prepare to share or shadow.
2788              * NB: Thanks to the immutable, garbage-collected property tree
2789              * maintained by jsscope.c in cx->runtime, we needn't worry about
2790              * sprop going away behind our back after we've unlocked scope.
2791              */
2792             JS_UNLOCK_SCOPE(cx, scope);
2794             /* Don't clone a shared prototype property. */
2795             if (attrs & JSPROP_SHARED)
2796                 return SPROP_SET(cx, sprop, obj, pobj, vp);
2798             /* Restore attrs to the ECMA default for new properties. */
2799             attrs = JSPROP_ENUMERATE;
2801             /*
2802              * Preserve the shortid, getter, and setter when shadowing any
2803              * property that has a shortid.  An old API convention requires
2804              * that the property's getter and setter functions receive the
2805              * shortid, not id, when they are called on the shadow we are
2806              * about to create in obj's scope.
2807              */
2808             if (sprop->flags & SPROP_HAS_SHORTID) {
2809                 flags = SPROP_HAS_SHORTID;
2810                 shortid = sprop->shortid;
2811                 getter = sprop->getter;
2812                 setter = sprop->setter;
2813             }
2815             /*
2816              * Forget we found the proto-property now that we've copied any
2817              * needed member values.
2818              */
2819             sprop = NULL;
2820         }
2821 #ifdef __GNUC__ /* suppress bogus gcc warnings */
2822     } else {
2823         scope = NULL;
2824 #endif
2825     }
2827     if (!sprop) {
2828         if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj)
2829             goto read_only_error;
2831         /* Find or make a property descriptor with the right heritage. */
2832         JS_LOCK_OBJ(cx, obj);
2833         scope = js_GetMutableScope(cx, obj);
2834         if (!scope) {
2835             JS_UNLOCK_OBJ(cx, obj);
2836             return JS_FALSE;
2837         }
2838         if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
2839             attrs |= JSPROP_SHARED;
2840         sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
2841                                     SPROP_INVALID_SLOT, attrs, flags, shortid);
2842         if (!sprop) {
2843             JS_UNLOCK_SCOPE(cx, scope);
2844             return JS_FALSE;
2845         }
2847         /* XXXbe called with obj locked */
2848         if (!clasp->addProperty(cx, obj, SPROP_USERID(sprop), vp)) {
2849             (void) js_RemoveScopeProperty(cx, scope, id);
2850             JS_UNLOCK_SCOPE(cx, scope);
2851             return JS_FALSE;
2852         }
2854         /* Initialize new property value (passed to setter) to undefined. */
2855         if (SPROP_HAS_VALID_SLOT(sprop, scope))
2856             LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID);
2858         PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
2859     }
2861     /* Get the current property value from its slot. */
2862     slot = sprop->slot;
2863     if (slot != SPROP_INVALID_SLOT) {
2864         JS_ASSERT(slot < obj->map->freeslot);
2865         pval = LOCKED_OBJ_GET_SLOT(obj, slot);
2867         /* If sprop has a stub setter, keep scope locked and just store *vp. */
2868         if (!sprop->setter)
2869             goto set_slot;
2870     }
2872     /* Avoid deadlock by unlocking obj's scope while calling sprop's setter. */
2873     JS_UNLOCK_SCOPE(cx, scope);
2875     /* Let the setter modify vp before copying from it to obj->slots[slot]. */
2876     if (!SPROP_SET(cx, sprop, obj, obj, vp))
2877         return JS_FALSE;
2879     /* Relock obj's scope until we are done with sprop. */
2880     JS_LOCK_SCOPE(cx, scope);
2882     /*
2883      * Check whether sprop is still around (was not deleted), and whether it
2884      * has a slot (it may never have had one, or we may have lost a race with
2885      * someone who cleared scope).
2886      */
2887     if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
2888   set_slot:
2889         GC_POKE(cx, pval);
2890         LOCKED_OBJ_SET_SLOT(obj, slot, *vp);
2891     }
2892     JS_UNLOCK_SCOPE(cx, scope);
2893     return JS_TRUE;
2895   read_only_error: {
2896     JSString *str = js_DecompileValueGenerator(cx,
2897                                                JSDVG_IGNORE_STACK,
2898                                                ID_TO_VALUE(id),
2899                                                NULL);
2900     if (str) {
2901         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2902                              JSMSG_READ_ONLY,
2903                              JS_GetStringBytes(str));
2904     }
2905     return JS_FALSE;
2906   }
2909 JSBool
2910 js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
2911                  uintN *attrsp)
2913     JSBool noprop, ok;
2914     JSScopeProperty *sprop;
2916     noprop = !prop;
2917     if (noprop) {
2918         if (!js_LookupProperty(cx, obj, id, &obj, &prop))
2919             return JS_FALSE;
2920         if (!prop) {
2921             *attrsp = 0;
2922             return JS_TRUE;
2923         }
2924         if (!OBJ_IS_NATIVE(obj)) {
2925             ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp);
2926             OBJ_DROP_PROPERTY(cx, obj, prop);
2927             return ok;
2928         }
2929     }
2930     sprop = (JSScopeProperty *)prop;
2931     *attrsp = sprop->attrs;
2932     if (noprop)
2933         OBJ_DROP_PROPERTY(cx, obj, prop);
2934     return JS_TRUE;
2937 JSBool
2938 js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
2939                  uintN *attrsp)
2941     JSBool noprop, ok;
2942     JSScopeProperty *sprop;
2944     noprop = !prop;
2945     if (noprop) {
2946         if (!js_LookupProperty(cx, obj, id, &obj, &prop))
2947             return JS_FALSE;
2948         if (!prop)
2949             return JS_TRUE;
2950         if (!OBJ_IS_NATIVE(obj)) {
2951             ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, attrsp);
2952             OBJ_DROP_PROPERTY(cx, obj, prop);
2953             return ok;
2954         }
2955     }
2956     sprop = (JSScopeProperty *)prop;
2957     sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
2958                                          *attrsp &
2959                                          ~(JSPROP_GETTER | JSPROP_SETTER), 0,
2960                                          sprop->getter, sprop->setter);
2961     if (noprop)
2962         OBJ_DROP_PROPERTY(cx, obj, prop);
2963     return (sprop != NULL);
2966 JSBool
2967 js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
2969 #if JS_HAS_PROP_DELETE
2971     JSObject *proto;
2972     JSProperty *prop;
2973     JSScopeProperty *sprop;
2974     JSString *str;
2975     JSScope *scope;
2976     JSBool ok;
2978     *rval = JSVERSION_IS_ECMA(cx->version) ? JSVAL_TRUE : JSVAL_VOID;
2980     /*
2981      * Handle old bug that took empty string as zero index.  Also convert
2982      * string indices to integers if appropriate.
2983      */
2984     CHECK_FOR_FUNNY_INDEX(id);
2986     if (!js_LookupProperty(cx, obj, id, &proto, &prop))
2987         return JS_FALSE;
2988     if (!prop || proto != obj) {
2989         /*
2990          * If the property was found in a native prototype, check whether it's
2991          * shared and permanent.  Such a property stands for direct properties
2992          * in all delegating objects, matching ECMA semantics without bloating
2993          * each delegating object.
2994          */
2995         if (prop) {
2996             if (OBJ_IS_NATIVE(proto)) {
2997                 sprop = (JSScopeProperty *)prop;
2998                 if (SPROP_IS_SHARED_PERMANENT(sprop))
2999                     *rval = JSVAL_FALSE;
3000             }
3001             OBJ_DROP_PROPERTY(cx, proto, prop);
3002             if (*rval == JSVAL_FALSE)
3003                 return JS_TRUE;
3004         }
3006         /*
3007          * If no property, or the property comes unshared or impermanent from
3008          * a prototype, call the class's delProperty hook, passing rval as the
3009          * result parameter.
3010          */
3011         return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, ID_TO_VALUE(id),
3012                                                    rval);
3013     }
3015     sprop = (JSScopeProperty *)prop;
3016     if (sprop->attrs & JSPROP_PERMANENT) {
3017         OBJ_DROP_PROPERTY(cx, obj, prop);
3018         if (JSVERSION_IS_ECMA(cx->version)) {
3019             *rval = JSVAL_FALSE;
3020             return JS_TRUE;
3021         }
3022         str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
3023                                          ID_TO_VALUE(id), NULL);
3024         if (str) {
3025             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3026                                  JSMSG_PERMANENT, JS_GetStringBytes(str));
3027         }
3028         return JS_FALSE;
3029     }
3031     /* XXXbe called with obj locked */
3032     if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, SPROP_USERID(sprop),
3033                                                 rval)) {
3034         OBJ_DROP_PROPERTY(cx, obj, prop);
3035         return JS_FALSE;
3036     }
3038     scope = OBJ_SCOPE(obj);
3039     if (SPROP_HAS_VALID_SLOT(sprop, scope))
3040         GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot));
3042     PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, NULL);
3043     ok = js_RemoveScopeProperty(cx, scope, id);
3044     OBJ_DROP_PROPERTY(cx, obj, prop);
3045     return ok;
3047 #else  /* !JS_HAS_PROP_DELETE */
3049     jsval null = JSVAL_NULL;
3051     *rval = JSVAL_VOID;
3052     return js_SetProperty(cx, obj, id, &null);
3054 #endif /* !JS_HAS_PROP_DELETE */
3057 JSBool
3058 js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
3060     jsval v;
3061     JSString *str;
3063     v = OBJECT_TO_JSVAL(obj);
3064     switch (hint) {
3065       case JSTYPE_STRING:
3066         /*
3067          * Propagate the exception if js_TryMethod finds an appropriate
3068          * method, and calling that method returned failure.
3069          */
3070         if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL,
3071                           &v))
3072             return JS_FALSE;
3074         if (!JSVAL_IS_PRIMITIVE(v)) {
3075             if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
3076                 return JS_FALSE;
3078             /*
3079              * JS1.2 never failed (except for malloc failure) to convert an
3080              * object to a string.  ECMA requires an error if both toString
3081              * and valueOf fail to produce a primitive value.
3082              */
3083             if (!JSVAL_IS_PRIMITIVE(v) && cx->version == JSVERSION_1_2) {
3084                 char *bytes = JS_smprintf("[object %s]",
3085                                           OBJ_GET_CLASS(cx, obj)->name);
3086                 if (!bytes)
3087                     return JS_FALSE;
3088                 str = JS_NewString(cx, bytes, strlen(bytes));
3089                 if (!str) {
3090                     free(bytes);
3091                     return JS_FALSE;
3092                 }
3093                 v = STRING_TO_JSVAL(str);
3094                 goto out;
3095             }
3096         }
3097         break;
3099       default:
3100         if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
3101             return JS_FALSE;
3102         if (!JSVAL_IS_PRIMITIVE(v)) {
3103             JSType type = JS_TypeOfValue(cx, v);
3104             if (type == hint ||
3105                 (type == JSTYPE_FUNCTION && hint == JSTYPE_OBJECT)) {
3106                 goto out;
3107             }
3108             /* Don't convert to string (source object literal) for JS1.2. */
3109             if (cx->version == JSVERSION_1_2 && hint == JSTYPE_BOOLEAN)
3110                 goto out;
3111             if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0,
3112                               NULL, &v))
3113                 return JS_FALSE;
3114         }
3115         break;
3116     }
3117     if (!JSVAL_IS_PRIMITIVE(v)) {
3118         /* Avoid recursive death through js_DecompileValueGenerator. */
3119         if (hint == JSTYPE_STRING) {
3120             str = JS_InternString(cx, OBJ_GET_CLASS(cx, obj)->name);
3121             if (!str)
3122                 return JS_FALSE;
3123         } else {
3124             str = NULL;
3125         }
3126         *vp = OBJECT_TO_JSVAL(obj);
3127         str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, str);
3128         if (str) {
3129             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3130                                  JSMSG_CANT_CONVERT_TO,
3131                                  JS_GetStringBytes(str),
3132                                  (hint == JSTYPE_VOID)
3133                                  ? "primitive type"
3134                                  : js_type_str[hint]);
3135         }
3136         return JS_FALSE;
3137     }
3138 out:
3139     *vp = v;
3140     return JS_TRUE;
3143 JSIdArray *
3144 js_NewIdArray(JSContext *cx, jsint length)
3146     JSIdArray *ida;
3148     ida = (JSIdArray *)
3149         JS_malloc(cx, sizeof(JSIdArray) + (length - 1) * sizeof(jsval));
3150     if (ida)
3151         ida->length = length;
3152     return ida;
3155 JSIdArray *
3156 js_GrowIdArray(JSContext *cx, JSIdArray *ida, jsint length)
3158     ida = (JSIdArray *)
3159         JS_realloc(cx, ida, sizeof(JSIdArray) + (length - 1) * sizeof(jsval));
3160     if (ida)
3161         ida->length = length;
3162     return ida;
3165 /* Private type used to iterate over all properties of a native JS object */
3166 typedef struct JSNativeIteratorState {
3167     jsint next_index;   /* index into jsid array */
3168     JSIdArray *ida;     /* All property ids in enumeration */
3169 } JSNativeIteratorState;
3171 /*
3172  * This function is used to enumerate the properties of native JSObjects
3173  * and those host objects that do not define a JSNewEnumerateOp-style iterator
3174  * function.
3175  */
3176 JSBool
3177 js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
3178              jsval *statep, jsid *idp)
3180     JSObject *proto_obj;
3181     JSClass *clasp;
3182     JSEnumerateOp enumerate;
3183     JSScopeProperty *sprop, *lastProp;
3184     jsint i, length;
3185     JSScope *scope;
3186     JSIdArray *ida;
3187     JSNativeIteratorState *state;
3189     clasp = OBJ_GET_CLASS(cx, obj);
3190     enumerate = clasp->enumerate;
3191     if (clasp->flags & JSCLASS_NEW_ENUMERATE)
3192         return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
3194     switch (enum_op) {
3196     case JSENUMERATE_INIT:
3197         if (!enumerate(cx, obj))
3198             goto init_error;
3199         length = 0;
3201         /*
3202          * The set of all property ids is pre-computed when the iterator
3203          * is initialized so as to avoid problems with properties being
3204          * deleted during the iteration.
3205          */
3206         JS_LOCK_OBJ(cx, obj);
3207         scope = OBJ_SCOPE(obj);
3209         /*
3210          * If this object shares a scope with its prototype, don't enumerate
3211          * its properties.  Otherwise they will be enumerated a second time
3212          * when the prototype object is enumerated.
3213          */
3214         proto_obj = OBJ_GET_PROTO(cx, obj);
3215         if (proto_obj && scope == OBJ_SCOPE(proto_obj)) {
3216             ida = js_NewIdArray(cx, 0);
3217             if (!ida) {
3218                 JS_UNLOCK_OBJ(cx, obj);
3219                 goto init_error;
3220             }
3221         } else {
3222             /* Object has a private scope; Enumerate all props in scope. */
3223             for (sprop = lastProp = SCOPE_LAST_PROP(scope); sprop;
3224                  sprop = sprop->parent) {
3225                 if ((
3226 #ifdef DUMP_CALL_TABLE
3227                      (cx->options & JSOPTION_LOGCALL_TOSOURCE) ||
3228 #endif
3229                      (sprop->attrs & JSPROP_ENUMERATE)) &&
3230                     !(sprop->flags & SPROP_IS_ALIAS) &&
3231                     (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
3232                      SCOPE_HAS_PROPERTY(scope, sprop))) {
3233                     length++;
3234                 }
3235             }
3236             ida = js_NewIdArray(cx, length);
3237             if (!ida) {
3238                 JS_UNLOCK_OBJ(cx, obj);
3239                 goto init_error;
3240             }
3241             i = length;
3242             for (sprop = lastProp; sprop; sprop = sprop->parent) {
3243                 if ((
3244 #ifdef DUMP_CALL_TABLE
3245                      (cx->options & JSOPTION_LOGCALL_TOSOURCE) ||
3246 #endif
3247                      (sprop->attrs & JSPROP_ENUMERATE)) &&
3248                     !(sprop->flags & SPROP_IS_ALIAS) &&
3249                     (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
3250                      SCOPE_HAS_PROPERTY(scope, sprop))) {
3251                     JS_ASSERT(i > 0);
3252                     ida->vector[--i] = sprop->id;
3253                 }
3254             }
3255         }
3256         JS_UNLOCK_OBJ(cx, obj);
3258         state = (JSNativeIteratorState *)
3259             JS_malloc(cx, sizeof(JSNativeIteratorState));
3260         if (!state) {
3261             JS_DestroyIdArray(cx, ida);
3262             goto init_error;
3263         }
3264         state->ida = ida;
3265         state->next_index = 0;
3266         *statep = PRIVATE_TO_JSVAL(state);
3267         if (idp)
3268             *idp = INT_TO_JSVAL(length);
3269         return JS_TRUE;
3271     case JSENUMERATE_NEXT:
3272         state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
3273         ida = state->ida;
3274         length = ida->length;
3275         if (state->next_index != length) {
3276             *idp = ida->vector[state->next_index++];
3277             return JS_TRUE;
3278         }
3280         /* Fall through ... */
3282     case JSENUMERATE_DESTROY:
3283         state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
3284         JS_DestroyIdArray(cx, state->ida);
3285         JS_free(cx, state);
3286         *statep = JSVAL_NULL;
3287         return JS_TRUE;
3289     default:
3290         JS_ASSERT(0);
3291         return JS_FALSE;
3292     }
3294 init_error:
3295     *statep = JSVAL_NULL;
3296     return JS_FALSE;
3299 JSBool
3300 js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
3301                jsval *vp, uintN *attrsp)
3303     JSObject *pobj;
3304     JSProperty *prop;
3305     JSScopeProperty *sprop;
3306     JSClass *clasp;
3307     JSBool ok;
3309     if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
3310         return JS_FALSE;
3311     if (!prop) {
3312         *vp = JSVAL_VOID;
3313         *attrsp = 0;
3314         clasp = OBJ_GET_CLASS(cx, obj);
3315         return !clasp->checkAccess ||
3316                clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp);
3317     }
3318     if (!OBJ_IS_NATIVE(pobj)) {
3319         OBJ_DROP_PROPERTY(cx, pobj, prop);
3320         return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp);
3321     }
3322     sprop = (JSScopeProperty *)prop;
3323     *vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)))
3324           ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
3325           : JSVAL_VOID;
3326     *attrsp = sprop->attrs;
3327     clasp = LOCKED_OBJ_GET_CLASS(obj);
3328     if (clasp->checkAccess) {
3329         JS_UNLOCK_OBJ(cx, pobj);
3330         ok = clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp);
3331         JS_LOCK_OBJ(cx, pobj);
3332     } else {
3333         ok = JS_TRUE;
3334     }
3335     OBJ_DROP_PROPERTY(cx, pobj, prop);
3336     return ok;
3339 #ifdef JS_THREADSAFE
3340 void
3341 js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
3343     JS_UNLOCK_OBJ(cx, obj);
3345 #endif
3347 static void
3348 ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
3350     /*
3351      * The decompiler may need to access the args of the function in
3352      * progress rather than the one we had hoped to call.
3353      * So we switch the cx->fp to the frame below us. We stick the
3354      * current frame in the dormantFrameChain to protect it from gc.
3355      */
3357     JSStackFrame *fp = cx->fp;
3358     if (fp->down) {
3359         JS_ASSERT(!fp->dormantNext);
3360         fp->dormantNext = cx->dormantFrameChain;
3361         cx->dormantFrameChain = fp;
3362         cx->fp = fp->down;
3363     }
3365     js_ReportIsNotFunction(cx, vp, flags);
3367     if (fp->down) {
3368         JS_ASSERT(cx->dormantFrameChain == fp);
3369         cx->dormantFrameChain = fp->dormantNext;
3370         fp->dormantNext = NULL;
3371         cx->fp = fp;
3372     }
3375 #ifdef NARCISSUS
3376 static JSBool
3377 GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval)
3379     JSObject *tmp;
3380     jsval xcval;
3382     while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
3383         obj = tmp;
3384     if (!OBJ_GET_PROPERTY(cx, obj,
3385                           (jsid)cx->runtime->atomState.ExecutionContextAtom,
3386                           &xcval)) {
3387         return JS_FALSE;
3388     }
3389     if (JSVAL_IS_PRIMITIVE(xcval)) {
3390         JS_ReportError(cx, "invalid ExecutionContext in global object");
3391         return JS_FALSE;
3392     }
3393     if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(xcval),
3394                           (jsid)cx->runtime->atomState.currentAtom,
3395                           rval)) {
3396         return JS_FALSE;
3397     }
3398     return JS_TRUE;
3400 #endif
3402 JSBool
3403 js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
3405     JSClass *clasp;
3407     clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
3408     if (!clasp->call) {
3409 #ifdef NARCISSUS
3410         JSObject *callee, *args;
3411         jsval fval, nargv[3];
3412         JSBool ok;
3414         callee = JSVAL_TO_OBJECT(argv[-2]);
3415         if (!OBJ_GET_PROPERTY(cx, callee,
3416                               (jsid)cx->runtime->atomState.callAtom,
3417                               &fval)) {
3418             return JS_FALSE;
3419         }
3420         if (JSVAL_IS_FUNCTION(cx, fval)) {
3421             if (!GetCurrentExecutionContext(cx, obj, &nargv[2]))
3422                 return JS_FALSE;
3423             args = js_GetArgsObject(cx, cx->fp);
3424             if (!args)
3425                 return JS_FALSE;
3426             nargv[0] = OBJECT_TO_JSVAL(obj);
3427             nargv[1] = OBJECT_TO_JSVAL(args);
3428             return js_InternalCall(cx, callee, fval, 3, nargv, rval);
3429         }
3430         if (JSVAL_IS_OBJECT(fval) && JSVAL_TO_OBJECT(fval) != callee) {
3431             argv[-2] = fval;
3432             ok = js_Call(cx, obj, argc, argv, rval);
3433             argv[-2] = OBJECT_TO_JSVAL(callee);
3434             return ok;
3435         }
3436 #endif
3437         ReportIsNotFunction(cx, &argv[-2], 0);
3438         return JS_FALSE;
3439     }
3440     return clasp->call(cx, obj, argc, argv, rval);
3443 JSBool
3444 js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
3445              jsval *rval)
3447     JSClass *clasp;
3449     clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
3450     if (!clasp->construct) {
3451 #ifdef NARCISSUS
3452         JSObject *callee, *args;
3453         jsval cval, nargv[2];
3454         JSBool ok;
3456         callee = JSVAL_TO_OBJECT(argv[-2]);
3457         if (!OBJ_GET_PROPERTY(cx, callee,
3458                               (jsid)cx->runtime->atomState.constructAtom,
3459                               &cval)) {
3460             return JS_FALSE;
3461         }
3462         if (JSVAL_IS_FUNCTION(cx, cval)) {
3463             if (!GetCurrentExecutionContext(cx, obj, &nargv[1]))
3464                 return JS_FALSE;
3465             args = js_GetArgsObject(cx, cx->fp);
3466             if (!args)
3467                 return JS_FALSE;
3468             nargv[0] = OBJECT_TO_JSVAL(args);
3469             return js_InternalCall(cx, callee, cval, 2, nargv, rval);
3470         }
3471         if (JSVAL_IS_OBJECT(cval) && JSVAL_TO_OBJECT(cval) != callee) {
3472             argv[-2] = cval;
3473             ok = js_Call(cx, obj, argc, argv, rval);
3474             argv[-2] = OBJECT_TO_JSVAL(callee);
3475             return ok;
3476         }
3477 #endif
3478         ReportIsNotFunction(cx, &argv[-2], JSV2F_CONSTRUCT);
3479         return JS_FALSE;
3480     }
3481     return clasp->construct(cx, obj, argc, argv, rval);
3484 JSBool
3485 js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
3487     JSClass *clasp;
3489     clasp = OBJ_GET_CLASS(cx, obj);
3490     if (clasp->hasInstance)
3491         return clasp->hasInstance(cx, obj, v, bp);
3492 #ifdef NARCISSUS
3493     {
3494         jsval fval, rval;
3496         if (!OBJ_GET_PROPERTY(cx, obj,
3497                               (jsid)cx->runtime->atomState.hasInstanceAtom,
3498                               &fval)) {
3499             return JS_FALSE;
3500         }
3501         if (JSVAL_IS_FUNCTION(cx, fval)) {
3502             return js_InternalCall(cx, obj, fval, 1, &v, &rval) &&
3503                    js_ValueToBoolean(cx, rval, bp);
3504         }
3505     }
3506 #endif
3507     *bp = JS_FALSE;
3508     return JS_TRUE;
3511 JSBool
3512 js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
3514     JSObject *obj2;
3516     *bp = JS_FALSE;
3517     if (JSVAL_IS_PRIMITIVE(v))
3518         return JS_TRUE;
3519     obj2 = JSVAL_TO_OBJECT(v);
3520     while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL) {
3521         if (obj2 == obj) {
3522             *bp = JS_TRUE;
3523             break;
3524         }
3525     }
3526     return JS_TRUE;
3529 JSBool
3530 js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop)
3532     return GetClassPrototype(cx, NULL, name, protop);
3535 static JSBool
3536 GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
3537                   JSObject **protop)
3539     jsval v;
3540     JSObject *ctor;
3542     if (!FindConstructor(cx, scope, name, &v))
3543         return JS_FALSE;
3544     if (JSVAL_IS_FUNCTION(cx, v)) {
3545         ctor = JSVAL_TO_OBJECT(v);
3546         if (!OBJ_GET_PROPERTY(cx, ctor,
3547                               (jsid)cx->runtime->atomState.classPrototypeAtom,
3548                               &v)) {
3549             return JS_FALSE;
3550         }
3551     }
3552     *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;
3553     return JS_TRUE;
3556 JSBool
3557 js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,
3558                      uintN attrs)
3560     /*
3561      * Use the given attributes for the prototype property of the constructor,
3562      * as user-defined constructors have a DontDelete prototype (which may be
3563      * reset), while native or "system" constructors have DontEnum | ReadOnly |
3564      * DontDelete.
3565      */
3566     if (!OBJ_DEFINE_PROPERTY(cx, ctor,
3567                              (jsid)cx->runtime->atomState.classPrototypeAtom,
3568                              OBJECT_TO_JSVAL(proto), NULL, NULL,
3569                              attrs, NULL)) {
3570         return JS_FALSE;
3571     }
3573     /*
3574      * ECMA says that Object.prototype.constructor, or f.prototype.constructor
3575      * for a user-defined function f, is DontEnum.
3576      */
3577     return OBJ_DEFINE_PROPERTY(cx, proto,
3578                                (jsid)cx->runtime->atomState.constructorAtom,
3579                                OBJECT_TO_JSVAL(ctor), NULL, NULL,
3580                                0, NULL);
3583 JSBool
3584 js_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
3586     JSObject *obj;
3588     if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
3589         obj = NULL;
3590     } else if (JSVAL_IS_OBJECT(v)) {
3591         obj = JSVAL_TO_OBJECT(v);
3592         if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v))
3593             return JS_FALSE;
3594         if (JSVAL_IS_OBJECT(v))
3595             obj = JSVAL_TO_OBJECT(v);
3596     } else {
3597         if (JSVAL_IS_STRING(v)) {
3598             obj = js_StringToObject(cx, JSVAL_TO_STRING(v));
3599         } else if (JSVAL_IS_INT(v)) {
3600             obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v));
3601         } else if (JSVAL_IS_DOUBLE(v)) {
3602             obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v));
3603         } else {
3604             JS_ASSERT(JSVAL_IS_BOOLEAN(v));
3605             obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v));
3606         }
3607         if (!obj)
3608             return JS_FALSE;
3609     }
3610     *objp = obj;
3611     return JS_TRUE;
3614 JSObject *
3615 js_ValueToNonNullObject(JSContext *cx, jsval v)
3617     JSObject *obj;
3618     JSString *str;
3620     if (!js_ValueToObject(cx, v, &obj))
3621         return NULL;
3622     if (!obj) {
3623         str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
3624         if (str) {
3625             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3626                                  JSMSG_NO_PROPERTIES, JS_GetStringBytes(str));
3627         }
3628     }
3629     return obj;
3632 JSBool
3633 js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval)
3635 #if JS_HAS_VALUEOF_HINT
3636     jsval argv[1];
3638     argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]);
3639     return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv,
3640                         rval);
3641 #else
3642     return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 0, NULL,
3643                         rval);
3644 #endif
3647 JSBool
3648 js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
3649              uintN argc, jsval *argv, jsval *rval)
3651     JSErrorReporter older;
3652     jsval fval;
3653     JSBool ok;
3655     /*
3656      * Report failure only if an appropriate method was found, and calling it
3657      * returned failure.  We propagate failure in this case to make exceptions
3658      * behave properly.
3659      */
3660     older = JS_SetErrorReporter(cx, NULL);
3661     if (!OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &fval)) {
3662         JS_ClearPendingException(cx);
3663         ok = JS_TRUE;
3664     } else if (!JSVAL_IS_PRIMITIVE(fval)) {
3665         ok = js_InternalCall(cx, obj, fval, argc, argv, rval);
3666         if (!ok)
3667             JS_ClearPendingException(cx);
3668     } else {
3669         ok = JS_TRUE;
3670     }
3671     JS_SetErrorReporter(cx, older);
3672     return ok;
3675 #if JS_HAS_XDR
3677 #include "jsxdrapi.h"
3679 JSBool
3680 js_XDRObject(JSXDRState *xdr, JSObject **objp)
3682     JSContext *cx;
3683     JSClass *clasp;
3684     const char *className;
3685     uint32 classId, classDef;
3686     JSBool ok;
3687     JSObject *proto;
3689     cx = xdr->cx;
3690     if (xdr->mode == JSXDR_ENCODE) {
3691         clasp = OBJ_GET_CLASS(cx, *objp);
3692         className = clasp->name;
3693         classId = JS_XDRFindClassIdByName(xdr, className);
3694         classDef = !classId;
3695         if (classDef && !JS_XDRRegisterClass(xdr, clasp, &classId))
3696             return JS_FALSE;
3697     } else {
3698         classDef = 0;
3699         className = NULL;
3700         clasp = NULL;           /* quell GCC overwarning */
3701     }
3703     /* XDR a flag word followed (if true) by the class name. */
3704     if (!JS_XDRUint32(xdr, &classDef))
3705         return JS_FALSE;
3706     if (classDef && !JS_XDRCString(xdr, (char **) &className))
3707         return JS_FALSE;
3709     /* From here on, return through out: to free className if it was set. */
3710     ok = JS_XDRUint32(xdr, &classId);
3711     if (!ok)
3712         goto out;
3714     if (xdr->mode != JSXDR_ENCODE) {
3715         if (classDef) {
3716             ok = js_GetClassPrototype(cx, className, &proto);
3717             if (!ok)
3718                 goto out;
3719             clasp = OBJ_GET_CLASS(cx, proto);
3720             ok = JS_XDRRegisterClass(xdr, clasp, &classId);
3721             if (!ok)
3722                 goto out;
3723         } else {
3724             clasp = JS_XDRFindClassById(xdr, classId);
3725             if (!clasp) {
3726                 char numBuf[12];
3727                 JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId);
3728                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3729                                      JSMSG_CANT_FIND_CLASS, numBuf);
3730                 ok = JS_FALSE;
3731                 goto out;
3732             }
3733         }
3734     }
3736     if (!clasp->xdrObject) {
3737         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3738                              JSMSG_CANT_XDR_CLASS, clasp->name);
3739         ok = JS_FALSE;
3740     } else {
3741         ok = clasp->xdrObject(xdr, objp);
3742     }
3743 out:
3744     if (xdr->mode != JSXDR_ENCODE && className)
3745         JS_free(cx, (void *)className);
3746     return ok;
3749 #endif /* JS_HAS_XDR */
3751 #ifdef DEBUG_brendan
3753 #include <stdio.h>
3754 #include <math.h>
3756 uint32 js_entry_count_max;
3757 uint32 js_entry_count_sum;
3758 double js_entry_count_sqsum;
3759 uint32 js_entry_count_hist[11];
3761 static void
3762 MeterEntryCount(uintN count)
3764     if (count) {
3765         js_entry_count_sum += count;
3766         js_entry_count_sqsum += (double)count * count;
3767         if (count > js_entry_count_max)
3768             js_entry_count_max = count;
3769     }
3770     js_entry_count_hist[JS_MIN(count, 10)]++;
3773 void
3774 js_DumpScopeMeters(JSRuntime *rt)
3776     static FILE *logfp;
3777     if (!logfp)
3778         logfp = fopen("/tmp/scope.stats", "a");
3780     {
3781         double mean = 0., var = 0., sigma = 0.;
3782         double nscopes = rt->liveScopes;
3783         double nentrys = js_entry_count_sum;
3784         if (nscopes > 0 && nentrys >= 0) {
3785             mean = nentrys / nscopes;
3786             var = nscopes * js_entry_count_sqsum - nentrys * nentrys;
3787             if (var < 0.0 || nscopes <= 1)
3788                 var = 0.0;
3789             else
3790                 var /= nscopes * (nscopes - 1);
3792             /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
3793             sigma = (var != 0.) ? sqrt(var) : 0.;
3794         }
3796         fprintf(logfp,
3797                 "scopes %g entries %g mean %g sigma %g max %u",
3798                 nscopes, nentrys, mean, sigma, js_entry_count_max);
3799     }
3801     fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u\n",
3802             js_entry_count_hist[0], js_entry_count_hist[1],
3803             js_entry_count_hist[2], js_entry_count_hist[3],
3804             js_entry_count_hist[4], js_entry_count_hist[5],
3805             js_entry_count_hist[6], js_entry_count_hist[7],
3806             js_entry_count_hist[8], js_entry_count_hist[9],
3807             js_entry_count_hist[10]);
3808     js_entry_count_sum = js_entry_count_max = 0;
3809     js_entry_count_sqsum = 0;
3810     memset(js_entry_count_hist, 0, sizeof js_entry_count_hist);
3811     fflush(logfp);
3814 #endif /* DEBUG_brendan */
3816 uint32
3817 js_Mark(JSContext *cx, JSObject *obj, void *arg)
3819     JSScope *scope;
3820     JSScopeProperty *sprop;
3821     JSClass *clasp;
3823     JS_ASSERT(OBJ_IS_NATIVE(obj));
3824     scope = OBJ_SCOPE(obj);
3825 #ifdef DEBUG_brendan
3826     if (scope->object == obj)
3827         MeterEntryCount(scope->entryCount);
3828 #endif
3830     JS_ASSERT(!SCOPE_LAST_PROP(scope) ||
3831               SCOPE_HAS_PROPERTY(scope, SCOPE_LAST_PROP(scope)));
3833     for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
3834         if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
3835             continue;
3836         MARK_SCOPE_PROPERTY(sprop);
3837         if (!JSVAL_IS_INT(sprop->id))
3838             GC_MARK_ATOM(cx, (JSAtom *)sprop->id, arg);
3840 #if JS_HAS_GETTER_SETTER
3841         if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
3842 #ifdef GC_MARK_DEBUG
3843             char buf[64];
3844             JSAtom *atom = (JSAtom *)sprop->id;
3845             const char *id = (atom && ATOM_IS_STRING(atom))
3846                              ? JS_GetStringBytes(ATOM_TO_STRING(atom))
3847                              : "unknown";
3848 #endif
3850             if (sprop->attrs & JSPROP_GETTER) {
3851 #ifdef GC_MARK_DEBUG
3852                 JS_snprintf(buf, sizeof buf, "%s %s",
3853                             id, js_getter_str);
3854 #endif
3855                 GC_MARK(cx,
3856                         JSVAL_TO_GCTHING((jsval) sprop->getter),
3857                         buf,
3858                         arg);
3859             }
3860             if (sprop->attrs & JSPROP_SETTER) {
3861 #ifdef GC_MARK_DEBUG
3862                 JS_snprintf(buf, sizeof buf, "%s %s",
3863                             id, js_setter_str);
3864 #endif
3865                 GC_MARK(cx,
3866                         JSVAL_TO_GCTHING((jsval) sprop->setter),
3867                         buf,
3868                         arg);
3869             }
3870         }
3871 #endif /* JS_HAS_GETTER_SETTER */
3872     }
3874     /* No one runs while the GC is running, so we can use LOCKED_... here. */
3875     clasp = LOCKED_OBJ_GET_CLASS(obj);
3876     if (clasp->mark)
3877         (void) clasp->mark(cx, obj, arg);
3879     if (scope->object != obj) {
3880         /*
3881          * An unmutated object that shares a prototype's scope.  We can't tell
3882          * how many slots are allocated and in use at obj->slots by looking at
3883          * scope, so we get obj->slots' length from its -1'st element.
3884          */
3885         return (uint32) obj->slots[-1];
3886     }
3887     return JS_MIN(scope->map.freeslot, scope->map.nslots);
3890 void
3891 js_Clear(JSContext *cx, JSObject *obj)
3893     JSScope *scope;
3894     JSRuntime *rt;
3895     JSScopeProperty *sprop;
3896     uint32 i, n;
3898     /*
3899      * Clear our scope and the property cache of all obj's properties only if
3900      * obj owns the scope (i.e., not if obj is unmutated and therefore sharing
3901      * its prototype's scope).  NB: we do not clear any reserved slots lying
3902      * below JSSLOT_FREE(clasp).
3903      */
3904     JS_LOCK_OBJ(cx, obj);
3905     scope = OBJ_SCOPE(obj);
3906     if (scope->object == obj) {
3907         /* Clear the property cache before we clear the scope. */
3908         rt = cx->runtime;
3909         for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
3910             if (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
3911                 SCOPE_HAS_PROPERTY(scope, sprop)) {
3912                 PROPERTY_CACHE_FILL(&rt->propertyCache, obj, sprop->id, NULL);
3913             }
3914         }
3916         /* Now that we're done using scope->lastProp/table, clear scope. */
3917         js_ClearScope(cx, scope);
3919         /* Clear slot values and reset freeslot so we're consistent. */
3920         i = scope->map.nslots;
3921         n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj));
3922         while (--i >= n)
3923             obj->slots[i] = JSVAL_VOID;
3924         scope->map.freeslot = n;
3925     }
3926     JS_UNLOCK_OBJ(cx, obj);
3929 jsval
3930 js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot)
3932     jsval v;
3934     JS_LOCK_OBJ(cx, obj);
3935     v = (slot < (uint32) obj->slots[-1]) ? obj->slots[slot] : JSVAL_VOID;
3936     JS_UNLOCK_OBJ(cx, obj);
3937     return v;
3940 JSBool
3941 js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
3943     JSScope *scope;
3944     uint32 nslots, i;
3945     JSClass *clasp;
3946     jsval *newslots;
3948     JS_LOCK_OBJ(cx, obj);
3949     scope = OBJ_SCOPE(obj);
3950     nslots = (uint32) obj->slots[-1];
3951     if (slot >= nslots) {
3952         /*
3953          * At this point, obj may or may not own scope.  If some path calls
3954          * js_GetMutableScope but does not add a slot-owning property, then
3955          * scope->object == obj but nslots will be nominal.  If obj shares a
3956          * prototype's scope, then we cannot update scope->map here, but we
3957          * must update obj->slots[-1] when we grow obj->slots.
3958          *
3959          * See js_Mark, before the last return, where we make a special case
3960          * for unmutated (scope->object != obj) objects.
3961          */
3962         JS_ASSERT(nslots == JS_INITIAL_NSLOTS);
3963         clasp = LOCKED_OBJ_GET_CLASS(obj);
3964         nslots = JSSLOT_FREE(clasp);
3965         if (clasp->reserveSlots)
3966             nslots += clasp->reserveSlots(cx, obj);
3967         JS_ASSERT(slot < nslots);
3969         newslots = (jsval *)
3970             JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval));
3971         if (!newslots) {
3972             JS_UNLOCK_SCOPE(cx, scope);
3973             return JS_FALSE;
3974         }
3975         for (i = 1 + newslots[0]; i <= nslots; i++)
3976             newslots[i] = JSVAL_VOID;
3977         if (scope->object == obj)
3978             scope->map.nslots = nslots;
3979         newslots[0] = nslots;
3980         obj->slots = newslots + 1;
3981     }
3983     /* Whether or not we grew nslots, we may need to advance freeslot. */
3984     if (scope->object == obj && slot >= scope->map.freeslot)
3985         scope->map.freeslot = slot + 1;
3987     obj->slots[slot] = v;
3988     JS_UNLOCK_SCOPE(cx, scope);
3989     return JS_TRUE;
3992 #ifdef DEBUG
3994 /* Routines to print out values during debugging. */
3996 void printChar(jschar *cp) {
3997     fprintf(stderr, "jschar* (0x%p) \"", (void *)cp);
3998     while (*cp)
3999         fputc(*cp++, stderr);
4000     fputc('"', stderr);
4001     fputc('\n', stderr);
4004 void printString(JSString *str) {
4005     size_t i, n;
4006     jschar *s;
4007     fprintf(stderr, "string (0x%p) \"", (void *)str);
4008     s = JSSTRING_CHARS(str);
4009     for (i=0, n=JSSTRING_LENGTH(str); i < n; i++)
4010         fputc(s[i], stderr);
4011     fputc('"', stderr);
4012     fputc('\n', stderr);
4015 void printVal(JSContext *cx, jsval val);
4017 void printObj(JSContext *cx, JSObject *jsobj) {
4018     jsuint i;
4019     jsval val;
4020     JSClass *clasp;
4022     fprintf(stderr, "object 0x%p\n", (void *)jsobj);
4023     clasp = OBJ_GET_CLASS(cx, jsobj);
4024     fprintf(stderr, "class 0x%p %s\n", (void *)clasp, clasp->name);
4025     for (i=0; i < jsobj->map->nslots; i++) {
4026         fprintf(stderr, "slot %3d ", i);
4027         val = jsobj->slots[i];
4028         if (JSVAL_IS_OBJECT(val))
4029             fprintf(stderr, "object 0x%p\n", (void *)JSVAL_TO_OBJECT(val));
4030         else
4031             printVal(cx, val);
4032     }
4035 void printVal(JSContext *cx, jsval val) {
4036     fprintf(stderr, "val %d (0x%p) = ", (int)val, (void *)val);
4037     if (JSVAL_IS_NULL(val)) {
4038         fprintf(stderr, "null\n");
4039     } else if (JSVAL_IS_VOID(val)) {
4040         fprintf(stderr, "undefined\n");
4041     } else if (JSVAL_IS_OBJECT(val)) {
4042         printObj(cx, JSVAL_TO_OBJECT(val));
4043     } else if (JSVAL_IS_INT(val)) {
4044         fprintf(stderr, "(int) %d\n", JSVAL_TO_INT(val));
4045     } else if (JSVAL_IS_STRING(val)) {
4046         printString(JSVAL_TO_STRING(val));
4047     } else if (JSVAL_IS_DOUBLE(val)) {
4048         fprintf(stderr, "(double) %g\n", *JSVAL_TO_DOUBLE(val));
4049     } else {
4050         JS_ASSERT(JSVAL_IS_BOOLEAN(val));
4051         fprintf(stderr, "(boolean) %s\n",
4052                 JSVAL_TO_BOOLEAN(val) ? "true" : "false");
4053     }
4054     fflush(stderr);
4057 void printId(JSContext *cx, jsid id) {
4058     fprintf(stderr, "id %d (0x%p) is ", (int)id, (void *)id);
4059     printVal(cx, ID_TO_VALUE(id));
4062 void printAtom(JSAtom *atom) {
4063     printString(ATOM_TO_STRING(atom));
4066 #endif