summaryrefslogtreecommitdiffstats
path: root/src/extension/script/js/jsobj.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/extension/script/js/jsobj.c')
-rw-r--r--src/extension/script/js/jsobj.c3900
1 files changed, 0 insertions, 3900 deletions
diff --git a/src/extension/script/js/jsobj.c b/src/extension/script/js/jsobj.c
deleted file mode 100644
index 1da1f138b..000000000
--- a/src/extension/script/js/jsobj.c
+++ /dev/null
@@ -1,3900 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Communicator client code, released
- * March 31, 1998.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/*
- * JS object implementation.
- */
-#include "jsstddef.h"
-#include <stdlib.h>
-#include <string.h>
-#include "jstypes.h"
-#include "jsarena.h" /* Added by JSIFY */
-#include "jsutil.h" /* Added by JSIFY */
-#include "jshash.h" /* Added by JSIFY */
-#include "jsdhash.h"
-#include "jsprf.h"
-#include "jsapi.h"
-#include "jsarray.h"
-#include "jsatom.h"
-#include "jsbool.h"
-#include "jscntxt.h"
-#include "jsconfig.h"
-#include "jsfun.h"
-#include "jsgc.h"
-#include "jsinterp.h"
-#include "jslock.h"
-#include "jsnum.h"
-#include "jsobj.h"
-#include "jsscope.h"
-#include "jsscript.h"
-#include "jsstr.h"
-#include "jsopcode.h"
-
-#include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */
-
-#ifdef JS_THREADSAFE
-#define NATIVE_DROP_PROPERTY js_DropProperty
-
-extern void
-js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);
-#else
-#define NATIVE_DROP_PROPERTY NULL
-#endif
-
-#ifdef XP_MAC
-#pragma export on
-#endif
-
-JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
- js_NewObjectMap, js_DestroyObjectMap,
-#if defined JS_THREADSAFE && defined DEBUG
- _js_LookupProperty, js_DefineProperty,
-#else
- js_LookupProperty, js_DefineProperty,
-#endif
- js_GetProperty, js_SetProperty,
- js_GetAttributes, js_SetAttributes,
- js_DeleteProperty, js_DefaultValue,
- js_Enumerate, js_CheckAccess,
- NULL, NATIVE_DROP_PROPERTY,
- js_Call, js_Construct,
- NULL, js_HasInstance,
- js_SetProtoOrParent, js_SetProtoOrParent,
- js_Mark, js_Clear,
- js_GetRequiredSlot, js_SetRequiredSlot
-};
-
-#ifdef XP_MAC
-#pragma export off
-#endif
-
-JSClass js_ObjectClass = {
- js_Object_str,
- 0,
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
- JSCLASS_NO_OPTIONAL_MEMBERS
-};
-
-#if JS_HAS_OBJ_PROTO_PROP
-
-static JSBool
-obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
-
-static JSBool
-obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
-
-static JSBool
-obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
-
-static JSPropertySpec object_props[] = {
- /* These two must come first; see object_props[slot].name usage below. */
- {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED,
- obj_getSlot, obj_setSlot},
- {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
- obj_getSlot, obj_setSlot},
- {js_count_str, 0, JSPROP_PERMANENT,obj_getCount, obj_getCount},
- {0,0,0,0,0}
-};
-
-/* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */
-#define JSSLOT_COUNT 2
-
-static JSBool
-ReportStrictSlot(JSContext *cx, uint32 slot)
-{
- if (slot == JSSLOT_PROTO)
- return JS_TRUE;
- return JS_ReportErrorFlagsAndNumber(cx,
- JSREPORT_WARNING | JSREPORT_STRICT,
- js_GetErrorMessage, NULL,
- JSMSG_DEPRECATED_USAGE,
- object_props[slot].name);
-}
-
-static JSBool
-obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
-{
- uint32 slot;
- JSAccessMode mode;
- uintN attrs;
-
- slot = (uint32) JSVAL_TO_INT(id);
- if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
- return JS_FALSE;
- if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
- id = (jsid)cx->runtime->atomState.protoAtom;
- mode = JSACC_PROTO;
- } else {
- id = (jsid)cx->runtime->atomState.parentAtom;
- mode = JSACC_PARENT;
- }
- if (!OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, &attrs))
- return JS_FALSE;
- *vp = OBJ_GET_SLOT(cx, obj, slot);
- return JS_TRUE;
-}
-
-static JSBool
-obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
-{
- JSObject *pobj;
- uint32 slot;
- uintN attrs;
-
- if (!JSVAL_IS_OBJECT(*vp))
- return JS_TRUE;
- pobj = JSVAL_TO_OBJECT(*vp);
- slot = (uint32) JSVAL_TO_INT(id);
- if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
- return JS_FALSE;
-
- /* __parent__ is readonly and permanent, only __proto__ may be set. */
- if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_PROTO | JSACC_WRITE, vp, &attrs))
- return JS_FALSE;
-
- return js_SetProtoOrParent(cx, obj, slot, pobj);
-}
-
-static JSBool
-obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
-{
- jsval iter_state;
- jsid num_properties;
- JSBool ok;
-
- if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))
- return JS_FALSE;
-
- /* Get the number of properties to enumerate. */
- iter_state = JSVAL_NULL;
- ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties);
- if (!ok)
- goto out;
-
- if (!JSVAL_IS_INT(num_properties)) {
- JS_ASSERT(0);
- *vp = JSVAL_ZERO;
- goto out;
- }
- *vp = num_properties;
-
-out:
- if (iter_state != JSVAL_NULL)
- ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
- return ok;
-}
-
-#else /* !JS_HAS_OBJ_PROTO_PROP */
-
-#define object_props NULL
-
-#endif /* !JS_HAS_OBJ_PROTO_PROP */
-
-JSBool
-js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)
-{
- JSRuntime *rt;
- JSObject *obj2, *oldproto;
- JSScope *scope, *newscope;
-
- /*
- * Serialize all proto and parent setting in order to detect cycles.
- * We nest locks in this function, and only here, in the following orders:
- *
- * (1) rt->setSlotLock < pobj's scope lock;
- * rt->setSlotLock < pobj's proto-or-parent's scope lock;
- * rt->setSlotLock < pobj's grand-proto-or-parent's scope lock;
- * etc...
- * (2) rt->setSlotLock < obj's scope lock < pobj's scope lock.
- *
- * We avoid AB-BA deadlock by restricting obj from being on pobj's parent
- * or proto chain (pobj may already be on obj's parent or proto chain; it
- * could be moving up or down). We finally order obj with respect to pobj
- * at the bottom of this routine (just before releasing rt->setSlotLock),
- * by making pobj be obj's prototype or parent.
- *
- * After we have set the slot and released rt->setSlotLock, another call
- * to js_SetProtoOrParent could nest locks according to the first order
- * list above, but it cannot deadlock with any other thread. For there
- * to be a deadlock, other parts of the engine would have to nest scope
- * locks in the opposite order. XXXbe ensure they don't!
- */
- rt = cx->runtime;
-#ifdef JS_THREADSAFE
-
- JS_ACQUIRE_LOCK(rt->setSlotLock);
- while (rt->setSlotBusy) {
- jsrefcount saveDepth;
-
- /* Take pains to avoid nesting rt->gcLock inside rt->setSlotLock! */
- JS_RELEASE_LOCK(rt->setSlotLock);
- saveDepth = JS_SuspendRequest(cx);
- JS_ACQUIRE_LOCK(rt->setSlotLock);
- if (rt->setSlotBusy)
- JS_WAIT_CONDVAR(rt->setSlotDone, JS_NO_TIMEOUT);
- JS_RELEASE_LOCK(rt->setSlotLock);
- JS_ResumeRequest(cx, saveDepth);
- JS_ACQUIRE_LOCK(rt->setSlotLock);
- }
- rt->setSlotBusy = JS_TRUE;
- JS_RELEASE_LOCK(rt->setSlotLock);
-
-#define SET_SLOT_DONE(rt) \
- JS_BEGIN_MACRO \
- JS_ACQUIRE_LOCK((rt)->setSlotLock); \
- (rt)->setSlotBusy = JS_FALSE; \
- JS_NOTIFY_ALL_CONDVAR((rt)->setSlotDone); \
- JS_RELEASE_LOCK((rt)->setSlotLock); \
- JS_END_MACRO
-
-#else
-
-#define SET_SLOT_DONE(rt) /* nothing */
-
-#endif
-
- obj2 = pobj;
- while (obj2) {
- if (obj2 == obj) {
- SET_SLOT_DONE(rt);
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_CYCLIC_VALUE,
-#if JS_HAS_OBJ_PROTO_PROP
- object_props[slot].name
-#else
- (slot == JSSLOT_PROTO) ? js_proto_str
- : js_parent_str
-#endif
- );
- return JS_FALSE;
- }
- obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot));
- }
-
- if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) {
- /* Check to see whether obj shares its prototype's scope. */
- JS_LOCK_OBJ(cx, obj);
- scope = OBJ_SCOPE(obj);
- oldproto = JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO));
- if (oldproto && OBJ_SCOPE(oldproto) == scope) {
- /* Either obj needs a new empty scope, or it should share pobj's. */
- if (!pobj ||
- !OBJ_IS_NATIVE(pobj) ||
- OBJ_GET_CLASS(cx, pobj) != LOCKED_OBJ_GET_CLASS(oldproto)) {
- /*
- * With no proto and no scope of its own, obj is truly empty.
- *
- * If pobj is not native, obj needs its own empty scope -- it
- * should not continue to share oldproto's scope once oldproto
- * is not on obj's prototype chain. That would put properties
- * from oldproto's scope ahead of properties defined by pobj,
- * in lookup order.
- *
- * If pobj's class differs from oldproto's, we may need a new
- * scope to handle differences in private and reserved slots,
- * so we suboptimally but safely make one.
- */
- scope = js_GetMutableScope(cx, obj);
- if (!scope) {
- JS_UNLOCK_OBJ(cx, obj);
- SET_SLOT_DONE(rt);
- return JS_FALSE;
- }
- } else if (OBJ_SCOPE(pobj) != scope) {
-#ifdef JS_THREADSAFE
- /*
- * We are about to nest scope locks. Help jslock.c:ShareScope
- * keep scope->u.count balanced for the JS_UNLOCK_SCOPE, while
- * avoiding deadlock, by recording scope in rt->setSlotScope.
- */
- if (scope->ownercx) {
- JS_ASSERT(scope->ownercx == cx);
- rt->setSlotScope = scope;
- }
-#endif
-
- /* We can't deadlock because we checked for cycles above (2). */
- JS_LOCK_OBJ(cx, pobj);
- newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map);
- obj->map = &newscope->map;
- js_DropObjectMap(cx, &scope->map, obj);
- JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
- scope = newscope;
-#ifdef JS_THREADSAFE
- rt->setSlotScope = NULL;
-#endif
- }
- }
- LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj));
- JS_UNLOCK_SCOPE(cx, scope);
- } else {
- OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj));
- }
-
- SET_SLOT_DONE(rt);
- return JS_TRUE;
-
-#undef SET_SLOT_DONE
-}
-
-JS_STATIC_DLL_CALLBACK(JSHashNumber)
-js_hash_object(const void *key)
-{
- return (JSHashNumber)key >> JSVAL_TAGBITS;
-}
-
-static JSHashEntry *
-MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
-{
- JSSharpObjectMap *map;
- JSHashTable *table;
- JSHashNumber hash;
- JSHashEntry **hep, *he;
- jsatomid sharpid;
- JSIdArray *ida;
- JSBool ok;
- jsint i, length;
- jsid id;
-#if JS_HAS_GETTER_SETTER
- JSObject *obj2;
- JSProperty *prop;
- uintN attrs;
-#endif
- jsval val;
-
- map = &cx->sharpObjectMap;
- table = map->table;
- hash = js_hash_object(obj);
- hep = JS_HashTableRawLookup(table, hash, obj);
- he = *hep;
- if (!he) {
- sharpid = 0;
- he = JS_HashTableRawAdd(table, hep, hash, obj, (void *)sharpid);
- if (!he) {
- JS_ReportOutOfMemory(cx);
- return NULL;
- }
- ida = JS_Enumerate(cx, obj);
- if (!ida)
- return NULL;
- ok = JS_TRUE;
- for (i = 0, length = ida->length; i < length; i++) {
- id = ida->vector[i];
-#if JS_HAS_GETTER_SETTER
- ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
- if (!ok)
- break;
- if (prop) {
- ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
- if (ok) {
- if (OBJ_IS_NATIVE(obj2) &&
- (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
- val = JSVAL_NULL;
- if (attrs & JSPROP_GETTER)
- val = (jsval) ((JSScopeProperty*)prop)->getter;
- if (attrs & JSPROP_SETTER) {
- if (val != JSVAL_NULL) {
- /* Mark the getter, then set val to setter. */
- ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
- NULL)
- != NULL);
- }
- val = (jsval) ((JSScopeProperty*)prop)->setter;
- }
- } else {
- ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
- }
- }
- OBJ_DROP_PROPERTY(cx, obj2, prop);
- }
-#else
- ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
-#endif
- if (!ok)
- break;
- if (!JSVAL_IS_PRIMITIVE(val) &&
- !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {
- ok = JS_FALSE;
- break;
- }
- }
- if (!ok || !idap)
- JS_DestroyIdArray(cx, ida);
- if (!ok)
- return NULL;
- } else {
- sharpid = (jsatomid) he->value;
- if (sharpid == 0) {
- sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
- he->value = (void *) sharpid;
- }
- ida = NULL;
- }
- if (idap)
- *idap = ida;
- return he;
-}
-
-JSHashEntry *
-js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
- jschar **sp)
-{
- JSSharpObjectMap *map;
- JSHashTable *table;
- JSIdArray *ida;
- JSHashNumber hash;
- JSHashEntry *he, **hep;
- jsatomid sharpid;
- char buf[20];
- size_t len;
-
- /* Set to null in case we return an early error. */
- *sp = NULL;
- map = &cx->sharpObjectMap;
- table = map->table;
- if (!table) {
- table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
- JS_CompareValues, NULL, NULL);
- if (!table) {
- JS_ReportOutOfMemory(cx);
- return NULL;
- }
- map->table = table;
- }
-
- ida = NULL;
- if (map->depth == 0) {
- he = MarkSharpObjects(cx, obj, &ida);
- if (!he)
- goto bad;
- JS_ASSERT((((jsatomid) he->value) & SHARP_BIT) == 0);
- if (!idap) {
- JS_DestroyIdArray(cx, ida);
- ida = NULL;
- }
- } else {
- hash = js_hash_object(obj);
- hep = JS_HashTableRawLookup(table, hash, obj);
- he = *hep;
-
- /*
- * It's possible that the value of a property has changed from the
- * first time the object's properties are traversed (when the property
- * ids are entered into the hash table) to the second (when they are
- * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not
- * idempotent.
- */
- if (!he) {
- he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
- if (!he) {
- JS_ReportOutOfMemory(cx);
- goto bad;
- }
- *sp = NULL;
- sharpid = 0;
- goto out;
- }
- }
-
- sharpid = (jsatomid) he->value;
- if (sharpid == 0) {
- *sp = NULL;
- } else {
- len = JS_snprintf(buf, sizeof buf, "#%u%c",
- sharpid >> SHARP_ID_SHIFT,
- (sharpid & SHARP_BIT) ? '#' : '=');
- *sp = js_InflateString(cx, buf, len);
- if (!*sp) {
- if (ida)
- JS_DestroyIdArray(cx, ida);
- goto bad;
- }
- }
-
-out:
- JS_ASSERT(he);
- if ((sharpid & SHARP_BIT) == 0) {
- if (idap && !ida) {
- ida = JS_Enumerate(cx, obj);
- if (!ida) {
- if (*sp) {
- JS_free(cx, *sp);
- *sp = NULL;
- }
- goto bad;
- }
- }
- map->depth++;
- }
-
- if (idap)
- *idap = ida;
- return he;
-
-bad:
- /* Clean up the sharpObjectMap table on outermost error. */
- if (map->depth == 0) {
- map->sharpgen = 0;
- JS_HashTableDestroy(map->table);
- map->table = NULL;
- }
- return NULL;
-}
-
-void
-js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
-{
- JSSharpObjectMap *map;
- JSIdArray *ida;
-
- map = &cx->sharpObjectMap;
- JS_ASSERT(map->depth > 0);
- if (--map->depth == 0) {
- map->sharpgen = 0;
- JS_HashTableDestroy(map->table);
- map->table = NULL;
- }
- if (idap) {
- ida = *idap;
- if (ida) {
- JS_DestroyIdArray(cx, ida);
- *idap = NULL;
- }
- }
-}
-
-#define OBJ_TOSTRING_EXTRA 3 /* for 3 local GC roots */
-
-#if JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE
-JSBool
-js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- JSBool ok, outermost;
- JSHashEntry *he;
- JSIdArray *ida;
- jschar *chars, *ochars, *vsharp;
- const jschar *idstrchars, *vchars;
- size_t nchars, idstrlength, gsoplength, vlength, vsharplength;
- char *comma;
- jsint i, j, length, valcnt;
- jsid id;
-#if JS_HAS_GETTER_SETTER
- JSObject *obj2;
- JSProperty *prop;
- uintN attrs;
-#endif
- jsval val[2];
- JSString *gsop[2];
- JSAtom *atom;
- JSString *idstr, *valstr, *str;
- int stackDummy;
-
- if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
- return JS_FALSE;
- }
-
- /*
- * obj_toString for 1.2 calls toSource, and doesn't want the extra parens
- * on the outside.
- */
- outermost = (cx->version != JSVERSION_1_2 && cx->sharpObjectMap.depth == 0);
- he = js_EnterSharpObject(cx, obj, &ida, &chars);
- if (!he)
- return JS_FALSE;
- if (IS_SHARP(he)) {
- /*
- * We didn't enter -- obj is already "sharp", meaning we've visited it
- * already in our depth first search, and therefore chars contains a
- * string of the form "#n#".
- */
- JS_ASSERT(!ida);
-#if JS_HAS_SHARP_VARS
- nchars = js_strlen(chars);
-#else
- chars[0] = '{';
- chars[1] = '}';
- chars[2] = 0;
- nchars = 2;
-#endif
- goto make_string;
- }
- JS_ASSERT(ida);
- ok = JS_TRUE;
-
- if (!chars) {
- /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
- chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
- nchars = 0;
- if (!chars)
- goto error;
- if (outermost)
- chars[nchars++] = '(';
- } else {
- /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
- MAKE_SHARP(he);
- nchars = js_strlen(chars);
- chars = (jschar *)
- realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
- if (!chars) {
- free(ochars);
- goto error;
- }
- if (outermost) {
- /*
- * No need for parentheses around the whole shebang, because #n=
- * unambiguously begins an object initializer, and never a block
- * statement.
- */
- outermost = JS_FALSE;
- }
- }
-
- chars[nchars++] = '{';
-
- comma = NULL;
-
- for (i = 0, length = ida->length; i < length; i++) {
- /* Get strings for id and value and GC-root them via argv. */
- id = ida->vector[i];
-
-#if JS_HAS_GETTER_SETTER
-
- ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
- if (!ok)
- goto error;
- valcnt = 0;
- if (prop) {
- ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
- if (!ok) {
- OBJ_DROP_PROPERTY(cx, obj2, prop);
- goto error;
- }
- if (OBJ_IS_NATIVE(obj2) &&
- (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
- if (attrs & JSPROP_GETTER) {
- val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter;
-#ifdef OLD_GETTER_SETTER
- gsop[valcnt] =
- ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
-#else
- gsop[valcnt] =
- ATOM_TO_STRING(cx->runtime->atomState.getAtom);
-#endif
- valcnt++;
- }
- if (attrs & JSPROP_SETTER) {
- val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter;
-#ifdef OLD_GETTER_SETTER
- gsop[valcnt] =
- ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
-#else
- gsop[valcnt] =
- ATOM_TO_STRING(cx->runtime->atomState.setAtom);
-#endif
- valcnt++;
- }
- } else {
- valcnt = 1;
- gsop[0] = NULL;
- ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
- }
- OBJ_DROP_PROPERTY(cx, obj2, prop);
- }
-
-#else /* !JS_HAS_GETTER_SETTER */
-
- valcnt = 1;
- gsop[0] = NULL;
- ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
-
-#endif /* !JS_HAS_GETTER_SETTER */
-
- if (!ok)
- goto error;
-
- /* Convert id to a jsval and then to a string. */
- atom = JSVAL_IS_INT(id) ? NULL : (JSAtom *)id;
- id = ID_TO_VALUE(id);
- idstr = js_ValueToString(cx, id);
- if (!idstr) {
- ok = JS_FALSE;
- goto error;
- }
- argv[0] = STRING_TO_JSVAL(idstr);
-
- /*
- * If id is a string that's a reserved identifier, or else id is not
- * an identifier at all, then it needs to be quoted.
- */
- if (atom && (ATOM_KEYWORD(atom) || !js_IsIdentifier(idstr))) {
- idstr = js_QuoteString(cx, idstr, (jschar)'\'');
- if (!idstr) {
- ok = JS_FALSE;
- goto error;
- }
- argv[0] = STRING_TO_JSVAL(idstr);
- }
- idstrchars = JSSTRING_CHARS(idstr);
- idstrlength = JSSTRING_LENGTH(idstr);
-
- for (j = 0; j < valcnt; j++) {
- /* Convert val[j] to its canonical source form. */
- valstr = js_ValueToSource(cx, val[j]);
- if (!valstr) {
- ok = JS_FALSE;
- goto error;
- }
- argv[1+j] = STRING_TO_JSVAL(valstr);
- vchars = JSSTRING_CHARS(valstr);
- vlength = JSSTRING_LENGTH(valstr);
-
-#ifndef OLD_GETTER_SETTER
- /* Remove 'function ' from beginning of valstr. */
- if (gsop[j]) {
- int n = strlen(js_function_str) + 1;
- vchars += n;
- vlength -= n;
- }
-#endif
-
- /* If val[j] is a non-sharp object, consider sharpening it. */
- vsharp = NULL;
- vsharplength = 0;
-#if JS_HAS_SHARP_VARS
- if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') {
- he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL,
- &vsharp);
- if (!he) {
- ok = JS_FALSE;
- goto error;
- }
- if (IS_SHARP(he)) {
- vchars = vsharp;
- vlength = js_strlen(vchars);
- } else {
- if (vsharp) {
- vsharplength = js_strlen(vsharp);
- MAKE_SHARP(he);
- }
- js_LeaveSharpObject(cx, NULL);
- }
- }
-#endif
-
- /* Allocate 1 + 1 at end for closing brace and terminating 0. */
- chars = (jschar *)
- realloc((ochars = chars),
- (nchars + (comma ? 2 : 0) +
- idstrlength + 1 +
- (gsop[j] ? 1 + JSSTRING_LENGTH(gsop[j]) : 0) +
- vsharplength + vlength +
- (outermost ? 2 : 1) + 1) * sizeof(jschar));
- if (!chars) {
- /* Save code space on error: let JS_free ignore null vsharp. */
- JS_free(cx, vsharp);
- free(ochars);
- goto error;
- }
-
- if (comma) {
- chars[nchars++] = comma[0];
- chars[nchars++] = comma[1];
- }
- comma = ", ";
-
-#ifdef OLD_GETTER_SETTER
- js_strncpy(&chars[nchars], idstrchars, idstrlength);
- nchars += idstrlength;
- if (gsop[j]) {
- chars[nchars++] = ' ';
- gsoplength = JSSTRING_LENGTH(gsop[j]);
- js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength);
- nchars += gsoplength;
- }
- chars[nchars++] = ':';
-#else
- if (gsop[j]) {
- gsoplength = JSSTRING_LENGTH(gsop[j]);
- js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength);
- nchars += gsoplength;
- chars[nchars++] = ' ';
- }
- js_strncpy(&chars[nchars], idstrchars, idstrlength);
- nchars += idstrlength;
- if (!gsop[j])
- chars[nchars++] = ':';
-#endif
- if (vsharplength) {
- js_strncpy(&chars[nchars], vsharp, vsharplength);
- nchars += vsharplength;
- }
- js_strncpy(&chars[nchars], vchars, vlength);
- nchars += vlength;
-
- if (vsharp)
- JS_free(cx, vsharp);
- }
- }
-
- chars[nchars++] = '}';
- if (outermost)
- chars[nchars++] = ')';
- chars[nchars] = 0;
-
- error:
- js_LeaveSharpObject(cx, &ida);
-
- if (!ok) {
- if (chars)
- free(chars);
- return ok;
- }
-
- if (!chars) {
- JS_ReportOutOfMemory(cx);
- return JS_FALSE;
- }
- make_string:
- str = js_NewString(cx, chars, nchars, 0);
- if (!str) {
- free(chars);
- return JS_FALSE;
- }
- *rval = STRING_TO_JSVAL(str);
- return JS_TRUE;
-}
-#endif /* JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE */
-
-JSBool
-js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- jschar *chars;
- size_t nchars;
- const char *clazz, *prefix;
- JSString *str;
-
-#if JS_HAS_INITIALIZERS
- if (cx->version == JSVERSION_1_2)
- return js_obj_toSource(cx, obj, argc, argv, rval);
-#endif
-
- clazz = OBJ_GET_CLASS(cx, obj)->name;
- nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
- chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar));
- if (!chars)
- return JS_FALSE;
-
- prefix = "[object ";
- nchars = 0;
- while ((chars[nchars] = (jschar)*prefix) != 0)
- nchars++, prefix++;
- while ((chars[nchars] = (jschar)*clazz) != 0)
- nchars++, clazz++;
- chars[nchars++] = ']';
- chars[nchars] = 0;
-
- str = js_NewString(cx, chars, nchars, 0);
- if (!str) {
- JS_free(cx, chars);
- return JS_FALSE;
- }
- *rval = STRING_TO_JSVAL(str);
- return JS_TRUE;
-}
-
-static JSBool
-obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- *rval = OBJECT_TO_JSVAL(obj);
- return JS_TRUE;
-}
-
-static JSBool
-obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- JSStackFrame *fp, *caller;
- JSBool indirectCall;
- JSObject *scopeobj;
- JSString *str;
- const char *file;
- uintN line;
- JSPrincipals *principals;
- JSScript *script;
- JSBool ok;
-#if JS_HAS_EVAL_THIS_SCOPE
- JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
- JSBool setCallerScopeChain = JS_FALSE, setCallerVarObj = JS_FALSE;
-#endif
-
- fp = cx->fp;
- caller = JS_GetScriptedCaller(cx, fp);
- indirectCall = (caller && caller->pc && *caller->pc != JSOP_EVAL);
-
- if (JSVERSION_IS_ECMA(cx->version) &&
- indirectCall &&
- !JS_ReportErrorFlagsAndNumber(cx,
- JSREPORT_WARNING | JSREPORT_STRICT,
- js_GetErrorMessage, NULL,
- JSMSG_BAD_INDIRECT_CALL,
- js_eval_str)) {
- return JS_FALSE;
- }
-
- if (!JSVAL_IS_STRING(argv[0])) {
- *rval = argv[0];
- return JS_TRUE;
- }
-
-#if JS_HAS_SCRIPT_OBJECT
- /*
- * Script.prototype.compile/exec and Object.prototype.eval all take an
- * optional trailing argument that overrides the scope object.
- */
- scopeobj = NULL;
- if (argc >= 2) {
- if (!js_ValueToObject(cx, argv[1], &scopeobj))
- return JS_FALSE;
- argv[1] = OBJECT_TO_JSVAL(scopeobj);
- }
- if (!scopeobj)
-#endif
- {
-#if JS_HAS_EVAL_THIS_SCOPE
- /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
- if (indirectCall) {
- callerScopeChain = caller->scopeChain;
- if (obj != callerScopeChain) {
- scopeobj = js_NewObject(cx, &js_WithClass, obj,
- callerScopeChain);
- if (!scopeobj)
- return JS_FALSE;
-
- /* Set fp->scopeChain too, for the compiler. */
- caller->scopeChain = fp->scopeChain = scopeobj;
- setCallerScopeChain = JS_TRUE;
- }
-
- callerVarObj = caller->varobj;
- if (obj != callerVarObj) {
- /* Set fp->varobj too, for the compiler. */
- caller->varobj = fp->varobj = obj;
- setCallerVarObj = JS_TRUE;
- }
- }
- /* From here on, control must exit through label out with ok set. */
-#endif
-
-#if JS_BUG_EVAL_THIS_SCOPE
- /* An old version used the object in which eval was found for scope. */
- scopeobj = obj;
-#else
- /* Compile using caller's current scope object. */
- if (caller)
- scopeobj = caller->scopeChain;
-#endif
- }
-
- str = JSVAL_TO_STRING(argv[0]);
- if (caller) {
- file = caller->script->filename;
- line = js_PCToLineNumber(cx, caller->script, caller->pc);
- principals = JS_EvalFramePrincipals(cx, fp, caller);
- } else {
- file = NULL;
- line = 0;
- principals = NULL;
- }
-
- /* XXXbe set only for the compiler, which does not currently test it */
- fp->flags |= JSFRAME_EVAL;
- script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,
- JSSTRING_CHARS(str),
- JSSTRING_LENGTH(str),
- file, line);
- if (!script) {
- ok = JS_FALSE;
- goto out;
- }
-
-#if !JS_BUG_EVAL_THIS_SCOPE
-#if JS_HAS_SCRIPT_OBJECT
- if (argc < 2)
-#endif
- {
- /* Execute using caller's new scope object (might be a Call object). */
- if (caller)
- scopeobj = caller->scopeChain;
- }
-#endif
- ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
- JS_DestroyScript(cx, script);
-
-out:
-#if JS_HAS_EVAL_THIS_SCOPE
- /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
- if (setCallerScopeChain)
- caller->scopeChain = callerScopeChain;
- if (setCallerVarObj)
- caller->varobj = callerVarObj;
-#endif
- return ok;
-}
-
-JS_STATIC_DLL_CALLBACK(const void *)
-resolving_GetKey(JSDHashTable *table, JSDHashEntryHdr *hdr)
-{
- JSResolvingEntry *entry = (JSResolvingEntry *)hdr;
-
- return &entry->key;
-}
-
-JS_STATIC_DLL_CALLBACK(JSDHashNumber)
-resolving_HashKey(JSDHashTable *table, const void *ptr)
-{
- const JSResolvingKey *key = (const JSResolvingKey *)ptr;
-
- return ((JSDHashNumber)key->obj >> JSVAL_TAGBITS) ^ key->id;
-}
-
-JS_PUBLIC_API(JSBool)
-resolving_MatchEntry(JSDHashTable *table,
- const JSDHashEntryHdr *hdr,
- const void *ptr)
-{
- const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
- const JSResolvingKey *key = (const JSResolvingKey *)ptr;
-
- return entry->key.obj == key->obj && entry->key.id == key->id;
-}
-
-static const JSDHashTableOps resolving_dhash_ops = {
- JS_DHashAllocTable,
- JS_DHashFreeTable,
- resolving_GetKey,
- resolving_HashKey,
- resolving_MatchEntry,
- JS_DHashMoveEntryStub,
- JS_DHashClearEntryStub,
- JS_DHashFinalizeStub,
- NULL
-};
-
-static JSBool
-StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
- JSResolvingEntry **entryp)
-{
- JSDHashTable *table;
- JSResolvingEntry *entry;
-
- table = cx->resolvingTable;
- if (!table) {
- table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
- sizeof(JSResolvingEntry),
- JS_DHASH_MIN_SIZE);
- if (!table)
- goto outofmem;
- cx->resolvingTable = table;
- }
-
- entry = (JSResolvingEntry *)
- JS_DHashTableOperate(table, key, JS_DHASH_ADD);
- if (!entry)
- goto outofmem;
-
- if (entry->flags & flag) {
- /* An entry for (key, flag) exists already -- dampen recursion. */
- entry = NULL;
- } else {
- /* Fill in key if we were the first to add entry, then set flag. */
- if (!entry->key.obj)
- entry->key = *key;
- entry->flags |= flag;
- }
- *entryp = entry;
- return JS_TRUE;
-
-outofmem:
- JS_ReportOutOfMemory(cx);
- return JS_FALSE;
-}
-
-static void
-StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
- JSResolvingEntry *entry, uint32 generation)
-{
- JSDHashTable *table;
-
- /*
- * Clear flag from entry->flags and return early if other flags remain.
- * We must take care to re-lookup entry if the table has changed since
- * it was found by StartResolving.
- */
- table = cx->resolvingTable;
- if (table->generation != generation) {
- entry = (JSResolvingEntry *)
- JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);
- }
- entry->flags &= ~flag;
- if (entry->flags)
- return;
-
- /*
- * Do a raw remove only if fewer entries were removed than would cause
- * alpha to be less than .5 (alpha is at most .75). Otherwise, we just
- * call JS_DHashTableOperate to re-lookup the key and remove its entry,
- * compressing or shrinking the table as needed.
- */
- if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2)
- JS_DHashTableRawRemove(table, &entry->hdr);
- else
- JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
-}
-
-#if JS_HAS_OBJ_WATCHPOINT
-
-static JSBool
-obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
- void *closure)
-{
- JSResolvingKey key;
- JSResolvingEntry *entry;
- uint32 generation;
- JSObject *funobj;
- jsval argv[3];
- JSBool ok;
-
- /* Avoid recursion on (obj, id) already being watched on cx. */
- key.obj = obj;
- key.id = id;
- if (!StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
- return JS_FALSE;
- if (!entry)
- return JS_TRUE;
- generation = cx->resolvingTable->generation;
-
- funobj = (JSObject *) closure;
- argv[0] = id;
- argv[1] = old;
- argv[2] = *nvp;
- ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(funobj), 3, argv, nvp);
- StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
- return ok;
-}
-
-static JSBool
-obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- JSFunction *fun;
- jsval userid, value;
- jsid propid;
- uintN attrs;
-
- fun = js_ValueToFunction(cx, &argv[1], 0);
- if (!fun)
- return JS_FALSE;
- argv[1] = OBJECT_TO_JSVAL(fun->object);
-
- /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
- userid = argv[0];
- if (!JS_ValueToId(cx, userid, &propid))
- return JS_FALSE;
-
- if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs))
- return JS_FALSE;
- if (attrs & JSPROP_READONLY)
- return JS_TRUE;
- return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, fun->object);
-}
-
-static JSBool
-obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- return JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL);
-}
-
-#endif /* JS_HAS_OBJ_WATCHPOINT */
-
-#if JS_HAS_NEW_OBJ_METHODS
-/*
- * Prototype and property query methods, to complement the 'in' and
- * 'instanceof' operators.
- */
-
-/* Proposed ECMA 15.2.4.5. */
-static JSBool
-obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- jsid id;
- JSObject *obj2;
- JSProperty *prop;
- JSScopeProperty *sprop;
-
- if (!JS_ValueToId(cx, argv[0], &id))
- return JS_FALSE;
- if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
- return JS_FALSE;
- if (!prop) {
- *rval = JSVAL_FALSE;
- } else if (obj2 == obj) {
- *rval = JSVAL_TRUE;
- } else if (OBJ_IS_NATIVE(obj2)) {
- sprop = (JSScopeProperty *)prop;
- *rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
- } else {
- *rval = JSVAL_FALSE;
- }
- if (prop)
- OBJ_DROP_PROPERTY(cx, obj2, prop);
- return JS_TRUE;
-}
-
-/* Proposed ECMA 15.2.4.6. */
-static JSBool
-obj_isPrototypeOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- JSBool b;
-
- if (!js_IsDelegate(cx, obj, *argv, &b))
- return JS_FALSE;
- *rval = BOOLEAN_TO_JSVAL(b);
- return JS_TRUE;
-}
-
-/* Proposed ECMA 15.2.4.7. */
-static JSBool
-obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- jsid id;
- uintN attrs;
- JSObject *obj2;
- JSProperty *prop;
- JSBool ok;
-
- if (!JS_ValueToId(cx, argv[0], &id))
- return JS_FALSE;
-
- if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
- return JS_FALSE;
-
- if (!prop) {
- *rval = JSVAL_FALSE;
- return JS_TRUE;
- }
-
- /*
- * XXX ECMA spec error compatible: return false unless hasOwnProperty.
- * The ECMA spec really should be fixed so propertyIsEnumerable and the
- * for..in loop agree on whether prototype properties are enumerable,
- * obviously by fixing this method (not by breaking the for..in loop!).
- *
- * We check here for shared permanent prototype properties, which should
- * be treated as if they are local to obj. They are an implementation
- * technique used to satisfy ECMA requirements; users should not be able
- * to distinguish a shared permanent proto-property from a local one.
- */
- if (obj2 != obj &&
- !(OBJ_IS_NATIVE(obj2) &&
- SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) {
- OBJ_DROP_PROPERTY(cx, obj2, prop);
- *rval = JSVAL_FALSE;
- return JS_TRUE;
- }
-
- ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
- OBJ_DROP_PROPERTY(cx, obj2, prop);
- if (ok)
- *rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);
- return ok;
-}
-#endif /* JS_HAS_NEW_OBJ_METHODS */
-
-#if JS_HAS_GETTER_SETTER
-static JSBool
-obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- jsval fval, junk;
- jsid id;
- JSBool found;
- uintN attrs;
-
- fval = argv[1];
- if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_BAD_GETTER_OR_SETTER,
- js_getter_str);
- return JS_FALSE;
- }
-
- if (!JS_ValueToId(cx, argv[0], &id))
- return JS_FALSE;
- if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, &found))
- return JS_FALSE;
- /*
- * Getters and setters are just like watchpoints from an access
- * control point of view.
- */
- if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
- return JS_FALSE;
- return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
- (JSPropertyOp) JSVAL_TO_OBJECT(fval), NULL,
- JSPROP_GETTER | JSPROP_SHARED, NULL);
-}
-
-static JSBool
-obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- jsval fval, junk;
- jsid id;
- JSBool found;
- uintN attrs;
-
- fval = argv[1];
- if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_BAD_GETTER_OR_SETTER,
- js_setter_str);
- return JS_FALSE;
- }
-
- if (!JS_ValueToId(cx, argv[0], &id))
- return JS_FALSE;
- if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, &found))
- return JS_FALSE;
- /*
- * Getters and setters are just like watchpoints from an access
- * control point of view.
- */
- if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
- return JS_FALSE;
- return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
- NULL, (JSPropertyOp) JSVAL_TO_OBJECT(fval),
- JSPROP_SETTER | JSPROP_SHARED, NULL);
-}
-
-static JSBool
-obj_lookupGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- jsid id;
- JSObject *pobj;
- JSScopeProperty *sprop;
-
- if (!JS_ValueToId(cx, argv[0], &id))
- return JS_FALSE;
- if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, (JSProperty **) &sprop))
- return JS_FALSE;
- if (sprop) {
- if (sprop->attrs & JSPROP_GETTER)
- *rval = OBJECT_TO_JSVAL(sprop->getter);
- OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
- }
- return JS_TRUE;
-}
-
-static JSBool
-obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- jsid id;
- JSObject *pobj;
- JSScopeProperty *sprop;
-
- if (!JS_ValueToId(cx, argv[0], &id))
- return JS_FALSE;
- if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, (JSProperty **) &sprop))
- return JS_FALSE;
- if (sprop) {
- if (sprop->attrs & JSPROP_SETTER)
- *rval = OBJECT_TO_JSVAL(sprop->setter);
- OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
- }
- return JS_TRUE;
-}
-#endif /* JS_HAS_GETTER_SETTER */
-
-#if JS_HAS_OBJ_WATCHPOINT
-const char js_watch_str[] = "watch";
-const char js_unwatch_str[] = "unwatch";
-#endif
-#if JS_HAS_NEW_OBJ_METHODS
-const char js_hasOwnProperty_str[] = "hasOwnProperty";
-const char js_isPrototypeOf_str[] = "isPrototypeOf";
-const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
-#endif
-#if JS_HAS_GETTER_SETTER
-const char js_defineGetter_str[] = "__defineGetter__";
-const char js_defineSetter_str[] = "__defineSetter__";
-const char js_lookupGetter_str[] = "__lookupGetter__";
-const char js_lookupSetter_str[] = "__lookupSetter__";
-#endif
-
-static JSFunctionSpec object_methods[] = {
-#if JS_HAS_TOSOURCE
- {js_toSource_str, js_obj_toSource, 0, 0, OBJ_TOSTRING_EXTRA},
-#endif
- {js_toString_str, js_obj_toString, 0, 0, OBJ_TOSTRING_EXTRA},
- {js_toLocaleString_str, js_obj_toString, 0, 0, OBJ_TOSTRING_EXTRA},
- {js_valueOf_str, obj_valueOf, 0,0,0},
- {js_eval_str, obj_eval, 1,0,0},
-#if JS_HAS_OBJ_WATCHPOINT
- {js_watch_str, obj_watch, 2,0,0},
- {js_unwatch_str, obj_unwatch, 1,0,0},
-#endif
-#if JS_HAS_NEW_OBJ_METHODS
- {js_hasOwnProperty_str, obj_hasOwnProperty, 1,0,0},
- {js_isPrototypeOf_str, obj_isPrototypeOf, 1,0,0},
- {js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0},
-#endif
-#if JS_HAS_GETTER_SETTER
- {js_defineGetter_str, obj_defineGetter, 2,0,0},
- {js_defineSetter_str, obj_defineSetter, 2,0,0},
- {js_lookupGetter_str, obj_lookupGetter, 1,0,0},
- {js_lookupSetter_str, obj_lookupSetter, 1,0,0},
-#endif
- {0,0,0,0,0}
-};
-
-static JSBool
-Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- if (argc == 0) {
- /* Trigger logic below to construct a blank object. */
- obj = NULL;
- } else {
- /* If argv[0] is null or undefined, obj comes back null. */
- if (!js_ValueToObject(cx, argv[0], &obj))
- return JS_FALSE;
- }
- if (!obj) {
- JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));
- if (cx->fp->flags & JSFRAME_CONSTRUCTING)
- return JS_TRUE;
- obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL);
- if (!obj)
- return JS_FALSE;
- }
- *rval = OBJECT_TO_JSVAL(obj);
- return JS_TRUE;
-}
-
-/*
- * ObjectOps and Class for with-statement stack objects.
- */
-static JSBool
-with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
- JSProperty **propp
-#if defined JS_THREADSAFE && defined DEBUG
- , const char *file, uintN line
-#endif
- )
-{
- JSObject *proto;
- JSScopeProperty *sprop;
- JSStackFrame *fp;
-
- proto = OBJ_GET_PROTO(cx, obj);
- if (!proto)
- return js_LookupProperty(cx, obj, id, objp, propp);
- if (!OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp))
- return JS_FALSE;
-
- /*
- * Check whether id names an argument or local variable in an active
- * function. If so, pretend we didn't find it, so that the real arg or
- * var property can be found in the function's call object, later on in
- * the scope chain. But skip unshared arg and var properties -- those
- * result when a script explicitly sets a function "static" property of
- * the same name. See jsinterp.c:SetFunctionSlot.
- *
- * XXX blame pre-ECMA reflection of function args and vars as properties
- */
- if ((sprop = (JSScopeProperty *) *propp) &&
- (proto = *objp, OBJ_IS_NATIVE(proto)) &&
- (sprop->getter == js_GetArgument ||
- sprop->getter == js_GetLocalVariable) &&
- (sprop->attrs & JSPROP_SHARED)) {
- JS_ASSERT(OBJ_GET_CLASS(cx, proto) == &js_FunctionClass);
- for (fp = cx->fp; fp && (!fp->fun || fp->fun->native); fp = fp->down)
- continue;
- if (fp && fp->fun == (JSFunction *) JS_GetPrivate(cx, proto)) {
- OBJ_DROP_PROPERTY(cx, proto, *propp);
- *objp = NULL;
- *propp = NULL;
- }
- }
- return JS_TRUE;
-}
-
-static JSBool
-with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
-{
- JSObject *proto = OBJ_GET_PROTO(cx, obj);
- if (!proto)
- return js_GetProperty(cx, obj, id, vp);
- return OBJ_GET_PROPERTY(cx, proto, id, vp);
-}
-
-static JSBool
-with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
-{
- JSObject *proto = OBJ_GET_PROTO(cx, obj);
- if (!proto)
- return js_SetProperty(cx, obj, id, vp);
- return OBJ_SET_PROPERTY(cx, proto, id, vp);
-}
-
-static JSBool
-with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
- uintN *attrsp)
-{
- JSObject *proto = OBJ_GET_PROTO(cx, obj);
- if (!proto)
- return js_GetAttributes(cx, obj, id, prop, attrsp);
- return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp);
-}
-
-static JSBool
-with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
- uintN *attrsp)
-{
- JSObject *proto = OBJ_GET_PROTO(cx, obj);
- if (!proto)
- return js_SetAttributes(cx, obj, id, prop, attrsp);
- return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp);
-}
-
-static JSBool
-with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
-{
- JSObject *proto = OBJ_GET_PROTO(cx, obj);
- if (!proto)
- return js_DeleteProperty(cx, obj, id, rval);
- return OBJ_DELETE_PROPERTY(cx, proto, id, rval);
-}
-
-static JSBool
-with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
-{
- JSObject *proto = OBJ_GET_PROTO(cx, obj);
- if (!proto)
- return js_DefaultValue(cx, obj, hint, vp);
- return OBJ_DEFAULT_VALUE(cx, proto, hint, vp);
-}
-
-static JSBool
-with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
- jsval *statep, jsid *idp)
-{
- JSObject *proto = OBJ_GET_PROTO(cx, obj);
- if (!proto)
- return js_Enumerate(cx, obj, enum_op, statep, idp);
- return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp);
-}
-
-static JSBool
-with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
- jsval *vp, uintN *attrsp)
-{
- JSObject *proto = OBJ_GET_PROTO(cx, obj);
- if (!proto)
- return js_CheckAccess(cx, obj, id, mode, vp, attrsp);
- return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp);
-}
-
-static JSObject *
-with_ThisObject(JSContext *cx, JSObject *obj)
-{
- JSObject *proto = OBJ_GET_PROTO(cx, obj);
- if (!proto)
- return obj;
- return OBJ_THIS_OBJECT(cx, proto);
-}
-
-JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = {
- js_NewObjectMap, js_DestroyObjectMap,
- with_LookupProperty, js_DefineProperty,
- with_GetProperty, with_SetProperty,
- with_GetAttributes, with_SetAttributes,
- with_DeleteProperty, with_DefaultValue,
- with_Enumerate, with_CheckAccess,
- with_ThisObject, NATIVE_DROP_PROPERTY,
- NULL, NULL,
- NULL, NULL,
- js_SetProtoOrParent, js_SetProtoOrParent,
- js_Mark, js_Clear,
- NULL, NULL
-};
-
-static JSObjectOps *
-with_getObjectOps(JSContext *cx, JSClass *clasp)
-{
- return &js_WithObjectOps;
-}
-
-JSClass js_WithClass = {
- "With",
- 0,
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
- with_getObjectOps,
- 0,0,0,0,0,0,0
-};
-
-#if JS_HAS_OBJ_PROTO_PROP
-static JSBool
-With(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- JSObject *parent, *proto;
- jsval v;
-
- if (!JS_ReportErrorFlagsAndNumber(cx,
- JSREPORT_WARNING | JSREPORT_STRICT,
- js_GetErrorMessage, NULL,
- JSMSG_DEPRECATED_USAGE,
- js_WithClass.name)) {
- return JS_FALSE;
- }
-
- if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
- obj = js_NewObject(cx, &js_WithClass, NULL, NULL);
- if (!obj)
- return JS_FALSE;
- *rval = OBJECT_TO_JSVAL(obj);
- }
-
- parent = cx->fp->scopeChain;
- if (argc > 0) {
- if (!js_ValueToObject(cx, argv[0], &proto))
- return JS_FALSE;
- v = OBJECT_TO_JSVAL(proto);
- if (!obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PROTO), &v))
- return JS_FALSE;
- if (argc > 1) {
- if (!js_ValueToObject(cx, argv[1], &parent))
- return JS_FALSE;
- }
- }
- v = OBJECT_TO_JSVAL(parent);
- return obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PARENT), &v);
-}
-#endif
-
-JSObject *
-js_InitObjectClass(JSContext *cx, JSObject *obj)
-{
- JSObject *proto;
- jsval eval;
-
-#if JS_HAS_SHARP_VARS
- JS_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >= ATOM_INDEX_LIMIT_LOG2 + 1);
-#endif
-
- proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1,
- object_props, object_methods, NULL, NULL);
- if (!proto)
- return NULL;
-
-#if JS_HAS_OBJ_PROTO_PROP
- if (!JS_InitClass(cx, obj, NULL, &js_WithClass, With, 0,
- NULL, NULL, NULL, NULL)) {
- return NULL;
- }
-#endif
-
- /* ECMA (15.1.2.1) says 'eval' is also a property of the global object. */
- if (!OBJ_GET_PROPERTY(cx, proto, (jsid)cx->runtime->atomState.evalAtom,
- &eval)) {
- return NULL;
- }
- if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.evalAtom,
- eval, NULL, NULL, 0, NULL)) {
- return NULL;
- }
-
- return proto;
-}
-
-void
-js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops,
- JSClass *clasp)
-{
- map->nrefs = nrefs;
- map->ops = ops;
- map->nslots = JS_INITIAL_NSLOTS;
- map->freeslot = JSSLOT_FREE(clasp);
-}
-
-JSObjectMap *
-js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,
- JSClass *clasp, JSObject *obj)
-{
- return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj);
-}
-
-void
-js_DestroyObjectMap(JSContext *cx, JSObjectMap *map)
-{
- js_DestroyScope(cx, (JSScope *)map);
-}
-
-JSObjectMap *
-js_HoldObjectMap(JSContext *cx, JSObjectMap *map)
-{
- JS_ASSERT(map->nrefs >= 0);
- JS_ATOMIC_INCREMENT(&map->nrefs);
- return map;
-}
-
-JSObjectMap *
-js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj)
-{
- JS_ASSERT(map->nrefs > 0);
- JS_ATOMIC_DECREMENT(&map->nrefs);
- if (map->nrefs == 0) {
- map->ops->destroyObjectMap(cx, map);
- return NULL;
- }
- if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj)
- ((JSScope *)map)->object = NULL;
- return map;
-}
-
-static JSBool
-GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
- JSObject **protop);
-
-JSObject *
-js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
-{
- JSObject *obj, *ctor;
- JSObjectOps *ops;
- JSObjectMap *map;
- jsval cval;
- uint32 nslots, i;
- jsval *newslots;
-
- /* Allocate an object from the GC heap and zero it. */
- obj = (JSObject *) js_AllocGCThing(cx, GCX_OBJECT);
- if (!obj)
- return NULL;
-
- /* Bootstrap the ur-object, and make it the default prototype object. */
- if (!proto) {
- if (!GetClassPrototype(cx, parent, clasp->name, &proto))
- goto bad;
- if (!proto && !GetClassPrototype(cx, parent, js_Object_str, &proto))
- goto bad;
- }
-
- /* Always call the class's getObjectOps hook if it has one. */
- ops = clasp->getObjectOps
- ? clasp->getObjectOps(cx, clasp)
- : &js_ObjectOps;
-
- /*
- * Share proto's map only if it has the same JSObjectOps, and only if
- * proto's class has the same private and reserved slots, as obj's map
- * and class have.
- */
- if (proto &&
- (map = proto->map)->ops == ops &&
- ((clasp->flags ^ OBJ_GET_CLASS(cx, proto)->flags) &
- (JSCLASS_HAS_PRIVATE |
- (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT)))
- == 0) {
- /* Default parent to the parent of the prototype's constructor. */
- if (!parent) {
- if (!OBJ_GET_PROPERTY(cx, proto,
- (jsid)cx->runtime->atomState.constructorAtom,
- &cval)) {
- goto bad;
- }
- if (JSVAL_IS_OBJECT(cval) && (ctor = JSVAL_TO_OBJECT(cval)) != NULL)
- parent = OBJ_GET_PARENT(cx, ctor);
- }
-
- /* Share the given prototype's map. */
- obj->map = js_HoldObjectMap(cx, map);
-
- /* Ensure that obj starts with the minimum slots for clasp. */
- nslots = JS_INITIAL_NSLOTS;
- } else {
- /* Leave parent alone. Allocate a new map for obj. */
- map = ops->newObjectMap(cx, 1, ops, clasp, obj);
- if (!map)
- goto bad;
- obj->map = map;
-
- /* Let ops->newObjectMap set nslots so as to reserve slots. */
- nslots = map->nslots;
- }
-
- /* Allocate a slots vector, with a -1'st element telling its length. */
- newslots = (jsval *) JS_malloc(cx, (nslots + 1) * sizeof(jsval));
- if (!newslots) {
- js_DropObjectMap(cx, obj->map, obj);
- obj->map = NULL;
- goto bad;
- }
- newslots[0] = nslots;
- newslots++;
-
- /* Set the proto, parent, and class properties. */
- newslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
- newslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);
- newslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp);
-
- /* Clear above JSSLOT_CLASS so the GC doesn't load uninitialized memory. */
- for (i = JSSLOT_CLASS + 1; i < nslots; i++)
- newslots[i] = JSVAL_VOID;
-
- /* Store newslots after initializing all of 'em, just in case. */
- obj->slots = newslots;
-
- if (cx->runtime->objectHook)
- cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData);
-
- return obj;
-
-bad:
- cx->newborn[GCX_OBJECT] = NULL;
- return NULL;
-}
-
-static JSBool
-FindConstructor(JSContext *cx, JSObject *scope, const char *name, jsval *vp)
-{
- JSAtom *atom;
- JSObject *obj;
- JSObject *pobj;
- JSScopeProperty *sprop;
-
- atom = js_Atomize(cx, name, strlen(name), 0);
- if (!atom)
- return JS_FALSE;
-
- if (scope || (cx->fp && (scope = cx->fp->scopeChain) != NULL)) {
- /* Find the topmost object in the scope chain. */
- do {
- obj = scope;
- scope = OBJ_GET_PARENT(cx, obj);
- } while (scope);
- } else {
- obj = cx->globalObject;
- if (!obj) {
- *vp = JSVAL_VOID;
- return JS_TRUE;
- }
- }
-
- if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, (JSProperty**)&sprop))
- return JS_FALSE;
- if (!sprop) {
- *vp = JSVAL_VOID;
- return JS_TRUE;
- }
-
- JS_ASSERT(OBJ_IS_NATIVE(pobj));
- JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
- *vp = OBJ_GET_SLOT(cx, pobj, sprop->slot);
- OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
- return JS_TRUE;
-}
-
-JSObject *
-js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
- JSObject *parent, uintN argc, jsval *argv)
-{
- jsval cval, rval;
- JSObject *obj, *ctor;
-
- if (!FindConstructor(cx, parent, clasp->name, &cval))
- return NULL;
- if (JSVAL_IS_PRIMITIVE(cval)) {
- js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
- return NULL;
- }
-
- /*
- * If proto or parent are NULL, set them to Constructor.prototype and/or
- * Constructor.__parent__, just like JSOP_NEW does.
- */
- ctor = JSVAL_TO_OBJECT(cval);
- if (!parent)
- parent = OBJ_GET_PARENT(cx, ctor);
- if (!proto) {
- if (!OBJ_GET_PROPERTY(cx, ctor,
- (jsid)cx->runtime->atomState.classPrototypeAtom,
- &rval)) {
- return NULL;
- }
- if (JSVAL_IS_OBJECT(rval))
- proto = JSVAL_TO_OBJECT(rval);
- }
-
- obj = js_NewObject(cx, clasp, proto, parent);
- if (!obj)
- return NULL;
-
- if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval))
- goto bad;
- return JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : obj;
-bad:
- cx->newborn[GCX_OBJECT] = NULL;
- return NULL;
-}
-
-void
-js_FinalizeObject(JSContext *cx, JSObject *obj)
-{
- JSObjectMap *map;
-
- /* Cope with stillborn objects that have no map. */
- map = obj->map;
- if (!map)
- return;
- JS_ASSERT(obj->slots);
-
- if (cx->runtime->objectHook)
- cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData);
-
- /* Remove all watchpoints with weak links to obj. */
- JS_ClearWatchPointsForObject(cx, obj);
-
- /*
- * Finalize obj first, in case it needs map and slots. Optimized to use
- * LOCKED_OBJ_GET_CLASS instead of OBJ_GET_CLASS, so we avoid "promoting"
- * obj's scope from lock-free to lock-full (see jslock.c:ClaimScope) when
- * we're called from the GC. Only the GC should call js_FinalizeObject,
- * and no other threads run JS (and possibly racing to update obj->slots)
- * while the GC is running.
- */
- LOCKED_OBJ_GET_CLASS(obj)->finalize(cx, obj);
-
- /* Drop map and free slots. */
- js_DropObjectMap(cx, map, obj);
- obj->map = NULL;
- JS_free(cx, obj->slots - 1);
- obj->slots = NULL;
-}
-
-/* XXXbe if one adds props, deletes earlier props, adds more, the last added
- won't recycle the deleted props' slots. */
-JSBool
-js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)
-{
- JSObjectMap *map;
- uint32 nslots, i;
- size_t nbytes;
- jsval *newslots;
-
- map = obj->map;
- JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
- nslots = map->nslots;
- if (map->freeslot >= nslots) {
- nslots = map->freeslot;
- JS_ASSERT(nslots >= JS_INITIAL_NSLOTS);
- nslots += (nslots + 1) / 2;
-
- nbytes = (nslots + 1) * sizeof(jsval);
-#if defined _MSC_VER && _MSC_VER <= 800
- if (nbytes > 60000U) {
- JS_ReportOutOfMemory(cx);
- return JS_FALSE;
- }
-#endif
-
- newslots = (jsval *) JS_realloc(cx, obj->slots - 1, nbytes);
- if (!newslots)
- return JS_FALSE;
- for (i = 1 + newslots[0]; i <= nslots; i++)
- newslots[i] = JSVAL_VOID;
- newslots[0] = map->nslots = nslots;
- obj->slots = newslots + 1;
- }
-
-#ifdef TOO_MUCH_GC
- obj->slots[map->freeslot] = JSVAL_VOID;
-#endif
- *slotp = map->freeslot++;
- return JS_TRUE;
-}
-
-void
-js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
-{
- JSObjectMap *map;
- uint32 nslots;
- size_t nbytes;
- jsval *newslots;
-
- OBJ_CHECK_SLOT(obj, slot);
- obj->slots[slot] = JSVAL_VOID;
- map = obj->map;
- JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
- if (map->freeslot == slot + 1)
- map->freeslot = slot;
- nslots = map->nslots;
- if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) {
- nslots = map->freeslot;
- nslots += nslots / 2;
- if (nslots < JS_INITIAL_NSLOTS)
- nslots = JS_INITIAL_NSLOTS;
- nbytes = (nslots + 1) * sizeof(jsval);
- newslots = (jsval *) JS_realloc(cx, obj->slots - 1, nbytes);
- if (!newslots)
- return;
- newslots[0] = map->nslots = nslots;
- obj->slots = newslots + 1;
- }
-}
-
-#if JS_BUG_EMPTY_INDEX_ZERO
-#define CHECK_FOR_EMPTY_INDEX(id) \
- JS_BEGIN_MACRO \
- if (JSSTRING_LENGTH(_str) == 0) \
- id = JSVAL_ZERO; \
- JS_END_MACRO
-#else
-#define CHECK_FOR_EMPTY_INDEX(id) /* nothing */
-#endif
-
-/* JSVAL_INT_MAX as a string */
-#define JSVAL_INT_MAX_STRING "1073741823"
-
-#define CHECK_FOR_FUNNY_INDEX(id) \
- JS_BEGIN_MACRO \
- if (!JSVAL_IS_INT(id)) { \
- JSAtom *atom_ = (JSAtom *)id; \
- JSString *str_ = ATOM_TO_STRING(atom_); \
- const jschar *cp_ = str_->chars; \
- JSBool negative_ = (*cp_ == '-'); \
- if (negative_) cp_++; \
- if (JS7_ISDEC(*cp_) && \
- str_->length - negative_ <= sizeof(JSVAL_INT_MAX_STRING)-1) { \
- id = CheckForFunnyIndex(id, cp_, negative_); \
- } else { \
- CHECK_FOR_EMPTY_INDEX(id); \
- } \
- } \
- JS_END_MACRO
-
-static jsid
-CheckForFunnyIndex(jsid id, const jschar *cp, JSBool negative)
-{
- jsuint index = JS7_UNDEC(*cp++);
- jsuint oldIndex = 0;
- jsuint c = 0;
-
- if (index != 0) {
- while (JS7_ISDEC(*cp)) {
- oldIndex = index;
- c = JS7_UNDEC(*cp);
- index = 10 * index + c;
- cp++;
- }
- }
- if (*cp == 0 &&
- (oldIndex < (JSVAL_INT_MAX / 10) ||
- (oldIndex == (JSVAL_INT_MAX / 10) &&
- c <= (JSVAL_INT_MAX % 10)))) {
- if (negative)
- index = 0 - index;
- id = INT_TO_JSVAL((jsint)index);
- }
- return id;
-}
-
-JSScopeProperty *
-js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
- JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
- uintN attrs, uintN flags, intN shortid)
-{
- JSScope *scope;
- JSScopeProperty *sprop;
-
- JS_LOCK_OBJ(cx, obj);
- scope = js_GetMutableScope(cx, obj);
- if (!scope) {
- sprop = NULL;
- } else {
- /*
- * Handle old bug that took empty string as zero index. Also convert
- * string indices to integers if appropriate.
- */
- CHECK_FOR_FUNNY_INDEX(id);
- sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs,
- flags, shortid);
- }
- JS_UNLOCK_OBJ(cx, obj);
- return sprop;
-}
-
-JSScopeProperty *
-js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
- JSScopeProperty *sprop, uintN attrs, uintN mask,
- JSPropertyOp getter, JSPropertyOp setter)
-{
- JSScope *scope;
-
- JS_LOCK_OBJ(cx, obj);
- scope = js_GetMutableScope(cx, obj);
- if (!scope) {
- sprop = NULL;
- } else {
- sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, attrs, mask,
- getter, setter);
- if (sprop) {
- PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, sprop->id,
- sprop);
- }
- }
- JS_UNLOCK_OBJ(cx, obj);
- return sprop;
-}
-
-JSBool
-js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
- JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
- JSProperty **propp)
-{
- return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs,
- 0, 0, propp);
-}
-
-JSBool
-js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
- JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
- uintN flags, intN shortid, JSProperty **propp)
-{
- JSClass *clasp;
- JSScope *scope;
- JSScopeProperty *sprop;
-
- /*
- * Handle old bug that took empty string as zero index. Also convert
- * string indices to integers if appropriate.
- */
- CHECK_FOR_FUNNY_INDEX(id);
-
-#if JS_HAS_GETTER_SETTER
- /*
- * If defining a getter or setter, we must check for its counterpart and
- * update the attributes and property ops. A getter or setter is really
- * only half of a property.
- */
- if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
- JSObject *pobj;
-
- /*
- * If JS_THREADSAFE and id is found, js_LookupProperty returns with
- * sprop non-null and pobj locked. If pobj == obj, the property is
- * already in obj and obj has its own (mutable) scope. So if we are
- * defining a getter whose setter was already defined, or vice versa,
- * finish the job via js_ChangeScopePropertyAttributes, and refresh
- * the property cache line for (obj, id) to map sprop.
- */
- if (!js_LookupProperty(cx, obj, id, &pobj, (JSProperty **)&sprop))
- return JS_FALSE;
- if (sprop &&
- pobj == obj &&
- (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
- sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(obj), sprop,
- attrs, sprop->attrs,
- (attrs & JSPROP_GETTER)
- ? getter
- : sprop->getter,
- (attrs & JSPROP_SETTER)
- ? setter
- : sprop->setter);
-
- /* NB: obj == pobj, so we can share unlock code at the bottom. */
- if (!sprop)
- goto bad;
- goto out;
- }
-
- if (sprop) {
- /* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */
- OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
- sprop = NULL;
- }
- }
-#endif /* JS_HAS_GETTER_SETTER */
-
- /* Lock if object locking is required by this implementation. */
- JS_LOCK_OBJ(cx, obj);
-
- /* Use the object's class getter and setter by default. */
- clasp = LOCKED_OBJ_GET_CLASS(obj);
- if (!getter)
- getter = clasp->getProperty;
- if (!setter)
- setter = clasp->setProperty;
-
- /* Get obj's own scope if it has one, or create a new one for obj. */
- scope = js_GetMutableScope(cx, obj);
- if (!scope)
- goto bad;
-
- /* Add the property to scope, or replace an existing one of the same id. */
- if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
- attrs |= JSPROP_SHARED;
- sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
- SPROP_INVALID_SLOT, attrs, flags, shortid);
- if (!sprop)
- goto bad;
-
- /* XXXbe called with lock held */
- if (!clasp->addProperty(cx, obj, SPROP_USERID(sprop), &value)) {
- (void) js_RemoveScopeProperty(cx, scope, id);
- goto bad;
- }
-
- if (SPROP_HAS_VALID_SLOT(sprop, scope))
- LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value);
-
-#if JS_HAS_GETTER_SETTER
-out:
-#endif
- PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
- if (propp)
- *propp = (JSProperty *) sprop;
- else
- JS_UNLOCK_OBJ(cx, obj);
- return JS_TRUE;
-
-bad:
- JS_UNLOCK_OBJ(cx, obj);
- return JS_FALSE;
-}
-
-#if defined JS_THREADSAFE && defined DEBUG
-JS_FRIEND_API(JSBool)
-_js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
- JSProperty **propp, const char *file, uintN line)
-#else
-JS_FRIEND_API(JSBool)
-js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
- JSProperty **propp)
-#endif
-{
- JSObject *start, *obj2, *proto;
- JSScope *scope;
- JSScopeProperty *sprop;
- JSClass *clasp;
- JSResolveOp resolve;
- JSResolvingKey key;
- JSResolvingEntry *entry;
- uint32 generation;
- JSNewResolveOp newresolve;
- uintN flags;
- uint32 format;
- JSBool ok;
-
- /*
- * Handle old bug that took empty string as zero index. Also convert
- * string indices to integers if appropriate.
- */
- CHECK_FOR_FUNNY_INDEX(id);
-
- /* Search scopes starting with obj and following the prototype link. */
- start = obj;
- for (;;) {
- JS_LOCK_OBJ(cx, obj);
- SET_OBJ_INFO(obj, file, line);
- scope = OBJ_SCOPE(obj);
- if (scope->object == obj) {
- sprop = SCOPE_GET_PROPERTY(scope, id);
- } else {
- /* Shared prototype scope: try resolve before lookup. */
- sprop = NULL;
- }
-
- /* Try obj's class resolve hook if id was not found in obj's scope. */
- if (!sprop) {
- clasp = LOCKED_OBJ_GET_CLASS(obj);
- resolve = clasp->resolve;
- if (resolve != JS_ResolveStub) {
- /* Avoid recursion on (obj, id) already being resolved on cx. */
- key.obj = obj;
- key.id = id;
-
- /*
- * Once we have successfully added an entry for (obj, key) to
- * cx->resolvingTable, control must go through cleanup: before
- * returning. But note that JS_DHASH_ADD may find an existing
- * entry, in which case we bail to suppress runaway recursion.
- */
- if (!StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) {
- JS_UNLOCK_OBJ(cx, obj);
- return JS_FALSE;
- }
- if (!entry) {
- /* Already resolving id in obj -- dampen recursion. */
- JS_UNLOCK_OBJ(cx, obj);
- goto out;
- }
- generation = cx->resolvingTable->generation;
-
- /* Null *propp here so we can test it at cleanup: safely. */
- *propp = NULL;
-
- if (clasp->flags & JSCLASS_NEW_RESOLVE) {
- newresolve = (JSNewResolveOp)resolve;
- flags = 0;
- if (cx->fp && cx->fp->pc) {
- format = js_CodeSpec[*cx->fp->pc].format;
- if ((format & JOF_MODEMASK) != JOF_NAME)
- flags |= JSRESOLVE_QUALIFIED;
- if ((format & JOF_ASSIGNING) ||
- (cx->fp->flags & JSFRAME_ASSIGNING)) {
- flags |= JSRESOLVE_ASSIGNING;
- }
- }
- obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START)
- ? start
- : NULL;
- JS_UNLOCK_OBJ(cx, obj);
-
- /* Protect id and all atoms from a GC nested in resolve. */
- JS_KEEP_ATOMS(cx->runtime);
- ok = newresolve(cx, obj, ID_TO_VALUE(id), flags, &obj2);
- JS_UNKEEP_ATOMS(cx->runtime);
- if (!ok)
- goto cleanup;
-
- JS_LOCK_OBJ(cx, obj);
- SET_OBJ_INFO(obj, file, line);
- if (obj2) {
- /* Resolved: juggle locks and lookup id again. */
- if (obj2 != obj) {
- JS_UNLOCK_OBJ(cx, obj);
- JS_LOCK_OBJ(cx, obj2);
- }
- scope = OBJ_SCOPE(obj2);
- if (!MAP_IS_NATIVE(&scope->map)) {
- /* Whoops, newresolve handed back a foreign obj2. */
- JS_ASSERT(obj2 != obj);
- JS_UNLOCK_OBJ(cx, obj2);
- ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp);
- if (!ok || *propp)
- goto cleanup;
- JS_LOCK_OBJ(cx, obj2);
- } else {
- /*
- * Require that obj2 have its own scope now, as we
- * do for old-style resolve. If it doesn't, then
- * id was not truly resolved, and we'll find it in
- * the proto chain, or miss it if obj2's proto is
- * not on obj's proto chain. That last case is a
- * "too bad!" case.
- */
- if (scope->object == obj2)
- sprop = SCOPE_GET_PROPERTY(scope, id);
- }
- if (obj2 != obj && !sprop) {
- JS_UNLOCK_OBJ(cx, obj2);
- JS_LOCK_OBJ(cx, obj);
- }
- }
- } else {
- /*
- * Old resolve always requires id re-lookup if obj owns
- * its scope after resolve returns.
- */
- JS_UNLOCK_OBJ(cx, obj);
- ok = resolve(cx, obj, ID_TO_VALUE(id));
- if (!ok)
- goto cleanup;
- JS_LOCK_OBJ(cx, obj);
- SET_OBJ_INFO(obj, file, line);
- scope = OBJ_SCOPE(obj);
- JS_ASSERT(MAP_IS_NATIVE(&scope->map));
- if (scope->object == obj)
- sprop = SCOPE_GET_PROPERTY(scope, id);
- }
-
- cleanup:
- StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
- if (!ok || *propp)
- return ok;
- }
- }
-
- if (sprop) {
- JS_ASSERT(OBJ_SCOPE(obj) == scope);
- *objp = scope->object; /* XXXbe hide in jsscope.[ch] */
-
- *propp = (JSProperty *) sprop;
- return JS_TRUE;
- }
-
- proto = LOCKED_OBJ_GET_PROTO(obj);
- JS_UNLOCK_OBJ(cx, obj);
- if (!proto)
- break;
- if (!OBJ_IS_NATIVE(proto))
- return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
- obj = proto;
- }
-
-out:
- *objp = NULL;
- *propp = NULL;
- return JS_TRUE;
-}
-
-JS_FRIEND_API(JSBool)
-js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
- JSProperty **propp)
-{
- JSRuntime *rt;
- JSObject *obj, *pobj, *lastobj;
- JSScopeProperty *sprop;
- JSProperty *prop;
-
- rt = cx->runtime;
- obj = cx->fp->scopeChain;
- do {
- /* Try the property cache and return immediately on cache hit. */
- if (OBJ_IS_NATIVE(obj)) {
- JS_LOCK_OBJ(cx, obj);
- PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop);
- if (sprop) {
- JS_ASSERT(OBJ_IS_NATIVE(obj));
- *objp = obj;
- *pobjp = obj;
- *propp = (JSProperty *) sprop;
- return JS_TRUE;
- }
- JS_UNLOCK_OBJ(cx, obj);
- }
-
- /* If cache miss, take the slow path. */
- if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
- return JS_FALSE;
- if (prop) {
- if (OBJ_IS_NATIVE(pobj)) {
- sprop = (JSScopeProperty *) prop;
- PROPERTY_CACHE_FILL(&rt->propertyCache, pobj, id, sprop);
- }
- *objp = obj;
- *pobjp = pobj;
- *propp = prop;
- return JS_TRUE;
- }
- lastobj = obj;
- } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);
-
- *objp = lastobj;
- *pobjp = NULL;
- *propp = NULL;
- return JS_TRUE;
-}
-
-JSObject *
-js_FindIdentifierBase(JSContext *cx, jsid id)
-{
- JSObject *obj, *pobj;
- JSProperty *prop;
-
- /*
- * Look for id's property along the "with" statement chain and the
- * statically-linked scope chain.
- */
- if (!js_FindProperty(cx, id, &obj, &pobj, &prop))
- return NULL;
- if (prop) {
- OBJ_DROP_PROPERTY(cx, pobj, prop);
- return obj;
- }
-
- /*
- * Use the top-level scope from the scope chain, which won't end in the
- * same scope as cx->globalObject for cross-context function calls.
- */
- JS_ASSERT(obj);
-
- /*
- * Property not found. Give a strict warning if binding an undeclared
- * top-level variable.
- */
- if (JS_HAS_STRICT_OPTION(cx)) {
- JSString *str = JSVAL_TO_STRING(ID_TO_VALUE(id));
- if (!JS_ReportErrorFlagsAndNumber(cx,
- JSREPORT_WARNING | JSREPORT_STRICT,
- js_GetErrorMessage, NULL,
- JSMSG_UNDECLARED_VAR,
- JS_GetStringBytes(str))) {
- return NULL;
- }
- }
- return obj;
-}
-
-JSBool
-js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
-{
- JSObject *obj2;
- JSScopeProperty *sprop;
- JSScope *scope;
- uint32 slot;
-
- /*
- * Handle old bug that took empty string as zero index. Also convert
- * string indices to integers if appropriate.
- */
- CHECK_FOR_FUNNY_INDEX(id);
-
- if (!js_LookupProperty(cx, obj, id, &obj2, (JSProperty **)&sprop))
- return JS_FALSE;
- if (!sprop) {
- jsval default_val;
-
-#if JS_BUG_NULL_INDEX_PROPS
- /* Indexed properties defaulted to null in old versions. */
- default_val = (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0)
- ? JSVAL_NULL
- : JSVAL_VOID;
-#else
- default_val = JSVAL_VOID;
-#endif
- *vp = default_val;
-
- if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp))
- return JS_FALSE;
-
- /*
- * Give a strict warning if foo.bar is evaluated by a script for an
- * object foo with no property named 'bar'.
- */
- if (JS_HAS_STRICT_OPTION(cx) &&
- *vp == default_val &&
- cx->fp && cx->fp->pc &&
- (*cx->fp->pc == JSOP_GETPROP || *cx->fp->pc == JSOP_GETELEM))
- {
- jsbytecode *pc, *endpc;
- JSString *str;
-
- /* Kludge to allow (typeof foo == "undefined") tests. */
- JS_ASSERT(cx->fp->script);
- pc = cx->fp->pc;
- pc += js_CodeSpec[*pc].length;
- endpc = cx->fp->script->code + cx->fp->script->length;
- while (pc < endpc) {
- if (*pc == JSOP_TYPEOF)
- return JS_TRUE;
- if (*pc != JSOP_GROUP)
- break;
- pc++;
- }
-
- /* Ok, bad undefined property reference: whine about it. */
- str = js_DecompileValueGenerator(cx, JS_FALSE, ID_TO_VALUE(id),
- NULL);
- if (!str ||
- !JS_ReportErrorFlagsAndNumber(cx,
- JSREPORT_WARNING|JSREPORT_STRICT,
- js_GetErrorMessage, NULL,
- JSMSG_UNDEFINED_PROP,
- JS_GetStringBytes(str))) {
- return JS_FALSE;
- }
- }
- return JS_TRUE;
- }
-
- if (!OBJ_IS_NATIVE(obj2)) {
- OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);
- return OBJ_GET_PROPERTY(cx, obj2, id, vp);
- }
-
- /* Unlock obj2 before calling getter, relock after to avoid deadlock. */
- scope = OBJ_SCOPE(obj2);
- slot = sprop->slot;
- if (slot != SPROP_INVALID_SLOT) {
- JS_ASSERT(slot < obj2->map->freeslot);
- *vp = LOCKED_OBJ_GET_SLOT(obj2, slot);
-
- /* If sprop has a stub getter, we're done. */
- if (!sprop->getter)
- goto out;
- } else {
- *vp = JSVAL_VOID;
- }
-
- JS_UNLOCK_SCOPE(cx, scope);
- if (!SPROP_GET(cx, sprop, obj, obj2, vp))
- return JS_FALSE;
- JS_LOCK_SCOPE(cx, scope);
-
- if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
- LOCKED_OBJ_SET_SLOT(obj2, slot, *vp);
- PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj2, id, sprop);
- }
-
-out:
- JS_UNLOCK_SCOPE(cx, scope);
- return JS_TRUE;
-}
-
-JSBool
-js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
-{
- JSObject *pobj;
- JSScopeProperty *sprop;
- JSScope *scope;
- uintN attrs, flags;
- intN shortid;
- JSClass *clasp;
- JSPropertyOp getter, setter;
- jsval pval;
- uint32 slot;
-
- /*
- * Handle old bug that took empty string as zero index. Also convert
- * string indices to integers if appropriate.
- */
- CHECK_FOR_FUNNY_INDEX(id);
-
- if (!js_LookupProperty(cx, obj, id, &pobj, (JSProperty **)&sprop))
- return JS_FALSE;
-
- if (sprop && !OBJ_IS_NATIVE(pobj)) {
- OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
- sprop = NULL;
- }
-
- /*
- * Now either sprop is null, meaning id was not found in obj or one of its
- * prototypes; or sprop is non-null, meaning id was found in pobj's scope.
- * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop
- * is held: we must OBJ_DROP_PROPERTY or JS_UNLOCK_SCOPE before we return
- * (the two are equivalent for native objects, but we use JS_UNLOCK_SCOPE
- * because it is cheaper).
- */
- attrs = JSPROP_ENUMERATE;
- flags = 0;
- shortid = 0;
- clasp = OBJ_GET_CLASS(cx, obj);
- getter = clasp->getProperty;
- setter = clasp->setProperty;
-
- if (sprop) {
- /*
- * Set scope for use below. It was locked by js_LookupProperty, and
- * we know pobj owns it (i.e., scope->object == pobj). Therefore we
- * optimize JS_UNLOCK_OBJ(cx, pobj) into JS_UNLOCK_SCOPE(cx, scope).
- */
- scope = OBJ_SCOPE(pobj);
-
- attrs = sprop->attrs;
- if ((attrs & JSPROP_READONLY) ||
- (SCOPE_IS_SEALED(scope) && pobj == obj)) {
- JS_UNLOCK_SCOPE(cx, scope);
- if ((attrs & JSPROP_READONLY) && JSVERSION_IS_ECMA(cx->version))
- return JS_TRUE;
- goto read_only_error;
- }
-
- if (pobj != obj) {
- /*
- * We found id in a prototype object: prepare to share or shadow.
- * NB: Thanks to the immutable, garbage-collected property tree
- * maintained by jsscope.c in cx->runtime, we needn't worry about
- * sprop going away behind our back after we've unlocked scope.
- */
- JS_UNLOCK_SCOPE(cx, scope);
-
- /* Don't clone a shared prototype property. */
- if (attrs & JSPROP_SHARED)
- return SPROP_SET(cx, sprop, obj, pobj, vp);
-
- /* Restore attrs to the ECMA default for new properties. */
- attrs = JSPROP_ENUMERATE;
-
- /*
- * Preserve the shortid, getter, and setter when shadowing any
- * property that has a shortid. An old API convention requires
- * that the property's getter and setter functions receive the
- * shortid, not id, when they are called on the shadow we are
- * about to create in obj's scope.
- */
- if (sprop->flags & SPROP_HAS_SHORTID) {
- flags = SPROP_HAS_SHORTID;
- shortid = sprop->shortid;
- getter = sprop->getter;
- setter = sprop->setter;
- }
-
- /*
- * Forget we found the proto-property now that we've copied any
- * needed member values.
- */
- sprop = NULL;
- }
-#ifdef __GNUC__ /* suppress bogus gcc warnings */
- } else {
- scope = NULL;
-#endif
- }
-
- if (!sprop) {
- if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj)
- goto read_only_error;
-
- /* Find or make a property descriptor with the right heritage. */
- JS_LOCK_OBJ(cx, obj);
- scope = js_GetMutableScope(cx, obj);
- if (!scope) {
- JS_UNLOCK_OBJ(cx, obj);
- return JS_FALSE;
- }
- if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
- attrs |= JSPROP_SHARED;
- sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
- SPROP_INVALID_SLOT, attrs, flags, shortid);
- if (!sprop) {
- JS_UNLOCK_SCOPE(cx, scope);
- return JS_FALSE;
- }
-
- /* XXXbe called with obj locked */
- if (!clasp->addProperty(cx, obj, SPROP_USERID(sprop), vp)) {
- (void) js_RemoveScopeProperty(cx, scope, id);
- JS_UNLOCK_SCOPE(cx, scope);
- return JS_FALSE;
- }
-
- /* Initialize new property value (passed to setter) to undefined. */
- if (SPROP_HAS_VALID_SLOT(sprop, scope))
- LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID);
-
- PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
- }
-
- /* Get the current property value from its slot. */
- slot = sprop->slot;
- if (slot != SPROP_INVALID_SLOT) {
- JS_ASSERT(slot < obj->map->freeslot);
- pval = LOCKED_OBJ_GET_SLOT(obj, slot);
-
- /* If sprop has a stub setter, keep scope locked and just store *vp. */
- if (!sprop->setter)
- goto set_slot;
- }
-
- /* Avoid deadlock by unlocking obj's scope while calling sprop's setter. */
- JS_UNLOCK_SCOPE(cx, scope);
-
- /* Let the setter modify vp before copying from it to obj->slots[slot]. */
- if (!SPROP_SET(cx, sprop, obj, obj, vp))
- return JS_FALSE;
-
- /* Relock obj's scope until we are done with sprop. */
- JS_LOCK_SCOPE(cx, scope);
-
- /*
- * Check whether sprop is still around (was not deleted), and whether it
- * has a slot (it may never have had one, or we may have lost a race with
- * someone who cleared scope).
- */
- if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
- set_slot:
- GC_POKE(cx, pval);
- LOCKED_OBJ_SET_SLOT(obj, slot, *vp);
- }
- JS_UNLOCK_SCOPE(cx, scope);
- return JS_TRUE;
-
- read_only_error: {
- JSString *str = js_DecompileValueGenerator(cx,
- JSDVG_IGNORE_STACK,
- ID_TO_VALUE(id),
- NULL);
- if (str) {
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_READ_ONLY,
- JS_GetStringBytes(str));
- }
- return JS_FALSE;
- }
-}
-
-JSBool
-js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
- uintN *attrsp)
-{
- JSBool noprop, ok;
- JSScopeProperty *sprop;
-
- noprop = !prop;
- if (noprop) {
- if (!js_LookupProperty(cx, obj, id, &obj, &prop))
- return JS_FALSE;
- if (!prop) {
- *attrsp = 0;
- return JS_TRUE;
- }
- if (!OBJ_IS_NATIVE(obj)) {
- ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp);
- OBJ_DROP_PROPERTY(cx, obj, prop);
- return ok;
- }
- }
- sprop = (JSScopeProperty *)prop;
- *attrsp = sprop->attrs;
- if (noprop)
- OBJ_DROP_PROPERTY(cx, obj, prop);
- return JS_TRUE;
-}
-
-JSBool
-js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
- uintN *attrsp)
-{
- JSBool noprop, ok;
- JSScopeProperty *sprop;
-
- noprop = !prop;
- if (noprop) {
- if (!js_LookupProperty(cx, obj, id, &obj, &prop))
- return JS_FALSE;
- if (!prop)
- return JS_TRUE;
- if (!OBJ_IS_NATIVE(obj)) {
- ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, attrsp);
- OBJ_DROP_PROPERTY(cx, obj, prop);
- return ok;
- }
- }
- sprop = (JSScopeProperty *)prop;
- sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
- *attrsp &
- ~(JSPROP_GETTER | JSPROP_SETTER), 0,
- sprop->getter, sprop->setter);
- if (noprop)
- OBJ_DROP_PROPERTY(cx, obj, prop);
- return (sprop != NULL);
-}
-
-JSBool
-js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
-{
-#if JS_HAS_PROP_DELETE
-
- JSObject *proto;
- JSProperty *prop;
- JSScopeProperty *sprop;
- JSString *str;
- JSScope *scope;
- JSBool ok;
-
- *rval = JSVERSION_IS_ECMA(cx->version) ? JSVAL_TRUE : JSVAL_VOID;
-
- /*
- * Handle old bug that took empty string as zero index. Also convert
- * string indices to integers if appropriate.
- */
- CHECK_FOR_FUNNY_INDEX(id);
-
- if (!js_LookupProperty(cx, obj, id, &proto, &prop))
- return JS_FALSE;
- if (!prop || proto != obj) {
- /*
- * If the property was found in a native prototype, check whether it's
- * shared and permanent. Such a property stands for direct properties
- * in all delegating objects, matching ECMA semantics without bloating
- * each delegating object.
- */
- if (prop) {
- if (OBJ_IS_NATIVE(proto)) {
- sprop = (JSScopeProperty *)prop;
- if (SPROP_IS_SHARED_PERMANENT(sprop))
- *rval = JSVAL_FALSE;
- }
- OBJ_DROP_PROPERTY(cx, proto, prop);
- if (*rval == JSVAL_FALSE)
- return JS_TRUE;
- }
-
- /*
- * If no property, or the property comes unshared or impermanent from
- * a prototype, call the class's delProperty hook, passing rval as the
- * result parameter.
- */
- return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, ID_TO_VALUE(id),
- rval);
- }
-
- sprop = (JSScopeProperty *)prop;
- if (sprop->attrs & JSPROP_PERMANENT) {
- OBJ_DROP_PROPERTY(cx, obj, prop);
- if (JSVERSION_IS_ECMA(cx->version)) {
- *rval = JSVAL_FALSE;
- return JS_TRUE;
- }
- str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
- ID_TO_VALUE(id), NULL);
- if (str) {
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_PERMANENT, JS_GetStringBytes(str));
- }
- return JS_FALSE;
- }
-
- /* XXXbe called with obj locked */
- if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, SPROP_USERID(sprop),
- rval)) {
- OBJ_DROP_PROPERTY(cx, obj, prop);
- return JS_FALSE;
- }
-
- scope = OBJ_SCOPE(obj);
- if (SPROP_HAS_VALID_SLOT(sprop, scope))
- GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot));
-
- PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, NULL);
- ok = js_RemoveScopeProperty(cx, scope, id);
- OBJ_DROP_PROPERTY(cx, obj, prop);
- return ok;
-
-#else /* !JS_HAS_PROP_DELETE */
-
- jsval null = JSVAL_NULL;
-
- *rval = JSVAL_VOID;
- return js_SetProperty(cx, obj, id, &null);
-
-#endif /* !JS_HAS_PROP_DELETE */
-}
-
-JSBool
-js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
-{
- jsval v;
- JSString *str;
-
- v = OBJECT_TO_JSVAL(obj);
- switch (hint) {
- case JSTYPE_STRING:
- /*
- * Propagate the exception if js_TryMethod finds an appropriate
- * method, and calling that method returned failure.
- */
- if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL,
- &v))
- return JS_FALSE;
-
- if (!JSVAL_IS_PRIMITIVE(v)) {
- if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
- return JS_FALSE;
-
- /*
- * JS1.2 never failed (except for malloc failure) to convert an
- * object to a string. ECMA requires an error if both toString
- * and valueOf fail to produce a primitive value.
- */
- if (!JSVAL_IS_PRIMITIVE(v) && cx->version == JSVERSION_1_2) {
- char *bytes = JS_smprintf("[object %s]",
- OBJ_GET_CLASS(cx, obj)->name);
- if (!bytes)
- return JS_FALSE;
- str = JS_NewString(cx, bytes, strlen(bytes));
- if (!str) {
- free(bytes);
- return JS_FALSE;
- }
- v = STRING_TO_JSVAL(str);
- goto out;
- }
- }
- break;
-
- default:
- if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
- return JS_FALSE;
- if (!JSVAL_IS_PRIMITIVE(v)) {
- JSType type = JS_TypeOfValue(cx, v);
- if (type == hint ||
- (type == JSTYPE_FUNCTION && hint == JSTYPE_OBJECT)) {
- goto out;
- }
- /* Don't convert to string (source object literal) for JS1.2. */
- if (cx->version == JSVERSION_1_2 && hint == JSTYPE_BOOLEAN)
- goto out;
- if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0,
- NULL, &v))
- return JS_FALSE;
- }
- break;
- }
- if (!JSVAL_IS_PRIMITIVE(v)) {
- /* Avoid recursive death through js_DecompileValueGenerator. */
- if (hint == JSTYPE_STRING) {
- str = JS_InternString(cx, OBJ_GET_CLASS(cx, obj)->name);
- if (!str)
- return JS_FALSE;
- } else {
- str = NULL;
- }
- *vp = OBJECT_TO_JSVAL(obj);
- str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, str);
- if (str) {
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_CANT_CONVERT_TO,
- JS_GetStringBytes(str),
- (hint == JSTYPE_VOID)
- ? "primitive type"
- : js_type_str[hint]);
- }
- return JS_FALSE;
- }
-out:
- *vp = v;
- return JS_TRUE;
-}
-
-JSIdArray *
-js_NewIdArray(JSContext *cx, jsint length)
-{
- JSIdArray *ida;
-
- ida = (JSIdArray *)
- JS_malloc(cx, sizeof(JSIdArray) + (length - 1) * sizeof(jsval));
- if (ida)
- ida->length = length;
- return ida;
-}
-
-JSIdArray *
-js_GrowIdArray(JSContext *cx, JSIdArray *ida, jsint length)
-{
- ida = (JSIdArray *)
- JS_realloc(cx, ida, sizeof(JSIdArray) + (length - 1) * sizeof(jsval));
- if (ida)
- ida->length = length;
- return ida;
-}
-
-/* Private type used to iterate over all properties of a native JS object */
-typedef struct JSNativeIteratorState {
- jsint next_index; /* index into jsid array */
- JSIdArray *ida; /* All property ids in enumeration */
-} JSNativeIteratorState;
-
-/*
- * This function is used to enumerate the properties of native JSObjects
- * and those host objects that do not define a JSNewEnumerateOp-style iterator
- * function.
- */
-JSBool
-js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
- jsval *statep, jsid *idp)
-{
- JSObject *proto_obj;
- JSClass *clasp;
- JSEnumerateOp enumerate;
- JSScopeProperty *sprop, *lastProp;
- jsint i, length;
- JSScope *scope;
- JSIdArray *ida;
- JSNativeIteratorState *state;
-
- clasp = OBJ_GET_CLASS(cx, obj);
- enumerate = clasp->enumerate;
- if (clasp->flags & JSCLASS_NEW_ENUMERATE)
- return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
-
- switch (enum_op) {
-
- case JSENUMERATE_INIT:
- if (!enumerate(cx, obj))
- goto init_error;
- length = 0;
-
- /*
- * The set of all property ids is pre-computed when the iterator
- * is initialized so as to avoid problems with properties being
- * deleted during the iteration.
- */
- JS_LOCK_OBJ(cx, obj);
- scope = OBJ_SCOPE(obj);
-
- /*
- * If this object shares a scope with its prototype, don't enumerate
- * its properties. Otherwise they will be enumerated a second time
- * when the prototype object is enumerated.
- */
- proto_obj = OBJ_GET_PROTO(cx, obj);
- if (proto_obj && scope == OBJ_SCOPE(proto_obj)) {
- ida = js_NewIdArray(cx, 0);
- if (!ida) {
- JS_UNLOCK_OBJ(cx, obj);
- goto init_error;
- }
- } else {
- /* Object has a private scope; Enumerate all props in scope. */
- for (sprop = lastProp = SCOPE_LAST_PROP(scope); sprop;
- sprop = sprop->parent) {
- if ((sprop->attrs & JSPROP_ENUMERATE) &&
- !(sprop->flags & SPROP_IS_ALIAS) &&
- (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
- SCOPE_HAS_PROPERTY(scope, sprop))) {
- length++;
- }
- }
- ida = js_NewIdArray(cx, length);
- if (!ida) {
- JS_UNLOCK_OBJ(cx, obj);
- goto init_error;
- }
- i = length;
- for (sprop = lastProp; sprop; sprop = sprop->parent) {
- if ((sprop->attrs & JSPROP_ENUMERATE) &&
- !(sprop->flags & SPROP_IS_ALIAS) &&
- (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
- SCOPE_HAS_PROPERTY(scope, sprop))) {
- JS_ASSERT(i > 0);
- ida->vector[--i] = sprop->id;
- }
- }
- }
- JS_UNLOCK_OBJ(cx, obj);
-
- state = (JSNativeIteratorState *)
- JS_malloc(cx, sizeof(JSNativeIteratorState));
- if (!state) {
- JS_DestroyIdArray(cx, ida);
- goto init_error;
- }
- state->ida = ida;
- state->next_index = 0;
- *statep = PRIVATE_TO_JSVAL(state);
- if (idp)
- *idp = INT_TO_JSVAL(length);
- return JS_TRUE;
-
- case JSENUMERATE_NEXT:
- state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
- ida = state->ida;
- length = ida->length;
- if (state->next_index != length) {
- *idp = ida->vector[state->next_index++];
- return JS_TRUE;
- }
-
- /* Fall through ... */
-
- case JSENUMERATE_DESTROY:
- state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
- JS_DestroyIdArray(cx, state->ida);
- JS_free(cx, state);
- *statep = JSVAL_NULL;
- return JS_TRUE;
-
- default:
- JS_ASSERT(0);
- return JS_FALSE;
- }
-
-init_error:
- *statep = JSVAL_NULL;
- return JS_FALSE;
-}
-
-JSBool
-js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
- jsval *vp, uintN *attrsp)
-{
- JSObject *pobj;
- JSProperty *prop;
- JSScopeProperty *sprop;
- JSClass *clasp;
- JSBool ok;
-
- if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
- return JS_FALSE;
- if (!prop) {
- *vp = JSVAL_VOID;
- *attrsp = 0;
- clasp = OBJ_GET_CLASS(cx, obj);
- return !clasp->checkAccess ||
- clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp);
- }
- if (!OBJ_IS_NATIVE(pobj)) {
- OBJ_DROP_PROPERTY(cx, pobj, prop);
- return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp);
- }
- sprop = (JSScopeProperty *)prop;
- *vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)))
- ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
- : JSVAL_VOID;
- *attrsp = sprop->attrs;
- clasp = LOCKED_OBJ_GET_CLASS(obj);
- if (clasp->checkAccess) {
- JS_UNLOCK_OBJ(cx, pobj);
- ok = clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp);
- JS_LOCK_OBJ(cx, pobj);
- } else {
- ok = JS_TRUE;
- }
- OBJ_DROP_PROPERTY(cx, pobj, prop);
- return ok;
-}
-
-#ifdef JS_THREADSAFE
-void
-js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
-{
- JS_UNLOCK_OBJ(cx, obj);
-}
-#endif
-
-static void
-ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
-{
- /*
- * The decompiler may need to access the args of the function in
- * progress rather than the one we had hoped to call.
- * So we switch the cx->fp to the frame below us. We stick the
- * current frame in the dormantFrameChain to protect it from gc.
- */
-
- JSStackFrame *fp = cx->fp;
- if (fp->down) {
- JS_ASSERT(!fp->dormantNext);
- fp->dormantNext = cx->dormantFrameChain;
- cx->dormantFrameChain = fp;
- cx->fp = fp->down;
- }
-
- js_ReportIsNotFunction(cx, vp, flags);
-
- if (fp->down) {
- JS_ASSERT(cx->dormantFrameChain == fp);
- cx->dormantFrameChain = fp->dormantNext;
- fp->dormantNext = NULL;
- cx->fp = fp;
- }
-}
-
-JSBool
-js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- JSClass *clasp;
-
- clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
- if (!clasp->call) {
- ReportIsNotFunction(cx, &argv[-2], 0);
- return JS_FALSE;
- }
- return clasp->call(cx, obj, argc, argv, rval);
-}
-
-JSBool
-js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- JSClass *clasp;
-
- clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
- if (!clasp->construct) {
- ReportIsNotFunction(cx, &argv[-2], JSV2F_CONSTRUCT);
- return JS_FALSE;
- }
- return clasp->construct(cx, obj, argc, argv, rval);
-}
-
-JSBool
-js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
-{
- JSClass *clasp;
-
- clasp = OBJ_GET_CLASS(cx, obj);
- if (clasp->hasInstance)
- return clasp->hasInstance(cx, obj, v, bp);
- *bp = JS_FALSE;
- return JS_TRUE;
-}
-
-JSBool
-js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
-{
- JSObject *obj2;
-
- *bp = JS_FALSE;
- if (JSVAL_IS_PRIMITIVE(v))
- return JS_TRUE;
- obj2 = JSVAL_TO_OBJECT(v);
- while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL) {
- if (obj2 == obj) {
- *bp = JS_TRUE;
- break;
- }
- }
- return JS_TRUE;
-}
-
-JSBool
-js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop)
-{
- return GetClassPrototype(cx, NULL, name, protop);
-}
-
-static JSBool
-GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
- JSObject **protop)
-{
- jsval v;
- JSObject *ctor;
-
- if (!FindConstructor(cx, scope, name, &v))
- return JS_FALSE;
- if (JSVAL_IS_FUNCTION(cx, v)) {
- ctor = JSVAL_TO_OBJECT(v);
- if (!OBJ_GET_PROPERTY(cx, ctor,
- (jsid)cx->runtime->atomState.classPrototypeAtom,
- &v)) {
- return JS_FALSE;
- }
- }
- *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;
- return JS_TRUE;
-}
-
-JSBool
-js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,
- uintN attrs)
-{
- /*
- * Use the given attributes for the prototype property of the constructor,
- * as user-defined constructors have a DontEnum | DontDelete prototype (it
- * may be reset), while native or "system" constructors require DontEnum |
- * ReadOnly | DontDelete.
- */
- if (!OBJ_DEFINE_PROPERTY(cx, ctor,
- (jsid)cx->runtime->atomState.classPrototypeAtom,
- OBJECT_TO_JSVAL(proto), NULL, NULL,
- attrs, NULL)) {
- return JS_FALSE;
- }
-
- /*
- * ECMA says that Object.prototype.constructor, or f.prototype.constructor
- * for a user-defined function f, is DontEnum.
- */
- return OBJ_DEFINE_PROPERTY(cx, proto,
- (jsid)cx->runtime->atomState.constructorAtom,
- OBJECT_TO_JSVAL(ctor), NULL, NULL,
- 0, NULL);
-}
-
-JSBool
-js_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
-{
- JSObject *obj;
-
- if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
- obj = NULL;
- } else if (JSVAL_IS_OBJECT(v)) {
- obj = JSVAL_TO_OBJECT(v);
- if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v))
- return JS_FALSE;
- if (JSVAL_IS_OBJECT(v))
- obj = JSVAL_TO_OBJECT(v);
- } else {
- if (JSVAL_IS_STRING(v)) {
- obj = js_StringToObject(cx, JSVAL_TO_STRING(v));
- } else if (JSVAL_IS_INT(v)) {
- obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v));
- } else if (JSVAL_IS_DOUBLE(v)) {
- obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v));
- } else {
- JS_ASSERT(JSVAL_IS_BOOLEAN(v));
- obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v));
- }
- if (!obj)
- return JS_FALSE;
- }
- *objp = obj;
- return JS_TRUE;
-}
-
-JSObject *
-js_ValueToNonNullObject(JSContext *cx, jsval v)
-{
- JSObject *obj;
- JSString *str;
-
- if (!js_ValueToObject(cx, v, &obj))
- return NULL;
- if (!obj) {
- str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
- if (str) {
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_NO_PROPERTIES, JS_GetStringBytes(str));
- }
- }
- return obj;
-}
-
-JSBool
-js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval)
-{
-#if JS_HAS_VALUEOF_HINT
- jsval argv[1];
-
- argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]);
- return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv,
- rval);
-#else
- return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 0, NULL,
- rval);
-#endif
-}
-
-JSBool
-js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
- uintN argc, jsval *argv, jsval *rval)
-{
- JSErrorReporter older;
- jsval fval;
- JSBool ok;
-
- /*
- * Report failure only if an appropriate method was found, and calling it
- * returned failure. We propagate failure in this case to make exceptions
- * behave properly.
- */
- older = JS_SetErrorReporter(cx, NULL);
- if (OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &fval) &&
- !JSVAL_IS_PRIMITIVE(fval)) {
- ok = js_InternalCall(cx, obj, fval, argc, argv, rval);
- } else {
- ok = JS_TRUE;
- }
- JS_SetErrorReporter(cx, older);
- return ok;
-}
-
-#if JS_HAS_XDR
-
-#include "jsxdrapi.h"
-
-JSBool
-js_XDRObject(JSXDRState *xdr, JSObject **objp)
-{
- JSContext *cx;
- JSClass *clasp;
- const char *className;
- uint32 classId, classDef;
- JSBool ok;
- JSObject *proto;
-
- cx = xdr->cx;
- if (xdr->mode == JSXDR_ENCODE) {
- clasp = OBJ_GET_CLASS(cx, *objp);
- className = clasp->name;
- classId = JS_XDRFindClassIdByName(xdr, className);
- classDef = !classId;
- if (classDef && !JS_XDRRegisterClass(xdr, clasp, &classId))
- return JS_FALSE;
- } else {
- classDef = 0;
- className = NULL;
- clasp = NULL; /* quell GCC overwarning */
- }
-
- /* XDR a flag word followed (if true) by the class name. */
- if (!JS_XDRUint32(xdr, &classDef))
- return JS_FALSE;
- if (classDef && !JS_XDRCString(xdr, (char **) &className))
- return JS_FALSE;
-
- /* From here on, return through out: to free className if it was set. */
- ok = JS_XDRUint32(xdr, &classId);
- if (!ok)
- goto out;
-
- if (xdr->mode != JSXDR_ENCODE) {
- if (classDef) {
- ok = js_GetClassPrototype(cx, className, &proto);
- if (!ok)
- goto out;
- clasp = OBJ_GET_CLASS(cx, proto);
- ok = JS_XDRRegisterClass(xdr, clasp, &classId);
- if (!ok)
- goto out;
- } else {
- clasp = JS_XDRFindClassById(xdr, classId);
- if (!clasp) {
- char numBuf[12];
- JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId);
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_CANT_FIND_CLASS, numBuf);
- ok = JS_FALSE;
- goto out;
- }
- }
- }
-
- if (!clasp->xdrObject) {
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_CANT_XDR_CLASS, clasp->name);
- ok = JS_FALSE;
- } else {
- ok = clasp->xdrObject(xdr, objp);
- }
-out:
- if (xdr->mode != JSXDR_ENCODE && className)
- JS_free(cx, (void *)className);
- return ok;
-}
-
-#endif /* JS_HAS_XDR */
-
-#ifdef DEBUG_brendan
-
-#include <stdio.h>
-#include <math.h>
-
-uint32 js_entry_count_max;
-uint32 js_entry_count_sum;
-double js_entry_count_sqsum;
-uint32 js_entry_count_hist[11];
-
-static void
-MeterEntryCount(uintN count)
-{
- if (count) {
- js_entry_count_sum += count;
- js_entry_count_sqsum += (double)count * count;
- if (count > js_entry_count_max)
- js_entry_count_max = count;
- }
- js_entry_count_hist[JS_MIN(count, 10)]++;
-}
-
-void
-js_DumpScopeMeters(JSRuntime *rt)
-{
- static FILE *logfp;
- if (!logfp)
- logfp = fopen("/tmp/scope.stats", "a");
-
- {
- double mean = 0., var = 0., sigma = 0.;
- double nscopes = rt->liveScopes;
- double nentrys = js_entry_count_sum;
- if (nscopes > 0 && nentrys >= 0) {
- mean = nentrys / nscopes;
- var = nscopes * js_entry_count_sqsum - nentrys * nentrys;
- if (var < 0.0 || nscopes <= 1)
- var = 0.0;
- else
- var /= nscopes * (nscopes - 1);
-
- /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
- sigma = (var != 0.) ? sqrt(var) : 0.;
- }
-
- fprintf(logfp,
- "scopes %g entries %g mean %g sigma %g max %u",
- nscopes, nentrys, mean, sigma, js_entry_count_max);
- }
-
- fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u\n",
- js_entry_count_hist[0], js_entry_count_hist[1],
- js_entry_count_hist[2], js_entry_count_hist[3],
- js_entry_count_hist[4], js_entry_count_hist[5],
- js_entry_count_hist[6], js_entry_count_hist[7],
- js_entry_count_hist[8], js_entry_count_hist[9],
- js_entry_count_hist[10]);
- js_entry_count_sum = js_entry_count_max = 0;
- js_entry_count_sqsum = 0;
- memset(js_entry_count_hist, 0, sizeof js_entry_count_hist);
- fflush(logfp);
-}
-
-#endif /* DEBUG_brendan */
-
-uint32
-js_Mark(JSContext *cx, JSObject *obj, void *arg)
-{
- JSScope *scope;
- JSScopeProperty *sprop;
- JSClass *clasp;
-
- JS_ASSERT(OBJ_IS_NATIVE(obj));
- scope = OBJ_SCOPE(obj);
-#ifdef DEBUG_brendan
- if (scope->object == obj)
- MeterEntryCount(scope->entryCount);
-#endif
-
- JS_ASSERT(!SCOPE_LAST_PROP(scope) ||
- SCOPE_HAS_PROPERTY(scope, SCOPE_LAST_PROP(scope)));
-
- for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
- if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
- continue;
- MARK_SCOPE_PROPERTY(sprop);
- if (!JSVAL_IS_INT(sprop->id))
- GC_MARK_ATOM(cx, (JSAtom *)sprop->id, arg);
-
-#if JS_HAS_GETTER_SETTER
- if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
-#ifdef GC_MARK_DEBUG
- char buf[64];
- JSAtom *atom = (JSAtom *)sprop->id;
- const char *id = (atom && ATOM_IS_STRING(atom))
- ? JS_GetStringBytes(ATOM_TO_STRING(atom))
- : "unknown";
-#endif
-
- if (sprop->attrs & JSPROP_GETTER) {
-#ifdef GC_MARK_DEBUG
- JS_snprintf(buf, sizeof buf, "%s %s",
- id, js_getter_str);
-#endif
- GC_MARK(cx,
- JSVAL_TO_GCTHING((jsval) sprop->getter),
- buf,
- arg);
- }
- if (sprop->attrs & JSPROP_SETTER) {
-#ifdef GC_MARK_DEBUG
- JS_snprintf(buf, sizeof buf, "%s %s",
- id, js_setter_str);
-#endif
- GC_MARK(cx,
- JSVAL_TO_GCTHING((jsval) sprop->setter),
- buf,
- arg);
- }
- }
-#endif /* JS_HAS_GETTER_SETTER */
- }
-
- /* No one runs while the GC is running, so we can use LOCKED_... here. */
- clasp = LOCKED_OBJ_GET_CLASS(obj);
- if (clasp->mark)
- (void) clasp->mark(cx, obj, arg);
-
- if (scope->object != obj) {
- /*
- * An unmutated object that shares a prototype's scope. We can't tell
- * how many slots are allocated and in use at obj->slots by looking at
- * scope, so we get obj->slots' length from its -1'st element.
- */
- return (uint32) obj->slots[-1];
- }
- return JS_MIN(obj->map->freeslot, obj->map->nslots);
-}
-
-void
-js_Clear(JSContext *cx, JSObject *obj)
-{
- JSScope *scope;
- JSRuntime *rt;
- JSScopeProperty *sprop;
- uint32 i, n;
-
- /*
- * Clear our scope and the property cache of all obj's properties only if
- * obj owns the scope (i.e., not if obj is unmutated and therefore sharing
- * its prototype's scope). NB: we do not clear any reserved slots lying
- * below JSSLOT_FREE(clasp).
- */
- JS_LOCK_OBJ(cx, obj);
- scope = OBJ_SCOPE(obj);
- if (scope->object == obj) {
- /* Clear the property cache before we clear the scope. */
- rt = cx->runtime;
- for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
- if (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
- SCOPE_HAS_PROPERTY(scope, sprop)) {
- PROPERTY_CACHE_FILL(&rt->propertyCache, obj, sprop->id, NULL);
- }
- }
-
- /* Now that we're done using scope->lastProp/table, clear scope. */
- js_ClearScope(cx, scope);
-
- /* Clear slot values and reset freeslot so we're consistent. */
- i = scope->map.nslots;
- n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj));
- while (--i >= n)
- obj->slots[i] = JSVAL_VOID;
- scope->map.freeslot = n;
- }
- JS_UNLOCK_OBJ(cx, obj);
-}
-
-jsval
-js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot)
-{
- jsval v;
-
- JS_LOCK_OBJ(cx, obj);
- v = (slot < (uint32) obj->slots[-1]) ? obj->slots[slot] : JSVAL_VOID;
- JS_UNLOCK_OBJ(cx, obj);
- return v;
-}
-
-void
-js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
-{
- uint32 nslots, rlimit, i;
- JSClass *clasp;
- jsval *newslots;
-
- JS_LOCK_OBJ(cx, obj);
- nslots = (uint32) obj->slots[-1];
- if (slot >= nslots) {
- clasp = LOCKED_OBJ_GET_CLASS(obj);
- rlimit = JSSLOT_START(clasp) + JSCLASS_RESERVED_SLOTS(clasp);
- JS_ASSERT(slot < rlimit);
- if (rlimit > nslots)
- nslots = rlimit;
-
- newslots = (jsval *)
- JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval));
- if (!newslots) {
- JS_UNLOCK_OBJ(cx, obj);
- return;
- }
- for (i = 1 + newslots[0]; i <= rlimit; i++)
- newslots[i] = JSVAL_VOID;
- newslots[0] = nslots;
- if (OBJ_SCOPE(obj)->object == obj)
- obj->map->nslots = nslots;
- obj->slots = newslots + 1;
- }
-
- obj->slots[slot] = v;
- JS_UNLOCK_OBJ(cx, obj);
-}
-
-#ifdef DEBUG
-
-/* Routines to print out values during debugging. */
-
-void printChar(jschar *cp) {
- fprintf(stderr, "jschar* (0x%p) \"", (void *)cp);
- while (*cp)
- fputc(*cp++, stderr);
- fputc('"', stderr);
- fputc('\n', stderr);
-}
-
-void printString(JSString *str) {
- size_t i, n;
- jschar *s;
- fprintf(stderr, "string (0x%p) \"", (void *)str);
- s = JSSTRING_CHARS(str);
- for (i=0, n=JSSTRING_LENGTH(str); i < n; i++)
- fputc(s[i], stderr);
- fputc('"', stderr);
- fputc('\n', stderr);
-}
-
-void printVal(JSContext *cx, jsval val);
-
-void printObj(JSContext *cx, JSObject *jsobj) {
- jsuint i;
- jsval val;
- JSClass *clasp;
-
- fprintf(stderr, "object 0x%p\n", (void *)jsobj);
- clasp = OBJ_GET_CLASS(cx, jsobj);
- fprintf(stderr, "class 0x%p %s\n", (void *)clasp, clasp->name);
- for (i=0; i < jsobj->map->nslots; i++) {
- fprintf(stderr, "slot %3d ", i);
- val = jsobj->slots[i];
- if (JSVAL_IS_OBJECT(val))
- fprintf(stderr, "object 0x%p\n", (void *)JSVAL_TO_OBJECT(val));
- else
- printVal(cx, val);
- }
-}
-
-void printVal(JSContext *cx, jsval val) {
- fprintf(stderr, "val %d (0x%p) = ", (int)val, (void *)val);
- if (JSVAL_IS_NULL(val)) {
- fprintf(stderr, "null\n");
- } else if (JSVAL_IS_VOID(val)) {
- fprintf(stderr, "undefined\n");
- } else if (JSVAL_IS_OBJECT(val)) {
- printObj(cx, JSVAL_TO_OBJECT(val));
- } else if (JSVAL_IS_INT(val)) {
- fprintf(stderr, "(int) %d\n", JSVAL_TO_INT(val));
- } else if (JSVAL_IS_STRING(val)) {
- printString(JSVAL_TO_STRING(val));
- } else if (JSVAL_IS_DOUBLE(val)) {
- fprintf(stderr, "(double) %g\n", *JSVAL_TO_DOUBLE(val));
- } else {
- JS_ASSERT(JSVAL_IS_BOOLEAN(val));
- fprintf(stderr, "(boolean) %s\n",
- JSVAL_TO_BOOLEAN(val) ? "true" : "false");
- }
- fflush(stderr);
-}
-
-void printId(JSContext *cx, jsid id) {
- fprintf(stderr, "id %d (0x%p) is ", (int)id, (void *)id);
- printVal(cx, ID_TO_VALUE(id));
-}
-
-void printAtom(JSAtom *atom) {
- printString(ATOM_TO_STRING(atom));
-}
-
-#endif