/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsobj.cpp
C++ | 2127 lines | 1648 code | 232 blank | 247 comment | 338 complexity | 9c5a81c78934020d816cc510d7eacc68 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
- /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sw=4 et tw=78:
- *
- * ***** 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 "jsbit.h"
- #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 "jsbuiltins.h"
- #include "jscntxt.h"
- #include "jsversion.h"
- #include "jsemit.h"
- #include "jsfun.h"
- #include "jsgc.h"
- #include "jsinterp.h"
- #include "jslock.h"
- #include "jsnum.h"
- #include "jsobj.h"
- #include "jsopcode.h"
- #include "jsparse.h"
- #include "jsscope.h"
- #include "jsscript.h"
- #include "jsstr.h"
- #include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */
- #include "jsstaticcheck.h"
- #if JS_HAS_GENERATORS
- #include "jsiter.h"
- #endif
- #if JS_HAS_XML_SUPPORT
- #include "jsxml.h"
- #endif
- #if JS_HAS_XDR
- #include "jsxdrapi.h"
- #endif
- #ifdef INCLUDE_MOZILLA_DTRACE
- #include "jsdtracef.h"
- #endif
- #include "jsautooplen.h"
- #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
- JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
- js_NewObjectMap, js_DestroyObjectMap,
- js_LookupProperty, js_DefineProperty,
- 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_TraceObject, js_Clear,
- js_GetRequiredSlot, js_SetRequiredSlot
- };
- JSClass js_ObjectClass = {
- js_Object_str,
- JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
- 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_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
- obj_getCount, NULL},
- {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;
- jsid propid;
- JSAccessMode mode;
- uintN attrs;
- JSObject *pobj;
- JSClass *clasp;
- JSExtendedClass *xclasp;
- slot = (uint32) JSVAL_TO_INT(id);
- if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
- propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
- mode = JSACC_PROTO;
- } else {
- propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
- mode = JSACC_PARENT;
- }
- /* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */
- if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs))
- return JS_FALSE;
- pobj = JSVAL_TO_OBJECT(*vp);
- if (pobj) {
- clasp = OBJ_GET_CLASS(cx, pobj);
- if (clasp == &js_CallClass || clasp == &js_BlockClass) {
- /* Censor activations and lexical scopes per ECMA-262. */
- *vp = JSVAL_NULL;
- } else if (clasp->flags & JSCLASS_IS_EXTENDED) {
- xclasp = (JSExtendedClass *) clasp;
- if (xclasp->outerObject) {
- pobj = xclasp->outerObject(cx, pobj);
- if (!pobj)
- return JS_FALSE;
- *vp = OBJECT_TO_JSVAL(pobj);
- }
- }
- }
- return JS_TRUE;
- }
- static JSBool
- obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
- {
- JSObject *pobj;
- uint32 slot;
- jsid propid;
- uintN attrs;
- if (!JSVAL_IS_OBJECT(*vp))
- return JS_TRUE;
- pobj = JSVAL_TO_OBJECT(*vp);
- if (pobj) {
- /*
- * Innerize pobj here to avoid sticking unwanted properties on the
- * outer object. This ensures that any with statements only grant
- * access to the inner object.
- */
- OBJ_TO_INNER_OBJECT(cx, pobj);
- if (!pobj)
- return JS_FALSE;
- }
- 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. */
- propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
- if (!OBJ_CHECK_ACCESS(cx, obj, propid,
- (JSAccessMode)(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)
- {
- JSSetSlotRequest ssr;
- JSRuntime *rt;
- /* Optimize the null case to avoid the unnecessary overhead of js_GC. */
- if (!pobj) {
- JS_LOCK_OBJ(cx, obj);
- if (slot == JSSLOT_PROTO && !js_GetMutableScope(cx, obj)) {
- JS_UNLOCK_OBJ(cx, obj);
- return JS_FALSE;
- }
- LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_NULL);
- JS_UNLOCK_OBJ(cx, obj);
- return JS_TRUE;
- }
- ssr.obj = obj;
- ssr.pobj = pobj;
- ssr.slot = (uint16) slot;
- ssr.errnum = (uint16) JSMSG_NOT_AN_ERROR;
- rt = cx->runtime;
- JS_LOCK_GC(rt);
- ssr.next = rt->setSlotRequests;
- rt->setSlotRequests = &ssr;
- for (;;) {
- js_GC(cx, GC_SET_SLOT_REQUEST);
- JS_UNLOCK_GC(rt);
- if (!rt->setSlotRequests)
- break;
- JS_LOCK_GC(rt);
- }
- if (ssr.errnum != JSMSG_NOT_AN_ERROR) {
- if (ssr.errnum == JSMSG_OUT_OF_MEMORY) {
- JS_ReportOutOfMemory(cx);
- } else {
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, ssr.errnum,
- #if JS_HAS_OBJ_PROTO_PROP
- object_props[slot].name
- #else
- (slot == JSSLOT_PROTO) ? js_proto_str
- : js_parent_str
- #endif
- );
- }
- return JS_FALSE;
- }
- // Maintain the "any Array prototype has indexed properties hazard" flag.
- if (slot == JSSLOT_PROTO &&
- OBJ_IS_ARRAY(cx, pobj) &&
- pobj->fslots[JSSLOT_ARRAY_LENGTH] != 0) {
- rt->anyArrayProtoHasElement = JS_TRUE;
- }
- return JS_TRUE;
- }
- static JSHashNumber
- js_hash_object(const void *key)
- {
- return (JSHashNumber)JS_PTR_TO_UINT32(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;
- JS_CHECK_RECURSION(cx, return NULL);
- 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,
- JS_UINT32_TO_PTR(sharpid));
- if (!he) {
- JS_ReportOutOfMemory(cx);
- return NULL;
- }
- /*
- * Increment map->depth to protect js_EnterSharpObject from reentering
- * itself badly. Without this fix, if we reenter the basis case where
- * map->depth == 0, when unwinding the inner call we will destroy the
- * newly-created hash table and crash.
- */
- ++map->depth;
- ida = JS_Enumerate(cx, obj);
- --map->depth;
- 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)
- continue;
- 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 = JS_PTR_TO_UINT32(he->value);
- if (sharpid == 0) {
- sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
- he->value = JS_UINT32_TO_PTR(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;
- if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_ENTER_SHARP))
- return NULL;
- /* 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;
- JS_KEEP_ATOMS(cx->runtime);
- }
- /* From this point the control must flow either through out: or bad:. */
- ida = NULL;
- if (map->depth == 0) {
- he = MarkSharpObjects(cx, obj, &ida);
- if (!he)
- goto bad;
- JS_ASSERT((JS_PTR_TO_UINT32(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;
- }
- sharpid = 0;
- goto out;
- }
- }
- sharpid = JS_PTR_TO_UINT32(he->value);
- if (sharpid != 0) {
- 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) {
- JS_UNKEEP_ATOMS(cx->runtime);
- 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) {
- JS_UNKEEP_ATOMS(cx->runtime);
- map->sharpgen = 0;
- JS_HashTableDestroy(map->table);
- map->table = NULL;
- }
- if (idap) {
- ida = *idap;
- if (ida) {
- JS_DestroyIdArray(cx, ida);
- *idap = NULL;
- }
- }
- }
- static intN
- gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg)
- {
- JS_CALL_OBJECT_TRACER((JSTracer *)arg, (JSObject *)he->key,
- "sharp table entry");
- return JS_DHASH_NEXT;
- }
- void
- js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map)
- {
- JS_ASSERT(map->depth > 0);
- JS_ASSERT(map->table);
- /*
- * During recursive calls to MarkSharpObjects a non-native object or
- * object with a custom getProperty method can potentially return an
- * unrooted value or even cut from the object graph an argument of one of
- * MarkSharpObjects recursive invocations. So we must protect map->table
- * entries against GC.
- *
- * We can not simply use JSTempValueRooter to mark the obj argument of
- * MarkSharpObjects during recursion as we have to protect *all* entries
- * in JSSharpObjectMap including those that contains otherwise unreachable
- * objects just allocated through custom getProperty. Otherwise newer
- * allocations can re-use the address of an object stored in the hashtable
- * confusing js_EnterSharpObject. So to address the problem we simply
- * mark all objects from map->table.
- *
- * An alternative "proper" solution is to use JSTempValueRooter in
- * MarkSharpObjects with code to remove during finalization entries
- * with otherwise unreachable objects. But this is way too complex
- * to justify spending efforts.
- */
- JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, trc);
- }
- #if JS_HAS_TOSOURCE
- static JSBool
- obj_toSource(JSContext *cx, uintN argc, jsval *vp)
- {
- JSBool ok, outermost;
- JSObject *obj;
- JSHashEntry *he;
- JSIdArray *ida;
- jschar *chars, *ochars, *vsharp;
- const jschar *idstrchars, *vchars;
- size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen;
- const char *comma;
- jsint i, j, length, valcnt;
- jsid id;
- #if JS_HAS_GETTER_SETTER
- JSObject *obj2;
- JSProperty *prop;
- uintN attrs;
- #endif
- jsval *val;
- jsval localroot[4] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL, JSVAL_NULL};
- JSTempValueRooter tvr;
- JSString *gsopold[2];
- JSString *gsop[2];
- JSString *idstr, *valstr, *str;
- JS_CHECK_RECURSION(cx, return JS_FALSE);
- MUST_FLOW_THROUGH("out");
- JS_PUSH_TEMP_ROOT(cx, 4, localroot, &tvr);
- /* If outermost, we need parentheses to be an expression, not a block. */
- outermost = (cx->sharpObjectMap.depth == 0);
- obj = JS_THIS_OBJECT(cx, vp);
- if (!obj || !(he = js_EnterSharpObject(cx, obj, &ida, &chars))) {
- ok = JS_FALSE;
- goto out;
- }
- 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;
- /*
- * We have four local roots for cooked and raw value GC safety. Hoist the
- * "localroot + 2" out of the loop using the val local, which refers to
- * the raw (unconverted, "uncooked") values.
- */
- val = localroot + 2;
- for (i = 0, length = ida->length; i < length; i++) {
- JSBool idIsLexicalIdentifier, needOldStyleGetterSetter;
- /* Get strings for id and value and GC-root them via vp. */
- id = ida->vector[i];
- #if JS_HAS_GETTER_SETTER
- ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
- if (!ok)
- goto error;
- #endif
- /*
- * Convert id to a jsval and then to a string. Decide early whether we
- * prefer get/set or old getter/setter syntax.
- */
- idstr = js_ValueToString(cx, ID_TO_VALUE(id));
- if (!idstr) {
- ok = JS_FALSE;
- OBJ_DROP_PROPERTY(cx, obj2, prop);
- goto error;
- }
- *vp = STRING_TO_JSVAL(idstr); /* local root */
- idIsLexicalIdentifier = js_IsIdentifier(idstr);
- needOldStyleGetterSetter =
- !idIsLexicalIdentifier ||
- js_CheckKeyword(JSSTRING_CHARS(idstr),
- JSSTRING_LENGTH(idstr)) != TOK_EOF;
- #if JS_HAS_GETTER_SETTER
- 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;
- gsopold[valcnt] =
- ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
- gsop[valcnt] =
- ATOM_TO_STRING(cx->runtime->atomState.getAtom);
- valcnt++;
- }
- if (attrs & JSPROP_SETTER) {
- val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter;
- gsopold[valcnt] =
- ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
- gsop[valcnt] =
- ATOM_TO_STRING(cx->runtime->atomState.setAtom);
- valcnt++;
- }
- } else {
- valcnt = 1;
- gsop[0] = NULL;
- gsopold[0] = NULL;
- ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
- }
- OBJ_DROP_PROPERTY(cx, obj2, prop);
- }
- #else /* !JS_HAS_GETTER_SETTER */
- /*
- * We simplify the source code at the price of minor dead code bloat in
- * the ECMA version (for testing only, see jsversion.h). The null
- * default values in gsop[j] suffice to disable non-ECMA getter and
- * setter code.
- */
- valcnt = 1;
- gsop[0] = NULL;
- gsopold[0] = NULL;
- ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
- #endif /* !JS_HAS_GETTER_SETTER */
- if (!ok)
- goto error;
- /*
- * If id is a string that's not an identifier, then it needs to be
- * quoted. Also, negative integer ids must be quoted.
- */
- if (JSID_IS_ATOM(id)
- ? !idIsLexicalIdentifier
- : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) {
- idstr = js_QuoteString(cx, idstr, (jschar)'\'');
- if (!idstr) {
- ok = JS_FALSE;
- goto error;
- }
- *vp = STRING_TO_JSVAL(idstr); /* local root */
- }
- JSSTRING_CHARS_AND_LENGTH(idstr, idstrchars, idstrlength);
- 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;
- }
- localroot[j] = STRING_TO_JSVAL(valstr); /* local root */
- JSSTRING_CHARS_AND_LENGTH(valstr, vchars, vlength);
- if (vchars[0] == '#')
- needOldStyleGetterSetter = JS_TRUE;
- if (needOldStyleGetterSetter)
- gsop[j] = gsopold[j];
- /* 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);
- needOldStyleGetterSetter = JS_TRUE;
- gsop[j] = gsopold[j];
- } else {
- if (vsharp) {
- vsharplength = js_strlen(vsharp);
- MAKE_SHARP(he);
- needOldStyleGetterSetter = JS_TRUE;
- gsop[j] = gsopold[j];
- }
- js_LeaveSharpObject(cx, NULL);
- }
- }
- #endif
- #ifndef OLD_GETTER_SETTER
- /*
- * Remove '(function ' from the beginning of valstr and ')' from the
- * end so that we can put "get" in front of the function definition.
- */
- if (gsop[j] && VALUE_IS_FUNCTION(cx, val[j]) &&
- !needOldStyleGetterSetter) {
- JSFunction *fun = JS_ValueToFunction(cx, val[j]);
- const jschar *start = vchars;
- const jschar *end = vchars + vlength;
- uint8 parenChomp = 0;
- if (vchars[0] == '(') {
- vchars++;
- parenChomp = 1;
- }
- /*
- * Try to jump "getter" or "setter" keywords, if we suspect
- * they might appear here. This code can be confused by people
- * defining Function.prototype.toString, so let's be cautious.
- */
- if (JSFUN_GETTER_TEST(fun->flags) ||
- JSFUN_SETTER_TEST(fun->flags)) { /* skip "getter/setter" */
- const jschar *tmp = js_strchr_limit(vchars, ' ', end);
- if (tmp)
- vchars = tmp + 1;
- }
- /* Try to jump "function" keyword. */
- if (vchars)
- vchars = js_strchr_limit(vchars, ' ', end);
- if (vchars) {
- if (*vchars == ' ')
- vchars++;
- vlength = end - vchars - parenChomp;
- } else {
- gsop[j] = NULL;
- vchars = start;
- }
- }
- #else
- needOldStyleGetterSetter = JS_TRUE;
- gsop[j] = gsopold[j];
- #endif
- #define SAFE_ADD(n) \
- JS_BEGIN_MACRO \
- size_t n_ = (n); \
- curlen += n_; \
- if (curlen < n_) \
- goto overflow; \
- JS_END_MACRO
- curlen = nchars;
- if (comma)
- SAFE_ADD(2);
- SAFE_ADD(idstrlength + 1);
- if (gsop[j])
- SAFE_ADD(JSSTRING_LENGTH(gsop[j]) + 1);
- SAFE_ADD(vsharplength);
- SAFE_ADD(vlength);
- /* Account for the trailing null. */
- SAFE_ADD((outermost ? 2 : 1) + 1);
- #undef SAFE_ADD
- if (curlen > (size_t)-1 / sizeof(jschar))
- goto overflow;
- /* Allocate 1 + 1 at end for closing brace and terminating 0. */
- chars = (jschar *)
- realloc((ochars = chars), curlen * 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 = ", ";
- if (needOldStyleGetterSetter) {
- 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 { /* New style "decompilation" */
- 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;
- /* Extraneous space after id here will be extracted later */
- chars[nchars++] = gsop[j] ? ' ' : ':';
- }
- 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);
- goto out;
- }
- if (!chars) {
- JS_ReportOutOfMemory(cx);
- ok = JS_FALSE;
- goto out;
- }
- make_string:
- str = js_NewString(cx, chars, nchars);
- if (!str) {
- free(chars);
- ok = JS_FALSE;
- goto out;
- }
- *vp = STRING_TO_JSVAL(str);
- ok = JS_TRUE;
- out:
- JS_POP_TEMP_ROOT(cx, &tvr);
- return ok;
- overflow:
- JS_free(cx, vsharp);
- free(chars);
- chars = NULL;
- goto error;
- }
- #endif /* JS_HAS_TOSOURCE */
- static JSBool
- obj_toString(JSContext *cx, uintN argc, jsval *vp)
- {
- JSObject *obj;
- jschar *chars;
- size_t nchars;
- const char *clazz, *prefix;
- JSString *str;
- obj = JS_THIS_OBJECT(cx, vp);
- if (!obj)
- return JS_FALSE;
- obj = js_GetWrappedObject(cx, obj);
- 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);
- if (!str) {
- JS_free(cx, chars);
- return JS_FALSE;
- }
- *vp = STRING_TO_JSVAL(str);
- return JS_TRUE;
- }
- static JSBool
- obj_toLocaleString(JSContext *cx, uintN argc, jsval *vp)
- {
- jsval thisv;
- JSString *str;
- thisv = JS_THIS(cx, vp);
- if (JSVAL_IS_NULL(thisv))
- return JS_FALSE;
- str = js_ValueToString(cx, thisv);
- if (!str)
- return JS_FALSE;
- *vp = STRING_TO_JSVAL(str);
- return JS_TRUE;
- }
- static JSBool
- obj_valueOf(JSContext *cx, uintN argc, jsval *vp)
- {
- *vp = JS_THIS(cx, vp);
- return !JSVAL_IS_NULL(*vp);
- }
- /*
- * Check whether principals subsumes scopeobj's principals, and return true
- * if so (or if scopeobj has no principals, for backward compatibility with
- * the JS API, which does not require principals), and false otherwise.
- */
- JSBool
- js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj,
- JSPrincipals *principals, JSAtom *caller)
- {
- JSSecurityCallbacks *callbacks;
- JSPrincipals *scopePrincipals;
- const char *callerstr;
- callbacks = JS_GetSecurityCallbacks(cx);
- if (callbacks && callbacks->findObjectPrincipals) {
- scopePrincipals = callbacks->findObjectPrincipals(cx, scopeobj);
- if (!principals || !scopePrincipals ||
- !principals->subsume(principals, scopePrincipals)) {
- callerstr = js_AtomToPrintableString(cx, caller);
- if (!callerstr)
- return JS_FALSE;
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_BAD_INDIRECT_CALL, callerstr);
- return JS_FALSE;
- }
- }
- return JS_TRUE;
- }
- JSObject *
- js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller)
- {
- JSClass *clasp;
- JSExtendedClass *xclasp;
- JSObject *inner;
- if (!scopeobj)
- goto bad;
- OBJ_TO_INNER_OBJECT(cx, scopeobj);
- if (!scopeobj)
- return NULL;
- inner = scopeobj;
- /* XXX This is an awful gross hack. */
- while (scopeobj) {
- clasp = OBJ_GET_CLASS(cx, scopeobj);
- if (clasp->flags & JSCLASS_IS_EXTENDED) {
- xclasp = (JSExtendedClass*)clasp;
- if (xclasp->innerObject &&
- xclasp->innerObject(cx, scopeobj) != scopeobj) {
- goto bad;
- }
- }
- scopeobj = OBJ_GET_PARENT(cx, scopeobj);
- }
- return inner;
- bad:
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_BAD_INDIRECT_CALL, caller);
- return NULL;
- }
- const char *
- js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
- JSPrincipals *principals, uintN *linenop)
- {
- uint32 flags;
- #ifdef DEBUG
- JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx);
- #endif
- JS_ASSERT(principals || !(callbacks && callbacks->findObjectPrincipals));
- flags = JS_GetScriptFilenameFlags(caller->script);
- if ((flags & JSFILENAME_PROTECTED) &&
- principals &&
- strcmp(principals->codebase, "[System Principal]")) {
- *linenop = 0;
- return principals->codebase;
- }
- if (caller->regs && *caller->regs->pc == JSOP_EVAL) {
- JS_ASSERT(caller->regs->pc[JSOP_EVAL_LENGTH] == JSOP_LINENO);
- *linenop = GET_UINT16(caller->regs->pc + JSOP_EVAL_LENGTH);
- } else {
- *linenop = js_FramePCToLineNumber(cx, caller);
- }
- return caller->script->filename;
- }
- 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;
- uint32 tcflags;
- JSScript *script;
- JSBool ok;
- #if JS_HAS_EVAL_THIS_SCOPE
- JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
- JSObject *setCallerScopeChain = NULL;
- JSBool setCallerVarObj = JS_FALSE;
- #endif
- fp = cx->fp;
- caller = JS_GetScriptedCaller(cx, fp);
- indirectCall = (caller && caller->regs && *caller->regs->pc != JSOP_EVAL);
- /*
- * Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and
- * calls that attempt to use a non-global object as the "with" object in
- * the former indirect case.
- */
- scopeobj = OBJ_GET_PARENT(cx, obj);
- if (scopeobj) {
- scopeobj = js_GetWrappedObject(cx, obj);
- scopeobj = OBJ_GET_PARENT(cx, scopeobj);
- }
- if (indirectCall || scopeobj) {
- uintN flags = scopeobj
- ? JSREPORT_ERROR
- : JSREPORT_STRICT | JSREPORT_WARNING;
- if (!JS_ReportErrorFlagsAndNumber(cx, flags, 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 the caller is a lightweight function and doesn't have a variables
- * object, then we need to provide one for the compiler to stick any
- * declared (var) variables into.
- */
- if (caller && !caller->varobj && !js_GetCallObject(cx, caller, NULL))
- return JS_FALSE;
- /* eval no longer takes an optional trailing argument. */
- if (argc >= 2 &&
- !JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT,
- js_GetErrorMessage, NULL,
- JSMSG_EVAL_ARITY)) {
- return JS_FALSE;
- }
- /* From here on, control must exit through label out with ok set. */
- MUST_FLOW_THROUGH("out");
- if (!scopeobj) {
- #if JS_HAS_EVAL_THIS_SCOPE
- /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
- if (indirectCall) {
- callerScopeChain = js_GetScopeChain(cx, caller);
- if (!callerScopeChain) {
- ok = JS_FALSE;
- goto out;
- }
- OBJ_TO_INNER_OBJECT(cx, obj);
- if (!obj) {
- ok = JS_FALSE;
- goto out;
- }
- if (obj != callerScopeChain) {
- ok = js_CheckPrincipalsAccess(cx, obj,
- caller->script->principals,
- cx->runtime->atomState.evalAtom);
- if (!ok)
- goto out;
- scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1);
- if (!scopeobj) {
- ok = JS_FALSE;
- goto out;
- }
- /* Set fp->scopeChain too, for the compiler. */
- caller->scopeChain = fp->scopeChain = scopeobj;
- /* Remember scopeobj so we can null its private when done. */
- setCallerScopeChain = scopeobj;
- }
- callerVarObj = caller->varobj;
- if (obj != callerVarObj) {
- /* Set fp->varobj too, for the compiler. */
- caller->varobj = fp->varobj = obj;
- setCallerVarObj = JS_TRUE;
- }
- }
- #endif
- /*
- * Compile using caller's current scope object.
- *
- * NB: This means that native callers (who reach this point through
- * the C API) must use the two parameter form.
- */
- if (caller) {
- scopeobj = js_GetScopeChain(cx, caller);
- if (!scopeobj) {
- ok = JS_FALSE;
- goto out;
- }
- }
- }
- /* Ensure we compile this eval with the right object in the scope chain. */
- scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str);
- if (!scopeobj) {
- ok = JS_FALSE;
- goto out;
- }
- str = JSVAL_TO_STRING(argv[0]);
- if (caller) {
- principals = JS_EvalFramePrincipals(cx, fp, caller);
- file = js_ComputeFilename(cx, caller, principals, &line);
- } else {
- file = NULL;
- line = 0;
- principals = NULL;
- }
- tcflags = TCF_COMPILE_N_GO;
- if (caller)
- tcflags |= TCF_PUT_STATIC_DEPTH(caller->script->staticDepth + 1);
- script = js_CompileScript(cx, scopeobj, caller, principals, tcflags,
- JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
- NULL, file, line);
- if (!script) {
- ok = JS_FALSE;
- goto out;
- }
- if (argc < 2) {
- /* Execute using caller's new scope object (might be a Call object). */
- if (caller)
- scopeobj = caller->scopeChain;
- }
- /*
- * Belt-and-braces: check that the lesser of eval's principals and the
- * caller's principals has access to scopeobj.
- */
- ok = js_CheckPrincipalsAccess(cx, scopeobj, principals,
- cx->runtime->atomState.evalAtom);
- if (ok)
- ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
- script->u.nextToGC = JS_SCRIPTS_TO_GC(cx);
- JS_SCRIPTS_TO_GC(cx) = script;
- #ifdef CHECK_SCRIPT_OWNER
- script->owner = NULL;
- #endif
- out:
- #if JS_HAS_EVAL_THIS_SCOPE
- /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
- if (setCallerScopeChain) {
- caller->scopeChain = callerScopeChain;
- JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass);
- JS_SetPrivate(cx, setCallerScopeChain, NULL);
- }
- if (setCallerVarObj)
- caller->varobj = callerVarObj;
- #endif
- return ok;
- }
- #if JS_HAS_OBJ_WATCHPOINT
- static JSBool
- obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
- void *closure)
- {
- JSObject *callable;
- JSSecurityCallbacks *callbacks;
- JSStackFrame *caller;
- JSPrincipals *subject, *watcher;
- JSResolvingKey key;
- JSResolvingEntry *entry;
- uint32 generation;
- jsval argv[3];
- JSBool ok;
- callable = (JSObject *) closure;
- callbacks = JS_GetSecurityCallbacks(cx);
- if (callbacks && callbacks->findObjectPrincipals) {
- /* Skip over any obj_watch_* frames between us and the real subject. */
- caller = JS_GetScriptedCaller(cx, cx->fp);
- if (caller) {
- /*
- * Only call the watch handler if the watcher is allowed to watch
- * the currently executing script.
- */
- watcher = callbacks->findObjectPrincipals(cx, callable);
- subject = JS_StackFramePrincipals(cx, caller);
- if (watcher && subject && !watcher->subsume(watcher, subject)) {
- /* Silently don't call the watch handler. */
- return JS_TRUE;
- }
- }
- }
- /* Avoid recursion on (obj, id) already being watched on cx. */
- key.obj = obj;
- key.id = id;
- if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
- return JS_FALSE;
- if (!entry)
- return JS_TRUE;
- generation = cx->resolvingTable->generation;
- argv[0] = id;
- argv[1] = old;
- argv[2] = *nvp;
- ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp);
- js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
- return ok;
- }
- static JSBool
- obj_watch(JSContext *cx, uintN argc, jsval *vp)
- {
- JSObject *callable;
- jsval userid, value;
- jsid propid;
- JSObject *obj;
- uintN attrs;
- if (argc <= 1) {
- js_ReportMissingArg(cx, vp, 1);
- return JS_FALSE;
- }
- callable = js_ValueToCallableObject(cx, &vp[3], 0);
- if (!callable)
- return JS_FALSE;
- /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
- userid = vp[2];
- if (!JS_ValueToId(cx, userid, &propid))
- return JS_FALSE;
- obj = JS_THIS_OBJECT(cx, vp);
- if (!obj || !OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs))
- return JS_FALSE;
- if (attrs & JSPROP_READONLY)
- return JS_TRUE;
- *vp = JSVAL_VOID;
- if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_MakeArraySlow(cx, obj))
- return JS_FALSE;
- return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable);
- }
- static JSBool
- obj_unwatch(JSContext *cx, uintN argc, jsval *vp)
- {
- JSObject *obj;
- obj = JS_THIS_OBJECT(cx, vp);
- if (!obj)
- return JS_FALSE;
- *vp = JSVAL_VOID;
- return JS_ClearWatchPoint(cx, obj, argc != 0 ? vp[2] : JSVAL_VOID,
- NULL, NULL);
- }
- #endif /* JS_HAS_OBJ_WATCHPOINT */
- /*
- * Prototype and property query methods, to complement the 'in' and
- * 'instanceof' operators.
- */
- /* Proposed ECMA 15.2.4.5. */
- static JSBool
- obj_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp)
- {
- JSObject *obj;
- obj = JS_THIS_OBJECT(cx, vp);
- return obj &&
- js_HasOwnPropertyHelper(cx, obj->map->ops->lookupProperty, argc, vp);
- }
- JSBool
- js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc,
- jsval *vp)
- {
- jsid id;
- JSObject *obj;
- if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
- return JS_FALSE;
- obj = JS_THIS_OBJECT(cx, vp);
- return obj && js_HasOwnProperty(cx, lookup, obj, id, vp);
- }
- JSBool
- js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id,
- jsval *vp)
- {
- JSObject *obj2;
- JSProperty *prop;
- JSScopeProperty *sprop;
- if (!lookup(cx, obj, id, &obj2, &prop))
- return JS_FALSE;
- if (!prop) {
- *vp = JSVAL_FALSE;
- } else if (obj2 == obj) {
- *vp = JSVAL_TRUE;
- } else {
- JSClass *clasp;
- JSExtendedClass *xclasp;
- JSObject *outer;
- clasp = OBJ_GET_CLASS(cx, obj2);
- if (!(clasp->flags & JSCLASS_IS_EXTENDED) ||
- !(xclasp = (JSExtendedClass *) clasp)->outerObject) {
- outer = NULL;
- } else {
- outer = xclasp->outerObject(cx, obj2);
- if (!outer)
- return JS_FALSE;
- }
- if (outer == obj) {
- *vp = JSVAL_TRUE;
- } else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj) == clasp) {
- /*
- * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
- * delegated property makes that property appear to be direct in
- * all delegating instances of the same native class. This hack
- * avoids bloating every function instance with its own 'length'
- * (AKA 'arity') property. But it must not extend across class
- * boundaries, to avoid making hasOwnProperty lie (bug 320854).
- *
- * It's not really a hack, of course: a permanent property can't
- * be deleted, and JSPROP_SHARED means "don't allocate a slot in
- * any instance, prototype or delegating". Without a slot, and
- * without the ability to remove and recreate (with differences)
- * the property, there is no way to tell whether it is directly
- * owned, or indirectly delegated.
- */
- sprop = (JSScopeProperty *)prop;
- *vp = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
- } else {
- *vp = JSVAL_FALSE;
- }
- }
- if (prop)
- OBJ_DROP_PROPERTY(cx, obj2, prop);
- return JS_TRUE;
- }
- #ifdef JS_TRACER
- static int32 FASTCALL
- Object_p_hasOwnProperty(JSContext* cx, JSObject* obj, JSString *str)
- {
- jsid id;
- jsval v;
- if (!js_ValueToStringId(cx, STRING_TO_JSVAL(str), &id))
- return JSVAL_TO_BOOLEAN(JSVAL_VOID);
- if (!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &v))
- return JSVAL_TO_BOOLEAN(JSVAL_VOID);
- JS_ASSERT(JSVAL_IS_BOOLEAN(v));
- return JSVAL_TO_BOOLEAN(v);
- }
- #endif
- /* Proposed ECMA 15.2.4.6. */
- static JSBool
- obj_isPrototypeOf(JSContext *cx, uintN argc, jsval *vp)
- {
- JSBool b;
- if (!js_IsDelegate(cx, JS_THIS_OBJECT(cx, vp),
- argc != 0 ? vp[2] : JSVAL_VOID, &b)) {
- return JS_FALSE;
- }
- *vp = BOOLEAN_TO_JSVAL(b);
- return JS_TRUE;
- }
- /* Proposed ECMA 15.2.4.7. */
- static JSBool
- obj_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp)
- {
- jsid id;
- JSObject *obj;
- if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
- return JS_FALSE;
- obj = JS_THIS_OBJECT(cx, vp);
- return obj && js_PropertyIsEnumerable(cx, obj, id, vp);
- }
- #ifdef JS_TRACER
- static int32 FASTCALL
- Object_p_propertyIsEnumerable(JSContext* cx, JSObject* obj, JSString *str)
- {
- jsid id = ATOM_TO_JSID(STRING_TO_JSVAL(str));
- jsval v;
- if (!js_PropertyIsEnumerable(cx, obj, id, &v))
- return JSVAL_TO_BOOLEAN(JSVAL_VOID);
- JS_ASSERT(JSVAL_IS_BOOLEAN(v));
- return JSVAL_TO_BOOLEAN(v);
- }
- #endif
- JSBool
- js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
- {
- JSObject *pobj;
- uintN attrs;
- JSProperty *prop;
- JSBool ok;
- if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
- return JS_FALSE;
- if (!prop) {
- *vp = 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 (pobj != obj &&
- !(OBJ_IS_NATIVE(pobj) &&
- SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) {
- OBJ_DROP_PROPERTY(cx, pobj, prop);
- *vp = JSVAL_FALSE;
- return JS_TRUE;
- }
- ok = OBJ_GET_ATTRIBUTES(cx, pobj, id, prop, &attrs);
- OBJ_DROP_PROPERTY(cx, pobj, prop);
- if (ok)
- *vp = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);
- return ok;
- }
- #if JS_HAS_GETTER_SETTER
- static JSBool
- obj_defineGetter(JSContext *cx, uintN argc, jsval *vp)
- {
- jsval fval, junk;
- jsid id;
- JSObject *obj;
- uintN attrs;
- if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) {
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_BAD_GETTER_OR_SETTER,
- js_getter_str);
- return JS_FALSE;
- }
- fval = vp[3];
- if (!JS_ValueToId(cx, vp[2], &id))
- return JS_FALSE;
- obj = JS_THIS_OBJECT(cx, vp);
- if (!obj || !js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
- 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;
- *vp = JSVAL_VOID;
- return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
- (JSPropertyOp) JSVAL_TO_OBJECT(fval),
- JS_PropertyStub,
- JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED,
- NULL);
- }
- static JSBool
- obj_defineSetter(JSContext *cx, uintN argc, jsval *vp)
- {
- jsval fval, junk;
- jsid id;
- JSObject *obj;
- uintN attrs;
- if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) {
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_BAD_GETTER_OR_SETTER,
- js_setter_str);
- return JS_FALSE;
- }
- fval = vp[3];
- if (!JS_ValueToId(cx, vp[2], &id))
- return JS_FALSE;
- obj = JS_THIS_OBJECT(cx, vp);
- if (!obj || !js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
- 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;
- *vp = JSVAL_VOID;
- return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
- JS_PropertyStub,
- (JSPropertyOp) JSVAL_TO_OBJECT(fval),
- JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED,
- NULL);
- }
- static JSBool
- obj_lookupGetter(JSContext *cx, uintN argc, jsval *vp)
- {
- jsid id;
- JSObject *obj, *pobj;
- JSProperty *prop;
- JSScopeProperty *sprop;
- if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
- return JS_FALSE;
- obj = JS_THIS_OBJECT(cx, vp);
- if (!obj || !OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
- return JS_FALSE;
- *vp = JSVAL_VOID;
- if (prop) {
- if (OBJ_IS_NATIVE(pobj)) {
- sprop = (JSScopeProperty *) prop;
- if (sprop->attrs & JSPROP_GETTER)
- *vp = OBJECT_TO_JSVAL(sprop->getter);
- }
- OBJ_DROP_PROPERTY(cx, pobj, prop);
- }
- return JS_TRUE;
- }
- static JSBool
- obj_lookupSetter(JSContext *cx, uintN argc, jsval *vp)
- {
- jsid id;
- JSObject *obj, *pobj;
- JSProperty *prop;
- JSScopeProperty *sprop;
- if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
- return JS_FALSE;
- obj = JS_THIS_OBJECT(cx, vp);
- if (!obj || !OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
- return JS_FALSE;
- *vp = JSVAL_VOID;
- if (prop) {
- if (OBJ_IS_NATIVE(pobj)) {
- sprop = (JSScopeProperty *) prop;
- if (sprop->attrs & JSPROP_SETTER)
- *vp = OBJECT_TO_JSVAL(sprop->setter);
- }
- OBJ_DROP_PROPERTY(cx, pobj, prop);
- }
- return JS_TRUE;
- }
- #endif /* JS_HAS_GETTER_SETTER */
- JSBool
- obj_getPrototypeOf(JSContext *cx, uintN argc, jsval *vp)
- {
- JSObject *obj;
- uintN attrs;
- if (argc == 0) {
- js_ReportMissingArg(cx, vp, 0);
- return JS_FALSE;
- }
- obj = js_ValueToNonNullObject(cx, vp[2]);
- if (!obj)
- return JS_FALSE;
- vp[2] = OBJECT_TO_JSVAL(obj);
- return OBJ_CHECK_ACCESS(cx, obj,
- ATOM_TO_JSID(cx->runtime->atomState.protoAtom),
- JSACC_PROTO, vp, &attrs);
- }
- #if JS_HAS_OBJ_WATCHPOINT
- const char js_watch_str[] = "watch";
- const char js_unwatch_str[] = "unwatch";
- #endif
- const char js_hasOwnProperty_str[] = "hasOwnProperty";
- const char js_isPrototypeOf_str[] = "isPrototypeOf";
- const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
- #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
- JS_DEFINE_TRCINFO_1(obj_hasOwnProperty,
- (3, (static, BOOL_FAIL, Object_p_hasOwnProperty, CONTEXT, THIS, STRING, 0, 0)))
- JS_DEFINE_TRCINFO_1(obj_propertyIsEnumerable,
- (3, (static, BOOL_FAIL, Object_p_propertyIsEnumerable, CONTEXT, THIS, STRING, 0, 0)))
- static JSFunctionSpec object_methods[] = {
- #if JS_HAS_TOSOURCE
- JS_FN(js_toSource_str, obj_toSource, 0,0),
- #endif
- JS_FN(js_toString_str, obj_toString, 0,0),
- JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0),
- JS_FN(js_valueOf_str, obj_valueOf, 0,0),
- #if JS_HAS_OBJ_WATCHPOINT
- JS_FN(js_watch_str, obj_watch, 2,0),
- JS_FN(js_unwatch_str, obj_unwatch, 1,0),
- #endif
- JS_TN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0,
- obj_hasOwnProperty_trcinfo),
- JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0),
- JS_TN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,
- obj_propertyIsEnumerable_trcinfo),
- #if JS_HAS_GETTER_SETTER
- JS_FN(js_defineGetter_str, obj_defineGetter, 2,0),
- JS_FN(js_defineSetter_str, obj_defineSetter, 2,0),
- JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0),
- JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0),
- #endif
- JS_FS_END
- };
- static JSFunctionSpec object_static_methods[] = {
- JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0),
- JS_FS_END
- };
- JSBool
- js_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, 0);
- 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)
- {
- JSObject *proto = OBJ_GET_PROTO(cx, obj);
- if (!proto)
- return js_LookupProperty(cx, obj, id, objp, propp);
- return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
- }
- 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_TraceObject, js_Clear,
- NULL, NULL
- };
- static JSObjectOps *
- with_getObjectOps(JSContext *cx, JSClass *clasp)
- {
- return &js_WithObjectOps;
- }
- JSClass js_WithClass = {
- "With",
- JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
- 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
- };
- JSObject *
- js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
- {
- JSObject *obj;
- obj = js_NewObject(cx, &js_WithClass, proto, parent, 0);
- if (!obj)
- return NULL;
- STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(cx->fp));
- OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
- return obj;
- }
- JSObject *
- js_NewBlockObject(JSContext *cx)
- {
- JSObject *obj;
- JSBool ok;
- /*
- * Null obj's proto slot so that Object.prototype.* does not pollute block
- * scopes. Make sure obj has its own scope too, since clearing proto does
- * not affect OBJ_SCOPE(obj).
- */
- obj = js_NewObject(cx, &js_BlockClass, NULL, NULL, 0);
- if (!obj)
- return NULL;
- JS_LOCK_OBJ(cx, obj);
- ok = js_GetMutableScope(cx, obj) != NULL;
- JS_UNLOCK_OBJ(cx, obj);
- if (!ok)
- return NULL;
- OBJ_CLEAR_PROTO(cx, obj);
- return obj;
- }
- JSObject *
- js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent,
- JSStackFrame *fp)
- {
- JSObject *clone;
- JS_ASSERT(STOBJ_GET_CLASS(proto) == &js_BlockClass);
- JS_ASSERT(!OBJ_IS_CLONED_BLOCK(proto));
- clone = js_NewObject(cx, &js_BlockClass, proto, parent, 0);
- if (!clone)
- return NULL;
- STOBJ_SET_SLOT(clone, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(fp));
- STOBJ_SET_SLOT(clone, JSSLOT_BLOCK_DEPTH,
- OBJ_GET_SLOT(cx, proto, JSSLOT_BLOCK_DEPTH));
- JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone));
- return clone;
- }
- JSBool
- js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
- {
- JSStackFrame *fp;
- JSObject *obj;
- uintN depth, count;
- /* Blocks have one fixed slot available for the first local.*/
- JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_BLOCK_DEPTH + 2);
- fp = cx->fp;
- obj = fp->scopeChain;
- JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
- JS_ASSERT(OBJ_GET_PRIVATE(cx, obj) == cx->fp);
- JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj));
- /*
- * Block objects should never be exposed to scripts. Thus the clone should
- * not own the property map and rather always share it with the prototype
- * object. This allows to skip updating OBJ_SCOPE(obj)->map.freeslot after
- * we copy the stack slots into reserved slots.
- */
- JS_ASSERT(OBJ_SCOPE(obj)->object != obj);
- /* Block objects should not have reserved slots before they are put. */
- JS_ASSERT(STOBJ_NSLOTS(obj) == JS_INITIAL_NSLOTS);
- /* The block and its locals must be on the current stack for GC safety. */
- depth = OBJ_BLOCK_DEPTH(cx, obj);
- count = OBJ_BLOCK_COUNT(cx, obj);
- JS_ASSERT(depth <= (size_t) (fp->regs->sp - StackBase(fp)));
- JS_ASSERT(count <= (size_t) (fp->regs->sp - StackBase(fp) - depth));
- /* See comments in CheckDestructuring from jsparse.c. */
- JS_ASSERT(count >= 1);
- depth += fp->script->nfixed;
- obj->fslots[JSSLOT_BLOCK_DEPTH + 1] = fp->slots[depth];
- if (normalUnwind && count > 1) {
- --count;
- JS_LOCK_OBJ(cx, obj);
- if (!js_ReallocSlots(cx, obj, JS_INITIAL_NSLOTS + count, JS_TRUE))
- normalUnwind = JS_FALSE;
- else
- memcpy(obj->dslots, fp->slots + depth + 1, count * sizeof(jsval));
- JS_UNLOCK_OBJ(cx, obj);
- }
- /* We must clear the private slot even with errors. */
- JS_SetPrivate(cx, obj, NULL);
- fp->scopeChain = OBJ_GET_PARENT(cx, obj);
- return normalUnwind;
- }
- static JSBool
- block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
- {
- uintN index;
- JSStackFrame *fp;
- JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL));
- JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj));
- if (!JSVAL_IS_INT(id))
- return JS_TRUE;
- index = (uint16) JSVAL_TO_INT(id);
- fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
- if (fp) {
- index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj);
- JS_ASSERT(index < fp->script->nslots);
- *vp = fp->slots[index];
- return JS_TRUE;
- }
- /* Reserve