Back to index

lightning-sunbird  0.9+nobinonly
jsobj.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * vim: set ts=8 sw=4 et tw=78:
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is Mozilla Communicator client code, released
00018  * March 31, 1998.
00019  *
00020  * The Initial Developer of the Original Code is
00021  * Netscape Communications Corporation.
00022  * Portions created by the Initial Developer are Copyright (C) 1998
00023  * the Initial Developer. All Rights Reserved.
00024  *
00025  * Contributor(s):
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 /*
00042  * JS object implementation.
00043  */
00044 #include "jsstddef.h"
00045 #include <stdlib.h>
00046 #include <string.h>
00047 #include "jstypes.h"
00048 #include "jsarena.h" /* Added by JSIFY */
00049 #include "jsbit.h"
00050 #include "jsutil.h" /* Added by JSIFY */
00051 #include "jshash.h" /* Added by JSIFY */
00052 #include "jsdhash.h"
00053 #include "jsprf.h"
00054 #include "jsapi.h"
00055 #include "jsarray.h"
00056 #include "jsatom.h"
00057 #include "jsbool.h"
00058 #include "jscntxt.h"
00059 #include "jsconfig.h"
00060 #include "jsfun.h"
00061 #include "jsgc.h"
00062 #include "jsinterp.h"
00063 #include "jslock.h"
00064 #include "jsnum.h"
00065 #include "jsobj.h"
00066 #include "jsscan.h"
00067 #include "jsscope.h"
00068 #include "jsscript.h"
00069 #include "jsstr.h"
00070 #include "jsopcode.h"
00071 
00072 #include "jsdbgapi.h"   /* whether or not JS_HAS_OBJ_WATCHPOINT */
00073 
00074 #if JS_HAS_GENERATORS
00075 #include "jsiter.h"
00076 #endif
00077 
00078 #if JS_HAS_XML_SUPPORT
00079 #include "jsxml.h"
00080 #endif
00081 
00082 #if JS_HAS_XDR
00083 #include "jsxdrapi.h"
00084 #endif
00085 
00086 #ifdef JS_THREADSAFE
00087 #define NATIVE_DROP_PROPERTY js_DropProperty
00088 
00089 extern void
00090 js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);
00091 #else
00092 #define NATIVE_DROP_PROPERTY NULL
00093 #endif
00094 
00095 JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
00096     js_NewObjectMap,        js_DestroyObjectMap,
00097     js_LookupProperty,      js_DefineProperty,
00098     js_GetProperty,         js_SetProperty,
00099     js_GetAttributes,       js_SetAttributes,
00100     js_DeleteProperty,      js_DefaultValue,
00101     js_Enumerate,           js_CheckAccess,
00102     NULL,                   NATIVE_DROP_PROPERTY,
00103     js_Call,                js_Construct,
00104     NULL,                   js_HasInstance,
00105     js_SetProtoOrParent,    js_SetProtoOrParent,
00106     js_Mark,                js_Clear,
00107     js_GetRequiredSlot,     js_SetRequiredSlot
00108 };
00109 
00110 JSClass js_ObjectClass = {
00111     js_Object_str,
00112     JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
00113     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
00114     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
00115     JSCLASS_NO_OPTIONAL_MEMBERS
00116 };
00117 
00118 #if JS_HAS_OBJ_PROTO_PROP
00119 
00120 static JSBool
00121 obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
00122 
00123 static JSBool
00124 obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
00125 
00126 static JSBool
00127 obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
00128 
00129 static JSPropertySpec object_props[] = {
00130     /* These two must come first; see object_props[slot].name usage below. */
00131     {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED,
00132                                                   obj_getSlot,  obj_setSlot},
00133     {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
00134                                                   obj_getSlot,  obj_setSlot},
00135     {js_count_str, 0,            JSPROP_PERMANENT,obj_getCount, obj_getCount},
00136     {0,0,0,0,0}
00137 };
00138 
00139 /* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */
00140 #define JSSLOT_COUNT 2
00141 
00142 static JSBool
00143 ReportStrictSlot(JSContext *cx, uint32 slot)
00144 {
00145     if (slot == JSSLOT_PROTO)
00146         return JS_TRUE;
00147     return JS_ReportErrorFlagsAndNumber(cx,
00148                                         JSREPORT_WARNING | JSREPORT_STRICT,
00149                                         js_GetErrorMessage, NULL,
00150                                         JSMSG_DEPRECATED_USAGE,
00151                                         object_props[slot].name);
00152 }
00153 
00154 static JSBool
00155 obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
00156 {
00157     uint32 slot;
00158     jsid propid;
00159     JSAccessMode mode;
00160     uintN attrs;
00161     JSObject *pobj;
00162     JSClass *clasp;
00163     JSExtendedClass *xclasp;
00164 
00165     slot = (uint32) JSVAL_TO_INT(id);
00166     if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
00167         propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
00168         mode = JSACC_PROTO;
00169     } else {
00170         propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
00171         mode = JSACC_PARENT;
00172     }
00173 
00174     /* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */
00175     if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs))
00176         return JS_FALSE;
00177 
00178     pobj = JSVAL_TO_OBJECT(*vp);
00179     if (pobj) {
00180         clasp = OBJ_GET_CLASS(cx, pobj);
00181         if (clasp == &js_CallClass || clasp == &js_BlockClass) {
00182             /* Censor activations and lexical scopes per ECMA-262. */
00183             *vp = JSVAL_NULL;
00184         } else if (clasp->flags & JSCLASS_IS_EXTENDED) {
00185             xclasp = (JSExtendedClass *) clasp;
00186             if (xclasp->outerObject) {
00187                 pobj = xclasp->outerObject(cx, pobj);
00188                 if (!pobj)
00189                     return JS_FALSE;
00190                 *vp = OBJECT_TO_JSVAL(pobj);
00191             }
00192         }
00193     }
00194     return JS_TRUE;
00195 }
00196 
00197 static JSBool
00198 obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
00199 {
00200     JSObject *pobj;
00201     uint32 slot;
00202     jsid propid;
00203     uintN attrs;
00204 
00205     if (!JSVAL_IS_OBJECT(*vp))
00206         return JS_TRUE;
00207     pobj = JSVAL_TO_OBJECT(*vp);
00208 
00209     if (pobj) {
00210         /*
00211          * Innerize pobj here to avoid sticking unwanted properties on the
00212          * outer object. This ensures that any with statements only grant
00213          * access to the inner object.
00214          */
00215         OBJ_TO_INNER_OBJECT(cx, pobj);
00216         if (!pobj)
00217             return JS_FALSE;
00218     }
00219     slot = (uint32) JSVAL_TO_INT(id);
00220     if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
00221         return JS_FALSE;
00222 
00223     /* __parent__ is readonly and permanent, only __proto__ may be set. */
00224     propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
00225     if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_PROTO|JSACC_WRITE, vp, &attrs))
00226         return JS_FALSE;
00227 
00228     return js_SetProtoOrParent(cx, obj, slot, pobj);
00229 }
00230 
00231 static JSBool
00232 obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
00233 {
00234     jsval iter_state;
00235     jsid num_properties;
00236     JSBool ok;
00237 
00238     if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))
00239         return JS_FALSE;
00240 
00241     /* Get the number of properties to enumerate. */
00242     iter_state = JSVAL_NULL;
00243     ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties);
00244     if (!ok)
00245         goto out;
00246 
00247     if (!JSVAL_IS_INT(num_properties)) {
00248         JS_ASSERT(0);
00249         *vp = JSVAL_ZERO;
00250         goto out;
00251     }
00252     *vp = num_properties;
00253 
00254 out:
00255     if (iter_state != JSVAL_NULL)
00256         ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
00257     return ok;
00258 }
00259 
00260 #else  /* !JS_HAS_OBJ_PROTO_PROP */
00261 
00262 #define object_props NULL
00263 
00264 #endif /* !JS_HAS_OBJ_PROTO_PROP */
00265 
00266 JSBool
00267 js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)
00268 {
00269     JSRuntime *rt;
00270     JSObject *obj2, *oldproto;
00271     JSScope *scope, *newscope;
00272 
00273     /*
00274      * Serialize all proto and parent setting in order to detect cycles.
00275      * We nest locks in this function, and only here, in the following orders:
00276      *
00277      * (1)  rt->setSlotLock < pobj's scope lock;
00278      *      rt->setSlotLock < pobj's proto-or-parent's scope lock;
00279      *      rt->setSlotLock < pobj's grand-proto-or-parent's scope lock;
00280      *      etc...
00281      * (2)  rt->setSlotLock < obj's scope lock < pobj's scope lock.
00282      *
00283      * We avoid AB-BA deadlock by restricting obj from being on pobj's parent
00284      * or proto chain (pobj may already be on obj's parent or proto chain; it
00285      * could be moving up or down).  We finally order obj with respect to pobj
00286      * at the bottom of this routine (just before releasing rt->setSlotLock),
00287      * by making pobj be obj's prototype or parent.
00288      *
00289      * After we have set the slot and released rt->setSlotLock, another call
00290      * to js_SetProtoOrParent could nest locks according to the first order
00291      * list above, but it cannot deadlock with any other thread.  For there
00292      * to be a deadlock, other parts of the engine would have to nest scope
00293      * locks in the opposite order.  XXXbe ensure they don't!
00294      */
00295     rt = cx->runtime;
00296 #ifdef JS_THREADSAFE
00297 
00298     JS_ACQUIRE_LOCK(rt->setSlotLock);
00299     while (rt->setSlotBusy) {
00300         jsrefcount saveDepth;
00301 
00302         /* Take pains to avoid nesting rt->gcLock inside rt->setSlotLock! */
00303         JS_RELEASE_LOCK(rt->setSlotLock);
00304         saveDepth = JS_SuspendRequest(cx);
00305         JS_ACQUIRE_LOCK(rt->setSlotLock);
00306         if (rt->setSlotBusy)
00307             JS_WAIT_CONDVAR(rt->setSlotDone, JS_NO_TIMEOUT);
00308         JS_RELEASE_LOCK(rt->setSlotLock);
00309         JS_ResumeRequest(cx, saveDepth);
00310         JS_ACQUIRE_LOCK(rt->setSlotLock);
00311     }
00312     rt->setSlotBusy = JS_TRUE;
00313     JS_RELEASE_LOCK(rt->setSlotLock);
00314 
00315 #define SET_SLOT_DONE(rt)                                                     \
00316     JS_BEGIN_MACRO                                                            \
00317         JS_ACQUIRE_LOCK((rt)->setSlotLock);                                   \
00318         (rt)->setSlotBusy = JS_FALSE;                                         \
00319         JS_NOTIFY_ALL_CONDVAR((rt)->setSlotDone);                             \
00320         JS_RELEASE_LOCK((rt)->setSlotLock);                                   \
00321     JS_END_MACRO
00322 
00323 #else
00324 
00325 #define SET_SLOT_DONE(rt)       /* nothing */
00326 
00327 #endif
00328 
00329     obj2 = pobj;
00330     while (obj2) {
00331         if (obj2 == obj) {
00332             SET_SLOT_DONE(rt);
00333             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
00334                                  JSMSG_CYCLIC_VALUE,
00335 #if JS_HAS_OBJ_PROTO_PROP
00336                                  object_props[slot].name
00337 #else
00338                                  (slot == JSSLOT_PROTO) ? js_proto_str
00339                                                         : js_parent_str
00340 #endif
00341                                  );
00342             return JS_FALSE;
00343         }
00344         obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot));
00345     }
00346 
00347     if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) {
00348         /* Check to see whether obj shares its prototype's scope. */
00349         JS_LOCK_OBJ(cx, obj);
00350         scope = OBJ_SCOPE(obj);
00351         oldproto = JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO));
00352         if (oldproto && OBJ_SCOPE(oldproto) == scope) {
00353             /* Either obj needs a new empty scope, or it should share pobj's. */
00354             if (!pobj ||
00355                 !OBJ_IS_NATIVE(pobj) ||
00356                 OBJ_GET_CLASS(cx, pobj) != LOCKED_OBJ_GET_CLASS(oldproto)) {
00357                 /*
00358                  * With no proto and no scope of its own, obj is truly empty.
00359                  *
00360                  * If pobj is not native, obj needs its own empty scope -- it
00361                  * should not continue to share oldproto's scope once oldproto
00362                  * is not on obj's prototype chain.  That would put properties
00363                  * from oldproto's scope ahead of properties defined by pobj,
00364                  * in lookup order.
00365                  *
00366                  * If pobj's class differs from oldproto's, we may need a new
00367                  * scope to handle differences in private and reserved slots,
00368                  * so we suboptimally but safely make one.
00369                  */
00370                 scope = js_GetMutableScope(cx, obj);
00371                 if (!scope) {
00372                     JS_UNLOCK_OBJ(cx, obj);
00373                     SET_SLOT_DONE(rt);
00374                     return JS_FALSE;
00375                 }
00376             } else if (OBJ_SCOPE(pobj) != scope) {
00377 #ifdef JS_THREADSAFE
00378                 /*
00379                  * We are about to nest scope locks.  Help jslock.c:ShareScope
00380                  * keep scope->u.count balanced for the JS_UNLOCK_SCOPE, while
00381                  * avoiding deadlock, by recording scope in rt->setSlotScope.
00382                  */
00383                 if (scope->ownercx) {
00384                     JS_ASSERT(scope->ownercx == cx);
00385                     rt->setSlotScope = scope;
00386                 }
00387 #endif
00388 
00389                 /* We can't deadlock because we checked for cycles above (2). */
00390                 JS_LOCK_OBJ(cx, pobj);
00391                 newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map);
00392                 obj->map = &newscope->map;
00393                 js_DropObjectMap(cx, &scope->map, obj);
00394                 JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
00395                 scope = newscope;
00396 #ifdef JS_THREADSAFE
00397                 rt->setSlotScope = NULL;
00398 #endif
00399             }
00400         }
00401         LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj));
00402         JS_UNLOCK_SCOPE(cx, scope);
00403     } else {
00404         OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj));
00405     }
00406 
00407     SET_SLOT_DONE(rt);
00408     return JS_TRUE;
00409 
00410 #undef SET_SLOT_DONE
00411 }
00412 
00413 JS_STATIC_DLL_CALLBACK(JSHashNumber)
00414 js_hash_object(const void *key)
00415 {
00416     return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS;
00417 }
00418 
00419 static JSHashEntry *
00420 MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
00421 {
00422     JSSharpObjectMap *map;
00423     JSHashTable *table;
00424     JSHashNumber hash;
00425     JSHashEntry **hep, *he;
00426     jsatomid sharpid;
00427     JSIdArray *ida;
00428     JSBool ok;
00429     jsint i, length;
00430     jsid id;
00431 #if JS_HAS_GETTER_SETTER
00432     JSObject *obj2;
00433     JSProperty *prop;
00434     uintN attrs;
00435 #endif
00436     jsval val;
00437     int stackDummy;
00438 
00439     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
00440         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
00441         return NULL;
00442     }
00443 
00444     map = &cx->sharpObjectMap;
00445     table = map->table;
00446     hash = js_hash_object(obj);
00447     hep = JS_HashTableRawLookup(table, hash, obj);
00448     he = *hep;
00449     if (!he) {
00450         sharpid = 0;
00451         he = JS_HashTableRawAdd(table, hep, hash, obj,
00452                                 JS_UINT32_TO_PTR(sharpid));
00453         if (!he) {
00454             JS_ReportOutOfMemory(cx);
00455             return NULL;
00456         }
00457 
00458         /*
00459          * Increment map->depth to protect js_EnterSharpObject from reentering
00460          * itself badly.  Without this fix, if we reenter the basis case where
00461          * map->depth == 0, when unwinding the inner call we will destroy the
00462          * newly-created hash table and crash.
00463          */
00464         ++map->depth;
00465         ida = JS_Enumerate(cx, obj);
00466         --map->depth;
00467         if (!ida)
00468             return NULL;
00469 
00470         ok = JS_TRUE;
00471         for (i = 0, length = ida->length; i < length; i++) {
00472             id = ida->vector[i];
00473 #if JS_HAS_GETTER_SETTER
00474             ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
00475             if (!ok)
00476                 break;
00477             if (!prop)
00478                 continue;
00479             ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
00480             if (ok) {
00481                 if (OBJ_IS_NATIVE(obj2) &&
00482                     (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
00483                     val = JSVAL_NULL;
00484                     if (attrs & JSPROP_GETTER)
00485                         val = (jsval) ((JSScopeProperty*)prop)->getter;
00486                     if (attrs & JSPROP_SETTER) {
00487                         if (val != JSVAL_NULL) {
00488                             /* Mark the getter, then set val to setter. */
00489                             ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
00490                                                    NULL)
00491                                   != NULL);
00492                         }
00493                         val = (jsval) ((JSScopeProperty*)prop)->setter;
00494                     }
00495                 } else {
00496                     ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
00497                 }
00498             }
00499             OBJ_DROP_PROPERTY(cx, obj2, prop);
00500 #else
00501             ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
00502 #endif
00503             if (!ok)
00504                 break;
00505             if (!JSVAL_IS_PRIMITIVE(val) &&
00506                 !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {
00507                 ok = JS_FALSE;
00508                 break;
00509             }
00510         }
00511         if (!ok || !idap)
00512             JS_DestroyIdArray(cx, ida);
00513         if (!ok)
00514             return NULL;
00515     } else {
00516         sharpid = JS_PTR_TO_UINT32(he->value);
00517         if (sharpid == 0) {
00518             sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
00519             he->value = JS_UINT32_TO_PTR(sharpid);
00520         }
00521         ida = NULL;
00522     }
00523     if (idap)
00524         *idap = ida;
00525     return he;
00526 }
00527 
00528 JSHashEntry *
00529 js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
00530                     jschar **sp)
00531 {
00532     JSSharpObjectMap *map;
00533     JSHashTable *table;
00534     JSIdArray *ida;
00535     JSHashNumber hash;
00536     JSHashEntry *he, **hep;
00537     jsatomid sharpid;
00538     char buf[20];
00539     size_t len;
00540 
00541     if (JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) &&
00542         cx->branchCallback &&
00543         !cx->branchCallback(cx, NULL)) {
00544         return NULL;
00545     }
00546 
00547     /* Set to null in case we return an early error. */
00548     *sp = NULL;
00549     map = &cx->sharpObjectMap;
00550     table = map->table;
00551     if (!table) {
00552         table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
00553                                 JS_CompareValues, NULL, NULL);
00554         if (!table) {
00555             JS_ReportOutOfMemory(cx);
00556             return NULL;
00557         }
00558         map->table = table;
00559         JS_KEEP_ATOMS(cx->runtime);
00560     }
00561 
00562     /* From this point the control must flow either through out: or bad:. */
00563     ida = NULL;
00564     if (map->depth == 0) {
00565         he = MarkSharpObjects(cx, obj, &ida);
00566         if (!he)
00567             goto bad;
00568         JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0);
00569         if (!idap) {
00570             JS_DestroyIdArray(cx, ida);
00571             ida = NULL;
00572         }
00573     } else {
00574         hash = js_hash_object(obj);
00575         hep = JS_HashTableRawLookup(table, hash, obj);
00576         he = *hep;
00577 
00578         /*
00579          * It's possible that the value of a property has changed from the
00580          * first time the object's properties are traversed (when the property
00581          * ids are entered into the hash table) to the second (when they are
00582          * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not
00583          * idempotent.
00584          */
00585         if (!he) {
00586             he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
00587             if (!he) {
00588                 JS_ReportOutOfMemory(cx);
00589                 goto bad;
00590             }
00591             sharpid = 0;
00592             goto out;
00593         }
00594     }
00595 
00596     sharpid = JS_PTR_TO_UINT32(he->value);
00597     if (sharpid != 0) {
00598         len = JS_snprintf(buf, sizeof buf, "#%u%c",
00599                           sharpid >> SHARP_ID_SHIFT,
00600                           (sharpid & SHARP_BIT) ? '#' : '=');
00601         *sp = js_InflateString(cx, buf, &len);
00602         if (!*sp) {
00603             if (ida)
00604                 JS_DestroyIdArray(cx, ida);
00605             goto bad;
00606         }
00607     }
00608 
00609 out:
00610     JS_ASSERT(he);
00611     if ((sharpid & SHARP_BIT) == 0) {
00612         if (idap && !ida) {
00613             ida = JS_Enumerate(cx, obj);
00614             if (!ida) {
00615                 if (*sp) {
00616                     JS_free(cx, *sp);
00617                     *sp = NULL;
00618                 }
00619                 goto bad;
00620             }
00621         }
00622         map->depth++;
00623     }
00624 
00625     if (idap)
00626         *idap = ida;
00627     return he;
00628 
00629 bad:
00630     /* Clean up the sharpObjectMap table on outermost error. */
00631     if (map->depth == 0) {
00632         JS_UNKEEP_ATOMS(cx->runtime);
00633         map->sharpgen = 0;
00634         JS_HashTableDestroy(map->table);
00635         map->table = NULL;
00636     }
00637     return NULL;
00638 }
00639 
00640 void
00641 js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
00642 {
00643     JSSharpObjectMap *map;
00644     JSIdArray *ida;
00645 
00646     map = &cx->sharpObjectMap;
00647     JS_ASSERT(map->depth > 0);
00648     if (--map->depth == 0) {
00649         JS_UNKEEP_ATOMS(cx->runtime);
00650         map->sharpgen = 0;
00651         JS_HashTableDestroy(map->table);
00652         map->table = NULL;
00653     }
00654     if (idap) {
00655         ida = *idap;
00656         if (ida) {
00657             JS_DestroyIdArray(cx, ida);
00658             *idap = NULL;
00659         }
00660     }
00661 }
00662 
00663 JS_STATIC_DLL_CALLBACK(intN)
00664 gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg)
00665 {
00666     GC_MARK((JSContext *)arg, (JSObject *)he->key, "sharp table entry");
00667     return JS_DHASH_NEXT;
00668 }
00669 
00670 void
00671 js_GCMarkSharpMap(JSContext *cx, JSSharpObjectMap *map)
00672 {
00673     JS_ASSERT(map->depth > 0);
00674     JS_ASSERT(map->table);
00675 
00676     /*
00677      * During recursive calls to MarkSharpObjects a non-native object or
00678      * object with a custom getProperty method can potentially return an
00679      * unrooted value or even cut from the object graph an argument of one of
00680      * MarkSharpObjects recursive invocations. So we must protect map->table
00681      * entries against GC.
00682      *
00683      * We can not simply use JSTempValueRooter to mark the obj argument of
00684      * MarkSharpObjects during recursion as we have to protect *all* entries
00685      * in JSSharpObjectMap including those that contains otherwise unreachable
00686      * objects just allocated through custom getProperty. Otherwise newer
00687      * allocations can re-use the address of an object stored in the hashtable
00688      * confusing js_EnterSharpObject. So to address the problem we simply
00689      * mark all objects from map->table.
00690      *
00691      * An alternative "proper" solution is to use JSTempValueRooter in
00692      * MarkSharpObjects with code to remove during finalization entries
00693      * with otherwise unreachable objects. But this is way too complex
00694      * to justify spending efforts.
00695      */
00696     JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, cx);
00697 }
00698 
00699 #define OBJ_TOSTRING_EXTRA      4       /* for 4 local GC roots */
00700 
00701 #if JS_HAS_TOSOURCE
00702 JSBool
00703 js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00704                 jsval *rval)
00705 {
00706     JSBool ok, outermost;
00707     JSHashEntry *he;
00708     JSIdArray *ida;
00709     jschar *chars, *ochars, *vsharp;
00710     const jschar *idstrchars, *vchars;
00711     size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen;
00712     char *comma;
00713     jsint i, j, length, valcnt;
00714     jsid id;
00715 #if JS_HAS_GETTER_SETTER
00716     JSObject *obj2;
00717     JSProperty *prop;
00718     uintN attrs;
00719 #endif
00720     jsval *val;
00721     JSString *gsopold[2];
00722     JSString *gsop[2];
00723     JSAtom *atom;
00724     JSString *idstr, *valstr, *str;
00725     int stackDummy;
00726 
00727     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
00728         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
00729         return JS_FALSE;
00730     }
00731 
00732     /* If outermost, we need parentheses to be an expression, not a block. */
00733     outermost = (cx->sharpObjectMap.depth == 0);
00734     he = js_EnterSharpObject(cx, obj, &ida, &chars);
00735     if (!he)
00736         return JS_FALSE;
00737     if (IS_SHARP(he)) {
00738         /*
00739          * We didn't enter -- obj is already "sharp", meaning we've visited it
00740          * already in our depth first search, and therefore chars contains a
00741          * string of the form "#n#".
00742          */
00743         JS_ASSERT(!ida);
00744 #if JS_HAS_SHARP_VARS
00745         nchars = js_strlen(chars);
00746 #else
00747         chars[0] = '{';
00748         chars[1] = '}';
00749         chars[2] = 0;
00750         nchars = 2;
00751 #endif
00752         goto make_string;
00753     }
00754     JS_ASSERT(ida);
00755     ok = JS_TRUE;
00756 
00757     if (!chars) {
00758         /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
00759         chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
00760         nchars = 0;
00761         if (!chars)
00762             goto error;
00763         if (outermost)
00764             chars[nchars++] = '(';
00765     } else {
00766         /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
00767         MAKE_SHARP(he);
00768         nchars = js_strlen(chars);
00769         chars = (jschar *)
00770             realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
00771         if (!chars) {
00772             free(ochars);
00773             goto error;
00774         }
00775         if (outermost) {
00776             /*
00777              * No need for parentheses around the whole shebang, because #n=
00778              * unambiguously begins an object initializer, and never a block
00779              * statement.
00780              */
00781             outermost = JS_FALSE;
00782         }
00783     }
00784 
00785 #ifdef DUMP_CALL_TABLE
00786     if (cx->options & JSOPTION_LOGCALL_TOSOURCE) {
00787         const char *classname = OBJ_GET_CLASS(cx, obj)->name;
00788         size_t classnchars = strlen(classname);
00789         static const char classpropid[] = "C";
00790         const char *cp;
00791         size_t onchars = nchars;
00792 
00793         /* 2 for ': ', 2 quotes around classname, 2 for ', ' after. */
00794         classnchars += sizeof classpropid - 1 + 2 + 2;
00795         if (ida->length)
00796             classnchars += 2;
00797 
00798         /* 2 for the braces, 1 for the terminator */
00799         chars = (jschar *)
00800             realloc((ochars = chars),
00801                     (nchars + classnchars + 2 + 1) * sizeof(jschar));
00802         if (!chars) {
00803             free(ochars);
00804             goto error;
00805         }
00806 
00807         chars[nchars++] = '{';          /* 1 from the 2 braces */
00808         for (cp = classpropid; *cp; cp++)
00809             chars[nchars++] = (jschar) *cp;
00810         chars[nchars++] = ':';
00811         chars[nchars++] = ' ';          /* 2 for ': ' */
00812         chars[nchars++] = '"';
00813         for (cp = classname; *cp; cp++)
00814             chars[nchars++] = (jschar) *cp;
00815         chars[nchars++] = '"';          /* 2 quotes */
00816         if (ida->length) {
00817             chars[nchars++] = ',';
00818             chars[nchars++] = ' ';      /* 2 for ', ' */
00819         }
00820 
00821         JS_ASSERT(nchars - onchars == 1 + classnchars);
00822     } else
00823 #endif
00824     chars[nchars++] = '{';
00825 
00826     comma = NULL;
00827 
00828     /*
00829      * We have four local roots for cooked and raw value GC safety.  Hoist the
00830      * "argv + 2" out of the loop using the val local, which refers to the raw
00831      * (unconverted, "uncooked") values.
00832      */
00833     val = argv + 2;
00834 
00835     for (i = 0, length = ida->length; i < length; i++) {
00836         JSBool idIsLexicalIdentifier, needOldStyleGetterSetter;
00837 
00838         /* Get strings for id and value and GC-root them via argv. */
00839         id = ida->vector[i];
00840 
00841 #if JS_HAS_GETTER_SETTER
00842         ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
00843         if (!ok)
00844             goto error;
00845 #endif
00846 
00847         /*
00848          * Convert id to a jsval and then to a string.  Decide early whether we
00849          * prefer get/set or old getter/setter syntax.
00850          */
00851         atom = JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL;
00852         idstr = js_ValueToString(cx, ID_TO_VALUE(id));
00853         if (!idstr) {
00854             ok = JS_FALSE;
00855             OBJ_DROP_PROPERTY(cx, obj2, prop);
00856             goto error;
00857         }
00858         *rval = STRING_TO_JSVAL(idstr);         /* local root */
00859         idIsLexicalIdentifier = js_IsIdentifier(idstr);
00860         needOldStyleGetterSetter =
00861             !idIsLexicalIdentifier ||
00862             js_CheckKeyword(JSSTRING_CHARS(idstr),
00863                             JSSTRING_LENGTH(idstr)) != TOK_EOF;
00864 
00865 #if JS_HAS_GETTER_SETTER
00866 
00867         valcnt = 0;
00868         if (prop) {
00869             ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
00870             if (!ok) {
00871                 OBJ_DROP_PROPERTY(cx, obj2, prop);
00872                 goto error;
00873             }
00874             if (OBJ_IS_NATIVE(obj2) &&
00875                 (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
00876                 if (attrs & JSPROP_GETTER) {
00877                     val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter;
00878                     gsopold[valcnt] =
00879                         ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
00880                     gsop[valcnt] =
00881                         ATOM_TO_STRING(cx->runtime->atomState.getAtom);
00882                     valcnt++;
00883                 }
00884                 if (attrs & JSPROP_SETTER) {
00885                     val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter;
00886                     gsopold[valcnt] =
00887                         ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
00888                     gsop[valcnt] =
00889                         ATOM_TO_STRING(cx->runtime->atomState.setAtom);
00890                     valcnt++;
00891                 }
00892             } else {
00893                 valcnt = 1;
00894                 gsop[0] = NULL;
00895                 gsopold[0] = NULL;
00896                 ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
00897             }
00898             OBJ_DROP_PROPERTY(cx, obj2, prop);
00899         }
00900 
00901 #else  /* !JS_HAS_GETTER_SETTER */
00902 
00903         /*
00904          * We simplify the source code at the price of minor dead code bloat in
00905          * the ECMA version (for testing only, see jsconfig.h).  The null
00906          * default values in gsop[j] suffice to disable non-ECMA getter and
00907          * setter code.
00908          */
00909         valcnt = 1;
00910         gsop[0] = NULL;
00911         gsopold[0] = NULL;
00912         ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
00913 
00914 #endif /* !JS_HAS_GETTER_SETTER */
00915 
00916         if (!ok)
00917             goto error;
00918 
00919         /*
00920          * If id is a string that's not an identifier, then it needs to be
00921          * quoted.  Also, negative integer ids must be quoted.
00922          */
00923         if (atom
00924             ? !idIsLexicalIdentifier
00925             : (JSID_IS_OBJECT(id) || JSID_TO_INT(id) < 0)) {
00926             idstr = js_QuoteString(cx, idstr, (jschar)'\'');
00927             if (!idstr) {
00928                 ok = JS_FALSE;
00929                 goto error;
00930             }
00931             *rval = STRING_TO_JSVAL(idstr);     /* local root */
00932         }
00933         idstrchars = JSSTRING_CHARS(idstr);
00934         idstrlength = JSSTRING_LENGTH(idstr);
00935 
00936         for (j = 0; j < valcnt; j++) {
00937             /* Convert val[j] to its canonical source form. */
00938             valstr = js_ValueToSource(cx, val[j]);
00939             if (!valstr) {
00940                 ok = JS_FALSE;
00941                 goto error;
00942             }
00943             argv[j] = STRING_TO_JSVAL(valstr);  /* local root */
00944             vchars = JSSTRING_CHARS(valstr);
00945             vlength = JSSTRING_LENGTH(valstr);
00946 
00947             if (vchars[0] == '#')
00948                 needOldStyleGetterSetter = JS_TRUE;
00949 
00950             if (needOldStyleGetterSetter)
00951                 gsop[j] = gsopold[j];
00952 
00953 #ifndef OLD_GETTER_SETTER
00954             /*
00955              * Remove '(function ' from the beginning of valstr and ')' from the
00956              * end so that we can put "get" in front of the function definition.
00957              */
00958             if (gsop[j] && VALUE_IS_FUNCTION(cx, val[j]) &&
00959                 !needOldStyleGetterSetter) {
00960                 const jschar *start = vchars;
00961                 if (vchars[0] == '(')
00962                     vchars++;
00963                 vchars = js_strchr_limit(vchars, '(', vchars + vlength);
00964                 if (vchars) {
00965                     vlength -= vchars - start + 1;
00966                 } else {
00967                     gsop[j] = NULL;
00968                     vchars = start;
00969                 }
00970             }
00971 #else
00972             needOldStyleGetterSetter = JS_TRUE;
00973             gsop[j] = gsopold[j];
00974 #endif
00975 
00976             /* If val[j] is a non-sharp object, consider sharpening it. */
00977             vsharp = NULL;
00978             vsharplength = 0;
00979 #if JS_HAS_SHARP_VARS
00980             if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') {
00981                 he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL,
00982                                          &vsharp);
00983                 if (!he) {
00984                     ok = JS_FALSE;
00985                     goto error;
00986                 }
00987                 if (IS_SHARP(he)) {
00988                     vchars = vsharp;
00989                     vlength = js_strlen(vchars);
00990                     needOldStyleGetterSetter = JS_TRUE;
00991                     gsop[j] = gsopold[j];
00992                 } else {
00993                     if (vsharp) {
00994                         vsharplength = js_strlen(vsharp);
00995                         MAKE_SHARP(he);
00996                         needOldStyleGetterSetter = JS_TRUE;
00997                         gsop[j] = gsopold[j];
00998                     }
00999                     js_LeaveSharpObject(cx, NULL);
01000                 }
01001             }
01002 #endif
01003 
01004 #define SAFE_ADD(n)                                                          \
01005     JS_BEGIN_MACRO                                                           \
01006         size_t n_ = (n);                                                     \
01007         curlen += n_;                                                        \
01008         if (curlen < n_)                                                     \
01009             goto overflow;                                                   \
01010     JS_END_MACRO
01011 
01012             curlen = nchars;
01013             if (comma)
01014                 SAFE_ADD(2);
01015             SAFE_ADD(idstrlength + 1);
01016             if (gsop[j])
01017                 SAFE_ADD(JSSTRING_LENGTH(gsop[j]) + 1);
01018             SAFE_ADD(vsharplength);
01019             SAFE_ADD(vlength);
01020             /* Account for the trailing null. */
01021             SAFE_ADD((outermost ? 2 : 1) + 1);
01022 #undef SAFE_ADD
01023 
01024             if (curlen > (size_t)-1 / sizeof(jschar))
01025                 goto overflow;
01026 
01027             /* Allocate 1 + 1 at end for closing brace and terminating 0. */
01028             chars = (jschar *)
01029                 realloc((ochars = chars), curlen * sizeof(jschar));
01030             if (!chars) {
01031                 /* Save code space on error: let JS_free ignore null vsharp. */
01032                 JS_free(cx, vsharp);
01033                 free(ochars);
01034                 goto error;
01035             }
01036 
01037             if (comma) {
01038                 chars[nchars++] = comma[0];
01039                 chars[nchars++] = comma[1];
01040             }
01041             comma = ", ";
01042 
01043             if (needOldStyleGetterSetter) {
01044                 js_strncpy(&chars[nchars], idstrchars, idstrlength);
01045                 nchars += idstrlength;
01046                 if (gsop[j]) {
01047                     chars[nchars++] = ' ';
01048                     gsoplength = JSSTRING_LENGTH(gsop[j]);
01049                     js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]),
01050                                gsoplength);
01051                     nchars += gsoplength;
01052                 }
01053                 chars[nchars++] = ':';
01054             } else {  /* New style "decompilation" */
01055                 if (gsop[j]) {
01056                     gsoplength = JSSTRING_LENGTH(gsop[j]);
01057                     js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]),
01058                                gsoplength);
01059                     nchars += gsoplength;
01060                     chars[nchars++] = ' ';
01061                 }
01062                 js_strncpy(&chars[nchars], idstrchars, idstrlength);
01063                 nchars += idstrlength;
01064                 /* Extraneous space after id here will be extracted later */
01065                 chars[nchars++] = gsop[j] ? ' ' : ':';
01066             }
01067 
01068             if (vsharplength) {
01069                 js_strncpy(&chars[nchars], vsharp, vsharplength);
01070                 nchars += vsharplength;
01071             }
01072             js_strncpy(&chars[nchars], vchars, vlength);
01073             nchars += vlength;
01074 
01075             if (vsharp)
01076                 JS_free(cx, vsharp);
01077 #ifdef DUMP_CALL_TABLE
01078             if (outermost && nchars >= js_LogCallToSourceLimit)
01079                 break;
01080 #endif
01081         }
01082     }
01083 
01084     chars[nchars++] = '}';
01085     if (outermost)
01086         chars[nchars++] = ')';
01087     chars[nchars] = 0;
01088 
01089   error:
01090     js_LeaveSharpObject(cx, &ida);
01091 
01092     if (!ok) {
01093         if (chars)
01094             free(chars);
01095         return ok;
01096     }
01097 
01098     if (!chars) {
01099         JS_ReportOutOfMemory(cx);
01100         return JS_FALSE;
01101     }
01102   make_string:
01103     str = js_NewString(cx, chars, nchars, 0);
01104     if (!str) {
01105         free(chars);
01106         return JS_FALSE;
01107     }
01108     *rval = STRING_TO_JSVAL(str);
01109     return JS_TRUE;
01110 
01111   overflow:
01112     JS_free(cx, vsharp);
01113     free(chars);
01114     chars = NULL;
01115     goto error;
01116 }
01117 #endif /* JS_HAS_TOSOURCE */
01118 
01119 JSBool
01120 js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01121                 jsval *rval)
01122 {
01123     jschar *chars;
01124     size_t nchars;
01125     const char *clazz, *prefix;
01126     JSString *str;
01127 
01128     clazz = OBJ_GET_CLASS(cx, obj)->name;
01129     nchars = 9 + strlen(clazz);         /* 9 for "[object ]" */
01130     chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar));
01131     if (!chars)
01132         return JS_FALSE;
01133 
01134     prefix = "[object ";
01135     nchars = 0;
01136     while ((chars[nchars] = (jschar)*prefix) != 0)
01137         nchars++, prefix++;
01138     while ((chars[nchars] = (jschar)*clazz) != 0)
01139         nchars++, clazz++;
01140     chars[nchars++] = ']';
01141     chars[nchars] = 0;
01142 
01143     str = js_NewString(cx, chars, nchars, 0);
01144     if (!str) {
01145         JS_free(cx, chars);
01146         return JS_FALSE;
01147     }
01148     *rval = STRING_TO_JSVAL(str);
01149     return JS_TRUE;
01150 }
01151 
01152 static JSBool
01153 js_obj_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01154                       jsval *rval)
01155 {
01156     JSString *str;
01157 
01158     str = js_ValueToString(cx, argv[-1]);
01159     if (!str)
01160         return JS_FALSE;
01161 
01162     *rval = STRING_TO_JSVAL(str);
01163     return JS_TRUE;
01164 }
01165 
01166 static JSBool
01167 obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01168 {
01169     *rval = OBJECT_TO_JSVAL(obj);
01170     return JS_TRUE;
01171 }
01172 
01173 /*
01174  * Check whether principals subsumes scopeobj's principals, and return true
01175  * if so (or if scopeobj has no principals, for backward compatibility with
01176  * the JS API, which does not require principals), and false otherwise.
01177  */
01178 JSBool
01179 js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj,
01180                          JSPrincipals *principals, JSAtom *caller)
01181 {
01182     JSRuntime *rt;
01183     JSPrincipals *scopePrincipals;
01184     const char *callerstr;
01185 
01186     rt = cx->runtime;
01187     if (rt->findObjectPrincipals) {
01188         scopePrincipals = rt->findObjectPrincipals(cx, scopeobj);
01189         if (!principals || !scopePrincipals ||
01190             !principals->subsume(principals, scopePrincipals)) {
01191             callerstr = js_AtomToPrintableString(cx, caller);
01192             if (!callerstr)
01193                 return JS_FALSE;
01194             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
01195                                  JSMSG_BAD_INDIRECT_CALL, callerstr);
01196             return JS_FALSE;
01197         }
01198     }
01199     return JS_TRUE;
01200 }
01201 
01202 JSObject *
01203 js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller)
01204 {
01205     JSClass *clasp;
01206     JSExtendedClass *xclasp;
01207     JSObject *inner;
01208 
01209     if (!scopeobj)
01210         goto bad;
01211 
01212     OBJ_TO_INNER_OBJECT(cx, scopeobj);
01213     if (!scopeobj)
01214         return NULL;
01215 
01216     inner = scopeobj;
01217 
01218     /* XXX This is an awful gross hack. */
01219     while (scopeobj) {
01220         clasp = OBJ_GET_CLASS(cx, scopeobj);
01221         if (clasp->flags & JSCLASS_IS_EXTENDED) {
01222             xclasp = (JSExtendedClass*)clasp;
01223             if (xclasp->innerObject &&
01224                 xclasp->innerObject(cx, scopeobj) != scopeobj) {
01225                 goto bad;
01226             }
01227         }
01228 
01229         scopeobj = OBJ_GET_PARENT(cx, scopeobj);
01230     }
01231 
01232     return inner;
01233 
01234 bad:
01235     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
01236                          JSMSG_BAD_INDIRECT_CALL, caller);
01237     return NULL;
01238 }
01239 
01240 const char *
01241 js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
01242                    JSPrincipals *principals, uintN *linenop)
01243 {
01244     uint32 flags;
01245 
01246     flags = JS_GetScriptFilenameFlags(caller->script);
01247     if ((flags & JSFILENAME_SYSTEM) &&
01248         strcmp(principals->codebase, "[System Principal]")) {
01249         *linenop = 0;
01250         return principals->codebase;
01251     }
01252 
01253     *linenop = js_PCToLineNumber(cx, caller->script, caller->pc);
01254     return caller->script->filename;
01255 }
01256 
01257 static JSBool
01258 obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01259 {
01260     JSStackFrame *fp, *caller;
01261     JSBool indirectCall;
01262     JSObject *scopeobj;
01263     JSString *str;
01264     const char *file;
01265     uintN line;
01266     JSPrincipals *principals;
01267     JSScript *script;
01268     JSBool ok;
01269 #if JS_HAS_EVAL_THIS_SCOPE
01270     JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
01271     JSObject *setCallerScopeChain = NULL;
01272     JSBool setCallerVarObj = JS_FALSE;
01273 #endif
01274 
01275     fp = cx->fp;
01276     caller = JS_GetScriptedCaller(cx, fp);
01277     JS_ASSERT(!caller || caller->pc);
01278     indirectCall = (caller && *caller->pc != JSOP_EVAL);
01279 
01280     /* 
01281      * Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and
01282      * calls that attempt to use a non-global object as the "with" object in
01283      * the former indirect case.
01284      */
01285     scopeobj = OBJ_GET_PARENT(cx, obj);
01286     if (indirectCall || scopeobj) {
01287         uintN flags = scopeobj
01288                       ? JSREPORT_ERROR
01289                       : JSREPORT_STRICT | JSREPORT_WARNING;
01290         if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL,
01291                                           JSMSG_BAD_INDIRECT_CALL,
01292                                           js_eval_str)) {
01293             return JS_FALSE;
01294         }
01295     }
01296 
01297     if (!JSVAL_IS_STRING(argv[0])) {
01298         *rval = argv[0];
01299         return JS_TRUE;
01300     }
01301 
01302     /*
01303      * If the caller is a lightweight function and doesn't have a variables
01304      * object, then we need to provide one for the compiler to stick any
01305      * declared (var) variables into.
01306      */
01307     if (caller && !caller->varobj && !js_GetCallObject(cx, caller, NULL))
01308         return JS_FALSE;
01309 
01310 #if JS_HAS_SCRIPT_OBJECT
01311     /*
01312      * Script.prototype.compile/exec and Object.prototype.eval all take an
01313      * optional trailing argument that overrides the scope object.
01314      */
01315     scopeobj = NULL;
01316     if (argc >= 2) {
01317         if (!js_ValueToObject(cx, argv[1], &scopeobj))
01318             return JS_FALSE;
01319         argv[1] = OBJECT_TO_JSVAL(scopeobj);
01320     }
01321     if (!scopeobj)
01322 #endif
01323     {
01324 #if JS_HAS_EVAL_THIS_SCOPE
01325         /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
01326         if (indirectCall) {
01327             callerScopeChain = js_GetScopeChain(cx, caller);
01328             if (!callerScopeChain)
01329                 return JS_FALSE;
01330             OBJ_TO_INNER_OBJECT(cx, obj);
01331             if (!obj)
01332                 return JS_FALSE;
01333             if (obj != callerScopeChain) {
01334                 if (!js_CheckPrincipalsAccess(cx, obj,
01335                                               caller->script->principals,
01336                                               cx->runtime->atomState.evalAtom))
01337                 {
01338                     return JS_FALSE;
01339                 }
01340 
01341                 scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1);
01342                 if (!scopeobj)
01343                     return JS_FALSE;
01344 
01345                 /* Set fp->scopeChain too, for the compiler. */
01346                 caller->scopeChain = fp->scopeChain = scopeobj;
01347 
01348                 /* Remember scopeobj so we can null its private when done. */
01349                 setCallerScopeChain = scopeobj;
01350             }
01351 
01352             callerVarObj = caller->varobj;
01353             if (obj != callerVarObj) {
01354                 /* Set fp->varobj too, for the compiler. */
01355                 caller->varobj = fp->varobj = obj;
01356                 setCallerVarObj = JS_TRUE;
01357             }
01358         }
01359         /* From here on, control must exit through label out with ok set. */
01360 #endif
01361 
01362         /* Compile using caller's current scope object. */
01363         if (caller) {
01364             scopeobj = js_GetScopeChain(cx, caller);
01365             if (!scopeobj) {
01366                 ok = JS_FALSE;
01367                 goto out;
01368             }
01369         }
01370     }
01371 
01372     /* Ensure we compile this eval with the right object in the scope chain. */
01373     scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str);
01374     if (!scopeobj) {
01375         ok = JS_FALSE;
01376         goto out;
01377     }
01378 
01379     str = JSVAL_TO_STRING(argv[0]);
01380     if (caller) {
01381         principals = JS_EvalFramePrincipals(cx, fp, caller);
01382         file = js_ComputeFilename(cx, caller, principals, &line);
01383     } else {
01384         file = NULL;
01385         line = 0;
01386         principals = NULL;
01387     }
01388 
01389     /*
01390      * Set JSFRAME_EVAL on fp and any frames (e.g., fun_call if eval.call was
01391      * invoked) between fp and its scripted caller, to help the compiler easily
01392      * find the same caller whose scope and var obj we've set.
01393      *
01394      * XXX this nonsense could, and perhaps should, go away with a better way
01395      * to pass params to the compiler than via the top-most frame.
01396      */
01397     do {
01398         fp->flags |= JSFRAME_EVAL;
01399     } while ((fp = fp->down) != caller);
01400 
01401     script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,
01402                                              JSSTRING_CHARS(str),
01403                                              JSSTRING_LENGTH(str),
01404                                              file, line);
01405     if (!script) {
01406         ok = JS_FALSE;
01407         goto out;
01408     }
01409 
01410 #if JS_HAS_SCRIPT_OBJECT
01411     if (argc < 2)
01412 #endif
01413     {
01414         /* Execute using caller's new scope object (might be a Call object). */
01415         if (caller)
01416             scopeobj = caller->scopeChain;
01417     }
01418 
01419     /*
01420      * Belt-and-braces: check that the lesser of eval's principals and the
01421      * caller's principals has access to scopeobj.
01422      */
01423     ok = js_CheckPrincipalsAccess(cx, scopeobj, principals,
01424                                   cx->runtime->atomState.evalAtom);
01425     if (ok)
01426         ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
01427 
01428     JS_DestroyScript(cx, script);
01429 
01430 out:
01431 #if JS_HAS_EVAL_THIS_SCOPE
01432     /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
01433     if (setCallerScopeChain) {
01434         caller->scopeChain = callerScopeChain;
01435         JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass);
01436         JS_SetPrivate(cx, setCallerScopeChain, NULL);
01437     }
01438     if (setCallerVarObj)
01439         caller->varobj = callerVarObj;
01440 #endif
01441 
01442     return ok;
01443 }
01444 
01445 #if JS_HAS_OBJ_WATCHPOINT
01446 
01447 static JSBool
01448 obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
01449                   void *closure)
01450 {
01451     JSObject *callable;
01452     JSRuntime *rt;
01453     JSStackFrame *caller;
01454     JSPrincipals *subject, *watcher;
01455     JSResolvingKey key;
01456     JSResolvingEntry *entry;
01457     uint32 generation;
01458     jsval argv[3];
01459     JSBool ok;
01460 
01461     callable = (JSObject *) closure;
01462 
01463     rt = cx->runtime;
01464     if (rt->findObjectPrincipals) {
01465         /* Skip over any obj_watch_* frames between us and the real subject. */
01466         caller = JS_GetScriptedCaller(cx, cx->fp);
01467         if (caller) {
01468             /*
01469              * Only call the watch handler if the watcher is allowed to watch
01470              * the currently executing script.
01471              */
01472             watcher = rt->findObjectPrincipals(cx, callable);
01473             subject = JS_StackFramePrincipals(cx, caller);
01474 
01475             if (watcher && subject && !watcher->subsume(watcher, subject)) {
01476                 /* Silently don't call the watch handler. */
01477                 return JS_TRUE;
01478             }
01479         }
01480     }
01481 
01482     /* Avoid recursion on (obj, id) already being watched on cx. */
01483     key.obj = obj;
01484     key.id = id;
01485     if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
01486         return JS_FALSE;
01487     if (!entry)
01488         return JS_TRUE;
01489     generation = cx->resolvingTable->generation;
01490 
01491     argv[0] = id;
01492     argv[1] = old;
01493     argv[2] = *nvp;
01494     ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp);
01495     js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
01496     return ok;
01497 }
01498 
01499 static JSBool
01500 obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01501 {
01502     JSObject *callable;
01503     jsval userid, value;
01504     jsid propid;
01505     uintN attrs;
01506 
01507     callable = js_ValueToCallableObject(cx, &argv[1], 0);
01508     if (!callable)
01509         return JS_FALSE;
01510 
01511     /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
01512     userid = argv[0];
01513     if (!JS_ValueToId(cx, userid, &propid))
01514         return JS_FALSE;
01515 
01516     if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs))
01517         return JS_FALSE;
01518     if (attrs & JSPROP_READONLY)
01519         return JS_TRUE;
01520     return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable);
01521 }
01522 
01523 static JSBool
01524 obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01525 {
01526     return JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL);
01527 }
01528 
01529 #endif /* JS_HAS_OBJ_WATCHPOINT */
01530 
01531 /*
01532  * Prototype and property query methods, to complement the 'in' and
01533  * 'instanceof' operators.
01534  */
01535 
01536 /* Proposed ECMA 15.2.4.5. */
01537 static JSBool
01538 obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01539                    jsval *rval)
01540 {
01541     return js_HasOwnPropertyHelper(cx, obj, obj->map->ops->lookupProperty,
01542                                    argc, argv, rval);
01543 }
01544 
01545 JSBool
01546 js_HasOwnPropertyHelper(JSContext *cx, JSObject *obj, JSLookupPropOp lookup,
01547                         uintN argc, jsval *argv, jsval *rval)
01548 {
01549     jsid id;
01550     JSObject *obj2;
01551     JSProperty *prop;
01552     JSScopeProperty *sprop;
01553 
01554     if (!JS_ValueToId(cx, argv[0], &id))
01555         return JS_FALSE;
01556     if (!lookup(cx, obj, id, &obj2, &prop))
01557         return JS_FALSE;
01558     if (!prop) {
01559         *rval = JSVAL_FALSE;
01560     } else if (obj2 == obj) {
01561         *rval = JSVAL_TRUE;
01562     } else {
01563         JSClass *clasp;
01564         JSExtendedClass *xclasp;
01565         JSObject *outer;
01566 
01567         clasp = OBJ_GET_CLASS(cx, obj2);
01568         if (!(clasp->flags & JSCLASS_IS_EXTENDED) ||
01569             !(xclasp = (JSExtendedClass *) clasp)->outerObject) {
01570             outer = NULL;
01571         } else {
01572             outer = xclasp->outerObject(cx, obj2);
01573             if (!outer)
01574                 return JS_FALSE;
01575         }
01576         if (outer == obj) {
01577             *rval = JSVAL_TRUE;
01578         } else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj) == clasp) {
01579             /*
01580              * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
01581              * delegated property makes that property appear to be direct in
01582              * all delegating instances of the same native class.  This hack
01583              * avoids bloating every function instance with its own 'length'
01584              * (AKA 'arity') property.  But it must not extend across class
01585              * boundaries, to avoid making hasOwnProperty lie (bug 320854).
01586              *
01587              * It's not really a hack, of course: a permanent property can't
01588              * be deleted, and JSPROP_SHARED means "don't allocate a slot in
01589              * any instance, prototype or delegating".  Without a slot, and
01590              * without the ability to remove and recreate (with differences)
01591              * the property, there is no way to tell whether it is directly
01592              * owned, or indirectly delegated.
01593              */
01594             sprop = (JSScopeProperty *)prop;
01595             *rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
01596         } else {
01597             *rval = JSVAL_FALSE;
01598         }
01599     }
01600     if (prop)
01601         OBJ_DROP_PROPERTY(cx, obj2, prop);
01602     return JS_TRUE;
01603 }
01604 
01605 /* Proposed ECMA 15.2.4.6. */
01606 static JSBool
01607 obj_isPrototypeOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01608                   jsval *rval)
01609 {
01610     JSBool b;
01611 
01612     if (!js_IsDelegate(cx, obj, *argv, &b))
01613         return JS_FALSE;
01614     *rval = BOOLEAN_TO_JSVAL(b);
01615     return JS_TRUE;
01616 }
01617 
01618 /* Proposed ECMA 15.2.4.7. */
01619 static JSBool
01620 obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01621                          jsval *rval)
01622 {
01623     jsid id;
01624     uintN attrs;
01625     JSObject *obj2;
01626     JSProperty *prop;
01627     JSBool ok;
01628 
01629     if (!JS_ValueToId(cx, argv[0], &id))
01630         return JS_FALSE;
01631 
01632     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
01633         return JS_FALSE;
01634 
01635     if (!prop) {
01636         *rval = JSVAL_FALSE;
01637         return JS_TRUE;
01638     }
01639 
01640     /*
01641      * XXX ECMA spec error compatible: return false unless hasOwnProperty.
01642      * The ECMA spec really should be fixed so propertyIsEnumerable and the
01643      * for..in loop agree on whether prototype properties are enumerable,
01644      * obviously by fixing this method (not by breaking the for..in loop!).
01645      *
01646      * We check here for shared permanent prototype properties, which should
01647      * be treated as if they are local to obj.  They are an implementation
01648      * technique used to satisfy ECMA requirements; users should not be able
01649      * to distinguish a shared permanent proto-property from a local one.
01650      */
01651     if (obj2 != obj &&
01652         !(OBJ_IS_NATIVE(obj2) &&
01653           SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) {
01654         OBJ_DROP_PROPERTY(cx, obj2, prop);
01655         *rval = JSVAL_FALSE;
01656         return JS_TRUE;
01657     }
01658 
01659     ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
01660     OBJ_DROP_PROPERTY(cx, obj2, prop);
01661     if (ok)
01662         *rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);
01663     return ok;
01664 }
01665 
01666 #if JS_HAS_GETTER_SETTER
01667 static JSBool
01668 obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01669                  jsval *rval)
01670 {
01671     jsval fval, junk;
01672     jsid id;
01673     uintN attrs;
01674 
01675     fval = argv[1];
01676     if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
01677         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
01678                              JSMSG_BAD_GETTER_OR_SETTER,
01679                              js_getter_str);
01680         return JS_FALSE;
01681     }
01682 
01683     if (!JS_ValueToId(cx, argv[0], &id))
01684         return JS_FALSE;
01685     if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
01686         return JS_FALSE;
01687     /*
01688      * Getters and setters are just like watchpoints from an access
01689      * control point of view.
01690      */
01691     if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
01692         return JS_FALSE;
01693     return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
01694                                (JSPropertyOp) JSVAL_TO_OBJECT(fval), NULL,
01695                                JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED,
01696                                NULL);
01697 }
01698 
01699 static JSBool
01700 obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01701                  jsval *rval)
01702 {
01703     jsval fval, junk;
01704     jsid id;
01705     uintN attrs;
01706 
01707     fval = argv[1];
01708     if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
01709         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
01710                              JSMSG_BAD_GETTER_OR_SETTER,
01711                              js_setter_str);
01712         return JS_FALSE;
01713     }
01714 
01715     if (!JS_ValueToId(cx, argv[0], &id))
01716         return JS_FALSE;
01717     if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
01718         return JS_FALSE;
01719     /*
01720      * Getters and setters are just like watchpoints from an access
01721      * control point of view.
01722      */
01723     if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
01724         return JS_FALSE;
01725     return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
01726                                NULL, (JSPropertyOp) JSVAL_TO_OBJECT(fval),
01727                                JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED,
01728                                NULL);
01729 }
01730 
01731 static JSBool
01732 obj_lookupGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01733                  jsval *rval)
01734 {
01735     jsid id;
01736     JSObject *pobj;
01737     JSProperty *prop;
01738     JSScopeProperty *sprop;
01739 
01740     if (!JS_ValueToId(cx, argv[0], &id))
01741         return JS_FALSE;
01742     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
01743         return JS_FALSE;
01744     if (prop) {
01745         if (OBJ_IS_NATIVE(pobj)) {
01746             sprop = (JSScopeProperty *) prop;
01747             if (sprop->attrs & JSPROP_GETTER)
01748                 *rval = OBJECT_TO_JSVAL(sprop->getter);
01749         }
01750         OBJ_DROP_PROPERTY(cx, pobj, prop);
01751     }
01752     return JS_TRUE;
01753 }
01754 
01755 static JSBool
01756 obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01757                  jsval *rval)
01758 {
01759     jsid id;
01760     JSObject *pobj;
01761     JSProperty *prop;
01762     JSScopeProperty *sprop;
01763 
01764     if (!JS_ValueToId(cx, argv[0], &id))
01765         return JS_FALSE;
01766     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
01767         return JS_FALSE;
01768     if (prop) {
01769         if (OBJ_IS_NATIVE(pobj)) {
01770             sprop = (JSScopeProperty *) prop;
01771             if (sprop->attrs & JSPROP_SETTER)
01772                 *rval = OBJECT_TO_JSVAL(sprop->setter);
01773         }
01774         OBJ_DROP_PROPERTY(cx, pobj, prop);
01775     }
01776     return JS_TRUE;
01777 }
01778 #endif /* JS_HAS_GETTER_SETTER */
01779 
01780 #if JS_HAS_OBJ_WATCHPOINT
01781 const char js_watch_str[] = "watch";
01782 const char js_unwatch_str[] = "unwatch";
01783 #endif
01784 const char js_hasOwnProperty_str[] = "hasOwnProperty";
01785 const char js_isPrototypeOf_str[] = "isPrototypeOf";
01786 const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
01787 #if JS_HAS_GETTER_SETTER
01788 const char js_defineGetter_str[] = "__defineGetter__";
01789 const char js_defineSetter_str[] = "__defineSetter__";
01790 const char js_lookupGetter_str[] = "__lookupGetter__";
01791 const char js_lookupSetter_str[] = "__lookupSetter__";
01792 #endif
01793 
01794 static JSFunctionSpec object_methods[] = {
01795 #if JS_HAS_TOSOURCE
01796     {js_toSource_str,             js_obj_toSource,    0, 0, OBJ_TOSTRING_EXTRA},
01797 #endif
01798     {js_toString_str,             js_obj_toString,    0, 0, OBJ_TOSTRING_EXTRA},
01799     {js_toLocaleString_str,       js_obj_toLocaleString, 0, 0, OBJ_TOSTRING_EXTRA},
01800     {js_valueOf_str,              obj_valueOf,        0,0,0},
01801 #if JS_HAS_OBJ_WATCHPOINT
01802     {js_watch_str,                obj_watch,          2,0,0},
01803     {js_unwatch_str,              obj_unwatch,        1,0,0},
01804 #endif
01805     {js_hasOwnProperty_str,       obj_hasOwnProperty, 1,0,0},
01806     {js_isPrototypeOf_str,        obj_isPrototypeOf,  1,0,0},
01807     {js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0},
01808 #if JS_HAS_GETTER_SETTER
01809     {js_defineGetter_str,         obj_defineGetter,   2,0,0},
01810     {js_defineSetter_str,         obj_defineSetter,   2,0,0},
01811     {js_lookupGetter_str,         obj_lookupGetter,   1,0,0},
01812     {js_lookupSetter_str,         obj_lookupSetter,   1,0,0},
01813 #endif
01814     {0,0,0,0,0}
01815 };
01816 
01817 static JSBool
01818 Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01819 {
01820     if (argc == 0) {
01821         /* Trigger logic below to construct a blank object. */
01822         obj = NULL;
01823     } else {
01824         /* If argv[0] is null or undefined, obj comes back null. */
01825         if (!js_ValueToObject(cx, argv[0], &obj))
01826             return JS_FALSE;
01827     }
01828     if (!obj) {
01829         JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));
01830         if (cx->fp->flags & JSFRAME_CONSTRUCTING)
01831             return JS_TRUE;
01832         obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL);
01833         if (!obj)
01834             return JS_FALSE;
01835     }
01836     *rval = OBJECT_TO_JSVAL(obj);
01837     return JS_TRUE;
01838 }
01839 
01840 /*
01841  * ObjectOps and Class for with-statement stack objects.
01842  */
01843 static JSBool
01844 with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
01845                     JSProperty **propp)
01846 {
01847     JSObject *proto = OBJ_GET_PROTO(cx, obj);
01848     if (!proto)
01849         return js_LookupProperty(cx, obj, id, objp, propp);
01850     return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
01851 }
01852 
01853 static JSBool
01854 with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
01855 {
01856     JSObject *proto = OBJ_GET_PROTO(cx, obj);
01857     if (!proto)
01858         return js_GetProperty(cx, obj, id, vp);
01859     return OBJ_GET_PROPERTY(cx, proto, id, vp);
01860 }
01861 
01862 static JSBool
01863 with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
01864 {
01865     JSObject *proto = OBJ_GET_PROTO(cx, obj);
01866     if (!proto)
01867         return js_SetProperty(cx, obj, id, vp);
01868     return OBJ_SET_PROPERTY(cx, proto, id, vp);
01869 }
01870 
01871 static JSBool
01872 with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
01873                    uintN *attrsp)
01874 {
01875     JSObject *proto = OBJ_GET_PROTO(cx, obj);
01876     if (!proto)
01877         return js_GetAttributes(cx, obj, id, prop, attrsp);
01878     return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp);
01879 }
01880 
01881 static JSBool
01882 with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
01883                    uintN *attrsp)
01884 {
01885     JSObject *proto = OBJ_GET_PROTO(cx, obj);
01886     if (!proto)
01887         return js_SetAttributes(cx, obj, id, prop, attrsp);
01888     return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp);
01889 }
01890 
01891 static JSBool
01892 with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
01893 {
01894     JSObject *proto = OBJ_GET_PROTO(cx, obj);
01895     if (!proto)
01896         return js_DeleteProperty(cx, obj, id, rval);
01897     return OBJ_DELETE_PROPERTY(cx, proto, id, rval);
01898 }
01899 
01900 static JSBool
01901 with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
01902 {
01903     JSObject *proto = OBJ_GET_PROTO(cx, obj);
01904     if (!proto)
01905         return js_DefaultValue(cx, obj, hint, vp);
01906     return OBJ_DEFAULT_VALUE(cx, proto, hint, vp);
01907 }
01908 
01909 static JSBool
01910 with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
01911                jsval *statep, jsid *idp)
01912 {
01913     JSObject *proto = OBJ_GET_PROTO(cx, obj);
01914     if (!proto)
01915         return js_Enumerate(cx, obj, enum_op, statep, idp);
01916     return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp);
01917 }
01918 
01919 static JSBool
01920 with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
01921                  jsval *vp, uintN *attrsp)
01922 {
01923     JSObject *proto = OBJ_GET_PROTO(cx, obj);
01924     if (!proto)
01925         return js_CheckAccess(cx, obj, id, mode, vp, attrsp);
01926     return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp);
01927 }
01928 
01929 static JSObject *
01930 with_ThisObject(JSContext *cx, JSObject *obj)
01931 {
01932     JSObject *proto = OBJ_GET_PROTO(cx, obj);
01933     if (!proto)
01934         return obj;
01935     return OBJ_THIS_OBJECT(cx, proto);
01936 }
01937 
01938 JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = {
01939     js_NewObjectMap,        js_DestroyObjectMap,
01940     with_LookupProperty,    js_DefineProperty,
01941     with_GetProperty,       with_SetProperty,
01942     with_GetAttributes,     with_SetAttributes,
01943     with_DeleteProperty,    with_DefaultValue,
01944     with_Enumerate,         with_CheckAccess,
01945     with_ThisObject,        NATIVE_DROP_PROPERTY,
01946     NULL,                   NULL,
01947     NULL,                   NULL,
01948     js_SetProtoOrParent,    js_SetProtoOrParent,
01949     js_Mark,                js_Clear,
01950     NULL,                   NULL
01951 };
01952 
01953 static JSObjectOps *
01954 with_getObjectOps(JSContext *cx, JSClass *clasp)
01955 {
01956     return &js_WithObjectOps;
01957 }
01958 
01959 JSClass js_WithClass = {
01960     "With",
01961     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
01962     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
01963     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
01964     with_getObjectOps,
01965     0,0,0,0,0,0,0
01966 };
01967 
01968 JSObject *
01969 js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
01970 {
01971     JSObject *obj;
01972 
01973     obj = js_NewObject(cx, &js_WithClass, proto, parent);
01974     if (!obj)
01975         return NULL;
01976     obj->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(cx->fp);
01977     OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
01978     return obj;
01979 }
01980 
01981 JSObject *
01982 js_NewBlockObject(JSContext *cx)
01983 {
01984     JSObject *obj;
01985 
01986     /*
01987      * Null obj's proto slot so that Object.prototype.* does not pollute block
01988      * scopes.  Make sure obj has its own scope too, since clearing proto does
01989      * not affect OBJ_SCOPE(obj).
01990      */
01991     obj = js_NewObject(cx, &js_BlockClass, NULL, NULL);
01992     if (!obj || !js_GetMutableScope(cx, obj))
01993         return NULL;
01994     OBJ_SET_PROTO(cx, obj, NULL);
01995     return obj;
01996 }
01997 
01998 JSObject *
01999 js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent,
02000                     JSStackFrame *fp)
02001 {
02002     JSObject *clone;
02003 
02004     clone = js_NewObject(cx, &js_BlockClass, proto, parent);
02005     if (!clone)
02006         return NULL;
02007     clone->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fp);
02008     clone->slots[JSSLOT_BLOCK_DEPTH] =
02009         OBJ_GET_SLOT(cx, proto, JSSLOT_BLOCK_DEPTH);
02010     return clone;
02011 }
02012 
02013 /*
02014  * XXXblock this reverses a path in the property tree -- try to share
02015  *          the prototype's scope harder!
02016  */
02017 JSBool
02018 js_PutBlockObject(JSContext *cx, JSObject *obj)
02019 {
02020     JSStackFrame *fp;
02021     uintN depth, slot;
02022     JSScopeProperty *sprop;
02023 
02024     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
02025     JS_ASSERT(fp);
02026     depth = OBJ_BLOCK_DEPTH(cx, obj);
02027     for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) {
02028         if (sprop->getter != js_BlockClass.getProperty)
02029             continue;
02030         if (!(sprop->flags & SPROP_HAS_SHORTID))
02031             continue;
02032         slot = depth + (uintN)sprop->shortid;
02033         JS_ASSERT(slot < fp->script->depth);
02034         if (!js_DefineNativeProperty(cx, obj, sprop->id,
02035                                      fp->spbase[slot], NULL, NULL,
02036                                      JSPROP_ENUMERATE | JSPROP_PERMANENT,
02037                                      SPROP_HAS_SHORTID, sprop->shortid,
02038                                      NULL)) {
02039             JS_SetPrivate(cx, obj, NULL);
02040             return JS_FALSE;
02041         }
02042     }
02043 
02044     return JS_SetPrivate(cx, obj, NULL);
02045 }
02046 
02047 static JSBool
02048 block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
02049 {
02050     JSStackFrame *fp;
02051     jsint slot;
02052 
02053     JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL));
02054     if (!JSVAL_IS_INT(id))
02055         return JS_TRUE;
02056 
02057     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
02058     if (!fp)
02059         return JS_TRUE;
02060 
02061     slot = OBJ_BLOCK_DEPTH(cx, obj) + (uint16) JSVAL_TO_INT(id);
02062     JS_ASSERT((uintN)slot < fp->script->depth);
02063     *vp = fp->spbase[slot];
02064     return JS_TRUE;
02065 }
02066 
02067 static JSBool
02068 block_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
02069 {
02070     JSStackFrame *fp;
02071     jsint slot;
02072 
02073     JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL));
02074     if (!JSVAL_IS_INT(id))
02075         return JS_TRUE;
02076 
02077     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
02078     if (!fp)
02079         return JS_TRUE;
02080 
02081     slot = OBJ_BLOCK_DEPTH(cx, obj) + (uint16) JSVAL_TO_INT(id);
02082     JS_ASSERT((uintN)slot < fp->script->depth);
02083     fp->spbase[slot] = *vp;
02084     return JS_TRUE;
02085 }
02086 
02087 #if JS_HAS_XDR
02088 
02089 #define NO_PARENT_INDEX (jsatomid)-1
02090 
02091 jsatomid
02092 FindObjectAtomIndex(JSAtomMap *map, JSObject *obj)
02093 {
02094     size_t i;
02095     JSAtom *atom;
02096 
02097     for (i = 0; i < map->length; i++) {
02098         atom = map->vector[i];
02099         if (ATOM_KEY(atom) == OBJECT_TO_JSVAL(obj))
02100             return i;
02101     }
02102 
02103     return NO_PARENT_INDEX;
02104 }
02105 
02106 static JSBool
02107 block_xdrObject(JSXDRState *xdr, JSObject **objp)
02108 {
02109     JSContext *cx;
02110     jsatomid parentId;
02111     JSAtomMap *atomMap;
02112     JSObject *obj, *parent;
02113     uint16 depth, count, i;
02114     uint32 tmp;
02115     JSTempValueRooter tvr;
02116     JSScopeProperty *sprop;
02117     jsid propid;
02118     JSAtom *atom;
02119     int16 shortid;
02120     JSBool ok;
02121 
02122     cx = xdr->cx;
02123 #ifdef __GNUC__
02124     obj = NULL;         /* quell GCC overwarning */
02125 #endif
02126 
02127     atomMap = &xdr->script->atomMap;
02128     if (xdr->mode == JSXDR_ENCODE) {
02129         obj = *objp;
02130         parent = OBJ_GET_PARENT(cx, obj);
02131         parentId = FindObjectAtomIndex(atomMap, parent);
02132         depth = OBJ_BLOCK_DEPTH(cx, obj);
02133         count = OBJ_BLOCK_COUNT(cx, obj);
02134         tmp = (uint32)(depth << 16) | count;
02135     }
02136 #ifdef __GNUC__ /* suppress bogus gcc warnings */
02137     else count = 0;
02138 #endif
02139 
02140     /* First, XDR the parent atomid. */
02141     if (!JS_XDRUint32(xdr, &parentId))
02142         return JS_FALSE;
02143 
02144     if (xdr->mode == JSXDR_DECODE) {
02145         obj = js_NewBlockObject(cx);
02146         if (!obj)
02147             return JS_FALSE;
02148         *objp = obj;
02149 
02150         /*
02151          * If there's a parent id, then get the parent out of our script's
02152          * atomMap. We know that we XDR block object in outer-to-inner order,
02153          * which means that getting the parent now will work.
02154          */
02155         if (parentId == NO_PARENT_INDEX) {
02156             parent = NULL;
02157         } else {
02158             atom = js_GetAtom(cx, atomMap, parentId);
02159             JS_ASSERT(ATOM_IS_OBJECT(atom));
02160             parent = ATOM_TO_OBJECT(atom);
02161         }
02162         obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);
02163     }
02164 
02165     JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(obj), &tvr);
02166 
02167     if (!JS_XDRUint32(xdr, &tmp)) {
02168         JS_POP_TEMP_ROOT(cx, &tvr);
02169         return JS_FALSE;
02170     }
02171 
02172     if (xdr->mode == JSXDR_DECODE) {
02173         depth = (uint16)(tmp >> 16);
02174         count = (uint16)tmp;
02175         obj->slots[JSSLOT_BLOCK_DEPTH] = INT_TO_JSVAL(depth);
02176     }
02177 
02178     /*
02179      * XDR the block object's properties. We know that there are 'count'
02180      * properties to XDR, stored as id/shortid pairs. We do not XDR any
02181      * non-native properties, only those that the compiler created.
02182      */
02183     sprop = NULL;
02184     ok = JS_TRUE;
02185     for (i = 0; i < count; i++) {
02186         if (xdr->mode == JSXDR_ENCODE) {
02187             /* Find a property to XDR. */
02188             do {
02189                 /* If sprop is NULL, this is the first property. */
02190                 sprop = sprop ? sprop->parent : OBJ_SCOPE(obj)->lastProp;
02191             } while (!(sprop->flags & SPROP_HAS_SHORTID));
02192 
02193             JS_ASSERT(sprop->getter == js_BlockClass.getProperty);
02194             propid = sprop->id;
02195             JS_ASSERT(JSID_IS_ATOM(propid));
02196             atom = JSID_TO_ATOM(propid);
02197             shortid = sprop->shortid;
02198             JS_ASSERT(shortid >= 0);
02199         }
02200 
02201         /* XDR the real id, then the shortid. */
02202         if (!js_XDRStringAtom(xdr, &atom) ||
02203             !JS_XDRUint16(xdr, (uint16 *)&shortid)) {
02204             ok = JS_FALSE;
02205             break;
02206         }
02207 
02208         if (xdr->mode == JSXDR_DECODE) {
02209             if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom),
02210                                          JSVAL_VOID, NULL, NULL,
02211                                          JSPROP_ENUMERATE | JSPROP_PERMANENT,
02212                                          SPROP_HAS_SHORTID, shortid, NULL)) {
02213                 ok = JS_FALSE;
02214                 break;
02215             }
02216         }
02217     }
02218 
02219     JS_POP_TEMP_ROOT(cx, &tvr);
02220     return ok;
02221 }
02222 
02223 #else
02224 # define block_xdrObject NULL
02225 #endif
02226 
02227 JSClass js_BlockClass = {
02228     "Block",
02229     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) |
02230     JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_CACHED_PROTO(JSProto_Block),
02231     JS_PropertyStub,  JS_PropertyStub,  block_getProperty, block_setProperty,
02232     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,    JS_FinalizeStub,
02233     NULL, NULL, NULL, NULL, block_xdrObject, NULL, NULL, NULL
02234 };
02235 
02236 JSObject*
02237 js_InitBlockClass(JSContext *cx, JSObject* obj)
02238 {
02239     JSObject *proto;
02240 
02241     proto = JS_InitClass(cx, obj, NULL, &js_BlockClass, NULL, 0, NULL,
02242                          NULL, NULL, NULL);
02243     if (!proto)
02244         return NULL;
02245 
02246     OBJ_SET_PROTO(cx, proto, NULL);
02247     return proto;
02248 }
02249 
02250 JSObject *
02251 js_InitObjectClass(JSContext *cx, JSObject *obj)
02252 {
02253     JSObject *proto;
02254 
02255     proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1,
02256                          object_props, object_methods, NULL, NULL);
02257     if (!proto)
02258         return NULL;
02259 
02260     /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
02261     if (!js_DefineFunction(cx, obj, cx->runtime->atomState.evalAtom,
02262                            obj_eval, 1, 0)) {
02263         return NULL;
02264     }
02265 
02266     return proto;
02267 }
02268 
02269 void
02270 js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops,
02271                  JSClass *clasp)
02272 {
02273     map->nrefs = nrefs;
02274     map->ops = ops;
02275     map->nslots = JS_INITIAL_NSLOTS;
02276     map->freeslot = JSSLOT_FREE(clasp);
02277 }
02278 
02279 JSObjectMap *
02280 js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,
02281                 JSClass *clasp, JSObject *obj)
02282 {
02283     return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj);
02284 }
02285 
02286 void
02287 js_DestroyObjectMap(JSContext *cx, JSObjectMap *map)
02288 {
02289     js_DestroyScope(cx, (JSScope *)map);
02290 }
02291 
02292 JSObjectMap *
02293 js_HoldObjectMap(JSContext *cx, JSObjectMap *map)
02294 {
02295     JS_ASSERT(map->nrefs >= 0);
02296     JS_ATOMIC_INCREMENT(&map->nrefs);
02297     return map;
02298 }
02299 
02300 JSObjectMap *
02301 js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj)
02302 {
02303     JS_ASSERT(map->nrefs > 0);
02304     JS_ATOMIC_DECREMENT(&map->nrefs);
02305     if (map->nrefs == 0) {
02306         map->ops->destroyObjectMap(cx, map);
02307         return NULL;
02308     }
02309     if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj)
02310         ((JSScope *)map)->object = NULL;
02311     return map;
02312 }
02313 
02314 static jsval *
02315 AllocSlots(JSContext *cx, jsval *slots, uint32 nslots)
02316 {
02317     size_t nbytes, obytes, minbytes;
02318     uint32 i, oslots;
02319     jsval *newslots;
02320 
02321     nbytes = (nslots + 1) * sizeof(jsval);
02322     if (slots) {
02323         oslots = slots[-1];
02324         obytes = (oslots + 1) * sizeof(jsval);
02325     } else {
02326         oslots = 0;
02327         obytes = 0;
02328     }
02329 
02330     if (nbytes <= GC_NBYTES_MAX) {
02331         newslots = (jsval *) js_NewGCThing(cx, GCX_PRIVATE, nbytes);
02332     } else {
02333         newslots = (jsval *)
02334                    JS_realloc(cx,
02335                               (obytes <= GC_NBYTES_MAX) ? NULL : slots - 1,
02336                               nbytes);
02337     }
02338     if (!newslots)
02339         return NULL;
02340 
02341     if (obytes != 0) {
02342         /* If either nbytes or obytes fit in a GC-thing, we must copy. */
02343         minbytes = JS_MIN(nbytes, obytes);
02344         if (minbytes <= GC_NBYTES_MAX)
02345             memcpy(newslots + 1, slots, minbytes - sizeof(jsval));
02346 
02347         /* If nbytes are in a GC-thing but obytes aren't, free obytes. */
02348         if (nbytes <= GC_NBYTES_MAX && obytes > GC_NBYTES_MAX)
02349             JS_free(cx, slots - 1);
02350 
02351         /* If we're extending an allocation, initialize free slots. */
02352         if (nslots > oslots) {
02353             for (i = 1 + oslots; i <= nslots; i++)
02354                 newslots[i] = JSVAL_VOID;
02355         }
02356     }
02357 
02358     newslots[0] = nslots;
02359     return ++newslots;
02360 }
02361 
02362 static void
02363 FreeSlots(JSContext *cx, jsval *slots)
02364 {
02365     size_t nbytes;
02366 
02367     /*
02368      * NB: We count on smaller GC-things being finalized before larger things
02369      * that become garbage during the same GC.  Without this assumption, we
02370      * couldn't load slots[-1] here without possibly loading a gcFreeList link
02371      * (see struct JSGCThing in jsgc.h).
02372      */
02373     nbytes = (slots[-1] + 1) * sizeof(jsval);
02374     if (nbytes > GC_NBYTES_MAX)
02375         JS_free(cx, slots - 1);
02376 }
02377 
02378 extern JSBool
02379 js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp)
02380 {
02381     JSProtoKey key;
02382     JSAtom *atom;
02383 
02384     key = JSCLASS_CACHED_PROTO_KEY(clasp);
02385     if (key != JSProto_Null) {
02386         *idp = INT_TO_JSID(key);
02387     } else if (clasp->flags & JSCLASS_IS_ANONYMOUS) {
02388         *idp = INT_TO_JSID(JSProto_Object);
02389     } else {
02390         atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
02391         if (!atom)
02392             return JS_FALSE;
02393         *idp = ATOM_TO_JSID(atom);
02394     }
02395     return JS_TRUE;
02396 }
02397 
02398 JSObject *
02399 js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
02400 {
02401     jsid id;
02402     JSObject *obj;
02403     JSObjectOps *ops;
02404     JSObjectMap *map;
02405     JSClass *protoclasp;
02406     uint32 nslots, i;
02407     jsval *newslots;
02408     JSTempValueRooter tvr;
02409 
02410     /* Bootstrap the ur-object, and make it the default prototype object. */
02411     if (!proto) {
02412         if (!js_GetClassId(cx, clasp, &id))
02413             return NULL;
02414         if (!js_GetClassPrototype(cx, parent, id, &proto))
02415             return NULL;
02416         if (!proto &&
02417             !js_GetClassPrototype(cx, parent, INT_TO_JSID(JSProto_Object),
02418                                   &proto)) {
02419             return NULL;
02420         }
02421     }
02422 
02423     /* Always call the class's getObjectOps hook if it has one. */
02424     ops = clasp->getObjectOps
02425           ? clasp->getObjectOps(cx, clasp)
02426           : &js_ObjectOps;
02427 
02428     /*
02429      * Allocate a zeroed object from the GC heap.  Do this *after* any other
02430      * GC-thing allocations under js_GetClassPrototype or clasp->getObjectOps,
02431      * to avoid displacing the newborn root for obj.
02432      */
02433     obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject));
02434     if (!obj)
02435         return NULL;
02436 
02437     /*
02438      * Root obj to prevent it from being collected out from under this call.
02439      * to js_NewObject.  AllocSlots can trigger a finalizer from a last-ditch
02440      * GC calling JS_ClearNewbornRoots. There's also the possibilty of things
02441      * happening under the objectHook call-out further below.
02442      */
02443     JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
02444 
02445     /*
02446      * Share proto's map only if it has the same JSObjectOps, and only if
02447      * proto's class has the same private and reserved slots as obj's map
02448      * and class have.  We assume that if prototype and object are of the
02449      * same class, they always have the same number of computed reserved
02450      * slots (returned via clasp->reserveSlots); otherwise, prototype and
02451      * object classes must have the same (null or not) reserveSlots hook.
02452      */
02453     if (proto &&
02454         (map = proto->map)->ops == ops &&
02455         ((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp ||
02456          (!((protoclasp->flags ^ clasp->flags) &
02457             (JSCLASS_HAS_PRIVATE |
02458              (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) &&
02459           protoclasp->reserveSlots == clasp->reserveSlots)))
02460     {
02461         /*
02462          * Default parent to the parent of the prototype, which was set from
02463          * the parent of the prototype's constructor.
02464          */
02465         if (!parent)
02466             parent = OBJ_GET_PARENT(cx, proto);
02467 
02468         /* Share the given prototype's map. */
02469         obj->map = js_HoldObjectMap(cx, map);
02470 
02471         /* Ensure that obj starts with the minimum slots for clasp. */
02472         nslots = JS_INITIAL_NSLOTS;
02473     } else {
02474         /* Leave parent alone.  Allocate a new map for obj. */
02475         map = ops->newObjectMap(cx, 1, ops, clasp, obj);
02476         if (!map)
02477             goto bad;
02478         obj->map = map;
02479 
02480         /* Let ops->newObjectMap set nslots so as to reserve slots. */
02481         nslots = map->nslots;
02482     }
02483 
02484     /* Allocate a slots vector, with a -1'st element telling its length. */
02485     newslots = AllocSlots(cx, NULL, nslots);
02486     if (!newslots) {
02487         js_DropObjectMap(cx, obj->map, obj);
02488         obj->map = NULL;
02489         goto bad;
02490     }
02491 
02492     /* Set the proto, parent, and class properties. */
02493     newslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
02494     newslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);
02495     newslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp);
02496 
02497     /* Clear above JSSLOT_CLASS so the GC doesn't load uninitialized memory. */
02498     for (i = JSSLOT_CLASS + 1; i < nslots; i++)
02499         newslots[i] = JSVAL_VOID;
02500 
02501     /* Store newslots after initializing all of 'em, just in case. */
02502     obj->slots = newslots;
02503 
02504     if (cx->runtime->objectHook) {
02505         JS_KEEP_ATOMS(cx->runtime);
02506         cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData);
02507         JS_UNKEEP_ATOMS(cx->runtime);
02508     }
02509 
02510 out:
02511     JS_POP_TEMP_ROOT(cx, &tvr);
02512     cx->weakRoots.newborn[GCX_OBJECT] = (JSGCThing *) obj;
02513     return obj;
02514 
02515 bad:
02516     obj = NULL;
02517     goto out;
02518 }
02519 
02520 JS_STATIC_DLL_CALLBACK(JSObject *)
02521 js_InitNullClass(JSContext *cx, JSObject *obj)
02522 {
02523     JS_ASSERT(0);
02524     return NULL;
02525 }
02526 
02527 #define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
02528 #include "jsproto.tbl"
02529 #undef JS_PROTO
02530 
02531 static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = {
02532 #define JS_PROTO(name,code,init) init,
02533 #include "jsproto.tbl"
02534 #undef JS_PROTO
02535 };
02536 
02537 JSBool
02538 js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
02539                   JSObject **objp)
02540 {
02541     JSBool ok;
02542     JSObject *tmp, *cobj;
02543     JSResolvingKey rkey;
02544     JSResolvingEntry *rentry;
02545     uint32 generation;
02546     JSObjectOp init;
02547     jsval v;
02548 
02549     while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
02550         obj = tmp;
02551     if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) {
02552         *objp = NULL;
02553         return JS_TRUE;
02554     }
02555 
02556     ok = JS_GetReservedSlot(cx, obj, key, &v);
02557     if (!ok)
02558         return JS_FALSE;
02559     if (!JSVAL_IS_PRIMITIVE(v)) {
02560         *objp = JSVAL_TO_OBJECT(v);
02561         return JS_TRUE;
02562     }
02563 
02564     rkey.obj = obj;
02565     rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
02566     if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry))
02567         return JS_FALSE;
02568     if (!rentry) {
02569         /* Already caching key in obj -- suppress recursion. */
02570         *objp = NULL;
02571         return JS_TRUE;
02572     }
02573     generation = cx->resolvingTable->generation;
02574 
02575     cobj = NULL;
02576     init = lazy_prototype_init[key];
02577     if (init) {
02578         if (!init(cx, obj)) {
02579             ok = JS_FALSE;
02580         } else {
02581             ok = JS_GetReservedSlot(cx, obj, key, &v);
02582             if (ok && !JSVAL_IS_PRIMITIVE(v))
02583                 cobj = JSVAL_TO_OBJECT(v);
02584         }
02585     }
02586 
02587     js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation);
02588     *objp = cobj;
02589     return ok;
02590 }
02591 
02592 JSBool
02593 js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj)
02594 {
02595     JS_ASSERT(!OBJ_GET_PARENT(cx, obj));
02596     if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL))
02597         return JS_TRUE;
02598 
02599     return JS_SetReservedSlot(cx, obj, key, OBJECT_TO_JSVAL(cobj));
02600 }
02601 
02602 JSBool
02603 js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp)
02604 {
02605     JSObject *obj, *cobj, *pobj;
02606     JSProtoKey key;
02607     JSProperty *prop;
02608     jsval v;
02609     JSScopeProperty *sprop;
02610 
02611     if (start || (cx->fp && (start = cx->fp->scopeChain) != NULL)) {
02612         /* Find the topmost object in the scope chain. */
02613         do {
02614             obj = start;
02615             start = OBJ_GET_PARENT(cx, obj);
02616         } while (start);
02617     } else {
02618         obj = cx->globalObject;
02619         if (!obj) {
02620             *vp = JSVAL_VOID;
02621             return JS_TRUE;
02622         }
02623     }
02624 
02625     OBJ_TO_INNER_OBJECT(cx, obj);
02626     if (!obj)
02627         return JS_FALSE;
02628 
02629     if (JSID_IS_INT(id)) {
02630         key = JSID_TO_INT(id);
02631         JS_ASSERT(key != JSProto_Null);
02632         if (!js_GetClassObject(cx, obj, key, &cobj))
02633             return JS_FALSE;
02634         if (cobj) {
02635             *vp = OBJECT_TO_JSVAL(cobj);
02636             return JS_TRUE;
02637         }
02638         id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
02639     }
02640 
02641     JS_ASSERT(OBJ_IS_NATIVE(obj));
02642     if (!js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME,
02643                                     &pobj, &prop)) {
02644         return JS_FALSE;
02645     }
02646     v = JSVAL_VOID;
02647     if (prop)  {
02648         if (OBJ_IS_NATIVE(pobj)) {
02649             sprop = (JSScopeProperty *) prop;
02650             if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))) {
02651                 v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
02652                 if (JSVAL_IS_PRIMITIVE(v))
02653                     v = JSVAL_VOID; 
02654             }
02655         }
02656         OBJ_DROP_PROPERTY(cx, pobj, prop);
02657     }
02658     *vp = v;
02659     return JS_TRUE;
02660 }
02661 
02662 JSObject *
02663 js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
02664                    JSObject *parent, uintN argc, jsval *argv)
02665 {
02666     jsid id;
02667     jsval cval, rval;
02668     JSTempValueRooter argtvr, tvr;
02669     JSObject *obj, *ctor;
02670 
02671     JS_PUSH_TEMP_ROOT(cx, argc, argv, &argtvr);
02672 
02673     if (!js_GetClassId(cx, clasp, &id) ||
02674         !js_FindClassObject(cx, parent, id, &cval)) {
02675         JS_POP_TEMP_ROOT(cx, &argtvr);
02676         return NULL;
02677     }
02678 
02679     if (JSVAL_IS_PRIMITIVE(cval)) {
02680         js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
02681         JS_POP_TEMP_ROOT(cx, &argtvr);
02682         return NULL;
02683     }
02684 
02685     /*
02686      * Protect cval in case a crazy getter for .prototype uproots it.  After
02687      * this point, all control flow must exit through label out with obj set.
02688      */
02689     JS_PUSH_SINGLE_TEMP_ROOT(cx, cval, &tvr);
02690 
02691     /*
02692      * If proto or parent are NULL, set them to Constructor.prototype and/or
02693      * Constructor.__parent__, just like JSOP_NEW does.
02694      */
02695     ctor = JSVAL_TO_OBJECT(cval);
02696     if (!parent)
02697         parent = OBJ_GET_PARENT(cx, ctor);
02698     if (!proto) {
02699         if (!OBJ_GET_PROPERTY(cx, ctor,
02700                               ATOM_TO_JSID(cx->runtime->atomState
02701                                            .classPrototypeAtom),
02702                               &rval)) {
02703             obj = NULL;
02704             goto out;
02705         }
02706         if (JSVAL_IS_OBJECT(rval))
02707             proto = JSVAL_TO_OBJECT(rval);
02708     }
02709 
02710     obj = js_NewObject(cx, clasp, proto, parent);
02711     if (!obj)
02712         goto out;
02713 
02714     if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval))
02715         goto bad;
02716 
02717     if (JSVAL_IS_PRIMITIVE(rval))
02718         goto out;
02719     obj = JSVAL_TO_OBJECT(rval);
02720 
02721     /*
02722      * If the instance's class differs from what was requested, throw a type
02723      * error.  If the given class has both the JSCLASS_HAS_PRIVATE and the
02724      * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its
02725      * private data set at this point, then the constructor was replaced and
02726      * we should throw a type error.
02727      */
02728     if (OBJ_GET_CLASS(cx, obj) != clasp ||
02729         (!(~clasp->flags & (JSCLASS_HAS_PRIVATE |
02730                             JSCLASS_CONSTRUCT_PROTOTYPE)) &&
02731          !JS_GetPrivate(cx, obj))) {
02732         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
02733                              JSMSG_WRONG_CONSTRUCTOR, clasp->name);
02734         goto bad;
02735     }
02736 
02737 out:
02738     JS_POP_TEMP_ROOT(cx, &tvr);
02739     JS_POP_TEMP_ROOT(cx, &argtvr);
02740     return obj;
02741 
02742 bad:
02743     cx->weakRoots.newborn[GCX_OBJECT] = NULL;
02744     obj = NULL;
02745     goto out;
02746 }
02747 
02748 void
02749 js_FinalizeObject(JSContext *cx, JSObject *obj)
02750 {
02751     JSObjectMap *map;
02752 
02753     /* Cope with stillborn objects that have no map. */
02754     map = obj->map;
02755     if (!map)
02756         return;
02757     JS_ASSERT(obj->slots);
02758 
02759     if (cx->runtime->objectHook)
02760         cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData);
02761 
02762     /* Remove all watchpoints with weak links to obj. */
02763     JS_ClearWatchPointsForObject(cx, obj);
02764 
02765     /*
02766      * Finalize obj first, in case it needs map and slots.  Optimized to use
02767      * LOCKED_OBJ_GET_CLASS instead of OBJ_GET_CLASS, so we avoid "promoting"
02768      * obj's scope from lock-free to lock-full (see jslock.c:ClaimScope) when
02769      * we're called from the GC.  Only the GC should call js_FinalizeObject,
02770      * and no other threads run JS (and possibly racing to update obj->slots)
02771      * while the GC is running.
02772      */
02773     LOCKED_OBJ_GET_CLASS(obj)->finalize(cx, obj);
02774 
02775     /* Drop map and free slots. */
02776     js_DropObjectMap(cx, map, obj);
02777     obj->map = NULL;
02778     FreeSlots(cx, obj->slots);
02779     obj->slots = NULL;
02780 }
02781 
02782 /* XXXbe if one adds props, deletes earlier props, adds more, the last added
02783          won't recycle the deleted props' slots. */
02784 JSBool
02785 js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)
02786 {
02787     JSObjectMap *map;
02788     JSClass *clasp;
02789     uint32 nslots;
02790     jsval *newslots;
02791 
02792     map = obj->map;
02793     JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
02794     clasp = LOCKED_OBJ_GET_CLASS(obj);
02795     if (map->freeslot == JSSLOT_FREE(clasp)) {
02796         /* Adjust map->freeslot to include computed reserved slots, if any. */
02797         if (clasp->reserveSlots)
02798             map->freeslot += clasp->reserveSlots(cx, obj);
02799     }
02800     nslots = map->nslots;
02801     if (map->freeslot >= nslots) {
02802         nslots = map->freeslot;
02803         JS_ASSERT(nslots >= JS_INITIAL_NSLOTS);
02804         nslots += (nslots + 1) / 2;
02805 
02806         newslots = AllocSlots(cx, obj->slots, nslots);
02807         if (!newslots)
02808             return JS_FALSE;
02809         map->nslots = nslots;
02810         obj->slots = newslots;
02811     }
02812 
02813     *slotp = map->freeslot++;
02814     return JS_TRUE;
02815 }
02816 
02817 void
02818 js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
02819 {
02820     JSObjectMap *map;
02821     uint32 nslots;
02822     jsval *newslots;
02823 
02824     OBJ_CHECK_SLOT(obj, slot);
02825     obj->slots[slot] = JSVAL_VOID;
02826     map = obj->map;
02827     JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
02828     if (map->freeslot == slot + 1)
02829         map->freeslot = slot;
02830     nslots = map->nslots;
02831     if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) {
02832         nslots = map->freeslot;
02833         nslots += nslots / 2;
02834         if (nslots < JS_INITIAL_NSLOTS)
02835             nslots = JS_INITIAL_NSLOTS;
02836 
02837         newslots = AllocSlots(cx, obj->slots, nslots);
02838         if (!newslots)
02839             return;
02840         map->nslots = nslots;
02841         obj->slots = newslots;
02842     }
02843 }
02844 
02845 /* JSVAL_INT_MAX as a string */
02846 #define JSVAL_INT_MAX_STRING "1073741823"
02847 
02848 #define CHECK_FOR_STRING_INDEX(id)                                            \
02849     JS_BEGIN_MACRO                                                            \
02850         if (JSID_IS_ATOM(id)) {                                               \
02851             JSAtom *atom_ = JSID_TO_ATOM(id);                                 \
02852             JSString *str_ = ATOM_TO_STRING(atom_);                           \
02853             const jschar *cp_ = str_->chars;                                  \
02854             JSBool negative_ = (*cp_ == '-');                                 \
02855             if (negative_) cp_++;                                             \
02856             if (JS7_ISDEC(*cp_)) {                                            \
02857                 size_t n_ = str_->length - negative_;                         \
02858                 if (n_ <= sizeof(JSVAL_INT_MAX_STRING) - 1)                   \
02859                     id = CheckForStringIndex(id, cp_, cp_ + n_, negative_);   \
02860             }                                                                 \
02861         }                                                                     \
02862     JS_END_MACRO
02863 
02864 static jsid
02865 CheckForStringIndex(jsid id, const jschar *cp, const jschar *end,
02866                     JSBool negative)
02867 {
02868     jsuint index = JS7_UNDEC(*cp++);
02869     jsuint oldIndex = 0;
02870     jsuint c = 0;
02871 
02872     if (index != 0) {
02873         while (JS7_ISDEC(*cp)) {
02874             oldIndex = index;
02875             c = JS7_UNDEC(*cp);
02876             index = 10 * index + c;
02877             cp++;
02878         }
02879     }
02880     if (cp == end &&
02881         (oldIndex < (JSVAL_INT_MAX / 10) ||
02882          (oldIndex == (JSVAL_INT_MAX / 10) &&
02883           c <= (JSVAL_INT_MAX % 10)))) {
02884         if (negative)
02885             index = 0 - index;
02886         id = INT_TO_JSID((jsint)index);
02887     }
02888     return id;
02889 }
02890 
02891 static JSBool
02892 HidePropertyName(JSContext *cx, jsid *idp)
02893 {
02894     jsid id;
02895     JSAtom *atom, *hidden;
02896 
02897     id = *idp;
02898     JS_ASSERT(JSID_IS_ATOM(id));
02899 
02900     atom = JSID_TO_ATOM(id);
02901     JS_ASSERT(!(atom->flags & ATOM_HIDDEN));
02902     JS_ASSERT(ATOM_IS_STRING(atom));
02903 
02904     hidden = js_AtomizeString(cx, ATOM_TO_STRING(atom), ATOM_HIDDEN);
02905     if (!hidden)
02906         return JS_FALSE;
02907 
02908     /*
02909      * Link hidden to unhidden atom to optimize call_enumerate -- this means
02910      * the GC must mark a hidden atom's unhidden counterpart (see js_MarkAtom
02911      * in jsgc.c).  It uses the atom's entry.value member for this linkage.
02912      */
02913     hidden->entry.value = atom;
02914     *idp = ATOM_TO_JSID(hidden);
02915     return JS_TRUE;
02916 }
02917 
02918 JSScopeProperty *
02919 js_AddHiddenProperty(JSContext *cx, JSObject *obj, jsid id,
02920                      JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
02921                      uintN attrs, uintN flags, intN shortid)
02922 {
02923     if (!HidePropertyName(cx, &id))
02924         return NULL;
02925 
02926     flags |= SPROP_IS_HIDDEN;
02927     return js_AddNativeProperty(cx, obj, id, getter, setter, slot, attrs,
02928                                 flags, shortid);
02929 }
02930 
02931 JSBool
02932 js_LookupHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
02933                         JSProperty **propp)
02934 {
02935     return HidePropertyName(cx, &id) &&
02936            js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_HIDDEN,
02937                                       objp, propp);
02938 }
02939 
02940 JSScopeProperty *
02941 js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
02942                      JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
02943                      uintN attrs, uintN flags, intN shortid)
02944 {
02945     JSScope *scope;
02946     JSScopeProperty *sprop;
02947 
02948     JS_LOCK_OBJ(cx, obj);
02949     scope = js_GetMutableScope(cx, obj);
02950     if (!scope) {
02951         sprop = NULL;
02952     } else {
02953         /*
02954          * Handle old bug that took empty string as zero index.  Also convert
02955          * string indices to integers if appropriate.
02956          */
02957         CHECK_FOR_STRING_INDEX(id);
02958         sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs,
02959                                     flags, shortid);
02960     }
02961     JS_UNLOCK_OBJ(cx, obj);
02962     return sprop;
02963 }
02964 
02965 JSScopeProperty *
02966 js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
02967                              JSScopeProperty *sprop, uintN attrs, uintN mask,
02968                              JSPropertyOp getter, JSPropertyOp setter)
02969 {
02970     JSScope *scope;
02971 
02972     JS_LOCK_OBJ(cx, obj);
02973     scope = js_GetMutableScope(cx, obj);
02974     if (!scope) {
02975         sprop = NULL;
02976     } else {
02977         sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, attrs, mask,
02978                                             getter, setter);
02979         if (sprop) {
02980             PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, sprop->id,
02981                                 sprop);
02982         }
02983     }
02984     JS_UNLOCK_OBJ(cx, obj);
02985     return sprop;
02986 }
02987 
02988 JSBool
02989 js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
02990                   JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
02991                   JSProperty **propp)
02992 {
02993     return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs,
02994                                    0, 0, propp);
02995 }
02996 
02997 /*
02998  * Backward compatibility requires allowing addProperty hooks to mutate the
02999  * nominal initial value of a slot-full property, while GC safety wants that
03000  * value to be stored before the call-out through the hook.  Optimize to do
03001  * both while saving cycles for classes that stub their addProperty hook.
03002  */
03003 #define ADD_PROPERTY_HELPER(cx,clasp,obj,scope,sprop,vp,cleanup)              \
03004     JS_BEGIN_MACRO                                                            \
03005         if ((clasp)->addProperty != JS_PropertyStub) {                        \
03006             jsval nominal_ = *(vp);                                           \
03007             if (!(clasp)->addProperty(cx, obj, SPROP_USERID(sprop), vp)) {    \
03008                 cleanup;                                                      \
03009             }                                                                 \
03010             if (*(vp) != nominal_) {                                          \
03011                 if (SPROP_HAS_VALID_SLOT(sprop, scope))                       \
03012                     LOCKED_OBJ_SET_SLOT(obj, (sprop)->slot, *(vp));           \
03013             }                                                                 \
03014         }                                                                     \
03015     JS_END_MACRO
03016 
03017 JSBool
03018 js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
03019                         JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
03020                         uintN flags, intN shortid, JSProperty **propp)
03021 {
03022     JSClass *clasp;
03023     JSScope *scope;
03024     JSScopeProperty *sprop;
03025 
03026     /*
03027      * Handle old bug that took empty string as zero index.  Also convert
03028      * string indices to integers if appropriate.
03029      */
03030     CHECK_FOR_STRING_INDEX(id);
03031 
03032 #if JS_HAS_GETTER_SETTER
03033     /*
03034      * If defining a getter or setter, we must check for its counterpart and
03035      * update the attributes and property ops.  A getter or setter is really
03036      * only half of a property.
03037      */
03038     if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
03039         JSObject *pobj;
03040         JSProperty *prop;
03041 
03042         /*
03043          * If JS_THREADSAFE and id is found, js_LookupProperty returns with
03044          * sprop non-null and pobj locked.  If pobj == obj, the property is
03045          * already in obj and obj has its own (mutable) scope.  So if we are
03046          * defining a getter whose setter was already defined, or vice versa,
03047          * finish the job via js_ChangeScopePropertyAttributes, and refresh
03048          * the property cache line for (obj, id) to map sprop.
03049          */
03050         if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
03051             return JS_FALSE;
03052         sprop = (JSScopeProperty *) prop;
03053         if (sprop &&
03054             pobj == obj &&
03055             (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
03056             sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(obj), sprop,
03057                                                 attrs, sprop->attrs,
03058                                                 (attrs & JSPROP_GETTER)
03059                                                 ? getter
03060                                                 : sprop->getter,
03061                                                 (attrs & JSPROP_SETTER)
03062                                                 ? setter
03063                                                 : sprop->setter);
03064 
03065             /* NB: obj == pobj, so we can share unlock code at the bottom. */
03066             if (!sprop)
03067                 goto bad;
03068             goto out;
03069         }
03070 
03071         if (prop) {
03072             /* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */
03073             OBJ_DROP_PROPERTY(cx, pobj, prop);
03074             prop = NULL;
03075         }
03076     }
03077 #endif /* JS_HAS_GETTER_SETTER */
03078 
03079     /* Lock if object locking is required by this implementation. */
03080     JS_LOCK_OBJ(cx, obj);
03081 
03082     /* Use the object's class getter and setter by default. */
03083     clasp = LOCKED_OBJ_GET_CLASS(obj);
03084     if (!getter)
03085         getter = clasp->getProperty;
03086     if (!setter)
03087         setter = clasp->setProperty;
03088 
03089     /* Get obj's own scope if it has one, or create a new one for obj. */
03090     scope = js_GetMutableScope(cx, obj);
03091     if (!scope)
03092         goto bad;
03093 
03094     /* Add the property to scope, or replace an existing one of the same id. */
03095     if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
03096         attrs |= JSPROP_SHARED;
03097     sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
03098                                 SPROP_INVALID_SLOT, attrs, flags, shortid);
03099     if (!sprop)
03100         goto bad;
03101 
03102     /* Store value before calling addProperty, in case the latter GC's. */
03103     if (SPROP_HAS_VALID_SLOT(sprop, scope))
03104         LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value);
03105 
03106     /* XXXbe called with lock held */
03107     ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value,
03108                         js_RemoveScopeProperty(cx, scope, id);
03109                         goto bad);
03110 
03111 #if JS_HAS_GETTER_SETTER
03112 out:
03113 #endif
03114     PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
03115     if (propp)
03116         *propp = (JSProperty *) sprop;
03117     else
03118         JS_UNLOCK_OBJ(cx, obj);
03119     return JS_TRUE;
03120 
03121 bad:
03122     JS_UNLOCK_OBJ(cx, obj);
03123     return JS_FALSE;
03124 }
03125 
03126 /*
03127  * Given pc pointing after a property accessing bytecode, return true if the
03128  * access is "object-detecting" in the sense used by web scripts, e.g., when
03129  * checking whether document.all is defined.
03130  */
03131 static JSBool
03132 Detecting(JSContext *cx, jsbytecode *pc)
03133 {
03134     JSScript *script;
03135     jsbytecode *endpc;
03136     JSOp op;
03137     JSAtom *atom;
03138 
03139     if (!cx->fp)
03140         return JS_FALSE;
03141     script = cx->fp->script;
03142     for (endpc = script->code + script->length; pc < endpc; pc++) {
03143         /* General case: a branch or equality op follows the access. */
03144         op = (JSOp) *pc;
03145         if (js_CodeSpec[op].format & JOF_DETECTING)
03146             return JS_TRUE;
03147 
03148         /*
03149          * Special case #1: handle (document.all == null).  Don't sweat about
03150          * JS1.2's revision of the equality operators here.
03151          */
03152         if (op == JSOP_NULL) {
03153             if (++pc < endpc)
03154                 return *pc == JSOP_EQ || *pc == JSOP_NE;
03155             break;
03156         }
03157 
03158         /*
03159          * Special case #2: handle (document.all == undefined).  Don't worry
03160          * about someone redefining undefined, which was added by Edition 3,
03161          * so is read/write for backward compatibility.
03162          */
03163         if (op == JSOP_NAME) {
03164             atom = GET_ATOM(cx, script, pc);
03165             if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] &&
03166                 (pc += js_CodeSpec[op].length) < endpc) {
03167                 op = (JSOp) *pc;
03168                 return op == JSOP_EQ || op == JSOP_NE ||
03169                        op == JSOP_NEW_EQ || op == JSOP_NEW_NE;
03170             }
03171             break;
03172         }
03173 
03174         /* At this point, anything but grouping means we're not detecting. */
03175         if (op != JSOP_GROUP)
03176             break;
03177     }
03178     return JS_FALSE;
03179 }
03180 
03181 JS_FRIEND_API(JSBool)
03182 js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
03183                   JSProperty **propp)
03184 {
03185     return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp);
03186 }
03187 
03188 JSBool
03189 js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
03190                            JSObject **objp, JSProperty **propp)
03191 {
03192     JSObject *start, *obj2, *proto;
03193     JSScope *scope;
03194     JSScopeProperty *sprop;
03195     JSClass *clasp;
03196     JSResolveOp resolve;
03197     JSResolvingKey key;
03198     JSResolvingEntry *entry;
03199     uint32 generation;
03200     JSNewResolveOp newresolve;
03201     jsbytecode *pc;
03202     const JSCodeSpec *cs;
03203     uint32 format;
03204     JSBool ok;
03205 
03206     /*
03207      * Handle old bug that took empty string as zero index.  Also convert
03208      * string indices to integers if appropriate.
03209      */
03210     CHECK_FOR_STRING_INDEX(id);
03211 
03212     /* Search scopes starting with obj and following the prototype link. */
03213     start = obj;
03214     for (;;) {
03215         JS_LOCK_OBJ(cx, obj);
03216         scope = OBJ_SCOPE(obj);
03217         if (scope->object == obj) {
03218             sprop = SCOPE_GET_PROPERTY(scope, id);
03219         } else {
03220             /* Shared prototype scope: try resolve before lookup. */
03221             sprop = NULL;
03222         }
03223 
03224         /* Try obj's class resolve hook if id was not found in obj's scope. */
03225         if (!sprop) {
03226             clasp = LOCKED_OBJ_GET_CLASS(obj);
03227             resolve = clasp->resolve;
03228             if (resolve != JS_ResolveStub) {
03229                 /* Avoid recursion on (obj, id) already being resolved on cx. */
03230                 key.obj = obj;
03231                 key.id = id;
03232 
03233                 /*
03234                  * Once we have successfully added an entry for (obj, key) to
03235                  * cx->resolvingTable, control must go through cleanup: before
03236                  * returning.  But note that JS_DHASH_ADD may find an existing
03237                  * entry, in which case we bail to suppress runaway recursion.
03238                  */
03239                 if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) {
03240                     JS_UNLOCK_OBJ(cx, obj);
03241                     return JS_FALSE;
03242                 }
03243                 if (!entry) {
03244                     /* Already resolving id in obj -- suppress recursion. */
03245                     JS_UNLOCK_OBJ(cx, obj);
03246                     goto out;
03247                 }
03248                 generation = cx->resolvingTable->generation;
03249 
03250                 /* Null *propp here so we can test it at cleanup: safely. */
03251                 *propp = NULL;
03252 
03253                 if (clasp->flags & JSCLASS_NEW_RESOLVE) {
03254                     newresolve = (JSNewResolveOp)resolve;
03255                     if (!(flags & JSRESOLVE_CLASSNAME) &&
03256                         cx->fp &&
03257                         (pc = cx->fp->pc)) {
03258                         cs = &js_CodeSpec[*pc];
03259                         format = cs->format;
03260                         if ((format & JOF_MODEMASK) != JOF_NAME)
03261                             flags |= JSRESOLVE_QUALIFIED;
03262                         if ((format & JOF_ASSIGNING) ||
03263                             (cx->fp->flags & JSFRAME_ASSIGNING)) {
03264                             flags |= JSRESOLVE_ASSIGNING;
03265                         } else {
03266                             pc += cs->length;
03267                             if (Detecting(cx, pc))
03268                                 flags |= JSRESOLVE_DETECTING;
03269                         }
03270                         if (format & JOF_DECLARING)
03271                             flags |= JSRESOLVE_DECLARING;
03272                     }
03273                     obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START)
03274                            ? start
03275                            : NULL;
03276                     JS_UNLOCK_OBJ(cx, obj);
03277 
03278                     /* Protect id and all atoms from a GC nested in resolve. */
03279                     JS_KEEP_ATOMS(cx->runtime);
03280                     ok = newresolve(cx, obj, ID_TO_VALUE(id), flags, &obj2);
03281                     JS_UNKEEP_ATOMS(cx->runtime);
03282                     if (!ok)
03283                         goto cleanup;
03284 
03285                     JS_LOCK_OBJ(cx, obj);
03286                     if (obj2) {
03287                         /* Resolved: juggle locks and lookup id again. */
03288                         if (obj2 != obj) {
03289                             JS_UNLOCK_OBJ(cx, obj);
03290                             JS_LOCK_OBJ(cx, obj2);
03291                         }
03292                         scope = OBJ_SCOPE(obj2);
03293                         if (!MAP_IS_NATIVE(&scope->map)) {
03294                             /* Whoops, newresolve handed back a foreign obj2. */
03295                             JS_ASSERT(obj2 != obj);
03296                             JS_UNLOCK_OBJ(cx, obj2);
03297                             ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp);
03298                             if (!ok || *propp)
03299                                 goto cleanup;
03300                             JS_LOCK_OBJ(cx, obj2);
03301                         } else {
03302                             /*
03303                              * Require that obj2 have its own scope now, as we
03304                              * do for old-style resolve.  If it doesn't, then
03305                              * id was not truly resolved, and we'll find it in
03306                              * the proto chain, or miss it if obj2's proto is
03307                              * not on obj's proto chain.  That last case is a
03308                              * "too bad!" case.
03309                              */
03310                             if (scope->object == obj2)
03311                                 sprop = SCOPE_GET_PROPERTY(scope, id);
03312                         }
03313                         if (sprop) {
03314                             JS_ASSERT(obj2 == scope->object);
03315                             obj = obj2;
03316                         } else if (obj2 != obj) {
03317                             JS_UNLOCK_OBJ(cx, obj2);
03318                             JS_LOCK_OBJ(cx, obj);
03319                         }
03320                     }
03321                 } else {
03322                     /*
03323                      * Old resolve always requires id re-lookup if obj owns
03324                      * its scope after resolve returns.
03325                      */
03326                     JS_UNLOCK_OBJ(cx, obj);
03327                     ok = resolve(cx, obj, ID_TO_VALUE(id));
03328                     if (!ok)
03329                         goto cleanup;
03330                     JS_LOCK_OBJ(cx, obj);
03331                     scope = OBJ_SCOPE(obj);
03332                     JS_ASSERT(MAP_IS_NATIVE(&scope->map));
03333                     if (scope->object == obj)
03334                         sprop = SCOPE_GET_PROPERTY(scope, id);
03335                 }
03336 
03337             cleanup:
03338                 js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
03339                 if (!ok || *propp)
03340                     return ok;
03341             }
03342         }
03343 
03344         if (sprop) {
03345             JS_ASSERT(OBJ_SCOPE(obj) == scope);
03346             *objp = scope->object;      /* XXXbe hide in jsscope.[ch] */
03347 
03348             *propp = (JSProperty *) sprop;
03349             return JS_TRUE;
03350         }
03351 
03352         proto = LOCKED_OBJ_GET_PROTO(obj);
03353         JS_UNLOCK_OBJ(cx, obj);
03354         if (!proto)
03355             break;
03356         if (!OBJ_IS_NATIVE(proto))
03357             return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
03358         obj = proto;
03359     }
03360 
03361 out:
03362     *objp = NULL;
03363     *propp = NULL;
03364     return JS_TRUE;
03365 }
03366 
03367 JS_FRIEND_API(JSBool)
03368 js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
03369                 JSProperty **propp)
03370 {
03371     JSRuntime *rt;
03372     JSObject *obj, *pobj, *lastobj;
03373     JSScopeProperty *sprop;
03374     JSProperty *prop;
03375 
03376     rt = cx->runtime;
03377     obj = cx->fp->scopeChain;
03378     do {
03379         /* Try the property cache and return immediately on cache hit. */
03380         if (OBJ_IS_NATIVE(obj)) {
03381             JS_LOCK_OBJ(cx, obj);
03382             PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop);
03383             if (sprop) {
03384                 JS_ASSERT(OBJ_IS_NATIVE(obj));
03385                 *objp = obj;
03386                 *pobjp = obj;
03387                 *propp = (JSProperty *) sprop;
03388                 return JS_TRUE;
03389             }
03390             JS_UNLOCK_OBJ(cx, obj);
03391         }
03392 
03393         /* If cache miss, take the slow path. */
03394         if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
03395             return JS_FALSE;
03396         if (prop) {
03397             if (OBJ_IS_NATIVE(pobj)) {
03398                 sprop = (JSScopeProperty *) prop;
03399                 PROPERTY_CACHE_FILL(&rt->propertyCache, pobj, id, sprop);
03400             }
03401             *objp = obj;
03402             *pobjp = pobj;
03403             *propp = prop;
03404             return JS_TRUE;
03405         }
03406         lastobj = obj;
03407     } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);
03408 
03409     *objp = lastobj;
03410     *pobjp = NULL;
03411     *propp = NULL;
03412     return JS_TRUE;
03413 }
03414 
03415 JSObject *
03416 js_FindIdentifierBase(JSContext *cx, jsid id)
03417 {
03418     JSObject *obj, *pobj;
03419     JSProperty *prop;
03420 
03421     /*
03422      * Look for id's property along the "with" statement chain and the
03423      * statically-linked scope chain.
03424      */
03425     if (!js_FindProperty(cx, id, &obj, &pobj, &prop))
03426         return NULL;
03427     if (prop) {
03428         OBJ_DROP_PROPERTY(cx, pobj, prop);
03429         return obj;
03430     }
03431 
03432     /*
03433      * Use the top-level scope from the scope chain, which won't end in the
03434      * same scope as cx->globalObject for cross-context function calls.
03435      */
03436     JS_ASSERT(obj);
03437 
03438     /*
03439      * Property not found.  Give a strict warning if binding an undeclared
03440      * top-level variable.
03441      */
03442     if (JS_HAS_STRICT_OPTION(cx)) {
03443         JSString *str = JSVAL_TO_STRING(ID_TO_VALUE(id));
03444         if (!JS_ReportErrorFlagsAndNumber(cx,
03445                                           JSREPORT_WARNING | JSREPORT_STRICT,
03446                                           js_GetErrorMessage, NULL,
03447                                           JSMSG_UNDECLARED_VAR,
03448                                           JS_GetStringBytes(str))) {
03449             return NULL;
03450         }
03451     }
03452     return obj;
03453 }
03454 
03455 JSBool
03456 js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj,
03457              JSScopeProperty *sprop, jsval *vp)
03458 {
03459     JSScope *scope;
03460     uint32 slot;
03461     int32 sample;
03462     JSTempValueRooter tvr;
03463     JSBool ok;
03464 
03465     JS_ASSERT(OBJ_IS_NATIVE(pobj));
03466     JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj));
03467     scope = OBJ_SCOPE(pobj);
03468     JS_ASSERT(scope->object == pobj);
03469 
03470     slot = sprop->slot;
03471     *vp = (slot != SPROP_INVALID_SLOT)
03472           ? LOCKED_OBJ_GET_SLOT(pobj, slot)
03473           : JSVAL_VOID;
03474     if (SPROP_HAS_STUB_GETTER(sprop))
03475         return JS_TRUE;
03476 
03477     sample = cx->runtime->propertyRemovals;
03478     JS_UNLOCK_SCOPE(cx, scope);
03479     JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);
03480     ok = SPROP_GET(cx, sprop, obj, pobj, vp);
03481     JS_POP_TEMP_ROOT(cx, &tvr);
03482     if (!ok)
03483         return JS_FALSE;
03484 
03485     JS_LOCK_SCOPE(cx, scope);
03486     JS_ASSERT(scope->object == pobj);
03487     if (SLOT_IN_SCOPE(slot, scope) &&
03488         (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
03489          SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) {
03490         LOCKED_OBJ_SET_SLOT(pobj, slot, *vp);
03491     }
03492 
03493     return JS_TRUE;
03494 }
03495 
03496 JSBool
03497 js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp)
03498 {
03499     JSScope *scope;
03500     uint32 slot;
03501     jsval pval;
03502     int32 sample;
03503     JSTempValueRooter tvr;
03504     JSBool ok;
03505 
03506     JS_ASSERT(OBJ_IS_NATIVE(obj));
03507     JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj));
03508     scope = OBJ_SCOPE(obj);
03509     JS_ASSERT(scope->object == obj);
03510 
03511     slot = sprop->slot;
03512     if (slot != SPROP_INVALID_SLOT) {
03513         pval = LOCKED_OBJ_GET_SLOT(obj, slot);
03514 
03515         /* If sprop has a stub setter, keep scope locked and just store *vp. */
03516         if (SPROP_HAS_STUB_SETTER(sprop))
03517             goto set_slot;
03518     } else {
03519         /*
03520          * Allow API consumers to create shared properties with stub setters.
03521          * Such properties lack value storage, so setting them is like writing
03522          * to /dev/null.
03523          */
03524         if (SPROP_HAS_STUB_SETTER(sprop))
03525             return JS_TRUE;
03526         pval = JSVAL_VOID;
03527     }
03528 
03529     sample = cx->runtime->propertyRemovals;
03530     JS_UNLOCK_SCOPE(cx, scope);
03531     JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);
03532     ok = SPROP_SET(cx, sprop, obj, obj, vp);
03533     JS_POP_TEMP_ROOT(cx, &tvr);
03534     if (!ok)
03535         return JS_FALSE;
03536 
03537     JS_LOCK_SCOPE(cx, scope);
03538     JS_ASSERT(scope->object == obj);
03539     if (SLOT_IN_SCOPE(slot, scope) &&
03540         (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
03541          SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) {
03542   set_slot:
03543         GC_POKE(cx, pval);
03544         LOCKED_OBJ_SET_SLOT(obj, slot, *vp);
03545     }
03546 
03547     return JS_TRUE;
03548 }
03549 
03550 JSBool
03551 js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
03552 {
03553     JSObject *obj2;
03554     JSProperty *prop;
03555     JSScopeProperty *sprop;
03556 
03557     /*
03558      * Handle old bug that took empty string as zero index.  Also convert
03559      * string indices to integers if appropriate.
03560      */
03561     CHECK_FOR_STRING_INDEX(id);
03562 
03563     if (!js_LookupProperty(cx, obj, id, &obj2, &prop))
03564         return JS_FALSE;
03565     if (!prop) {
03566         jsbytecode *pc;
03567 
03568         *vp = JSVAL_VOID;
03569 
03570         if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp))
03571             return JS_FALSE;
03572 
03573         /*
03574          * Give a strict warning if foo.bar is evaluated by a script for an
03575          * object foo with no property named 'bar'.
03576          */
03577         if (JSVAL_IS_VOID(*vp) && cx->fp && (pc = cx->fp->pc)) {
03578             JSOp op;
03579             uintN flags;
03580             JSString *str;
03581 
03582             op = *pc;
03583             if (op == JSOP_GETXPROP || op == JSOP_GETXELEM) {
03584                 flags = JSREPORT_ERROR;
03585             } else {
03586                 if (!JS_HAS_STRICT_OPTION(cx) ||
03587                     (op != JSOP_GETPROP && op != JSOP_GETELEM)) {
03588                     return JS_TRUE;
03589                 }
03590 
03591                 /*
03592                  * XXX do not warn about missing __iterator__ as the function
03593                  * may be called from JS_GetMethodById. See bug 355145.
03594                  */
03595                 if (id == ATOM_TO_JSID(cx->runtime->atomState.iteratorAtom))
03596                     return JS_TRUE;
03597 
03598                 /* Kludge to allow (typeof foo == "undefined") tests. */
03599                 JS_ASSERT(cx->fp->script);
03600                 pc += js_CodeSpec[op].length;
03601                 if (Detecting(cx, pc))
03602                     return JS_TRUE;
03603 
03604                 flags = JSREPORT_WARNING | JSREPORT_STRICT;
03605             }
03606 
03607             /* Ok, bad undefined property reference: whine about it. */
03608             str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
03609                                              ID_TO_VALUE(id), NULL);
03610             if (!str ||
03611                 !JS_ReportErrorFlagsAndNumber(cx, flags,
03612                                               js_GetErrorMessage, NULL,
03613                                               JSMSG_UNDEFINED_PROP,
03614                                               JS_GetStringBytes(str))) {
03615                 return JS_FALSE;
03616             }
03617         }
03618         return JS_TRUE;
03619     }
03620 
03621     if (!OBJ_IS_NATIVE(obj2)) {
03622         OBJ_DROP_PROPERTY(cx, obj2, prop);
03623         return OBJ_GET_PROPERTY(cx, obj2, id, vp);
03624     }
03625 
03626     sprop = (JSScopeProperty *) prop;
03627     if (!js_NativeGet(cx, obj, obj2, sprop, vp))
03628         return JS_FALSE;
03629 
03630     PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj2, id, sprop);
03631     JS_UNLOCK_OBJ(cx, obj2);
03632     return JS_TRUE;
03633 }
03634 
03635 JSBool
03636 js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
03637 {
03638     JSObject *pobj;
03639     JSProperty *prop;
03640     JSScopeProperty *sprop;
03641     JSScope *scope;
03642     uintN attrs, flags;
03643     intN shortid;
03644     JSClass *clasp;
03645     JSPropertyOp getter, setter;
03646 
03647     /*
03648      * Handle old bug that took empty string as zero index.  Also convert
03649      * string indices to integers if appropriate.
03650      */
03651     CHECK_FOR_STRING_INDEX(id);
03652 
03653     if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
03654         return JS_FALSE;
03655 
03656     if (prop && !OBJ_IS_NATIVE(pobj)) {
03657         OBJ_DROP_PROPERTY(cx, pobj, prop);
03658         prop = NULL;
03659     }
03660     sprop = (JSScopeProperty *) prop;
03661 
03662     /*
03663      * Now either sprop is null, meaning id was not found in obj or one of its
03664      * prototypes; or sprop is non-null, meaning id was found in pobj's scope.
03665      * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop
03666      * is held: we must OBJ_DROP_PROPERTY or JS_UNLOCK_SCOPE before we return
03667      * (the two are equivalent for native objects, but we use JS_UNLOCK_SCOPE
03668      * because it is cheaper).
03669      */
03670     attrs = JSPROP_ENUMERATE;
03671     flags = 0;
03672     shortid = 0;
03673     clasp = OBJ_GET_CLASS(cx, obj);
03674     getter = clasp->getProperty;
03675     setter = clasp->setProperty;
03676 
03677     if (sprop) {
03678         /*
03679          * Set scope for use below.  It was locked by js_LookupProperty, and
03680          * we know pobj owns it (i.e., scope->object == pobj).  Therefore we
03681          * optimize JS_UNLOCK_OBJ(cx, pobj) into JS_UNLOCK_SCOPE(cx, scope).
03682          */
03683         scope = OBJ_SCOPE(pobj);
03684 
03685         attrs = sprop->attrs;
03686         if ((attrs & JSPROP_READONLY) ||
03687             (SCOPE_IS_SEALED(scope) && pobj == obj)) {
03688             JS_UNLOCK_SCOPE(cx, scope);
03689 
03690             /*
03691              * Here, we'll either return true or goto read_only_error, which
03692              * reports a strict warning or throws an error.  So we redefine
03693              * the |flags| local variable to be JSREPORT_* flags to pass to
03694              * JS_ReportErrorFlagsAndNumberUC at label read_only_error.  We
03695              * must likewise re-task flags further below for the other 'goto
03696              * read_only_error;' case.
03697              */
03698             flags = JSREPORT_ERROR;
03699             if ((attrs & JSPROP_READONLY) && JS_VERSION_IS_ECMA(cx)) {
03700                 if (!JS_HAS_STRICT_OPTION(cx)) {
03701                     /* Just return true per ECMA if not in strict mode. */
03702                     return JS_TRUE;
03703                 }
03704 
03705                 /* Strict mode: report a read-only strict warning. */
03706                 flags = JSREPORT_STRICT | JSREPORT_WARNING;
03707             }
03708             goto read_only_error;
03709         }
03710 
03711         if (pobj != obj) {
03712             /*
03713              * We found id in a prototype object: prepare to share or shadow.
03714              * NB: Thanks to the immutable, garbage-collected property tree
03715              * maintained by jsscope.c in cx->runtime, we needn't worry about
03716              * sprop going away behind our back after we've unlocked scope.
03717              */
03718             JS_UNLOCK_SCOPE(cx, scope);
03719 
03720             /* Don't clone a shared prototype property. */
03721             if (attrs & JSPROP_SHARED) {
03722                 if (SPROP_HAS_STUB_SETTER(sprop) &&
03723                     !(sprop->attrs & JSPROP_GETTER)) {
03724                     return JS_TRUE;
03725                 }
03726                 return SPROP_SET(cx, sprop, obj, pobj, vp);
03727             }
03728 
03729             /* Restore attrs to the ECMA default for new properties. */
03730             attrs = JSPROP_ENUMERATE;
03731 
03732             /*
03733              * Preserve the shortid, getter, and setter when shadowing any
03734              * property that has a shortid.  An old API convention requires
03735              * that the property's getter and setter functions receive the
03736              * shortid, not id, when they are called on the shadow we are
03737              * about to create in obj's scope.
03738              */
03739             if (sprop->flags & SPROP_HAS_SHORTID) {
03740                 flags = SPROP_HAS_SHORTID;
03741                 shortid = sprop->shortid;
03742                 getter = sprop->getter;
03743                 setter = sprop->setter;
03744             }
03745 
03746             /*
03747              * Forget we found the proto-property now that we've copied any
03748              * needed member values.
03749              */
03750             sprop = NULL;
03751         }
03752 #ifdef __GNUC__ /* suppress bogus gcc warnings */
03753     } else {
03754         scope = NULL;
03755 #endif
03756     }
03757 
03758     if (!sprop) {
03759         if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj) {
03760             flags = JSREPORT_ERROR;
03761             goto read_only_error;
03762         }
03763 
03764         /* Find or make a property descriptor with the right heritage. */
03765         JS_LOCK_OBJ(cx, obj);
03766         scope = js_GetMutableScope(cx, obj);
03767         if (!scope) {
03768             JS_UNLOCK_OBJ(cx, obj);
03769             return JS_FALSE;
03770         }
03771         if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
03772             attrs |= JSPROP_SHARED;
03773         sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
03774                                     SPROP_INVALID_SLOT, attrs, flags, shortid);
03775         if (!sprop) {
03776             JS_UNLOCK_SCOPE(cx, scope);
03777             return JS_FALSE;
03778         }
03779 
03780         /*
03781          * Initialize the new property value (passed to setter) to undefined.
03782          * Note that we store before calling addProperty, to match the order
03783          * in js_DefineNativeProperty.
03784          */
03785         if (SPROP_HAS_VALID_SLOT(sprop, scope))
03786             LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID);
03787 
03788         /* XXXbe called with obj locked */
03789         ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, vp,
03790                             js_RemoveScopeProperty(cx, scope, id);
03791                             JS_UNLOCK_SCOPE(cx, scope);
03792                             return JS_FALSE);
03793 
03794         PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
03795     }
03796 
03797     if (!js_NativeSet(cx, obj, sprop, vp))
03798         return JS_FALSE;
03799     JS_UNLOCK_SCOPE(cx, scope);
03800     return JS_TRUE;
03801 
03802   read_only_error: {
03803     JSString *str = js_DecompileValueGenerator(cx,
03804                                                JSDVG_IGNORE_STACK,
03805                                                ID_TO_VALUE(id),
03806                                                NULL);
03807     if (!str)
03808         return JS_FALSE;
03809     return JS_ReportErrorFlagsAndNumberUC(cx, flags, js_GetErrorMessage,
03810                                           NULL, JSMSG_READ_ONLY,
03811                                           JS_GetStringChars(str));
03812   }
03813 }
03814 
03815 JSBool
03816 js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
03817                  uintN *attrsp)
03818 {
03819     JSBool noprop, ok;
03820     JSScopeProperty *sprop;
03821 
03822     noprop = !prop;
03823     if (noprop) {
03824         if (!js_LookupProperty(cx, obj, id, &obj, &prop))
03825             return JS_FALSE;
03826         if (!prop) {
03827             *attrsp = 0;
03828             return JS_TRUE;
03829         }
03830         if (!OBJ_IS_NATIVE(obj)) {
03831             ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp);
03832             OBJ_DROP_PROPERTY(cx, obj, prop);
03833             return ok;
03834         }
03835     }
03836     sprop = (JSScopeProperty *)prop;
03837     *attrsp = sprop->attrs;
03838     if (noprop)
03839         OBJ_DROP_PROPERTY(cx, obj, prop);
03840     return JS_TRUE;
03841 }
03842 
03843 JSBool
03844 js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
03845                  uintN *attrsp)
03846 {
03847     JSBool noprop, ok;
03848     JSScopeProperty *sprop;
03849 
03850     noprop = !prop;
03851     if (noprop) {
03852         if (!js_LookupProperty(cx, obj, id, &obj, &prop))
03853             return JS_FALSE;
03854         if (!prop)
03855             return JS_TRUE;
03856         if (!OBJ_IS_NATIVE(obj)) {
03857             ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, attrsp);
03858             OBJ_DROP_PROPERTY(cx, obj, prop);
03859             return ok;
03860         }
03861     }
03862     sprop = (JSScopeProperty *)prop;
03863     sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, *attrsp, 0,
03864                                          sprop->getter, sprop->setter);
03865     if (noprop)
03866         OBJ_DROP_PROPERTY(cx, obj, prop);
03867     return (sprop != NULL);
03868 }
03869 
03870 JSBool
03871 js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
03872 {
03873     JSObject *proto;
03874     JSProperty *prop;
03875     JSScopeProperty *sprop;
03876     JSString *str;
03877     JSScope *scope;
03878     JSBool ok;
03879 
03880     *rval = JSVAL_TRUE;
03881 
03882     /*
03883      * Handle old bug that took empty string as zero index.  Also convert
03884      * string indices to integers if appropriate.
03885      */
03886     CHECK_FOR_STRING_INDEX(id);
03887 
03888     if (!js_LookupProperty(cx, obj, id, &proto, &prop))
03889         return JS_FALSE;
03890     if (!prop || proto != obj) {
03891         /*
03892          * If the property was found in a native prototype, check whether it's
03893          * shared and permanent.  Such a property stands for direct properties
03894          * in all delegating objects, matching ECMA semantics without bloating
03895          * each delegating object.
03896          */
03897         if (prop) {
03898             if (OBJ_IS_NATIVE(proto)) {
03899                 sprop = (JSScopeProperty *)prop;
03900                 if (SPROP_IS_SHARED_PERMANENT(sprop))
03901                     *rval = JSVAL_FALSE;
03902             }
03903             OBJ_DROP_PROPERTY(cx, proto, prop);
03904             if (*rval == JSVAL_FALSE)
03905                 return JS_TRUE;
03906         }
03907 
03908         /*
03909          * If no property, or the property comes unshared or impermanent from
03910          * a prototype, call the class's delProperty hook, passing rval as the
03911          * result parameter.
03912          */
03913         return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, ID_TO_VALUE(id),
03914                                                    rval);
03915     }
03916 
03917     sprop = (JSScopeProperty *)prop;
03918     if (sprop->attrs & JSPROP_PERMANENT) {
03919         OBJ_DROP_PROPERTY(cx, obj, prop);
03920         if (JS_VERSION_IS_ECMA(cx)) {
03921             *rval = JSVAL_FALSE;
03922             return JS_TRUE;
03923         }
03924         str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
03925                                          ID_TO_VALUE(id), NULL);
03926         if (str) {
03927             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
03928                                  JSMSG_PERMANENT, JS_GetStringBytes(str));
03929         }
03930         return JS_FALSE;
03931     }
03932 
03933     /* XXXbe called with obj locked */
03934     if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, SPROP_USERID(sprop),
03935                                                 rval)) {
03936         OBJ_DROP_PROPERTY(cx, obj, prop);
03937         return JS_FALSE;
03938     }
03939 
03940     scope = OBJ_SCOPE(obj);
03941     if (SPROP_HAS_VALID_SLOT(sprop, scope))
03942         GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot));
03943 
03944     PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, NULL);
03945     ok = js_RemoveScopeProperty(cx, scope, id);
03946     OBJ_DROP_PROPERTY(cx, obj, prop);
03947     return ok;
03948 }
03949 
03950 JSBool
03951 js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
03952 {
03953     jsval v, save;
03954     JSString *str;
03955 
03956     v = save = OBJECT_TO_JSVAL(obj);
03957     switch (hint) {
03958       case JSTYPE_STRING:
03959         /*
03960          * Propagate the exception if js_TryMethod finds an appropriate
03961          * method, and calling that method returned failure.
03962          */
03963         if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL,
03964                           &v)) {
03965             return JS_FALSE;
03966         }
03967 
03968         if (!JSVAL_IS_PRIMITIVE(v)) {
03969             if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
03970                 return JS_FALSE;
03971         }
03972         break;
03973 
03974       default:
03975         if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
03976             return JS_FALSE;
03977         if (!JSVAL_IS_PRIMITIVE(v)) {
03978             JSType type = JS_TypeOfValue(cx, v);
03979             if (type == hint ||
03980                 (type == JSTYPE_FUNCTION && hint == JSTYPE_OBJECT)) {
03981                 goto out;
03982             }
03983             if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0,
03984                               NULL, &v)) {
03985                 return JS_FALSE;
03986             }
03987         }
03988         break;
03989     }
03990     if (!JSVAL_IS_PRIMITIVE(v)) {
03991         /* Avoid recursive death through js_DecompileValueGenerator. */
03992         if (hint == JSTYPE_STRING) {
03993             str = JS_InternString(cx, OBJ_GET_CLASS(cx, obj)->name);
03994             if (!str)
03995                 return JS_FALSE;
03996         } else {
03997             str = NULL;
03998         }
03999         *vp = OBJECT_TO_JSVAL(obj);
04000         str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, save, str);
04001         if (str) {
04002             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
04003                                  JSMSG_CANT_CONVERT_TO,
04004                                  JS_GetStringBytes(str),
04005                                  (hint == JSTYPE_VOID)
04006                                  ? "primitive type"
04007                                  : js_type_strs[hint]);
04008         }
04009         return JS_FALSE;
04010     }
04011 out:
04012     *vp = v;
04013     return JS_TRUE;
04014 }
04015 
04016 JSIdArray *
04017 js_NewIdArray(JSContext *cx, jsint length)
04018 {
04019     JSIdArray *ida;
04020 
04021     ida = (JSIdArray *)
04022           JS_malloc(cx, sizeof(JSIdArray) + (length-1) * sizeof(jsval));
04023     if (ida)
04024         ida->length = length;
04025     return ida;
04026 }
04027 
04028 JSIdArray *
04029 js_SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length)
04030 {
04031     JSIdArray *rida;
04032 
04033     rida = (JSIdArray *)
04034            JS_realloc(cx, ida, sizeof(JSIdArray) + (length-1) * sizeof(jsval));
04035     if (!rida)
04036         JS_DestroyIdArray(cx, ida);
04037     else
04038         rida->length = length;
04039     return rida;
04040 }
04041 
04042 /* Private type used to iterate over all properties of a native JS object */
04043 struct JSNativeIteratorState {
04044     jsint                   next_index; /* index into jsid array */
04045     JSIdArray               *ida;       /* all property ids in enumeration */
04046     JSNativeIteratorState   *next;      /* double-linked list support */
04047     JSNativeIteratorState   **prevp;
04048 };
04049 
04050 /*
04051  * This function is used to enumerate the properties of native JSObjects
04052  * and those host objects that do not define a JSNewEnumerateOp-style iterator
04053  * function.
04054  */
04055 JSBool
04056 js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
04057              jsval *statep, jsid *idp)
04058 {
04059     JSRuntime *rt;
04060     JSObject *proto;
04061     JSClass *clasp;
04062     JSEnumerateOp enumerate;
04063     JSScopeProperty *sprop, *lastProp;
04064     jsint i, length;
04065     JSScope *scope;
04066     JSIdArray *ida;
04067     JSNativeIteratorState *state;
04068 
04069     rt = cx->runtime;
04070     clasp = OBJ_GET_CLASS(cx, obj);
04071     enumerate = clasp->enumerate;
04072     if (clasp->flags & JSCLASS_NEW_ENUMERATE)
04073         return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
04074 
04075     switch (enum_op) {
04076       case JSENUMERATE_INIT:
04077         if (!enumerate(cx, obj))
04078             return JS_FALSE;
04079         length = 0;
04080 
04081         /*
04082          * The set of all property ids is pre-computed when the iterator
04083          * is initialized so as to avoid problems with properties being
04084          * deleted during the iteration.
04085          */
04086         JS_LOCK_OBJ(cx, obj);
04087         scope = OBJ_SCOPE(obj);
04088 
04089         /*
04090          * If this object shares a scope with its prototype, don't enumerate
04091          * its properties.  Otherwise they will be enumerated a second time
04092          * when the prototype object is enumerated.
04093          */
04094         proto = OBJ_GET_PROTO(cx, obj);
04095         if (proto && scope == OBJ_SCOPE(proto)) {
04096             ida = js_NewIdArray(cx, 0);
04097             if (!ida) {
04098                 JS_UNLOCK_OBJ(cx, obj);
04099                 return JS_FALSE;
04100             }
04101         } else {
04102             /* Object has a private scope; Enumerate all props in scope. */
04103             for (sprop = lastProp = SCOPE_LAST_PROP(scope); sprop;
04104                  sprop = sprop->parent) {
04105                 if ((
04106 #ifdef DUMP_CALL_TABLE
04107                      (cx->options & JSOPTION_LOGCALL_TOSOURCE) ||
04108 #endif
04109                      (sprop->attrs & JSPROP_ENUMERATE)) &&
04110                     !(sprop->flags & SPROP_IS_ALIAS) &&
04111                     (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
04112                      SCOPE_HAS_PROPERTY(scope, sprop))) {
04113                     length++;
04114                 }
04115             }
04116             ida = js_NewIdArray(cx, length);
04117             if (!ida) {
04118                 JS_UNLOCK_OBJ(cx, obj);
04119                 return JS_FALSE;
04120             }
04121             i = length;
04122             for (sprop = lastProp; sprop; sprop = sprop->parent) {
04123                 if ((
04124 #ifdef DUMP_CALL_TABLE
04125                      (cx->options & JSOPTION_LOGCALL_TOSOURCE) ||
04126 #endif
04127                      (sprop->attrs & JSPROP_ENUMERATE)) &&
04128                     !(sprop->flags & SPROP_IS_ALIAS) &&
04129                     (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
04130                      SCOPE_HAS_PROPERTY(scope, sprop))) {
04131                     JS_ASSERT(i > 0);
04132                     ida->vector[--i] = sprop->id;
04133                 }
04134             }
04135         }
04136         JS_UNLOCK_OBJ(cx, obj);
04137 
04138         state = (JSNativeIteratorState *)
04139             JS_malloc(cx, sizeof(JSNativeIteratorState));
04140         if (!state) {
04141             JS_DestroyIdArray(cx, ida);
04142             return JS_FALSE;
04143         }
04144         state->ida = ida;
04145         state->next_index = 0;
04146 
04147         JS_LOCK_RUNTIME(rt);
04148         state->next = rt->nativeIteratorStates;
04149         if (state->next)
04150             state->next->prevp = &state->next;
04151         state->prevp = &rt->nativeIteratorStates;
04152         *state->prevp = state;
04153         JS_UNLOCK_RUNTIME(rt);
04154 
04155         *statep = PRIVATE_TO_JSVAL(state);
04156         if (idp)
04157             *idp = INT_TO_JSVAL(length);
04158         break;
04159 
04160       case JSENUMERATE_NEXT:
04161         state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
04162         ida = state->ida;
04163         length = ida->length;
04164         if (state->next_index != length) {
04165             *idp = ida->vector[state->next_index++];
04166             break;
04167         }
04168         /* FALL THROUGH */
04169 
04170       case JSENUMERATE_DESTROY:
04171         state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
04172 
04173         JS_LOCK_RUNTIME(rt);
04174         JS_ASSERT(rt->nativeIteratorStates);
04175         JS_ASSERT(*state->prevp == state);
04176         if (state->next) {
04177             JS_ASSERT(state->next->prevp == &state->next);
04178             state->next->prevp = state->prevp;
04179         }
04180         *state->prevp = state->next;
04181         JS_UNLOCK_RUNTIME(rt);
04182 
04183         JS_DestroyIdArray(cx, state->ida);
04184         JS_free(cx, state);
04185         *statep = JSVAL_NULL;
04186         break;
04187     }
04188     return JS_TRUE;
04189 }
04190 
04191 void
04192 js_MarkNativeIteratorStates(JSContext *cx)
04193 {
04194     JSNativeIteratorState *state;
04195     jsid *cursor, *end, id;
04196 
04197     state = cx->runtime->nativeIteratorStates;
04198     if (!state)
04199         return;
04200 
04201     do {
04202         JS_ASSERT(*state->prevp == state);
04203         cursor = state->ida->vector;
04204         end = cursor + state->ida->length;
04205         for (; cursor != end; ++cursor) {
04206             id = *cursor;
04207             MARK_ID(cx, id);
04208         }
04209     } while ((state = state->next) != NULL);
04210 }
04211 
04212 JSBool
04213 js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
04214                jsval *vp, uintN *attrsp)
04215 {
04216     JSBool writing;
04217     JSObject *pobj;
04218     JSProperty *prop;
04219     JSClass *clasp;
04220     JSScopeProperty *sprop;
04221     JSCheckAccessOp check;
04222 
04223     writing = (mode & JSACC_WRITE) != 0;
04224     switch (mode & JSACC_TYPEMASK) {
04225       case JSACC_PROTO:
04226         pobj = obj;
04227         if (!writing)
04228             *vp = OBJ_GET_SLOT(cx, obj, JSSLOT_PROTO);
04229         *attrsp = JSPROP_PERMANENT;
04230         break;
04231 
04232       case JSACC_PARENT:
04233         JS_ASSERT(!writing);
04234         pobj = obj;
04235         *vp = OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT);
04236         *attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
04237         break;
04238 
04239       default:
04240         if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
04241             return JS_FALSE;
04242         if (!prop) {
04243             if (!writing)
04244                 *vp = JSVAL_VOID;
04245             *attrsp = 0;
04246             clasp = OBJ_GET_CLASS(cx, obj);
04247             return !clasp->checkAccess ||
04248                    clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp);
04249         }
04250         if (!OBJ_IS_NATIVE(pobj)) {
04251             OBJ_DROP_PROPERTY(cx, pobj, prop);
04252             return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp);
04253         }
04254 
04255         sprop = (JSScopeProperty *)prop;
04256         *attrsp = sprop->attrs;
04257         if (!writing) {
04258             *vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)))
04259                   ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
04260                   : JSVAL_VOID;
04261         }
04262         OBJ_DROP_PROPERTY(cx, pobj, prop);
04263     }
04264 
04265     /*
04266      * If obj's class has a stub (null) checkAccess hook, use the per-runtime
04267      * checkObjectAccess callback, if configured.
04268      *
04269      * We don't want to require all classes to supply a checkAccess hook; we
04270      * need that hook only for certain classes used when precompiling scripts
04271      * and functions ("brutal sharing").  But for general safety of built-in
04272      * magic properties such as __proto__ and __parent__, we route all access
04273      * checks, even for classes that stub out checkAccess, through the global
04274      * checkObjectAccess hook.  This covers precompilation-based sharing and
04275      * (possibly unintended) runtime sharing across trust boundaries.
04276      */
04277     clasp = OBJ_GET_CLASS(cx, pobj);
04278     check = clasp->checkAccess;
04279     if (!check)
04280         check = cx->runtime->checkObjectAccess;
04281     return !check || check(cx, pobj, ID_TO_VALUE(id), mode, vp);
04282 }
04283 
04284 #ifdef JS_THREADSAFE
04285 void
04286 js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
04287 {
04288     JS_UNLOCK_OBJ(cx, obj);
04289 }
04290 #endif
04291 
04292 static void
04293 ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
04294 {
04295     /*
04296      * The decompiler may need to access the args of the function in
04297      * progress rather than the one we had hoped to call.
04298      * So we switch the cx->fp to the frame below us. We stick the
04299      * current frame in the dormantFrameChain to protect it from gc.
04300      */
04301 
04302     JSStackFrame *fp = cx->fp;
04303     if (fp->down) {
04304         JS_ASSERT(!fp->dormantNext);
04305         fp->dormantNext = cx->dormantFrameChain;
04306         cx->dormantFrameChain = fp;
04307         cx->fp = fp->down;
04308     }
04309 
04310     js_ReportIsNotFunction(cx, vp, flags);
04311 
04312     if (fp->down) {
04313         JS_ASSERT(cx->dormantFrameChain == fp);
04314         cx->dormantFrameChain = fp->dormantNext;
04315         fp->dormantNext = NULL;
04316         cx->fp = fp;
04317     }
04318 }
04319 
04320 #ifdef NARCISSUS
04321 static JSBool
04322 GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval)
04323 {
04324     JSObject *tmp;
04325     jsval xcval;
04326 
04327     while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
04328         obj = tmp;
04329     if (!OBJ_GET_PROPERTY(cx, obj,
04330                           ATOM_TO_JSID(cx->runtime->atomState
04331                                        .ExecutionContextAtom),
04332                           &xcval)) {
04333         return JS_FALSE;
04334     }
04335     if (JSVAL_IS_PRIMITIVE(xcval)) {
04336         JS_ReportError(cx, "invalid ExecutionContext in global object");
04337         return JS_FALSE;
04338     }
04339     if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(xcval),
04340                           ATOM_TO_JSID(cx->runtime->atomState.currentAtom),
04341                           rval)) {
04342         return JS_FALSE;
04343     }
04344     return JS_TRUE;
04345 }
04346 #endif
04347 
04348 JSBool
04349 js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
04350 {
04351     JSClass *clasp;
04352 
04353     clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
04354     if (!clasp->call) {
04355 #ifdef NARCISSUS
04356         JSObject *callee, *args;
04357         jsval fval, nargv[3];
04358         JSBool ok;
04359 
04360         callee = JSVAL_TO_OBJECT(argv[-2]);
04361         if (!OBJ_GET_PROPERTY(cx, callee,
04362                               ATOM_TO_JSID(cx->runtime->atomState.callAtom),
04363                               &fval)) {
04364             return JS_FALSE;
04365         }
04366         if (VALUE_IS_FUNCTION(cx, fval)) {
04367             if (!GetCurrentExecutionContext(cx, obj, &nargv[2]))
04368                 return JS_FALSE;
04369             args = js_GetArgsObject(cx, cx->fp);
04370             if (!args)
04371                 return JS_FALSE;
04372             nargv[0] = OBJECT_TO_JSVAL(obj);
04373             nargv[1] = OBJECT_TO_JSVAL(args);
04374             return js_InternalCall(cx, callee, fval, 3, nargv, rval);
04375         }
04376         if (JSVAL_IS_OBJECT(fval) && JSVAL_TO_OBJECT(fval) != callee) {
04377             argv[-2] = fval;
04378             ok = js_Call(cx, obj, argc, argv, rval);
04379             argv[-2] = OBJECT_TO_JSVAL(callee);
04380             return ok;
04381         }
04382 #endif
04383         ReportIsNotFunction(cx, &argv[-2], cx->fp->flags & JSFRAME_ITERATOR);
04384         return JS_FALSE;
04385     }
04386     return clasp->call(cx, obj, argc, argv, rval);
04387 }
04388 
04389 JSBool
04390 js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
04391              jsval *rval)
04392 {
04393     JSClass *clasp;
04394 
04395     clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
04396     if (!clasp->construct) {
04397 #ifdef NARCISSUS
04398         JSObject *callee, *args;
04399         jsval cval, nargv[2];
04400         JSBool ok;
04401 
04402         callee = JSVAL_TO_OBJECT(argv[-2]);
04403         if (!OBJ_GET_PROPERTY(cx, callee,
04404                               ATOM_TO_JSID(cx->runtime->atomState
04405                                            .constructAtom),
04406                               &cval)) {
04407             return JS_FALSE;
04408         }
04409         if (VALUE_IS_FUNCTION(cx, cval)) {
04410             if (!GetCurrentExecutionContext(cx, obj, &nargv[1]))
04411                 return JS_FALSE;
04412             args = js_GetArgsObject(cx, cx->fp);
04413             if (!args)
04414                 return JS_FALSE;
04415             nargv[0] = OBJECT_TO_JSVAL(args);
04416             return js_InternalCall(cx, callee, cval, 2, nargv, rval);
04417         }
04418         if (JSVAL_IS_OBJECT(cval) && JSVAL_TO_OBJECT(cval) != callee) {
04419             argv[-2] = cval;
04420             ok = js_Call(cx, obj, argc, argv, rval);
04421             argv[-2] = OBJECT_TO_JSVAL(callee);
04422             return ok;
04423         }
04424 #endif
04425         ReportIsNotFunction(cx, &argv[-2], JSV2F_CONSTRUCT);
04426         return JS_FALSE;
04427     }
04428     return clasp->construct(cx, obj, argc, argv, rval);
04429 }
04430 
04431 JSBool
04432 js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
04433 {
04434     JSClass *clasp;
04435     JSString *str;
04436 
04437     clasp = OBJ_GET_CLASS(cx, obj);
04438     if (clasp->hasInstance)
04439         return clasp->hasInstance(cx, obj, v, bp);
04440 #ifdef NARCISSUS
04441     {
04442         jsval fval, rval;
04443 
04444         if (!OBJ_GET_PROPERTY(cx, obj,
04445                               ATOM_TO_JSID(cx->runtime->atomState
04446                                            .hasInstanceAtom),
04447                               &fval)) {
04448             return JS_FALSE;
04449         }
04450         if (VALUE_IS_FUNCTION(cx, fval)) {
04451             return js_InternalCall(cx, obj, fval, 1, &v, &rval) &&
04452                    js_ValueToBoolean(cx, rval, bp);
04453         }
04454     }
04455 #endif
04456     str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
04457                                      OBJECT_TO_JSVAL(obj), NULL);
04458     if (str) {
04459         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
04460                              JSMSG_BAD_INSTANCEOF_RHS,
04461                              JS_GetStringBytes(str));
04462     }
04463     return JS_FALSE;
04464 }
04465 
04466 JSBool
04467 js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
04468 {
04469     JSObject *obj2;
04470 
04471     *bp = JS_FALSE;
04472     if (JSVAL_IS_PRIMITIVE(v))
04473         return JS_TRUE;
04474     obj2 = JSVAL_TO_OBJECT(v);
04475     while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL) {
04476         if (obj2 == obj) {
04477             *bp = JS_TRUE;
04478             break;
04479         }
04480     }
04481     return JS_TRUE;
04482 }
04483 
04484 JSBool
04485 js_GetClassPrototype(JSContext *cx, JSObject *scope, jsid id,
04486                      JSObject **protop)
04487 {
04488     jsval v;
04489     JSObject *ctor;
04490 
04491     if (!js_FindClassObject(cx, scope, id, &v))
04492         return JS_FALSE;
04493     if (VALUE_IS_FUNCTION(cx, v)) {
04494         ctor = JSVAL_TO_OBJECT(v);
04495         if (!OBJ_GET_PROPERTY(cx, ctor,
04496                               ATOM_TO_JSID(cx->runtime->atomState
04497                                            .classPrototypeAtom),
04498                               &v)) {
04499             return JS_FALSE;
04500         }
04501         if (!JSVAL_IS_PRIMITIVE(v)) {
04502             /*
04503              * Set the newborn root in case v is otherwise unreferenced.
04504              * It's ok to overwrite newborn roots here, since the getter
04505              * called just above could have.  Unlike the common GC rooting
04506              * model, our callers do not have to protect protop thanks to
04507              * this newborn root, since they all immediately create a new
04508              * instance that delegates to this object, or just query the
04509              * prototype for its class.
04510              */
04511             cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(v);
04512         }
04513     }
04514     *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;
04515     return JS_TRUE;
04516 }
04517 
04518 /*
04519  * For shared precompilation of function objects, we support cloning on entry
04520  * to an execution context in which the function declaration or expression
04521  * should be processed as if it were not precompiled, where the precompiled
04522  * function's scope chain does not match the execution context's.  The cloned
04523  * function object carries its execution-context scope in its parent slot; it
04524  * links to the precompiled function (the "clone-parent") via its proto slot.
04525  *
04526  * Note that this prototype-based delegation leaves an unchecked access path
04527  * from the clone to the clone-parent's 'constructor' property.  If the clone
04528  * lives in a less privileged or shared scope than the clone-parent, this is
04529  * a security hole, a sharing hazard, or both.  Therefore we check all such
04530  * accesses with the following getter/setter pair, which we use when defining
04531  * 'constructor' in f.prototype for all function objects f.
04532  */
04533 static JSBool
04534 CheckCtorGetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
04535 {
04536     JSAtom *atom;
04537     uintN attrs;
04538 
04539     atom = cx->runtime->atomState.constructorAtom;
04540     JS_ASSERT(id == ATOM_KEY(atom));
04541     return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_READ,
04542                             vp, &attrs);
04543 }
04544 
04545 static JSBool
04546 CheckCtorSetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
04547 {
04548     JSAtom *atom;
04549     uintN attrs;
04550 
04551     atom = cx->runtime->atomState.constructorAtom;
04552     JS_ASSERT(id == ATOM_KEY(atom));
04553     return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_WRITE,
04554                             vp, &attrs);
04555 }
04556 
04557 JSBool
04558 js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,
04559                      uintN attrs)
04560 {
04561     /*
04562      * Use the given attributes for the prototype property of the constructor,
04563      * as user-defined constructors have a DontDelete prototype (which may be
04564      * reset), while native or "system" constructors have DontEnum | ReadOnly |
04565      * DontDelete.
04566      */
04567     if (!OBJ_DEFINE_PROPERTY(cx, ctor,
04568                              ATOM_TO_JSID(cx->runtime->atomState
04569                                           .classPrototypeAtom),
04570                              OBJECT_TO_JSVAL(proto),
04571                              JS_PropertyStub, JS_PropertyStub,
04572                              attrs, NULL)) {
04573         return JS_FALSE;
04574     }
04575 
04576     /*
04577      * ECMA says that Object.prototype.constructor, or f.prototype.constructor
04578      * for a user-defined function f, is DontEnum.
04579      */
04580     return OBJ_DEFINE_PROPERTY(cx, proto,
04581                                ATOM_TO_JSID(cx->runtime->atomState
04582                                             .constructorAtom),
04583                                OBJECT_TO_JSVAL(ctor),
04584                                CheckCtorGetAccess, CheckCtorSetAccess,
04585                                0, NULL);
04586 }
04587 
04588 JSBool
04589 js_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
04590 {
04591     JSObject *obj;
04592 
04593     if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
04594         obj = NULL;
04595     } else if (JSVAL_IS_OBJECT(v)) {
04596         obj = JSVAL_TO_OBJECT(v);
04597         if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v))
04598             return JS_FALSE;
04599         if (JSVAL_IS_OBJECT(v))
04600             obj = JSVAL_TO_OBJECT(v);
04601     } else {
04602         if (JSVAL_IS_STRING(v)) {
04603             obj = js_StringToObject(cx, JSVAL_TO_STRING(v));
04604         } else if (JSVAL_IS_INT(v)) {
04605             obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v));
04606         } else if (JSVAL_IS_DOUBLE(v)) {
04607             obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v));
04608         } else {
04609             JS_ASSERT(JSVAL_IS_BOOLEAN(v));
04610             obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v));
04611         }
04612         if (!obj)
04613             return JS_FALSE;
04614     }
04615     *objp = obj;
04616     return JS_TRUE;
04617 }
04618 
04619 JSObject *
04620 js_ValueToNonNullObject(JSContext *cx, jsval v)
04621 {
04622     JSObject *obj;
04623     JSString *str;
04624 
04625     if (!js_ValueToObject(cx, v, &obj))
04626         return NULL;
04627     if (!obj) {
04628         str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
04629         if (str) {
04630             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
04631                                  JSMSG_NO_PROPERTIES, JS_GetStringBytes(str));
04632         }
04633     }
04634     return obj;
04635 }
04636 
04637 JSBool
04638 js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval)
04639 {
04640     jsval argv[1];
04641 
04642     argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]);
04643     return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv,
04644                         rval);
04645 }
04646 
04647 JSBool
04648 js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
04649              uintN argc, jsval *argv, jsval *rval)
04650 {
04651     JSErrorReporter older;
04652     jsid id;
04653     jsval fval;
04654     JSBool ok;
04655     int stackDummy;
04656 
04657     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
04658         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
04659         return JS_FALSE;
04660     }
04661 
04662     /*
04663      * Report failure only if an appropriate method was found, and calling it
04664      * returned failure.  We propagate failure in this case to make exceptions
04665      * behave properly.
04666      */
04667     older = JS_SetErrorReporter(cx, NULL);
04668     id = ATOM_TO_JSID(atom);
04669     fval = JSVAL_VOID;
04670 #if JS_HAS_XML_SUPPORT
04671     if (OBJECT_IS_XML(cx, obj)) {
04672         JSXMLObjectOps *ops;
04673 
04674         ops = (JSXMLObjectOps *) obj->map->ops;
04675         obj = ops->getMethod(cx, obj, id, &fval);
04676         ok = (obj != NULL);
04677     } else
04678 #endif
04679     {
04680         ok = OBJ_GET_PROPERTY(cx, obj, id, &fval);
04681     }
04682     if (!ok)
04683         JS_ClearPendingException(cx);
04684     JS_SetErrorReporter(cx, older);
04685 
04686     return JSVAL_IS_PRIMITIVE(fval) ||
04687            js_InternalCall(cx, obj, fval, argc, argv, rval);
04688 }
04689 
04690 #if JS_HAS_XDR
04691 
04692 JSBool
04693 js_XDRObject(JSXDRState *xdr, JSObject **objp)
04694 {
04695     JSContext *cx;
04696     JSAtom *atom;
04697     JSClass *clasp;
04698     uint32 classId, classDef;
04699     JSProtoKey protoKey;
04700     jsid classKey;
04701     JSObject *proto;
04702 
04703     cx = xdr->cx;
04704     atom = NULL;
04705     if (xdr->mode == JSXDR_ENCODE) {
04706         clasp = OBJ_GET_CLASS(cx, *objp);
04707         classId = JS_XDRFindClassIdByName(xdr, clasp->name);
04708         classDef = !classId;
04709         if (classDef) {
04710             if (!JS_XDRRegisterClass(xdr, clasp, &classId))
04711                 return JS_FALSE;
04712             protoKey = JSCLASS_CACHED_PROTO_KEY(clasp);
04713             if (protoKey != JSProto_Null) {
04714                 classDef |= (protoKey << 1);
04715             } else {
04716                 atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
04717                 if (!atom)
04718                     return JS_FALSE;
04719             }
04720         }
04721     } else {
04722         clasp = NULL;           /* quell GCC overwarning */
04723         classDef = 0;
04724     }
04725 
04726     /*
04727      * XDR a flag word, which could be 0 for a class use, in which case no
04728      * name follows, only the id in xdr's class registry; 1 for a class def,
04729      * in which case the flag word is followed by the class name transferred
04730      * from or to atom; or a value greater than 1, an odd number that when
04731      * divided by two yields the JSProtoKey for class.  In the last case, as
04732      * in the 0 classDef case, no name is transferred via atom.
04733      */
04734     if (!JS_XDRUint32(xdr, &classDef))
04735         return JS_FALSE;
04736     if (classDef == 1 && !js_XDRCStringAtom(xdr, &atom))
04737         return JS_FALSE;
04738 
04739     if (!JS_XDRUint32(xdr, &classId))
04740         return JS_FALSE;
04741 
04742     if (xdr->mode == JSXDR_DECODE) {
04743         if (classDef) {
04744             /* NB: we know that JSProto_Null is 0 here, for backward compat. */
04745             protoKey = classDef >> 1;
04746             classKey = (protoKey != JSProto_Null)
04747                        ? INT_TO_JSID(protoKey)
04748                        : ATOM_TO_JSID(atom);
04749             if (!js_GetClassPrototype(cx, NULL, classKey, &proto))
04750                 return JS_FALSE;
04751             clasp = OBJ_GET_CLASS(cx, proto);
04752             if (!JS_XDRRegisterClass(xdr, clasp, &classId))
04753                 return JS_FALSE;
04754         } else {
04755             clasp = JS_XDRFindClassById(xdr, classId);
04756             if (!clasp) {
04757                 char numBuf[12];
04758                 JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId);
04759                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
04760                                      JSMSG_CANT_FIND_CLASS, numBuf);
04761                 return JS_FALSE;
04762             }
04763         }
04764     }
04765 
04766     if (!clasp->xdrObject) {
04767         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
04768                              JSMSG_CANT_XDR_CLASS, clasp->name);
04769         return JS_FALSE;
04770     }
04771     return clasp->xdrObject(xdr, objp);
04772 }
04773 
04774 #endif /* JS_HAS_XDR */
04775 
04776 #ifdef DEBUG_brendan
04777 
04778 #include <stdio.h>
04779 #include <math.h>
04780 
04781 uint32 js_entry_count_max;
04782 uint32 js_entry_count_sum;
04783 double js_entry_count_sqsum;
04784 uint32 js_entry_count_hist[11];
04785 
04786 static void
04787 MeterEntryCount(uintN count)
04788 {
04789     if (count) {
04790         js_entry_count_sum += count;
04791         js_entry_count_sqsum += (double)count * count;
04792         if (count > js_entry_count_max)
04793             js_entry_count_max = count;
04794     }
04795     js_entry_count_hist[JS_MIN(count, 10)]++;
04796 }
04797 
04798 #define DEBUG_scopemeters
04799 #endif /* DEBUG_brendan */
04800 
04801 #ifdef DEBUG_scopemeters
04802 void
04803 js_DumpScopeMeters(JSRuntime *rt)
04804 {
04805     static FILE *logfp;
04806     if (!logfp)
04807         logfp = fopen("/tmp/scope.stats", "a");
04808 
04809     {
04810         double mean = 0., var = 0., sigma = 0.;
04811         double nscopes = rt->liveScopes;
04812         double nentrys = js_entry_count_sum;
04813         if (nscopes > 0 && nentrys >= 0) {
04814             mean = nentrys / nscopes;
04815             var = nscopes * js_entry_count_sqsum - nentrys * nentrys;
04816             if (var < 0.0 || nscopes <= 1)
04817                 var = 0.0;
04818             else
04819                 var /= nscopes * (nscopes - 1);
04820 
04821             /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
04822             sigma = (var != 0.) ? sqrt(var) : 0.;
04823         }
04824 
04825         fprintf(logfp,
04826                 "scopes %g entries %g mean %g sigma %g max %u",
04827                 nscopes, nentrys, mean, sigma, js_entry_count_max);
04828     }
04829 
04830     fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u\n",
04831             js_entry_count_hist[0], js_entry_count_hist[1],
04832             js_entry_count_hist[2], js_entry_count_hist[3],
04833             js_entry_count_hist[4], js_entry_count_hist[5],
04834             js_entry_count_hist[6], js_entry_count_hist[7],
04835             js_entry_count_hist[8], js_entry_count_hist[9],
04836             js_entry_count_hist[10]);
04837     js_entry_count_sum = js_entry_count_max = 0;
04838     js_entry_count_sqsum = 0;
04839     memset(js_entry_count_hist, 0, sizeof js_entry_count_hist);
04840     fflush(logfp);
04841 }
04842 #endif
04843 
04844 uint32
04845 js_Mark(JSContext *cx, JSObject *obj, void *arg)
04846 {
04847     JSScope *scope;
04848     JSScopeProperty *sprop;
04849     JSClass *clasp;
04850 
04851     JS_ASSERT(OBJ_IS_NATIVE(obj));
04852     scope = OBJ_SCOPE(obj);
04853 #ifdef DEBUG_brendan
04854     if (scope->object == obj)
04855         MeterEntryCount(scope->entryCount);
04856 #endif
04857 
04858     JS_ASSERT(!SCOPE_LAST_PROP(scope) ||
04859               SCOPE_HAS_PROPERTY(scope, SCOPE_LAST_PROP(scope)));
04860 
04861     for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
04862         if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
04863             continue;
04864         MARK_SCOPE_PROPERTY(cx, sprop);
04865     }
04866 
04867     /* No one runs while the GC is running, so we can use LOCKED_... here. */
04868     clasp = LOCKED_OBJ_GET_CLASS(obj);
04869     if (clasp->mark)
04870         (void) clasp->mark(cx, obj, NULL);
04871 
04872     if (scope->object != obj) {
04873         /*
04874          * An unmutated object that shares a prototype's scope.  We can't tell
04875          * how many slots are allocated and in use at obj->slots by looking at
04876          * scope, so we get obj->slots' length from its -1'st element.
04877          */
04878         return (uint32) obj->slots[-1];
04879     }
04880     return JS_MIN(scope->map.freeslot, scope->map.nslots);
04881 }
04882 
04883 void
04884 js_Clear(JSContext *cx, JSObject *obj)
04885 {
04886     JSScope *scope;
04887     JSRuntime *rt;
04888     JSScopeProperty *sprop;
04889     uint32 i, n;
04890 
04891     /*
04892      * Clear our scope and the property cache of all obj's properties only if
04893      * obj owns the scope (i.e., not if obj is unmutated and therefore sharing
04894      * its prototype's scope).  NB: we do not clear any reserved slots lying
04895      * below JSSLOT_FREE(clasp).
04896      */
04897     JS_LOCK_OBJ(cx, obj);
04898     scope = OBJ_SCOPE(obj);
04899     if (scope->object == obj) {
04900         /* Clear the property cache before we clear the scope. */
04901         rt = cx->runtime;
04902         for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
04903             if (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
04904                 SCOPE_HAS_PROPERTY(scope, sprop)) {
04905                 PROPERTY_CACHE_FILL(&rt->propertyCache, obj, sprop->id, NULL);
04906             }
04907         }
04908 
04909         /* Now that we're done using scope->lastProp/table, clear scope. */
04910         js_ClearScope(cx, scope);
04911 
04912         /* Clear slot values and reset freeslot so we're consistent. */
04913         i = scope->map.nslots;
04914         n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj));
04915         while (--i >= n)
04916             obj->slots[i] = JSVAL_VOID;
04917         scope->map.freeslot = n;
04918     }
04919     JS_UNLOCK_OBJ(cx, obj);
04920 }
04921 
04922 jsval
04923 js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot)
04924 {
04925     jsval v;
04926 
04927     JS_LOCK_OBJ(cx, obj);
04928     v = (slot < (uint32) obj->slots[-1]) ? obj->slots[slot] : JSVAL_VOID;
04929     JS_UNLOCK_OBJ(cx, obj);
04930     return v;
04931 }
04932 
04933 JSBool
04934 js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
04935 {
04936     JSScope *scope;
04937     uint32 nslots;
04938     JSClass *clasp;
04939     jsval *newslots;
04940 
04941     JS_LOCK_OBJ(cx, obj);
04942     scope = OBJ_SCOPE(obj);
04943     nslots = (uint32) obj->slots[-1];
04944     if (slot >= nslots) {
04945         /*
04946          * At this point, obj may or may not own scope.  If some path calls
04947          * js_GetMutableScope but does not add a slot-owning property, then
04948          * scope->object == obj but nslots will be nominal.  If obj shares a
04949          * prototype's scope, then we cannot update scope->map here, but we
04950          * must update obj->slots[-1] when we grow obj->slots.
04951          *
04952          * See js_Mark, before the last return, where we make a special case
04953          * for unmutated (scope->object != obj) objects.
04954          */
04955         JS_ASSERT(nslots == JS_INITIAL_NSLOTS);
04956         clasp = LOCKED_OBJ_GET_CLASS(obj);
04957         nslots = JSSLOT_FREE(clasp);
04958         if (clasp->reserveSlots)
04959             nslots += clasp->reserveSlots(cx, obj);
04960         JS_ASSERT(slot < nslots);
04961 
04962         newslots = AllocSlots(cx, obj->slots, nslots);
04963         if (!newslots) {
04964             JS_UNLOCK_SCOPE(cx, scope);
04965             return JS_FALSE;
04966         }
04967         if (scope->object == obj)
04968             scope->map.nslots = nslots;
04969         obj->slots = newslots;
04970     }
04971 
04972     /* Whether or not we grew nslots, we may need to advance freeslot. */
04973     if (scope->object == obj && slot >= scope->map.freeslot)
04974         scope->map.freeslot = slot + 1;
04975 
04976     obj->slots[slot] = v;
04977     JS_UNLOCK_SCOPE(cx, scope);
04978     return JS_TRUE;
04979 }
04980 
04981 #ifdef DEBUG
04982 
04983 /* Routines to print out values during debugging. */
04984 
04985 void printChar(jschar *cp) {
04986     fprintf(stderr, "jschar* (0x%p) \"", (void *)cp);
04987     while (*cp)
04988         fputc(*cp++, stderr);
04989     fputc('"', stderr);
04990     fputc('\n', stderr);
04991 }
04992 
04993 void printString(JSString *str) {
04994     size_t i, n;
04995     jschar *s;
04996     fprintf(stderr, "string (0x%p) \"", (void *)str);
04997     s = JSSTRING_CHARS(str);
04998     for (i=0, n=JSSTRING_LENGTH(str); i < n; i++)
04999         fputc(s[i], stderr);
05000     fputc('"', stderr);
05001     fputc('\n', stderr);
05002 }
05003 
05004 void printVal(JSContext *cx, jsval val);
05005 
05006 void printObj(JSContext *cx, JSObject *jsobj) {
05007     jsuint i;
05008     jsval val;
05009     JSClass *clasp;
05010 
05011     fprintf(stderr, "object 0x%p\n", (void *)jsobj);
05012     clasp = OBJ_GET_CLASS(cx, jsobj);
05013     fprintf(stderr, "class 0x%p %s\n", (void *)clasp, clasp->name);
05014     for (i=0; i < jsobj->map->nslots; i++) {
05015         fprintf(stderr, "slot %3d ", i);
05016         val = jsobj->slots[i];
05017         if (JSVAL_IS_OBJECT(val))
05018             fprintf(stderr, "object 0x%p\n", (void *)JSVAL_TO_OBJECT(val));
05019         else
05020             printVal(cx, val);
05021     }
05022 }
05023 
05024 void printVal(JSContext *cx, jsval val) {
05025     fprintf(stderr, "val %d (0x%p) = ", (int)val, (void *)val);
05026     if (JSVAL_IS_NULL(val)) {
05027         fprintf(stderr, "null\n");
05028     } else if (JSVAL_IS_VOID(val)) {
05029         fprintf(stderr, "undefined\n");
05030     } else if (JSVAL_IS_OBJECT(val)) {
05031         printObj(cx, JSVAL_TO_OBJECT(val));
05032     } else if (JSVAL_IS_INT(val)) {
05033         fprintf(stderr, "(int) %d\n", JSVAL_TO_INT(val));
05034     } else if (JSVAL_IS_STRING(val)) {
05035         printString(JSVAL_TO_STRING(val));
05036     } else if (JSVAL_IS_DOUBLE(val)) {
05037         fprintf(stderr, "(double) %g\n", *JSVAL_TO_DOUBLE(val));
05038     } else {
05039         JS_ASSERT(JSVAL_IS_BOOLEAN(val));
05040         fprintf(stderr, "(boolean) %s\n",
05041                 JSVAL_TO_BOOLEAN(val) ? "true" : "false");
05042     }
05043     fflush(stderr);
05044 }
05045 
05046 void printId(JSContext *cx, jsid id) {
05047     fprintf(stderr, "id %d (0x%p) is ", (int)id, (void *)id);
05048     printVal(cx, ID_TO_VALUE(id));
05049 }
05050 
05051 void printAtom(JSAtom *atom) {
05052     printString(ATOM_TO_STRING(atom));
05053 }
05054 
05055 #endif