2001-09-20 04:02:59 +04:00
|
|
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
2006-05-16 18:47:28 +04:00
|
|
|
* vim: set ts=8 sw=4 et tw=78:
|
1998-03-28 05:44:41 +03:00
|
|
|
*
|
2003-11-15 03:11:16 +03:00
|
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
1998-03-28 05:44:41 +03:00
|
|
|
*
|
2003-11-15 03:11:16 +03:00
|
|
|
* 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.
|
1998-03-28 05:44:41 +03:00
|
|
|
*
|
2001-09-20 04:02:59 +04:00
|
|
|
* The Original Code is Mozilla Communicator client code, released
|
|
|
|
* March 31, 1998.
|
1999-09-29 03:12:09 +04:00
|
|
|
*
|
2003-11-15 03:11:16 +03:00
|
|
|
* 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.
|
1999-09-29 03:12:09 +04:00
|
|
|
*
|
2000-06-27 06:37:25 +04:00
|
|
|
* Contributor(s):
|
1999-09-29 03:12:09 +04:00
|
|
|
*
|
2003-11-15 03:11:16 +03:00
|
|
|
* 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 ***** */
|
1998-03-28 05:44:41 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* JS object implementation.
|
|
|
|
*/
|
1998-06-10 03:04:48 +04:00
|
|
|
#include "jsstddef.h"
|
1998-03-28 05:44:41 +03:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
1998-10-14 14:22:38 +04:00
|
|
|
#include "jstypes.h"
|
|
|
|
#include "jsarena.h" /* Added by JSIFY */
|
2006-06-04 06:19:09 +04:00
|
|
|
#include "jsbit.h"
|
1998-10-14 14:22:38 +04:00
|
|
|
#include "jsutil.h" /* Added by JSIFY */
|
|
|
|
#include "jshash.h" /* Added by JSIFY */
|
2001-05-17 05:43:58 +04:00
|
|
|
#include "jsdhash.h"
|
1998-10-14 14:22:38 +04:00
|
|
|
#include "jsprf.h"
|
1998-03-28 05:44:41 +03:00
|
|
|
#include "jsapi.h"
|
2002-02-23 06:49:27 +03:00
|
|
|
#include "jsarray.h"
|
1998-03-28 05:44:41 +03:00
|
|
|
#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"
|
2006-07-06 05:25:44 +04:00
|
|
|
#include "jsscan.h"
|
1998-03-28 05:44:41 +03:00
|
|
|
#include "jsscope.h"
|
|
|
|
#include "jsscript.h"
|
|
|
|
#include "jsstr.h"
|
2000-02-02 04:10:31 +03:00
|
|
|
#include "jsopcode.h"
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2002-03-09 01:46:13 +03:00
|
|
|
#include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2006-05-21 02:27:28 +04:00
|
|
|
#if JS_HAS_GENERATORS
|
|
|
|
#include "jsiter.h"
|
|
|
|
#endif
|
|
|
|
|
2006-04-07 00:15:24 +04:00
|
|
|
#if JS_HAS_XML_SUPPORT
|
|
|
|
#include "jsxml.h"
|
|
|
|
#endif
|
|
|
|
|
2006-08-01 02:52:27 +04:00
|
|
|
#if JS_HAS_XDR
|
|
|
|
#include "jsxdrapi.h"
|
|
|
|
#endif
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
#define NATIVE_DROP_PROPERTY js_DropProperty
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
extern void
|
|
|
|
js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);
|
|
|
|
#else
|
|
|
|
#define NATIVE_DROP_PROPERTY NULL
|
|
|
|
#endif
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
|
2000-06-27 06:37:25 +04:00
|
|
|
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,
|
2000-08-26 06:30:22 +04:00
|
|
|
js_Mark, js_Clear,
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
js_GetRequiredSlot, js_SetRequiredSlot
|
1998-04-24 04:31:11 +04:00
|
|
|
};
|
1998-03-28 05:44:41 +03:00
|
|
|
|
|
|
|
JSClass js_ObjectClass = {
|
2000-08-19 23:17:32 +04:00
|
|
|
js_Object_str,
|
2006-04-20 03:36:16 +04:00
|
|
|
JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
|
1998-03-28 05:44:41 +03:00
|
|
|
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
1999-11-12 00:52:35 +03:00
|
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
|
2005-06-22 01:31:26 +04:00
|
|
|
JSCLASS_NO_OPTIONAL_MEMBERS
|
1998-03-28 05:44:41 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
#if JS_HAS_OBJ_PROTO_PROP
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
|
|
|
|
|
2000-06-27 06:37:25 +04:00
|
|
|
static JSBool
|
1998-03-28 05:44:41 +03:00
|
|
|
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. */
|
2000-06-27 06:37:25 +04:00
|
|
|
{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},
|
1999-11-12 00:52:35 +03:00
|
|
|
{0,0,0,0,0}
|
1998-03-28 05:44:41 +03:00
|
|
|
};
|
|
|
|
|
2000-01-18 14:06:05 +03:00
|
|
|
/* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */
|
|
|
|
#define JSSLOT_COUNT 2
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
ReportStrictSlot(JSContext *cx, uint32 slot)
|
|
|
|
{
|
2001-02-24 06:07:58 +03:00
|
|
|
if (slot == JSSLOT_PROTO)
|
|
|
|
return JS_TRUE;
|
2000-01-18 14:06:05 +03:00
|
|
|
return JS_ReportErrorFlagsAndNumber(cx,
|
|
|
|
JSREPORT_WARNING | JSREPORT_STRICT,
|
|
|
|
js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_DEPRECATED_USAGE,
|
|
|
|
object_props[slot].name);
|
|
|
|
}
|
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
static JSBool
|
|
|
|
obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|
|
|
{
|
1999-07-28 10:48:44 +04:00
|
|
|
uint32 slot;
|
2005-06-22 01:31:26 +04:00
|
|
|
jsid propid;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSAccessMode mode;
|
|
|
|
uintN attrs;
|
2005-07-31 00:57:07 +04:00
|
|
|
JSObject *pobj;
|
|
|
|
JSClass *clasp;
|
|
|
|
JSExtendedClass *xclasp;
|
1998-04-24 04:31:11 +04:00
|
|
|
|
1999-07-28 10:48:44 +04:00
|
|
|
slot = (uint32) JSVAL_TO_INT(id);
|
1998-04-24 04:31:11 +04:00
|
|
|
if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
|
2005-06-22 01:31:26 +04:00
|
|
|
propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
|
2001-02-07 02:19:44 +03:00
|
|
|
mode = JSACC_PROTO;
|
1998-04-24 04:31:11 +04:00
|
|
|
} else {
|
2005-06-22 01:31:26 +04:00
|
|
|
propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
|
2001-02-07 02:19:44 +03:00
|
|
|
mode = JSACC_PARENT;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
2005-07-31 00:57:07 +04:00
|
|
|
|
|
|
|
/* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */
|
2005-06-22 01:31:26 +04:00
|
|
|
if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
2005-07-31 00:57:07 +04:00
|
|
|
|
|
|
|
pobj = JSVAL_TO_OBJECT(*vp);
|
|
|
|
if (pobj) {
|
|
|
|
clasp = OBJ_GET_CLASS(cx, pobj);
|
2006-10-06 03:33:03 +04:00
|
|
|
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) {
|
2005-07-31 00:57:07 +04:00
|
|
|
xclasp = (JSExtendedClass *) clasp;
|
|
|
|
if (xclasp->outerObject) {
|
|
|
|
pobj = xclasp->outerObject(cx, pobj);
|
|
|
|
if (!pobj)
|
|
|
|
return JS_FALSE;
|
|
|
|
*vp = OBJECT_TO_JSVAL(pobj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2000-06-27 06:37:25 +04:00
|
|
|
static JSBool
|
1998-03-28 05:44:41 +03:00
|
|
|
obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|
|
|
{
|
2000-06-27 06:37:25 +04:00
|
|
|
JSObject *pobj;
|
2002-05-22 04:53:12 +04:00
|
|
|
uint32 slot;
|
2005-06-22 01:31:26 +04:00
|
|
|
jsid propid;
|
2002-05-22 04:53:12 +04:00
|
|
|
uintN attrs;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
|
|
|
if (!JSVAL_IS_OBJECT(*vp))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_TRUE;
|
2000-06-27 06:37:25 +04:00
|
|
|
pobj = JSVAL_TO_OBJECT(*vp);
|
2006-03-23 00:44:31 +03:00
|
|
|
|
2006-03-23 05:24:45 +03:00
|
|
|
if (pobj) {
|
|
|
|
/*
|
2006-05-16 18:47:28 +04:00
|
|
|
* 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.
|
2006-03-23 05:24:45 +03:00
|
|
|
*/
|
|
|
|
OBJ_TO_INNER_OBJECT(cx, pobj);
|
|
|
|
if (!pobj)
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
1999-07-28 10:48:44 +04:00
|
|
|
slot = (uint32) JSVAL_TO_INT(id);
|
2000-01-18 14:06:05 +03:00
|
|
|
if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
|
|
|
|
return JS_FALSE;
|
2002-05-22 04:06:40 +04:00
|
|
|
|
|
|
|
/* __parent__ is readonly and permanent, only __proto__ may be set. */
|
2005-06-22 01:31:26 +04:00
|
|
|
propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
|
|
|
|
if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_PROTO|JSACC_WRITE, vp, &attrs))
|
2002-05-22 04:06:40 +04:00
|
|
|
return JS_FALSE;
|
|
|
|
|
2000-06-27 06:37:25 +04:00
|
|
|
return js_SetProtoOrParent(cx, obj, slot, pobj);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|
|
|
{
|
1999-10-22 05:44:47 +04:00
|
|
|
jsval iter_state;
|
1998-04-24 04:31:11 +04:00
|
|
|
jsid num_properties;
|
1999-10-22 05:44:47 +04:00
|
|
|
JSBool ok;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2000-01-18 14:06:05 +03:00
|
|
|
if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))
|
|
|
|
return JS_FALSE;
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
/* Get the number of properties to enumerate. */
|
1999-10-22 05:44:47 +04:00
|
|
|
iter_state = JSVAL_NULL;
|
|
|
|
ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties);
|
|
|
|
if (!ok)
|
2001-02-07 02:19:44 +03:00
|
|
|
goto out;
|
1999-10-22 05:44:47 +04:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!JSVAL_IS_INT(num_properties)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_ASSERT(0);
|
1999-10-22 05:44:47 +04:00
|
|
|
*vp = JSVAL_ZERO;
|
2001-02-07 02:19:44 +03:00
|
|
|
goto out;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
*vp = num_properties;
|
|
|
|
|
1999-10-22 05:44:47 +04:00
|
|
|
out:
|
|
|
|
if (iter_state != JSVAL_NULL)
|
2001-02-07 02:19:44 +03:00
|
|
|
ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
|
1999-10-15 08:32:46 +04:00
|
|
|
return ok;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#else /* !JS_HAS_OBJ_PROTO_PROP */
|
|
|
|
|
|
|
|
#define object_props NULL
|
|
|
|
|
|
|
|
#endif /* !JS_HAS_OBJ_PROTO_PROP */
|
|
|
|
|
2000-06-27 06:37:25 +04:00
|
|
|
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;
|
2001-05-08 05:31:02 +04:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
|
2000-06-27 06:37:25 +04:00
|
|
|
JS_ACQUIRE_LOCK(rt->setSlotLock);
|
2001-05-08 05:31:02 +04:00
|
|
|
while (rt->setSlotBusy) {
|
2001-07-25 06:43:40 +04:00
|
|
|
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);
|
2001-05-08 05:31:02 +04:00
|
|
|
JS_ResumeRequest(cx, saveDepth);
|
2001-07-25 06:43:40 +04:00
|
|
|
JS_ACQUIRE_LOCK(rt->setSlotLock);
|
2001-05-08 05:31:02 +04:00
|
|
|
}
|
|
|
|
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
|
|
|
|
|
2000-06-27 06:37:25 +04:00
|
|
|
obj2 = pobj;
|
|
|
|
while (obj2) {
|
|
|
|
if (obj2 == obj) {
|
2001-05-08 05:31:02 +04:00
|
|
|
SET_SLOT_DONE(rt);
|
2000-06-27 06:37:25 +04:00
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
2003-06-08 01:08:45 +04:00
|
|
|
JSMSG_CYCLIC_VALUE,
|
|
|
|
#if JS_HAS_OBJ_PROTO_PROP
|
|
|
|
object_props[slot].name
|
|
|
|
#else
|
|
|
|
(slot == JSSLOT_PROTO) ? js_proto_str
|
|
|
|
: js_parent_str
|
|
|
|
#endif
|
|
|
|
);
|
2000-06-27 06:37:25 +04:00
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot));
|
|
|
|
}
|
|
|
|
|
2000-09-21 05:37:02 +04:00
|
|
|
if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) {
|
|
|
|
/* Check to see whether obj shares its prototype's scope. */
|
2000-06-27 06:37:25 +04:00
|
|
|
JS_LOCK_OBJ(cx, obj);
|
|
|
|
scope = OBJ_SCOPE(obj);
|
2006-12-09 18:02:37 +03:00
|
|
|
oldproto = LOCKED_OBJ_GET_PROTO(obj);
|
2000-09-21 05:37:02 +04:00
|
|
|
if (oldproto && OBJ_SCOPE(oldproto) == scope) {
|
|
|
|
/* Either obj needs a new empty scope, or it should share pobj's. */
|
2002-05-03 00:59:35 +04:00
|
|
|
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.
|
|
|
|
*/
|
2000-09-21 05:37:02 +04:00
|
|
|
scope = js_GetMutableScope(cx, obj);
|
|
|
|
if (!scope) {
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
2001-05-08 05:31:02 +04:00
|
|
|
SET_SLOT_DONE(rt);
|
2000-09-21 05:37:02 +04:00
|
|
|
return JS_FALSE;
|
|
|
|
}
|
2002-05-03 00:59:35 +04:00
|
|
|
} else if (OBJ_SCOPE(pobj) != scope) {
|
All this r=mccabe, r=beard, and sr=jband -- many thanks to all who helped,
especially to jband for his great stress-test setup and particularly helpful
(in terms of reproducing bugs in draft patches) MP and laptop machines.
- Radical(*) object (scope) locking optimization: don't lock if a scope is
accessed on the context that exclusively owns it (initially, the context
on which the scope was created). Once a scope becomes shared among more
than one owner-context, give it the usual thin or fat lock, per existing
jslock.c code.
I did this at the memory cost of another word per JSScope, ownercx, which
raised scope size from 12 to 13 words if !DEBUG. I also added a linked
list head pointer, rt->scopeSharingTodo, and a scopeSharingDone condition
variable to JSRuntime, and a scopeToShare pointer to JSContext that's
necessary for deadlock avoidance.
The rt->scopeSharingTodo list links JSScopes through the scope->u.link
union arm, which overlays the pre-existing scope->count (now u.count)
member. This list holds scopes still exclusively owned by a context, but
wanted by js_LockScope calls active on other threads. Those calls wait
on the rt->scopeSharingDone condition, which is notified every time an
owner-context ends the request running on it, in which code active on
that context may be using scope freely until end of request.
The code that waits on rt->scopeSharingDone must first suspend any and
all requests active on the calling context, and resume those contexts
after the wait is notified. This means a GC could happen while the
thread locking a scope owned by another thread's context blocks; all
calls to JS_LOCK_OBJ must therefore first home fp->sp above any live
operands, e.g. The interpreter takes care to do that already.
To avoid AB-BA deadlocks, if a js_LockScope attempt on one context finds
that the owner-context of the scope is already waiting on a scope owned
by the current context (or indirectly depending on such a scope lock),
the attempt converts the scope from lock-free exclusive ownership to
shared ownership (thin or fat lock).
- Fix js_SetupLocks and the js_LockGlobal/js_UnlockGlobal code to avoid
divmod instruction costs, strength-reducing to bit-mask instructions.
- The radical lock-free scope change required care in handling the 0=>1
and 1=>0 transitions of cx->requestDepth, which was till now thread-local
because part of the JSContext not manipulated by other threads. It's
still updated only by cx's thread, but it is read by other threads in
the course of attempting to claim exclusive ownership of a scope for more
lock-free JS object operations.
- The JS_SuspendRequest and JS_ResumeRequest APIs have changed incompatibly
to require their caller to save and restore the requestCount found when
JS_SuspendRequest is called. This is necessary to avoid deadlock; sorry
for the incompatible change.
- Fixed various nits in jslock.[ch], including using Init/Finish rather
than New/Destroy for the methods that take a JSThinLock and initialize
and finish/free its members. Another example: JS_ATOMIC_ADDREF is now
JS_ATOMIC_INCREMENT and JS_ATOMIC_DECREMENT, so the two cases can be
mapped to PR_AtomicIncrement and PR_AtomicDecrement. This entailed
changing jsrefcount from jsword to int32 (PRInt32).
- No need to use JS_ATOMIC_INCREMENT on JSScopeProperty.nrefs, as it is
always and everywhere protected by the property's JSScope.lock.
- Cleaned up gratuitous casts in jscntxt.c by using &cx->links, etc.
- The lock used for mutual exclusion around both request begin and end vs.
GC synchronization is rt->gcLock, and this lock now also protects all
scope->ownercx pointer changes from non-null (exclusive) to null (shared),
the rt->scopeSharingTodo/scope->u.link list operations, and of course the
rt->scopeSharingDone condition.
But this means that js_GC cannot hold rt->gcLock across the bulk of its
body, in particular the mark phase, during which JS_GetPrivate calls,
e.g., may need to "promote" scope locks from lock-free to thin or fat,
because doing so would double-trip. There never was any good reason to
hold rt->gcLock so long, of course -- locks are for mutual exclusion, not
for waiting or notifying a thread -- those operations require a condition,
rt->gcDone, which we already use along with rt->gcLevel to keep racing GC
attempts at bay.
So now that rt->gcLock does not protect the mark phase, the enumeration
of rt->gcRootsHash can race badly with JS_RemoveRootRT, an API that may
legitimately be called outside of a request, without even a context. It
turns out that people may be cheating on the request model even with
JS_AddRoot, JS_AddNamedRoot, and JS_RemoveRoot calls, so we must make
all of those interlock with the GC using gcLevel and gcDone, unless they
are called on the gcThread.
Also, since bug 49816 was fixed, there has been no need for a separate
finalize phase, or for rt->gcFinalVec. Finalizers can no longer allocate
newborn GC-things that might be swept (because unmarked), or double-trip
on rt->gcLock (which is no longer held). So js_GC finalizes as it sweeps,
just as it did in days of old.
- I added comments to jslock.h making it plain that callers of JS_LOCK_OBJ
and JS_UNLOCK_OBJ must either be implementations of js_ObjectOps hooks,
or code reachable only from those hooks; or else must be predicated on
OBJ_IS_NATIVE tests. It turns out jsinterp.c's CACHED_GET and CACHED_SET
macros neglected to do such tests, limiting the ability of JS embeddings
to implement JSObjectOps with their own non-JSScope JSObjectMap subclass.
Fixed, small performance hit that the lock-free optimization should more
than make up for.
- jslock.c now gives a #error if you try to compile it on a platform that
lacks a compare-and-swap instruction. The #error says to use NSPR locks.
Before this change, some platforms would emulate compare-and-swap using
a global PRLock, which is always worse in runtime than using per-scope
PRLocks.
2000-12-04 05:43:31 +03:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
/*
|
2000-12-21 01:36:01 +03:00
|
|
|
* 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.
|
All this r=mccabe, r=beard, and sr=jband -- many thanks to all who helped,
especially to jband for his great stress-test setup and particularly helpful
(in terms of reproducing bugs in draft patches) MP and laptop machines.
- Radical(*) object (scope) locking optimization: don't lock if a scope is
accessed on the context that exclusively owns it (initially, the context
on which the scope was created). Once a scope becomes shared among more
than one owner-context, give it the usual thin or fat lock, per existing
jslock.c code.
I did this at the memory cost of another word per JSScope, ownercx, which
raised scope size from 12 to 13 words if !DEBUG. I also added a linked
list head pointer, rt->scopeSharingTodo, and a scopeSharingDone condition
variable to JSRuntime, and a scopeToShare pointer to JSContext that's
necessary for deadlock avoidance.
The rt->scopeSharingTodo list links JSScopes through the scope->u.link
union arm, which overlays the pre-existing scope->count (now u.count)
member. This list holds scopes still exclusively owned by a context, but
wanted by js_LockScope calls active on other threads. Those calls wait
on the rt->scopeSharingDone condition, which is notified every time an
owner-context ends the request running on it, in which code active on
that context may be using scope freely until end of request.
The code that waits on rt->scopeSharingDone must first suspend any and
all requests active on the calling context, and resume those contexts
after the wait is notified. This means a GC could happen while the
thread locking a scope owned by another thread's context blocks; all
calls to JS_LOCK_OBJ must therefore first home fp->sp above any live
operands, e.g. The interpreter takes care to do that already.
To avoid AB-BA deadlocks, if a js_LockScope attempt on one context finds
that the owner-context of the scope is already waiting on a scope owned
by the current context (or indirectly depending on such a scope lock),
the attempt converts the scope from lock-free exclusive ownership to
shared ownership (thin or fat lock).
- Fix js_SetupLocks and the js_LockGlobal/js_UnlockGlobal code to avoid
divmod instruction costs, strength-reducing to bit-mask instructions.
- The radical lock-free scope change required care in handling the 0=>1
and 1=>0 transitions of cx->requestDepth, which was till now thread-local
because part of the JSContext not manipulated by other threads. It's
still updated only by cx's thread, but it is read by other threads in
the course of attempting to claim exclusive ownership of a scope for more
lock-free JS object operations.
- The JS_SuspendRequest and JS_ResumeRequest APIs have changed incompatibly
to require their caller to save and restore the requestCount found when
JS_SuspendRequest is called. This is necessary to avoid deadlock; sorry
for the incompatible change.
- Fixed various nits in jslock.[ch], including using Init/Finish rather
than New/Destroy for the methods that take a JSThinLock and initialize
and finish/free its members. Another example: JS_ATOMIC_ADDREF is now
JS_ATOMIC_INCREMENT and JS_ATOMIC_DECREMENT, so the two cases can be
mapped to PR_AtomicIncrement and PR_AtomicDecrement. This entailed
changing jsrefcount from jsword to int32 (PRInt32).
- No need to use JS_ATOMIC_INCREMENT on JSScopeProperty.nrefs, as it is
always and everywhere protected by the property's JSScope.lock.
- Cleaned up gratuitous casts in jscntxt.c by using &cx->links, etc.
- The lock used for mutual exclusion around both request begin and end vs.
GC synchronization is rt->gcLock, and this lock now also protects all
scope->ownercx pointer changes from non-null (exclusive) to null (shared),
the rt->scopeSharingTodo/scope->u.link list operations, and of course the
rt->scopeSharingDone condition.
But this means that js_GC cannot hold rt->gcLock across the bulk of its
body, in particular the mark phase, during which JS_GetPrivate calls,
e.g., may need to "promote" scope locks from lock-free to thin or fat,
because doing so would double-trip. There never was any good reason to
hold rt->gcLock so long, of course -- locks are for mutual exclusion, not
for waiting or notifying a thread -- those operations require a condition,
rt->gcDone, which we already use along with rt->gcLevel to keep racing GC
attempts at bay.
So now that rt->gcLock does not protect the mark phase, the enumeration
of rt->gcRootsHash can race badly with JS_RemoveRootRT, an API that may
legitimately be called outside of a request, without even a context. It
turns out that people may be cheating on the request model even with
JS_AddRoot, JS_AddNamedRoot, and JS_RemoveRoot calls, so we must make
all of those interlock with the GC using gcLevel and gcDone, unless they
are called on the gcThread.
Also, since bug 49816 was fixed, there has been no need for a separate
finalize phase, or for rt->gcFinalVec. Finalizers can no longer allocate
newborn GC-things that might be swept (because unmarked), or double-trip
on rt->gcLock (which is no longer held). So js_GC finalizes as it sweeps,
just as it did in days of old.
- I added comments to jslock.h making it plain that callers of JS_LOCK_OBJ
and JS_UNLOCK_OBJ must either be implementations of js_ObjectOps hooks,
or code reachable only from those hooks; or else must be predicated on
OBJ_IS_NATIVE tests. It turns out jsinterp.c's CACHED_GET and CACHED_SET
macros neglected to do such tests, limiting the ability of JS embeddings
to implement JSObjectOps with their own non-JSScope JSObjectMap subclass.
Fixed, small performance hit that the lock-free optimization should more
than make up for.
- jslock.c now gives a #error if you try to compile it on a platform that
lacks a compare-and-swap instruction. The #error says to use NSPR locks.
Before this change, some platforms would emulate compare-and-swap using
a global PRLock, which is always worse in runtime than using per-scope
PRLocks.
2000-12-04 05:43:31 +03:00
|
|
|
*/
|
|
|
|
if (scope->ownercx) {
|
2000-12-21 01:36:01 +03:00
|
|
|
JS_ASSERT(scope->ownercx == cx);
|
|
|
|
rt->setSlotScope = scope;
|
All this r=mccabe, r=beard, and sr=jband -- many thanks to all who helped,
especially to jband for his great stress-test setup and particularly helpful
(in terms of reproducing bugs in draft patches) MP and laptop machines.
- Radical(*) object (scope) locking optimization: don't lock if a scope is
accessed on the context that exclusively owns it (initially, the context
on which the scope was created). Once a scope becomes shared among more
than one owner-context, give it the usual thin or fat lock, per existing
jslock.c code.
I did this at the memory cost of another word per JSScope, ownercx, which
raised scope size from 12 to 13 words if !DEBUG. I also added a linked
list head pointer, rt->scopeSharingTodo, and a scopeSharingDone condition
variable to JSRuntime, and a scopeToShare pointer to JSContext that's
necessary for deadlock avoidance.
The rt->scopeSharingTodo list links JSScopes through the scope->u.link
union arm, which overlays the pre-existing scope->count (now u.count)
member. This list holds scopes still exclusively owned by a context, but
wanted by js_LockScope calls active on other threads. Those calls wait
on the rt->scopeSharingDone condition, which is notified every time an
owner-context ends the request running on it, in which code active on
that context may be using scope freely until end of request.
The code that waits on rt->scopeSharingDone must first suspend any and
all requests active on the calling context, and resume those contexts
after the wait is notified. This means a GC could happen while the
thread locking a scope owned by another thread's context blocks; all
calls to JS_LOCK_OBJ must therefore first home fp->sp above any live
operands, e.g. The interpreter takes care to do that already.
To avoid AB-BA deadlocks, if a js_LockScope attempt on one context finds
that the owner-context of the scope is already waiting on a scope owned
by the current context (or indirectly depending on such a scope lock),
the attempt converts the scope from lock-free exclusive ownership to
shared ownership (thin or fat lock).
- Fix js_SetupLocks and the js_LockGlobal/js_UnlockGlobal code to avoid
divmod instruction costs, strength-reducing to bit-mask instructions.
- The radical lock-free scope change required care in handling the 0=>1
and 1=>0 transitions of cx->requestDepth, which was till now thread-local
because part of the JSContext not manipulated by other threads. It's
still updated only by cx's thread, but it is read by other threads in
the course of attempting to claim exclusive ownership of a scope for more
lock-free JS object operations.
- The JS_SuspendRequest and JS_ResumeRequest APIs have changed incompatibly
to require their caller to save and restore the requestCount found when
JS_SuspendRequest is called. This is necessary to avoid deadlock; sorry
for the incompatible change.
- Fixed various nits in jslock.[ch], including using Init/Finish rather
than New/Destroy for the methods that take a JSThinLock and initialize
and finish/free its members. Another example: JS_ATOMIC_ADDREF is now
JS_ATOMIC_INCREMENT and JS_ATOMIC_DECREMENT, so the two cases can be
mapped to PR_AtomicIncrement and PR_AtomicDecrement. This entailed
changing jsrefcount from jsword to int32 (PRInt32).
- No need to use JS_ATOMIC_INCREMENT on JSScopeProperty.nrefs, as it is
always and everywhere protected by the property's JSScope.lock.
- Cleaned up gratuitous casts in jscntxt.c by using &cx->links, etc.
- The lock used for mutual exclusion around both request begin and end vs.
GC synchronization is rt->gcLock, and this lock now also protects all
scope->ownercx pointer changes from non-null (exclusive) to null (shared),
the rt->scopeSharingTodo/scope->u.link list operations, and of course the
rt->scopeSharingDone condition.
But this means that js_GC cannot hold rt->gcLock across the bulk of its
body, in particular the mark phase, during which JS_GetPrivate calls,
e.g., may need to "promote" scope locks from lock-free to thin or fat,
because doing so would double-trip. There never was any good reason to
hold rt->gcLock so long, of course -- locks are for mutual exclusion, not
for waiting or notifying a thread -- those operations require a condition,
rt->gcDone, which we already use along with rt->gcLevel to keep racing GC
attempts at bay.
So now that rt->gcLock does not protect the mark phase, the enumeration
of rt->gcRootsHash can race badly with JS_RemoveRootRT, an API that may
legitimately be called outside of a request, without even a context. It
turns out that people may be cheating on the request model even with
JS_AddRoot, JS_AddNamedRoot, and JS_RemoveRoot calls, so we must make
all of those interlock with the GC using gcLevel and gcDone, unless they
are called on the gcThread.
Also, since bug 49816 was fixed, there has been no need for a separate
finalize phase, or for rt->gcFinalVec. Finalizers can no longer allocate
newborn GC-things that might be swept (because unmarked), or double-trip
on rt->gcLock (which is no longer held). So js_GC finalizes as it sweeps,
just as it did in days of old.
- I added comments to jslock.h making it plain that callers of JS_LOCK_OBJ
and JS_UNLOCK_OBJ must either be implementations of js_ObjectOps hooks,
or code reachable only from those hooks; or else must be predicated on
OBJ_IS_NATIVE tests. It turns out jsinterp.c's CACHED_GET and CACHED_SET
macros neglected to do such tests, limiting the ability of JS embeddings
to implement JSObjectOps with their own non-JSScope JSObjectMap subclass.
Fixed, small performance hit that the lock-free optimization should more
than make up for.
- jslock.c now gives a #error if you try to compile it on a platform that
lacks a compare-and-swap instruction. The #error says to use NSPR locks.
Before this change, some platforms would emulate compare-and-swap using
a global PRLock, which is always worse in runtime than using per-scope
PRLocks.
2000-12-04 05:43:31 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2000-09-21 05:37:02 +04:00
|
|
|
/* 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;
|
2000-12-21 01:36:01 +03:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
rt->setSlotScope = NULL;
|
|
|
|
#endif
|
2000-09-21 05:37:02 +04:00
|
|
|
}
|
2000-06-27 06:37:25 +04:00
|
|
|
}
|
2006-12-09 18:02:37 +03:00
|
|
|
LOCKED_OBJ_SET_PROTO(obj, pobj);
|
2000-06-27 06:37:25 +04:00
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
|
|
|
} else {
|
|
|
|
OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj));
|
|
|
|
}
|
|
|
|
|
2001-05-08 05:31:02 +04:00
|
|
|
SET_SLOT_DONE(rt);
|
2000-06-27 06:37:25 +04:00
|
|
|
return JS_TRUE;
|
2001-05-08 05:31:02 +04:00
|
|
|
|
|
|
|
#undef SET_SLOT_DONE
|
2000-06-27 06:37:25 +04:00
|
|
|
}
|
|
|
|
|
1998-10-14 14:22:38 +04:00
|
|
|
JS_STATIC_DLL_CALLBACK(JSHashNumber)
|
1998-03-28 05:44:41 +03:00
|
|
|
js_hash_object(const void *key)
|
|
|
|
{
|
2005-01-12 22:56:32 +03:00
|
|
|
return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
1998-10-14 14:22:38 +04:00
|
|
|
static JSHashEntry *
|
1998-04-24 04:31:11 +04:00
|
|
|
MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
|
|
|
JSSharpObjectMap *map;
|
1998-10-14 14:22:38 +04:00
|
|
|
JSHashTable *table;
|
|
|
|
JSHashNumber hash;
|
|
|
|
JSHashEntry **hep, *he;
|
1998-03-28 05:44:41 +03:00
|
|
|
jsatomid sharpid;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSIdArray *ida;
|
|
|
|
JSBool ok;
|
|
|
|
jsint i, length;
|
1999-09-21 04:13:48 +04:00
|
|
|
jsid id;
|
2000-05-15 07:54:50 +04:00
|
|
|
#if JS_HAS_GETTER_SETTER
|
1999-09-21 04:13:48 +04:00
|
|
|
JSObject *obj2;
|
|
|
|
JSProperty *prop;
|
|
|
|
uintN attrs;
|
2000-05-15 07:54:50 +04:00
|
|
|
#endif
|
1998-03-28 05:44:41 +03:00
|
|
|
jsval val;
|
2005-10-04 09:03:58 +04:00
|
|
|
int stackDummy;
|
|
|
|
|
|
|
|
if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
|
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
|
|
|
|
return NULL;
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
|
|
|
map = &cx->sharpObjectMap;
|
|
|
|
table = map->table;
|
|
|
|
hash = js_hash_object(obj);
|
1998-10-14 14:22:38 +04:00
|
|
|
hep = JS_HashTableRawLookup(table, hash, obj);
|
1998-03-28 05:44:41 +03:00
|
|
|
he = *hep;
|
|
|
|
if (!he) {
|
2001-02-07 02:19:44 +03:00
|
|
|
sharpid = 0;
|
2005-01-12 22:56:32 +03:00
|
|
|
he = JS_HashTableRawAdd(table, hep, hash, obj,
|
|
|
|
JS_UINT32_TO_PTR(sharpid));
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!he) {
|
|
|
|
JS_ReportOutOfMemory(cx);
|
|
|
|
return NULL;
|
|
|
|
}
|
2005-08-12 07:32:29 +04:00
|
|
|
|
2005-12-01 05:26:35 +03:00
|
|
|
/*
|
2005-08-12 04:46:56 +04:00
|
|
|
* Increment map->depth to protect js_EnterSharpObject from reentering
|
2005-08-12 07:32:29 +04:00
|
|
|
* 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.
|
2005-08-12 04:46:56 +04:00
|
|
|
*/
|
2005-12-01 05:26:35 +03:00
|
|
|
++map->depth;
|
2001-02-07 02:19:44 +03:00
|
|
|
ida = JS_Enumerate(cx, obj);
|
2005-12-01 05:26:35 +03:00
|
|
|
--map->depth;
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!ida)
|
|
|
|
return NULL;
|
2005-08-12 07:32:29 +04:00
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
ok = JS_TRUE;
|
|
|
|
for (i = 0, length = ida->length; i < length; i++) {
|
1999-09-21 04:13:48 +04:00
|
|
|
id = ida->vector[i];
|
|
|
|
#if JS_HAS_GETTER_SETTER
|
|
|
|
ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
|
|
|
|
if (!ok)
|
|
|
|
break;
|
2004-11-04 04:52:28 +03:00
|
|
|
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);
|
2000-10-25 11:00:31 +04:00
|
|
|
}
|
2004-11-04 04:52:28 +03:00
|
|
|
val = (jsval) ((JSScopeProperty*)prop)->setter;
|
1999-09-21 04:13:48 +04:00
|
|
|
}
|
2004-11-04 04:52:28 +03:00
|
|
|
} else {
|
|
|
|
ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
|
1999-09-21 04:13:48 +04:00
|
|
|
}
|
2000-10-25 11:00:31 +04:00
|
|
|
}
|
2004-11-04 04:52:28 +03:00
|
|
|
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
1999-09-21 04:13:48 +04:00
|
|
|
#else
|
|
|
|
ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
|
|
|
|
#endif
|
|
|
|
if (!ok)
|
|
|
|
break;
|
2001-02-07 02:19:44 +03:00
|
|
|
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;
|
1998-03-28 05:44:41 +03:00
|
|
|
} else {
|
2005-01-12 22:56:32 +03:00
|
|
|
sharpid = JS_PTR_TO_UINT32(he->value);
|
2001-02-07 02:19:44 +03:00
|
|
|
if (sharpid == 0) {
|
2001-11-07 03:15:44 +03:00
|
|
|
sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
|
2005-01-12 22:56:32 +03:00
|
|
|
he->value = JS_UINT32_TO_PTR(sharpid);
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
|
|
|
ida = NULL;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
if (idap)
|
2001-02-07 02:19:44 +03:00
|
|
|
*idap = ida;
|
1998-03-28 05:44:41 +03:00
|
|
|
return he;
|
|
|
|
}
|
|
|
|
|
1998-10-14 14:22:38 +04:00
|
|
|
JSHashEntry *
|
1998-04-24 04:31:11 +04:00
|
|
|
js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
|
2001-02-07 02:19:44 +03:00
|
|
|
jschar **sp)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
|
|
|
JSSharpObjectMap *map;
|
1998-10-14 14:22:38 +04:00
|
|
|
JSHashTable *table;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSIdArray *ida;
|
1998-10-14 14:22:38 +04:00
|
|
|
JSHashNumber hash;
|
|
|
|
JSHashEntry *he, **hep;
|
1998-03-28 05:44:41 +03:00
|
|
|
jsatomid sharpid;
|
|
|
|
char buf[20];
|
|
|
|
size_t len;
|
|
|
|
|
2004-12-07 02:17:19 +03:00
|
|
|
if (JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) &&
|
|
|
|
cx->branchCallback &&
|
|
|
|
!cx->branchCallback(cx, NULL)) {
|
2004-12-10 04:31:16 +03:00
|
|
|
return NULL;
|
2004-12-07 02:17:19 +03:00
|
|
|
}
|
|
|
|
|
2001-11-07 03:15:44 +03:00
|
|
|
/* Set to null in case we return an early error. */
|
|
|
|
*sp = NULL;
|
1998-03-28 05:44:41 +03:00
|
|
|
map = &cx->sharpObjectMap;
|
|
|
|
table = map->table;
|
|
|
|
if (!table) {
|
2001-02-07 02:19:44 +03:00
|
|
|
table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
|
|
|
|
JS_CompareValues, NULL, NULL);
|
|
|
|
if (!table) {
|
|
|
|
JS_ReportOutOfMemory(cx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
map->table = table;
|
2006-09-11 15:17:05 +04:00
|
|
|
JS_KEEP_ATOMS(cx->runtime);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
2006-09-11 15:17:05 +04:00
|
|
|
/* From this point the control must flow either through out: or bad:. */
|
1998-04-24 04:31:11 +04:00
|
|
|
ida = NULL;
|
1998-03-28 05:44:41 +03:00
|
|
|
if (map->depth == 0) {
|
2001-02-07 02:19:44 +03:00
|
|
|
he = MarkSharpObjects(cx, obj, &ida);
|
|
|
|
if (!he)
|
|
|
|
goto bad;
|
2005-04-17 22:17:23 +04:00
|
|
|
JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0);
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!idap) {
|
|
|
|
JS_DestroyIdArray(cx, ida);
|
|
|
|
ida = NULL;
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
} else {
|
2001-02-07 02:19:44 +03:00
|
|
|
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;
|
|
|
|
}
|
1998-11-05 03:08:43 +03:00
|
|
|
sharpid = 0;
|
2001-02-07 02:19:44 +03:00
|
|
|
goto out;
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
2005-01-12 22:56:32 +03:00
|
|
|
sharpid = JS_PTR_TO_UINT32(he->value);
|
2005-10-09 08:25:35 +04:00
|
|
|
if (sharpid != 0) {
|
2001-02-07 02:19:44 +03:00
|
|
|
len = JS_snprintf(buf, sizeof buf, "#%u%c",
|
2001-11-07 03:15:44 +03:00
|
|
|
sharpid >> SHARP_ID_SHIFT,
|
|
|
|
(sharpid & SHARP_BIT) ? '#' : '=');
|
2005-11-05 04:06:30 +03:00
|
|
|
*sp = js_InflateString(cx, buf, &len);
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!*sp) {
|
|
|
|
if (ida)
|
|
|
|
JS_DestroyIdArray(cx, ida);
|
|
|
|
goto bad;
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
1998-06-10 03:04:48 +04:00
|
|
|
out:
|
2000-09-01 22:01:04 +04:00
|
|
|
JS_ASSERT(he);
|
1998-04-24 04:31:11 +04:00
|
|
|
if ((sharpid & SHARP_BIT) == 0) {
|
2001-02-07 02:19:44 +03:00
|
|
|
if (idap && !ida) {
|
|
|
|
ida = JS_Enumerate(cx, obj);
|
|
|
|
if (!ida) {
|
|
|
|
if (*sp) {
|
|
|
|
JS_free(cx, *sp);
|
|
|
|
*sp = NULL;
|
|
|
|
}
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
map->depth++;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
1998-06-10 03:04:48 +04:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
if (idap)
|
2001-02-07 02:19:44 +03:00
|
|
|
*idap = ida;
|
1998-03-28 05:44:41 +03:00
|
|
|
return he;
|
2000-09-01 22:01:04 +04:00
|
|
|
|
|
|
|
bad:
|
|
|
|
/* Clean up the sharpObjectMap table on outermost error. */
|
|
|
|
if (map->depth == 0) {
|
2006-09-11 15:17:05 +04:00
|
|
|
JS_UNKEEP_ATOMS(cx->runtime);
|
2001-02-07 02:19:44 +03:00
|
|
|
map->sharpgen = 0;
|
|
|
|
JS_HashTableDestroy(map->table);
|
|
|
|
map->table = NULL;
|
2000-09-01 22:01:04 +04:00
|
|
|
}
|
|
|
|
return NULL;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
1998-04-24 04:31:11 +04:00
|
|
|
js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
|
|
|
JSSharpObjectMap *map;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSIdArray *ida;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
|
|
|
map = &cx->sharpObjectMap;
|
1998-10-14 14:22:38 +04:00
|
|
|
JS_ASSERT(map->depth > 0);
|
1998-03-28 05:44:41 +03:00
|
|
|
if (--map->depth == 0) {
|
2006-09-11 15:17:05 +04:00
|
|
|
JS_UNKEEP_ATOMS(cx->runtime);
|
2001-02-07 02:19:44 +03:00
|
|
|
map->sharpgen = 0;
|
|
|
|
JS_HashTableDestroy(map->table);
|
|
|
|
map->table = NULL;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
if (idap) {
|
2001-02-07 02:19:44 +03:00
|
|
|
ida = *idap;
|
|
|
|
if (ida) {
|
|
|
|
JS_DestroyIdArray(cx, ida);
|
|
|
|
*idap = NULL;
|
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
2006-06-15 14:14:42 +04:00
|
|
|
JS_STATIC_DLL_CALLBACK(intN)
|
|
|
|
gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg)
|
|
|
|
{
|
|
|
|
GC_MARK((JSContext *)arg, (JSObject *)he->key, "sharp table entry");
|
|
|
|
return JS_DHASH_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
js_GCMarkSharpMap(JSContext *cx, 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, cx);
|
|
|
|
}
|
|
|
|
|
2005-10-20 22:58:24 +04:00
|
|
|
#define OBJ_TOSTRING_EXTRA 4 /* for 4 local GC roots */
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2006-04-27 01:33:01 +04:00
|
|
|
#if JS_HAS_TOSOURCE
|
1998-03-28 05:44:41 +03:00
|
|
|
JSBool
|
1998-04-24 04:31:11 +04:00
|
|
|
js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
2001-02-07 02:19:44 +03:00
|
|
|
jsval *rval)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
1999-10-06 13:09:31 +04:00
|
|
|
JSBool ok, outermost;
|
1998-10-14 14:22:38 +04:00
|
|
|
JSHashEntry *he;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSIdArray *ida;
|
2001-02-07 02:19:44 +03:00
|
|
|
jschar *chars, *ochars, *vsharp;
|
2001-10-25 04:26:38 +04:00
|
|
|
const jschar *idstrchars, *vchars;
|
2006-06-21 04:42:04 +04:00
|
|
|
size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen;
|
1998-04-24 04:31:11 +04:00
|
|
|
char *comma;
|
1999-09-21 04:13:48 +04:00
|
|
|
jsint i, j, length, valcnt;
|
1998-04-24 04:31:11 +04:00
|
|
|
jsid id;
|
2000-05-15 07:54:50 +04:00
|
|
|
#if JS_HAS_GETTER_SETTER
|
1999-09-21 04:13:48 +04:00
|
|
|
JSObject *obj2;
|
|
|
|
JSProperty *prop;
|
|
|
|
uintN attrs;
|
2000-05-15 07:54:50 +04:00
|
|
|
#endif
|
2005-10-20 22:58:24 +04:00
|
|
|
jsval *val;
|
1999-09-21 04:13:48 +04:00
|
|
|
JSString *gsop[2];
|
2001-04-04 11:45:22 +04:00
|
|
|
JSAtom *atom;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSString *idstr, *valstr, *str;
|
2003-09-24 20:49:58 +04:00
|
|
|
int stackDummy;
|
|
|
|
|
|
|
|
if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
|
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2006-04-27 01:33:01 +04:00
|
|
|
/* If outermost, we need parentheses to be an expression, not a block. */
|
|
|
|
outermost = (cx->sharpObjectMap.depth == 0);
|
1998-04-24 04:31:11 +04:00
|
|
|
he = js_EnterSharpObject(cx, obj, &ida, &chars);
|
|
|
|
if (!he)
|
2001-02-07 02:19:44 +03:00
|
|
|
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);
|
1998-03-28 05:44:41 +03:00
|
|
|
#if JS_HAS_SHARP_VARS
|
2001-02-07 02:19:44 +03:00
|
|
|
nchars = js_strlen(chars);
|
1998-03-28 05:44:41 +03:00
|
|
|
#else
|
2001-02-07 02:19:44 +03:00
|
|
|
chars[0] = '{';
|
|
|
|
chars[1] = '}';
|
|
|
|
chars[2] = 0;
|
|
|
|
nchars = 2;
|
1998-03-28 05:44:41 +03:00
|
|
|
#endif
|
2001-02-07 02:19:44 +03:00
|
|
|
goto make_string;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
1998-10-14 14:22:38 +04:00
|
|
|
JS_ASSERT(ida);
|
1998-04-24 04:31:11 +04:00
|
|
|
ok = JS_TRUE;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!chars) {
|
2001-02-07 02:19:44 +03:00
|
|
|
/* 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++] = '(';
|
1998-04-24 04:31:11 +04:00
|
|
|
} else {
|
2001-02-07 02:19:44 +03:00
|
|
|
/* js_EnterSharpObject returned a string of the form "#n=" in chars. */
|
|
|
|
MAKE_SHARP(he);
|
|
|
|
nchars = js_strlen(chars);
|
|
|
|
chars = (jschar *)
|
2000-02-04 05:01:49 +03:00
|
|
|
realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
|
2001-02-07 02:19:44 +03:00
|
|
|
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;
|
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
1999-10-06 13:09:31 +04:00
|
|
|
|
2004-04-13 05:25:17 +04:00
|
|
|
#ifdef DUMP_CALL_TABLE
|
|
|
|
if (cx->options & JSOPTION_LOGCALL_TOSOURCE) {
|
|
|
|
const char *classname = OBJ_GET_CLASS(cx, obj)->name;
|
|
|
|
size_t classnchars = strlen(classname);
|
|
|
|
static const char classpropid[] = "C";
|
|
|
|
const char *cp;
|
|
|
|
size_t onchars = nchars;
|
|
|
|
|
|
|
|
/* 2 for ': ', 2 quotes around classname, 2 for ', ' after. */
|
|
|
|
classnchars += sizeof classpropid - 1 + 2 + 2;
|
|
|
|
if (ida->length)
|
|
|
|
classnchars += 2;
|
|
|
|
|
|
|
|
/* 2 for the braces, 1 for the terminator */
|
|
|
|
chars = (jschar *)
|
|
|
|
realloc((ochars = chars),
|
|
|
|
(nchars + classnchars + 2 + 1) * sizeof(jschar));
|
|
|
|
if (!chars) {
|
|
|
|
free(ochars);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
chars[nchars++] = '{'; /* 1 from the 2 braces */
|
|
|
|
for (cp = classpropid; *cp; cp++)
|
|
|
|
chars[nchars++] = (jschar) *cp;
|
|
|
|
chars[nchars++] = ':';
|
|
|
|
chars[nchars++] = ' '; /* 2 for ': ' */
|
|
|
|
chars[nchars++] = '"';
|
|
|
|
for (cp = classname; *cp; cp++)
|
|
|
|
chars[nchars++] = (jschar) *cp;
|
|
|
|
chars[nchars++] = '"'; /* 2 quotes */
|
|
|
|
if (ida->length) {
|
|
|
|
chars[nchars++] = ',';
|
|
|
|
chars[nchars++] = ' '; /* 2 for ', ' */
|
|
|
|
}
|
|
|
|
|
|
|
|
JS_ASSERT(nchars - onchars == 1 + classnchars);
|
|
|
|
} else
|
|
|
|
#endif
|
1998-04-24 04:31:11 +04:00
|
|
|
chars[nchars++] = '{';
|
|
|
|
|
|
|
|
comma = NULL;
|
|
|
|
|
2005-10-20 22:58:24 +04:00
|
|
|
/*
|
|
|
|
* We have four local roots for cooked and raw value GC safety. Hoist the
|
|
|
|
* "argv + 2" out of the loop using the val local, which refers to the raw
|
|
|
|
* (unconverted, "uncooked") values.
|
|
|
|
*/
|
|
|
|
val = argv + 2;
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
for (i = 0, length = ida->length; i < length; i++) {
|
2001-02-07 02:19:44 +03:00
|
|
|
/* Get strings for id and value and GC-root them via argv. */
|
|
|
|
id = ida->vector[i];
|
2000-05-15 07:54:50 +04:00
|
|
|
|
1999-09-21 04:13:48 +04:00
|
|
|
#if JS_HAS_GETTER_SETTER
|
2006-10-13 08:01:50 +04:00
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
|
|
|
|
if (!ok)
|
|
|
|
goto error;
|
2002-02-23 06:49:27 +03:00
|
|
|
valcnt = 0;
|
2000-10-25 11:00:31 +04:00
|
|
|
if (prop) {
|
|
|
|
ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
|
2002-02-23 06:49:27 +03:00
|
|
|
if (!ok) {
|
|
|
|
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
2000-10-25 11:00:31 +04:00
|
|
|
goto error;
|
2002-02-23 06:49:27 +03:00
|
|
|
}
|
2000-10-25 11:00:31 +04:00
|
|
|
if (OBJ_IS_NATIVE(obj2) &&
|
1999-09-21 04:13:48 +04:00
|
|
|
(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
|
|
|
|
if (attrs & JSPROP_GETTER) {
|
2002-02-23 06:49:27 +03:00
|
|
|
val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter;
|
2000-03-03 02:21:03 +03:00
|
|
|
#ifdef OLD_GETTER_SETTER
|
1999-09-21 04:13:48 +04:00
|
|
|
gsop[valcnt] =
|
|
|
|
ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
|
2000-03-03 02:21:03 +03:00
|
|
|
#else
|
2006-10-13 08:01:50 +04:00
|
|
|
gsop[valcnt] =
|
|
|
|
ATOM_TO_STRING(cx->runtime->atomState.getAtom);
|
2000-03-03 02:21:03 +03:00
|
|
|
#endif
|
1999-09-21 04:13:48 +04:00
|
|
|
valcnt++;
|
|
|
|
}
|
|
|
|
if (attrs & JSPROP_SETTER) {
|
2002-02-23 06:49:27 +03:00
|
|
|
val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter;
|
2000-03-03 02:21:03 +03:00
|
|
|
#ifdef OLD_GETTER_SETTER
|
1999-09-21 04:13:48 +04:00
|
|
|
gsop[valcnt] =
|
|
|
|
ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
|
2000-03-03 02:21:03 +03:00
|
|
|
#else
|
2006-10-13 08:01:50 +04:00
|
|
|
gsop[valcnt] =
|
|
|
|
ATOM_TO_STRING(cx->runtime->atomState.setAtom);
|
2000-03-03 02:21:03 +03:00
|
|
|
#endif
|
1999-09-21 04:13:48 +04:00
|
|
|
valcnt++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
valcnt = 1;
|
|
|
|
gsop[0] = NULL;
|
|
|
|
ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
|
|
|
|
}
|
|
|
|
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
2000-10-25 11:00:31 +04:00
|
|
|
}
|
2000-05-15 07:54:50 +04:00
|
|
|
|
|
|
|
#else /* !JS_HAS_GETTER_SETTER */
|
|
|
|
|
1999-09-21 04:13:48 +04:00
|
|
|
valcnt = 1;
|
2000-05-15 07:54:50 +04:00
|
|
|
gsop[0] = NULL;
|
1999-09-21 04:13:48 +04:00
|
|
|
ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
|
2000-05-15 07:54:50 +04:00
|
|
|
|
|
|
|
#endif /* !JS_HAS_GETTER_SETTER */
|
|
|
|
|
1999-09-21 04:13:48 +04:00
|
|
|
if (!ok)
|
|
|
|
goto error;
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2006-10-13 08:01:50 +04:00
|
|
|
/* Convert id to a jsval and then to a string. */
|
|
|
|
atom = JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL;
|
|
|
|
id = ID_TO_VALUE(id);
|
|
|
|
idstr = js_ValueToString(cx, id);
|
|
|
|
if (!idstr) {
|
|
|
|
ok = JS_FALSE;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
*rval = STRING_TO_JSVAL(idstr); /* local root */
|
|
|
|
|
2001-04-04 11:45:22 +04:00
|
|
|
/*
|
2006-01-26 11:47:51 +03:00
|
|
|
* If id is a string that's not an identifier, then it needs to be
|
|
|
|
* quoted. Also, negative integer ids must be quoted.
|
2001-04-04 11:45:22 +04:00
|
|
|
*/
|
2004-08-05 12:06:26 +04:00
|
|
|
if (atom
|
2006-10-13 08:01:50 +04:00
|
|
|
? !js_IsIdentifier(idstr)
|
2004-10-05 14:19:07 +04:00
|
|
|
: (JSID_IS_OBJECT(id) || JSID_TO_INT(id) < 0)) {
|
1999-09-21 04:13:48 +04:00
|
|
|
idstr = js_QuoteString(cx, idstr, (jschar)'\'');
|
|
|
|
if (!idstr) {
|
|
|
|
ok = JS_FALSE;
|
|
|
|
goto error;
|
|
|
|
}
|
2005-10-20 22:58:24 +04:00
|
|
|
*rval = STRING_TO_JSVAL(idstr); /* local root */
|
1999-09-21 04:13:48 +04:00
|
|
|
}
|
2001-10-25 04:26:38 +04:00
|
|
|
idstrchars = JSSTRING_CHARS(idstr);
|
|
|
|
idstrlength = JSSTRING_LENGTH(idstr);
|
1999-09-21 04:13:48 +04:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2005-10-20 22:58:24 +04:00
|
|
|
argv[j] = STRING_TO_JSVAL(valstr); /* local root */
|
2001-10-25 04:26:38 +04:00
|
|
|
vchars = JSSTRING_CHARS(valstr);
|
|
|
|
vlength = JSSTRING_LENGTH(valstr);
|
1999-09-21 04:13:48 +04:00
|
|
|
|
2000-03-03 02:21:03 +03:00
|
|
|
#ifndef OLD_GETTER_SETTER
|
2005-09-02 20:59:14 +04:00
|
|
|
/*
|
|
|
|
* Remove '(function ' from the beginning of valstr and ')' from the
|
|
|
|
* end so that we can put "get" in front of the function definition.
|
|
|
|
*/
|
2006-10-13 08:01:50 +04:00
|
|
|
if (gsop[j] && VALUE_IS_FUNCTION(cx, val[j])) {
|
2006-10-10 02:52:22 +04:00
|
|
|
size_t n = strlen(js_function_str) + 2;
|
|
|
|
JS_ASSERT(vlength > n);
|
2000-03-03 02:21:03 +03:00
|
|
|
vchars += n;
|
2005-09-02 20:59:14 +04:00
|
|
|
vlength -= n + 1;
|
2000-03-03 02:21:03 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
1999-09-21 04:13:48 +04:00
|
|
|
/* If val[j] is a non-sharp object, consider sharpening it. */
|
|
|
|
vsharp = NULL;
|
|
|
|
vsharplength = 0;
|
1998-04-24 04:31:11 +04:00
|
|
|
#if JS_HAS_SHARP_VARS
|
1999-09-21 04:13:48 +04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
#endif
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2006-06-21 04:42:04 +04:00
|
|
|
#define SAFE_ADD(n) \
|
2006-06-21 22:16:52 +04:00
|
|
|
JS_BEGIN_MACRO \
|
|
|
|
size_t n_ = (n); \
|
|
|
|
curlen += n_; \
|
|
|
|
if (curlen < n_) \
|
|
|
|
goto overflow; \
|
|
|
|
JS_END_MACRO
|
2006-06-21 04:42:04 +04:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
1999-09-21 04:13:48 +04:00
|
|
|
/* Allocate 1 + 1 at end for closing brace and terminating 0. */
|
2000-02-04 05:01:49 +03:00
|
|
|
chars = (jschar *)
|
2006-06-21 04:42:04 +04:00
|
|
|
realloc((ochars = chars), curlen * sizeof(jschar));
|
1999-09-21 04:13:48 +04:00
|
|
|
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 = ", ";
|
|
|
|
|
2000-03-03 02:21:03 +03:00
|
|
|
#ifdef OLD_GETTER_SETTER
|
2001-10-25 04:26:38 +04:00
|
|
|
js_strncpy(&chars[nchars], idstrchars, idstrlength);
|
|
|
|
nchars += idstrlength;
|
1999-09-21 04:13:48 +04:00
|
|
|
if (gsop[j]) {
|
|
|
|
chars[nchars++] = ' ';
|
2001-10-25 04:26:38 +04:00
|
|
|
gsoplength = JSSTRING_LENGTH(gsop[j]);
|
|
|
|
js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength);
|
|
|
|
nchars += gsoplength;
|
1999-09-21 04:13:48 +04:00
|
|
|
}
|
|
|
|
chars[nchars++] = ':';
|
2000-03-03 02:21:03 +03:00
|
|
|
#else
|
2006-10-13 08:01:50 +04:00
|
|
|
if (gsop[j]) {
|
|
|
|
gsoplength = JSSTRING_LENGTH(gsop[j]);
|
|
|
|
js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength);
|
|
|
|
nchars += gsoplength;
|
|
|
|
chars[nchars++] = ' ';
|
2000-03-03 02:21:03 +03:00
|
|
|
}
|
2006-10-13 08:01:50 +04:00
|
|
|
js_strncpy(&chars[nchars], idstrchars, idstrlength);
|
|
|
|
nchars += idstrlength;
|
|
|
|
chars[nchars++] = gsop[j] ? ' ' : ':';
|
2000-03-03 02:21:03 +03:00
|
|
|
#endif
|
1999-09-21 04:13:48 +04:00
|
|
|
if (vsharplength) {
|
|
|
|
js_strncpy(&chars[nchars], vsharp, vsharplength);
|
|
|
|
nchars += vsharplength;
|
|
|
|
}
|
|
|
|
js_strncpy(&chars[nchars], vchars, vlength);
|
|
|
|
nchars += vlength;
|
|
|
|
|
|
|
|
if (vsharp)
|
|
|
|
JS_free(cx, vsharp);
|
2004-04-13 05:25:17 +04:00
|
|
|
#ifdef DUMP_CALL_TABLE
|
|
|
|
if (outermost && nchars >= js_LogCallToSourceLimit)
|
|
|
|
break;
|
|
|
|
#endif
|
1999-09-21 04:13:48 +04:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
|
1999-10-06 13:09:31 +04:00
|
|
|
chars[nchars++] = '}';
|
|
|
|
if (outermost)
|
2001-02-07 02:19:44 +03:00
|
|
|
chars[nchars++] = ')';
|
1999-10-06 13:09:31 +04:00
|
|
|
chars[nchars] = 0;
|
1998-11-05 03:08:43 +03:00
|
|
|
|
2000-09-01 22:01:04 +04:00
|
|
|
error:
|
1998-04-24 04:31:11 +04:00
|
|
|
js_LeaveSharpObject(cx, &ida);
|
|
|
|
|
|
|
|
if (!ok) {
|
2001-02-07 02:19:44 +03:00
|
|
|
if (chars)
|
|
|
|
free(chars);
|
|
|
|
return ok;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!chars) {
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_ReportOutOfMemory(cx);
|
|
|
|
return JS_FALSE;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
make_string:
|
1998-03-28 05:44:41 +03:00
|
|
|
str = js_NewString(cx, chars, nchars, 0);
|
|
|
|
if (!str) {
|
2001-02-07 02:19:44 +03:00
|
|
|
free(chars);
|
|
|
|
return JS_FALSE;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
*rval = STRING_TO_JSVAL(str);
|
|
|
|
return JS_TRUE;
|
2006-06-21 04:42:04 +04:00
|
|
|
|
|
|
|
overflow:
|
|
|
|
JS_free(cx, vsharp);
|
|
|
|
free(chars);
|
|
|
|
chars = NULL;
|
|
|
|
goto error;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
2006-04-27 01:33:01 +04:00
|
|
|
#endif /* JS_HAS_TOSOURCE */
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
JSBool
|
|
|
|
js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
2001-02-07 02:19:44 +03:00
|
|
|
jsval *rval)
|
1998-04-24 04:31:11 +04:00
|
|
|
{
|
|
|
|
jschar *chars;
|
|
|
|
size_t nchars;
|
2000-08-19 12:37:07 +04:00
|
|
|
const char *clazz, *prefix;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSString *str;
|
|
|
|
|
|
|
|
clazz = OBJ_GET_CLASS(cx, obj)->name;
|
2001-04-14 11:34:58 +04:00
|
|
|
nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
|
2000-02-04 05:01:49 +03:00
|
|
|
chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar));
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!chars)
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
prefix = "[object ";
|
|
|
|
nchars = 0;
|
|
|
|
while ((chars[nchars] = (jschar)*prefix) != 0)
|
2001-02-07 02:19:44 +03:00
|
|
|
nchars++, prefix++;
|
1998-04-24 04:31:11 +04:00
|
|
|
while ((chars[nchars] = (jschar)*clazz) != 0)
|
2001-02-07 02:19:44 +03:00
|
|
|
nchars++, clazz++;
|
1998-04-24 04:31:11 +04:00
|
|
|
chars[nchars++] = ']';
|
|
|
|
chars[nchars] = 0;
|
|
|
|
|
|
|
|
str = js_NewString(cx, chars, nchars, 0);
|
|
|
|
if (!str) {
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_free(cx, chars);
|
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
*rval = STRING_TO_JSVAL(str);
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2005-09-30 01:54:37 +04:00
|
|
|
static JSBool
|
|
|
|
js_obj_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|
|
|
jsval *rval)
|
|
|
|
{
|
2005-09-30 03:15:40 +04:00
|
|
|
JSString *str;
|
2005-09-30 01:54:37 +04:00
|
|
|
|
2005-09-30 03:15:40 +04:00
|
|
|
str = js_ValueToString(cx, argv[-1]);
|
|
|
|
if (!str)
|
2005-09-30 01:54:37 +04:00
|
|
|
return JS_FALSE;
|
|
|
|
|
2005-09-30 03:15:40 +04:00
|
|
|
*rval = STRING_TO_JSVAL(str);
|
2005-09-30 01:54:37 +04:00
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
static JSBool
|
|
|
|
obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
|
|
|
*rval = OBJECT_TO_JSVAL(obj);
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2005-10-07 05:37:11 +04:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2005-10-22 05:03:06 +04:00
|
|
|
JSBool
|
|
|
|
js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj,
|
2006-01-26 01:54:34 +03:00
|
|
|
JSPrincipals *principals, JSAtom *caller)
|
2005-10-07 05:37:11 +04:00
|
|
|
{
|
|
|
|
JSRuntime *rt;
|
|
|
|
JSPrincipals *scopePrincipals;
|
2006-01-26 01:54:34 +03:00
|
|
|
const char *callerstr;
|
2005-10-07 05:37:11 +04:00
|
|
|
|
|
|
|
rt = cx->runtime;
|
|
|
|
if (rt->findObjectPrincipals) {
|
|
|
|
scopePrincipals = rt->findObjectPrincipals(cx, scopeobj);
|
2005-10-09 08:25:35 +04:00
|
|
|
if (!principals || !scopePrincipals ||
|
2005-10-07 05:37:11 +04:00
|
|
|
!principals->subsume(principals, scopePrincipals)) {
|
2006-01-26 01:54:34 +03:00
|
|
|
callerstr = js_AtomToPrintableString(cx, caller);
|
|
|
|
if (!callerstr)
|
|
|
|
return JS_FALSE;
|
2005-10-07 05:37:11 +04:00
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
2006-01-26 01:54:34 +03:00
|
|
|
JSMSG_BAD_INDIRECT_CALL, callerstr);
|
2005-10-07 05:37:11 +04:00
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2005-10-22 05:03:06 +04:00
|
|
|
JSObject *
|
2005-10-14 23:05:43 +04:00
|
|
|
js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller)
|
|
|
|
{
|
|
|
|
JSClass *clasp;
|
|
|
|
JSExtendedClass *xclasp;
|
2005-10-22 05:03:06 +04:00
|
|
|
JSObject *inner;
|
|
|
|
|
2005-10-31 22:50:49 +03:00
|
|
|
if (!scopeobj)
|
|
|
|
goto bad;
|
|
|
|
|
2005-10-22 05:03:06 +04:00
|
|
|
OBJ_TO_INNER_OBJECT(cx, scopeobj);
|
|
|
|
if (!scopeobj)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
inner = scopeobj;
|
2005-10-14 23:05:43 +04:00
|
|
|
|
|
|
|
/* 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) {
|
2005-10-31 22:50:49 +03:00
|
|
|
goto bad;
|
2005-10-14 23:05:43 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
scopeobj = OBJ_GET_PARENT(cx, scopeobj);
|
|
|
|
}
|
|
|
|
|
2005-10-22 05:03:06 +04:00
|
|
|
return inner;
|
2005-10-31 22:50:49 +03:00
|
|
|
|
|
|
|
bad:
|
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_BAD_INDIRECT_CALL, caller);
|
|
|
|
return NULL;
|
2005-10-14 23:05:43 +04:00
|
|
|
}
|
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
static JSBool
|
|
|
|
obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
2001-02-14 12:11:09 +03:00
|
|
|
JSStackFrame *fp, *caller;
|
2000-01-18 14:06:05 +03:00
|
|
|
JSBool indirectCall;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSObject *scopeobj;
|
1998-03-28 05:44:41 +03:00
|
|
|
JSString *str;
|
|
|
|
const char *file;
|
|
|
|
uintN line;
|
2005-10-07 05:37:11 +04:00
|
|
|
JSPrincipals *principals;
|
1998-03-28 05:44:41 +03:00
|
|
|
JSScript *script;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSBool ok;
|
1998-03-28 05:44:41 +03:00
|
|
|
#if JS_HAS_EVAL_THIS_SCOPE
|
2001-02-14 12:11:09 +03:00
|
|
|
JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
|
2006-07-18 03:49:36 +04:00
|
|
|
JSObject *setCallerScopeChain = NULL;
|
|
|
|
JSBool setCallerVarObj = JS_FALSE;
|
1998-03-28 05:44:41 +03:00
|
|
|
#endif
|
|
|
|
|
2001-02-14 12:11:09 +03:00
|
|
|
fp = cx->fp;
|
2003-11-02 04:04:50 +03:00
|
|
|
caller = JS_GetScriptedCaller(cx, fp);
|
2006-03-08 20:15:29 +03:00
|
|
|
JS_ASSERT(!caller || caller->pc);
|
|
|
|
indirectCall = (caller && *caller->pc != JSOP_EVAL);
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2006-04-27 01:33:01 +04:00
|
|
|
if (indirectCall &&
|
2001-01-31 04:12:15 +03:00
|
|
|
!JS_ReportErrorFlagsAndNumber(cx,
|
|
|
|
JSREPORT_WARNING | JSREPORT_STRICT,
|
|
|
|
js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_BAD_INDIRECT_CALL,
|
|
|
|
js_eval_str)) {
|
1998-10-14 14:22:38 +04:00
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
|
1998-11-05 03:08:43 +03:00
|
|
|
if (!JSVAL_IS_STRING(argv[0])) {
|
2001-02-07 02:19:44 +03:00
|
|
|
*rval = argv[0];
|
|
|
|
return JS_TRUE;
|
1998-11-05 03:08:43 +03:00
|
|
|
}
|
|
|
|
|
2005-12-15 05:30:16 +03:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
#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) {
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!js_ValueToObject(cx, argv[1], &scopeobj))
|
|
|
|
return JS_FALSE;
|
|
|
|
argv[1] = OBJECT_TO_JSVAL(scopeobj);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!scopeobj)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
#if JS_HAS_EVAL_THIS_SCOPE
|
2001-02-07 02:19:44 +03:00
|
|
|
/* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
|
|
|
|
if (indirectCall) {
|
2006-05-21 02:27:28 +04:00
|
|
|
callerScopeChain = js_GetScopeChain(cx, caller);
|
|
|
|
if (!callerScopeChain)
|
|
|
|
return JS_FALSE;
|
2006-03-23 00:44:31 +03:00
|
|
|
OBJ_TO_INNER_OBJECT(cx, obj);
|
|
|
|
if (!obj)
|
|
|
|
return JS_FALSE;
|
2001-02-14 12:11:09 +03:00
|
|
|
if (obj != callerScopeChain) {
|
2005-10-22 05:03:06 +04:00
|
|
|
if (!js_CheckPrincipalsAccess(cx, obj,
|
|
|
|
caller->script->principals,
|
2006-04-20 03:36:16 +04:00
|
|
|
cx->runtime->atomState.evalAtom))
|
|
|
|
{
|
2005-10-07 05:37:11 +04:00
|
|
|
return JS_FALSE;
|
2005-10-22 05:03:06 +04:00
|
|
|
}
|
2005-10-07 05:37:11 +04:00
|
|
|
|
2006-05-21 02:27:28 +04:00
|
|
|
scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1);
|
2001-02-14 12:11:09 +03:00
|
|
|
if (!scopeobj)
|
|
|
|
return JS_FALSE;
|
|
|
|
|
|
|
|
/* Set fp->scopeChain too, for the compiler. */
|
|
|
|
caller->scopeChain = fp->scopeChain = scopeobj;
|
2006-07-18 03:49:36 +04:00
|
|
|
|
|
|
|
/* Remember scopeobj so we can null its private when done. */
|
|
|
|
setCallerScopeChain = scopeobj;
|
2001-02-14 12:11:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
callerVarObj = caller->varobj;
|
|
|
|
if (obj != callerVarObj) {
|
|
|
|
/* Set fp->varobj too, for the compiler. */
|
|
|
|
caller->varobj = fp->varobj = obj;
|
|
|
|
setCallerVarObj = JS_TRUE;
|
|
|
|
}
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
|
|
|
/* From here on, control must exit through label out with ok set. */
|
1998-03-28 05:44:41 +03:00
|
|
|
#endif
|
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
/* Compile using caller's current scope object. */
|
2006-05-21 02:27:28 +04:00
|
|
|
if (caller) {
|
|
|
|
scopeobj = js_GetScopeChain(cx, caller);
|
|
|
|
if (!scopeobj) {
|
|
|
|
ok = JS_FALSE;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2005-10-08 04:28:45 +04:00
|
|
|
/* Ensure we compile this eval with the right object in the scope chain. */
|
2005-10-22 05:03:06 +04:00
|
|
|
scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str);
|
2005-10-08 04:28:45 +04:00
|
|
|
if (!scopeobj)
|
|
|
|
return JS_FALSE;
|
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
str = JSVAL_TO_STRING(argv[0]);
|
2003-11-02 04:04:50 +03:00
|
|
|
if (caller) {
|
2001-02-07 02:19:44 +03:00
|
|
|
file = caller->script->filename;
|
2003-07-27 02:37:11 +04:00
|
|
|
line = js_PCToLineNumber(cx, caller->script, caller->pc);
|
2003-11-02 04:04:50 +03:00
|
|
|
principals = JS_EvalFramePrincipals(cx, fp, caller);
|
1998-03-28 05:44:41 +03:00
|
|
|
} else {
|
2001-02-07 02:19:44 +03:00
|
|
|
file = NULL;
|
|
|
|
line = 0;
|
|
|
|
principals = NULL;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
2000-04-05 06:17:38 +04:00
|
|
|
|
2004-12-16 03:25:03 +03:00
|
|
|
/*
|
|
|
|
* Set JSFRAME_EVAL on fp and any frames (e.g., fun_call if eval.call was
|
|
|
|
* invoked) between fp and its scripted caller, to help the compiler easily
|
|
|
|
* find the same caller whose scope and var obj we've set.
|
|
|
|
*
|
|
|
|
* XXX this nonsense could, and perhaps should, go away with a better way
|
|
|
|
* to pass params to the compiler than via the top-most frame.
|
|
|
|
*/
|
|
|
|
do {
|
|
|
|
fp->flags |= JSFRAME_EVAL;
|
|
|
|
} while ((fp = fp->down) != caller);
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,
|
2001-10-25 04:26:38 +04:00
|
|
|
JSSTRING_CHARS(str),
|
|
|
|
JSSTRING_LENGTH(str),
|
2001-02-07 02:19:44 +03:00
|
|
|
file, line);
|
1998-03-28 05:44:41 +03:00
|
|
|
if (!script) {
|
2001-02-07 02:19:44 +03:00
|
|
|
ok = JS_FALSE;
|
|
|
|
goto out;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
#if JS_HAS_SCRIPT_OBJECT
|
|
|
|
if (argc < 2)
|
|
|
|
#endif
|
|
|
|
{
|
2001-02-07 02:19:44 +03:00
|
|
|
/* Execute using caller's new scope object (might be a Call object). */
|
2003-11-02 04:04:50 +03:00
|
|
|
if (caller)
|
|
|
|
scopeobj = caller->scopeChain;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
2005-04-12 05:35:05 +04:00
|
|
|
|
2005-07-09 03:26:36 +04:00
|
|
|
/*
|
|
|
|
* Belt-and-braces: check that the lesser of eval's principals and the
|
|
|
|
* caller's principals has access to scopeobj.
|
|
|
|
*/
|
2006-01-26 01:54:34 +03:00
|
|
|
ok = js_CheckPrincipalsAccess(cx, scopeobj, principals,
|
|
|
|
cx->runtime->atomState.evalAtom);
|
2006-02-01 06:18:17 +03:00
|
|
|
if (ok)
|
|
|
|
ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
|
2005-04-12 05:35:05 +04:00
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
JS_DestroyScript(cx, script);
|
|
|
|
|
|
|
|
out:
|
|
|
|
#if JS_HAS_EVAL_THIS_SCOPE
|
1998-04-24 04:31:11 +04:00
|
|
|
/* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
|
2006-07-18 03:49:36 +04:00
|
|
|
if (setCallerScopeChain) {
|
2001-02-14 12:11:09 +03:00
|
|
|
caller->scopeChain = callerScopeChain;
|
2006-07-18 03:49:36 +04:00
|
|
|
JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass);
|
|
|
|
JS_SetPrivate(cx, setCallerScopeChain, NULL);
|
|
|
|
}
|
2001-02-14 12:11:09 +03:00
|
|
|
if (setCallerVarObj)
|
|
|
|
caller->varobj = callerVarObj;
|
1998-03-28 05:44:41 +03:00
|
|
|
#endif
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if JS_HAS_OBJ_WATCHPOINT
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
|
2001-02-07 02:19:44 +03:00
|
|
|
void *closure)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
2006-11-23 22:08:08 +03:00
|
|
|
JSObject *callable;
|
2006-11-06 22:14:50 +03:00
|
|
|
JSRuntime *rt;
|
|
|
|
JSStackFrame *caller;
|
|
|
|
JSPrincipals *subject, *watcher;
|
2003-08-04 01:59:50 +04:00
|
|
|
JSResolvingKey key;
|
|
|
|
JSResolvingEntry *entry;
|
|
|
|
uint32 generation;
|
1998-03-28 05:44:41 +03:00
|
|
|
jsval argv[3];
|
2003-08-04 01:59:50 +04:00
|
|
|
JSBool ok;
|
|
|
|
|
2006-11-23 22:08:08 +03:00
|
|
|
callable = (JSObject *) closure;
|
2006-11-06 22:14:50 +03:00
|
|
|
|
|
|
|
rt = cx->runtime;
|
|
|
|
if (rt->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.
|
|
|
|
*/
|
2006-11-23 22:08:08 +03:00
|
|
|
watcher = rt->findObjectPrincipals(cx, callable);
|
2006-11-06 22:14:50 +03:00
|
|
|
subject = JS_StackFramePrincipals(cx, caller);
|
|
|
|
|
|
|
|
if (watcher && subject && !watcher->subsume(watcher, subject)) {
|
|
|
|
/* Silently don't call the watch handler. */
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-08-04 01:59:50 +04:00
|
|
|
/* Avoid recursion on (obj, id) already being watched on cx. */
|
|
|
|
key.obj = obj;
|
|
|
|
key.id = id;
|
2004-08-19 21:57:36 +04:00
|
|
|
if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
|
2003-08-04 01:59:50 +04:00
|
|
|
return JS_FALSE;
|
|
|
|
if (!entry)
|
|
|
|
return JS_TRUE;
|
|
|
|
generation = cx->resolvingTable->generation;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
|
|
|
argv[0] = id;
|
|
|
|
argv[1] = old;
|
|
|
|
argv[2] = *nvp;
|
2006-11-23 22:08:08 +03:00
|
|
|
ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp);
|
2004-08-19 21:57:36 +04:00
|
|
|
js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
|
2003-08-04 01:59:50 +04:00
|
|
|
return ok;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
2005-10-25 12:00:30 +04:00
|
|
|
JSObject *callable;
|
1998-04-24 04:31:11 +04:00
|
|
|
jsval userid, value;
|
2002-02-23 06:49:27 +03:00
|
|
|
jsid propid;
|
1998-04-24 04:31:11 +04:00
|
|
|
uintN attrs;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2005-10-25 12:00:30 +04:00
|
|
|
callable = js_ValueToCallableObject(cx, &argv[1], 0);
|
|
|
|
if (!callable)
|
|
|
|
return JS_FALSE;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
|
|
|
/* Compute the unique int/atom symbol id needed by js_LookupProperty. */
|
|
|
|
userid = argv[0];
|
2002-02-23 06:49:27 +03:00
|
|
|
if (!JS_ValueToId(cx, userid, &propid))
|
2000-06-01 02:10:53 +04:00
|
|
|
return JS_FALSE;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2002-02-23 06:49:27 +03:00
|
|
|
if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
if (attrs & JSPROP_READONLY)
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_TRUE;
|
2005-10-25 12:00:30 +04:00
|
|
|
return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
2002-02-23 06:49:27 +03:00
|
|
|
return JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* JS_HAS_OBJ_WATCHPOINT */
|
|
|
|
|
1998-11-05 03:08:43 +03:00
|
|
|
/*
|
|
|
|
* Prototype and property query methods, to complement the 'in' and
|
|
|
|
* 'instanceof' operators.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Proposed ECMA 15.2.4.5. */
|
2004-11-17 10:43:01 +03:00
|
|
|
static JSBool
|
|
|
|
obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|
|
|
jsval *rval)
|
|
|
|
{
|
|
|
|
return js_HasOwnPropertyHelper(cx, obj, obj->map->ops->lookupProperty,
|
|
|
|
argc, argv, rval);
|
|
|
|
}
|
|
|
|
|
2004-10-05 14:19:07 +04:00
|
|
|
JSBool
|
2004-11-17 10:43:01 +03:00
|
|
|
js_HasOwnPropertyHelper(JSContext *cx, JSObject *obj, JSLookupPropOp lookup,
|
|
|
|
uintN argc, jsval *argv, jsval *rval)
|
1998-11-05 03:08:43 +03:00
|
|
|
{
|
2000-06-01 02:10:53 +04:00
|
|
|
jsid id;
|
1998-11-05 03:08:43 +03:00
|
|
|
JSObject *obj2;
|
|
|
|
JSProperty *prop;
|
- [jsemit.c] Fix horrid stupid bugs generating JSOP_ARGCNT and JSOP_ARGSUB,
where any occurrence of arguments.length or arguments[0], e.g., would be
"optimized" to use those bytecodes. This is just wrong if the occurrence
is an operand of delete, ++, --, or the left-hand-side of an assignment
operator!
- [jsfun.c, jsinterp.c] args_getProperty etc. must use JS_GetInstancePrivate,
not JS_GetPrivate, as the arguments object is exposed, and can be made a
prototype of other objects that do not have private data, or private data
that's a JSStackFrame*. Same goes for fun_getProperty, js_GetArgument, etc.
- [jsfun.c, jsobj.c, jsstr.c] No need to specialize fun_delProperty and
str_delProperty to help convince users and ECMA conformance tests that
fun.length and str.length are direct properties of instances, instead of
being delegated to Function.prototype.length and String.prototype.length.
This special case is done universally in js_DeleteProperty for all SHARED
and PERMANENT proto-properties.
- [jshash.c] Sneaking this followup-fix for bug 69271 in: use JS_HASH_BITS
rather than hardcoded 32.
- [jsobj.c, jsscope.[ch]] Fix misnamed js_HashValue (it takes a jsid, so it
is now js_HashId).
- [jsscript.c] script_compile needs to call JS_InstanceOf, to ensure that obj
is a Script object.
2001-03-22 05:52:42 +03:00
|
|
|
JSScopeProperty *sprop;
|
1999-06-26 07:28:47 +04:00
|
|
|
|
2000-06-01 02:10:53 +04:00
|
|
|
if (!JS_ValueToId(cx, argv[0], &id))
|
|
|
|
return JS_FALSE;
|
2004-11-17 10:43:01 +03:00
|
|
|
if (!lookup(cx, obj, id, &obj2, &prop))
|
1998-11-05 03:08:43 +03:00
|
|
|
return JS_FALSE;
|
- [jsemit.c] Fix horrid stupid bugs generating JSOP_ARGCNT and JSOP_ARGSUB,
where any occurrence of arguments.length or arguments[0], e.g., would be
"optimized" to use those bytecodes. This is just wrong if the occurrence
is an operand of delete, ++, --, or the left-hand-side of an assignment
operator!
- [jsfun.c, jsinterp.c] args_getProperty etc. must use JS_GetInstancePrivate,
not JS_GetPrivate, as the arguments object is exposed, and can be made a
prototype of other objects that do not have private data, or private data
that's a JSStackFrame*. Same goes for fun_getProperty, js_GetArgument, etc.
- [jsfun.c, jsobj.c, jsstr.c] No need to specialize fun_delProperty and
str_delProperty to help convince users and ECMA conformance tests that
fun.length and str.length are direct properties of instances, instead of
being delegated to Function.prototype.length and String.prototype.length.
This special case is done universally in js_DeleteProperty for all SHARED
and PERMANENT proto-properties.
- [jshash.c] Sneaking this followup-fix for bug 69271 in: use JS_HASH_BITS
rather than hardcoded 32.
- [jsobj.c, jsscope.[ch]] Fix misnamed js_HashValue (it takes a jsid, so it
is now js_HashId).
- [jsscript.c] script_compile needs to call JS_InstanceOf, to ensure that obj
is a Script object.
2001-03-22 05:52:42 +03:00
|
|
|
if (!prop) {
|
|
|
|
*rval = JSVAL_FALSE;
|
|
|
|
} else if (obj2 == obj) {
|
|
|
|
*rval = JSVAL_TRUE;
|
|
|
|
} else {
|
2005-11-21 22:40:55 +03:00
|
|
|
JSClass *clasp;
|
|
|
|
JSExtendedClass *xclasp;
|
|
|
|
|
|
|
|
clasp = OBJ_GET_CLASS(cx, obj);
|
|
|
|
xclasp = (clasp->flags & JSCLASS_IS_EXTENDED)
|
|
|
|
? (JSExtendedClass *)clasp
|
|
|
|
: NULL;
|
|
|
|
if (xclasp && xclasp->outerObject &&
|
|
|
|
xclasp->outerObject(cx, obj2) == obj) {
|
|
|
|
*rval = JSVAL_TRUE;
|
2005-12-20 00:14:31 +03:00
|
|
|
} else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj2) == 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.
|
|
|
|
*/
|
2005-11-21 22:40:55 +03:00
|
|
|
sprop = (JSScopeProperty *)prop;
|
|
|
|
*rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
|
|
|
|
} else {
|
|
|
|
*rval = JSVAL_FALSE;
|
|
|
|
}
|
- [jsemit.c] Fix horrid stupid bugs generating JSOP_ARGCNT and JSOP_ARGSUB,
where any occurrence of arguments.length or arguments[0], e.g., would be
"optimized" to use those bytecodes. This is just wrong if the occurrence
is an operand of delete, ++, --, or the left-hand-side of an assignment
operator!
- [jsfun.c, jsinterp.c] args_getProperty etc. must use JS_GetInstancePrivate,
not JS_GetPrivate, as the arguments object is exposed, and can be made a
prototype of other objects that do not have private data, or private data
that's a JSStackFrame*. Same goes for fun_getProperty, js_GetArgument, etc.
- [jsfun.c, jsobj.c, jsstr.c] No need to specialize fun_delProperty and
str_delProperty to help convince users and ECMA conformance tests that
fun.length and str.length are direct properties of instances, instead of
being delegated to Function.prototype.length and String.prototype.length.
This special case is done universally in js_DeleteProperty for all SHARED
and PERMANENT proto-properties.
- [jshash.c] Sneaking this followup-fix for bug 69271 in: use JS_HASH_BITS
rather than hardcoded 32.
- [jsobj.c, jsscope.[ch]] Fix misnamed js_HashValue (it takes a jsid, so it
is now js_HashId).
- [jsscript.c] script_compile needs to call JS_InstanceOf, to ensure that obj
is a Script object.
2001-03-22 05:52:42 +03:00
|
|
|
}
|
1998-11-05 03:08:43 +03:00
|
|
|
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;
|
1999-07-01 06:24:32 +04:00
|
|
|
*rval = BOOLEAN_TO_JSVAL(b);
|
1998-11-05 03:08:43 +03:00
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Proposed ECMA 15.2.4.7. */
|
|
|
|
static JSBool
|
|
|
|
obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|
|
|
jsval *rval)
|
|
|
|
{
|
2000-06-01 02:10:53 +04:00
|
|
|
jsid id;
|
1998-11-05 03:08:43 +03:00
|
|
|
uintN attrs;
|
2000-11-29 05:05:10 +03:00
|
|
|
JSObject *obj2;
|
|
|
|
JSProperty *prop;
|
2002-02-23 06:49:27 +03:00
|
|
|
JSBool ok;
|
1999-06-26 07:28:47 +04:00
|
|
|
|
2000-06-01 02:10:53 +04:00
|
|
|
if (!JS_ValueToId(cx, argv[0], &id))
|
1998-11-05 03:08:43 +03:00
|
|
|
return JS_FALSE;
|
2002-02-23 06:49:27 +03:00
|
|
|
|
2000-11-29 05:05:10 +03:00
|
|
|
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
|
|
|
|
return JS_FALSE;
|
2002-02-23 06:49:27 +03:00
|
|
|
|
2002-09-17 03:00:03 +04:00
|
|
|
if (!prop) {
|
|
|
|
*rval = JSVAL_FALSE;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2002-09-12 23:34:59 +04:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2002-09-17 03:00:03 +04:00
|
|
|
if (obj2 != obj &&
|
2002-09-12 23:34:59 +04:00
|
|
|
!(OBJ_IS_NATIVE(obj2) &&
|
|
|
|
SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) {
|
2002-02-23 06:49:27 +03:00
|
|
|
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
2000-11-29 05:05:10 +03:00
|
|
|
*rval = JSVAL_FALSE;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
2002-02-23 06:49:27 +03:00
|
|
|
|
2002-09-12 23:34:59 +04:00
|
|
|
ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
|
2002-09-17 03:00:03 +04:00
|
|
|
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
2002-02-23 06:49:27 +03:00
|
|
|
if (ok)
|
|
|
|
*rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);
|
|
|
|
return ok;
|
1998-11-05 03:08:43 +03:00
|
|
|
}
|
|
|
|
|
2000-03-03 02:21:03 +03:00
|
|
|
#if JS_HAS_GETTER_SETTER
|
|
|
|
static JSBool
|
|
|
|
obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
2000-06-02 04:02:46 +04:00
|
|
|
jsval *rval)
|
2000-03-03 02:21:03 +03:00
|
|
|
{
|
2000-07-11 00:21:38 +04:00
|
|
|
jsval fval, junk;
|
2000-06-01 02:10:53 +04:00
|
|
|
jsid id;
|
2000-07-11 00:21:38 +04:00
|
|
|
uintN attrs;
|
2000-03-03 02:21:03 +03:00
|
|
|
|
2000-04-04 12:17:05 +04:00
|
|
|
fval = argv[1];
|
2000-03-03 02:21:03 +03:00
|
|
|
if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_BAD_GETTER_OR_SETTER,
|
2000-03-03 02:21:03 +03:00
|
|
|
js_getter_str);
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
2000-04-05 06:17:38 +04:00
|
|
|
}
|
2000-03-03 02:21:03 +03:00
|
|
|
|
2000-06-01 02:10:53 +04:00
|
|
|
if (!JS_ValueToId(cx, argv[0], &id))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
2004-04-13 05:25:17 +04:00
|
|
|
if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
|
2000-06-02 04:02:46 +04:00
|
|
|
return JS_FALSE;
|
2000-07-11 00:21:38 +04:00
|
|
|
/*
|
|
|
|
* 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;
|
2000-06-01 02:10:53 +04:00
|
|
|
return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
|
2000-04-04 12:17:05 +04:00
|
|
|
(JSPropertyOp) JSVAL_TO_OBJECT(fval), NULL,
|
2004-05-12 04:44:23 +04:00
|
|
|
JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED,
|
|
|
|
NULL);
|
2000-03-03 02:21:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
2000-06-02 04:02:46 +04:00
|
|
|
jsval *rval)
|
2000-03-03 02:21:03 +03:00
|
|
|
{
|
2000-07-11 00:21:38 +04:00
|
|
|
jsval fval, junk;
|
2000-06-01 02:10:53 +04:00
|
|
|
jsid id;
|
2000-07-11 00:21:38 +04:00
|
|
|
uintN attrs;
|
2000-03-03 02:21:03 +03:00
|
|
|
|
2000-04-04 12:17:05 +04:00
|
|
|
fval = argv[1];
|
|
|
|
if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_BAD_GETTER_OR_SETTER,
|
2000-03-03 02:21:03 +03:00
|
|
|
js_setter_str);
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
2000-03-03 02:21:03 +03:00
|
|
|
}
|
|
|
|
|
2000-06-01 02:10:53 +04:00
|
|
|
if (!JS_ValueToId(cx, argv[0], &id))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
2004-04-13 05:25:17 +04:00
|
|
|
if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
|
2000-06-02 04:02:46 +04:00
|
|
|
return JS_FALSE;
|
2000-07-11 00:21:38 +04:00
|
|
|
/*
|
|
|
|
* 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;
|
2000-06-01 02:10:53 +04:00
|
|
|
return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
|
2000-04-04 12:17:05 +04:00
|
|
|
NULL, (JSPropertyOp) JSVAL_TO_OBJECT(fval),
|
2004-05-12 04:44:23 +04:00
|
|
|
JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED,
|
|
|
|
NULL);
|
2000-03-03 02:21:03 +03:00
|
|
|
}
|
2001-04-09 03:23:34 +04:00
|
|
|
|
|
|
|
static JSBool
|
|
|
|
obj_lookupGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|
|
|
jsval *rval)
|
|
|
|
{
|
|
|
|
jsid id;
|
|
|
|
JSObject *pobj;
|
2004-04-13 05:25:17 +04:00
|
|
|
JSProperty *prop;
|
2001-04-09 03:23:34 +04:00
|
|
|
JSScopeProperty *sprop;
|
|
|
|
|
|
|
|
if (!JS_ValueToId(cx, argv[0], &id))
|
|
|
|
return JS_FALSE;
|
2004-04-13 05:25:17 +04:00
|
|
|
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
|
2001-04-09 03:23:34 +04:00
|
|
|
return JS_FALSE;
|
2004-04-13 05:25:17 +04:00
|
|
|
if (prop) {
|
|
|
|
if (OBJ_IS_NATIVE(pobj)) {
|
|
|
|
sprop = (JSScopeProperty *) prop;
|
|
|
|
if (sprop->attrs & JSPROP_GETTER)
|
|
|
|
*rval = OBJECT_TO_JSVAL(sprop->getter);
|
|
|
|
}
|
|
|
|
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
2001-04-09 03:23:34 +04:00
|
|
|
}
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|
|
|
jsval *rval)
|
|
|
|
{
|
|
|
|
jsid id;
|
|
|
|
JSObject *pobj;
|
2004-04-13 05:25:17 +04:00
|
|
|
JSProperty *prop;
|
2001-04-09 03:23:34 +04:00
|
|
|
JSScopeProperty *sprop;
|
|
|
|
|
|
|
|
if (!JS_ValueToId(cx, argv[0], &id))
|
|
|
|
return JS_FALSE;
|
2004-04-13 05:25:17 +04:00
|
|
|
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
|
2001-04-09 03:23:34 +04:00
|
|
|
return JS_FALSE;
|
2004-04-13 05:25:17 +04:00
|
|
|
if (prop) {
|
|
|
|
if (OBJ_IS_NATIVE(pobj)) {
|
|
|
|
sprop = (JSScopeProperty *) prop;
|
|
|
|
if (sprop->attrs & JSPROP_SETTER)
|
|
|
|
*rval = OBJECT_TO_JSVAL(sprop->setter);
|
|
|
|
}
|
|
|
|
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
2001-04-09 03:23:34 +04:00
|
|
|
}
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
2000-03-03 02:21:03 +03:00
|
|
|
#endif /* JS_HAS_GETTER_SETTER */
|
|
|
|
|
2000-08-19 12:37:07 +04:00
|
|
|
#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__";
|
2001-04-09 03:23:34 +04:00
|
|
|
const char js_lookupGetter_str[] = "__lookupGetter__";
|
|
|
|
const char js_lookupSetter_str[] = "__lookupSetter__";
|
2000-08-19 12:37:07 +04:00
|
|
|
#endif
|
2000-03-03 02:21:03 +03:00
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
static JSFunctionSpec object_methods[] = {
|
1998-04-24 04:31:11 +04:00
|
|
|
#if JS_HAS_TOSOURCE
|
2000-08-19 12:37:07 +04:00
|
|
|
{js_toSource_str, js_obj_toSource, 0, 0, OBJ_TOSTRING_EXTRA},
|
1998-04-24 04:31:11 +04:00
|
|
|
#endif
|
2000-08-19 12:37:07 +04:00
|
|
|
{js_toString_str, js_obj_toString, 0, 0, OBJ_TOSTRING_EXTRA},
|
2005-09-30 01:54:37 +04:00
|
|
|
{js_toLocaleString_str, js_obj_toLocaleString, 0, 0, OBJ_TOSTRING_EXTRA},
|
2000-08-19 12:37:07 +04:00
|
|
|
{js_valueOf_str, obj_valueOf, 0,0,0},
|
|
|
|
{js_eval_str, obj_eval, 1,0,0},
|
1998-03-28 05:44:41 +03:00
|
|
|
#if JS_HAS_OBJ_WATCHPOINT
|
2000-08-19 12:37:07 +04:00
|
|
|
{js_watch_str, obj_watch, 2,0,0},
|
|
|
|
{js_unwatch_str, obj_unwatch, 1,0,0},
|
1998-11-05 03:08:43 +03:00
|
|
|
#endif
|
2004-11-17 10:43:01 +03:00
|
|
|
{js_hasOwnProperty_str, obj_hasOwnProperty, 1,0,0},
|
2000-08-19 12:37:07 +04:00
|
|
|
{js_isPrototypeOf_str, obj_isPrototypeOf, 1,0,0},
|
|
|
|
{js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0},
|
2000-03-03 02:21:03 +03:00
|
|
|
#if JS_HAS_GETTER_SETTER
|
2000-08-19 12:37:07 +04:00
|
|
|
{js_defineGetter_str, obj_defineGetter, 2,0,0},
|
|
|
|
{js_defineSetter_str, obj_defineSetter, 2,0,0},
|
2001-04-09 03:23:34 +04:00
|
|
|
{js_lookupGetter_str, obj_lookupGetter, 1,0,0},
|
|
|
|
{js_lookupSetter_str, obj_lookupSetter, 1,0,0},
|
1998-03-28 05:44:41 +03:00
|
|
|
#endif
|
1999-11-12 00:52:35 +03:00
|
|
|
{0,0,0,0,0}
|
1998-03-28 05:44:41 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
1998-04-24 04:31:11 +04:00
|
|
|
if (argc == 0) {
|
2001-02-07 02:19:44 +03:00
|
|
|
/* Trigger logic below to construct a blank object. */
|
|
|
|
obj = NULL;
|
1998-04-24 04:31:11 +04:00
|
|
|
} else {
|
2001-02-07 02:19:44 +03:00
|
|
|
/* If argv[0] is null or undefined, obj comes back null. */
|
|
|
|
if (!js_ValueToObject(cx, argv[0], &obj))
|
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
if (!obj) {
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));
|
Fix for bug 99663 (for loop resolves properties of the object being enumerated
with JSRESOLVE_ASSIGNING, wrongly), plus a few miscellaneous bugfixes.
- Combine the JSStackFrame members constructing, special, overrides, and
reserved into a uint32 flags member.
- Separate JOF_ASSIGNING from the JOF_SET bytecode format flag, and impute
JSRESOLVE_ASSIGNING from the presence of JOF_ASSIGNING among the current
opcode's format flags. To handle the for-in loop opcodes, which do more
than simply assign -- in particular, they do property lookups whose resolve
hook outcalls should not be flagged with JSRESOLVE_ASSIGNING -- a new frame
flag, JSFRAME_ASSIGNING, has been added.
- Fix interpreter version selection to respect JS_SetVersion, whose effect on
cx->version is "sticky".
- Fix js_DecompileValueGenerator to deal with JSOP_ENUMELEM -- it never had,
as this testcase shows (it crashes without this patch):
version(120);
eval("function fe(s) { for (it[s] in this); }");
try { fe('rdonly'); } catch (e) { print(e); }
2001-10-03 10:39:30 +04:00
|
|
|
if (cx->fp->flags & JSFRAME_CONSTRUCTING)
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_TRUE;
|
|
|
|
obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL);
|
|
|
|
if (!obj)
|
|
|
|
return JS_FALSE;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
*rval = OBJECT_TO_JSVAL(obj);
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1998-04-24 04:31:11 +04:00
|
|
|
* ObjectOps and Class for with-statement stack objects.
|
1998-03-28 05:44:41 +03:00
|
|
|
*/
|
|
|
|
static JSBool
|
1998-04-24 04:31:11 +04:00
|
|
|
with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
|
2004-10-05 14:19:07 +04:00
|
|
|
JSProperty **propp)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
2005-05-15 22:24:31 +04:00
|
|
|
JSObject *proto = OBJ_GET_PROTO(cx, obj);
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!proto)
|
2001-02-07 02:19:44 +03:00
|
|
|
return js_LookupProperty(cx, obj, id, objp, propp);
|
2005-05-15 22:24:31 +04:00
|
|
|
return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
|
|
|
{
|
|
|
|
JSObject *proto = OBJ_GET_PROTO(cx, obj);
|
|
|
|
if (!proto)
|
2001-02-07 02:19:44 +03:00
|
|
|
return js_GetProperty(cx, obj, id, vp);
|
1998-04-24 04:31:11 +04:00
|
|
|
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)
|
2001-02-07 02:19:44 +03:00
|
|
|
return js_SetProperty(cx, obj, id, vp);
|
1998-04-24 04:31:11 +04:00
|
|
|
return OBJ_SET_PROPERTY(cx, proto, id, vp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
|
2001-02-07 02:19:44 +03:00
|
|
|
uintN *attrsp)
|
1998-04-24 04:31:11 +04:00
|
|
|
{
|
|
|
|
JSObject *proto = OBJ_GET_PROTO(cx, obj);
|
|
|
|
if (!proto)
|
2001-02-07 02:19:44 +03:00
|
|
|
return js_GetAttributes(cx, obj, id, prop, attrsp);
|
1998-04-24 04:31:11 +04:00
|
|
|
return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
|
2001-02-07 02:19:44 +03:00
|
|
|
uintN *attrsp)
|
1998-04-24 04:31:11 +04:00
|
|
|
{
|
|
|
|
JSObject *proto = OBJ_GET_PROTO(cx, obj);
|
|
|
|
if (!proto)
|
2001-02-07 02:19:44 +03:00
|
|
|
return js_SetAttributes(cx, obj, id, prop, attrsp);
|
1998-04-24 04:31:11 +04:00
|
|
|
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)
|
2001-02-07 02:19:44 +03:00
|
|
|
return js_DeleteProperty(cx, obj, id, rval);
|
1998-04-24 04:31:11 +04:00
|
|
|
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)
|
2001-02-07 02:19:44 +03:00
|
|
|
return js_DefaultValue(cx, obj, hint, vp);
|
1998-04-24 04:31:11 +04:00
|
|
|
return OBJ_DEFAULT_VALUE(cx, proto, hint, vp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
|
2001-02-07 02:19:44 +03:00
|
|
|
jsval *statep, jsid *idp)
|
1998-04-24 04:31:11 +04:00
|
|
|
{
|
|
|
|
JSObject *proto = OBJ_GET_PROTO(cx, obj);
|
|
|
|
if (!proto)
|
2001-02-07 02:19:44 +03:00
|
|
|
return js_Enumerate(cx, obj, enum_op, statep, idp);
|
1998-04-24 04:31:11 +04:00
|
|
|
return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
|
2001-02-07 02:19:44 +03:00
|
|
|
jsval *vp, uintN *attrsp)
|
1998-04-24 04:31:11 +04:00
|
|
|
{
|
|
|
|
JSObject *proto = OBJ_GET_PROTO(cx, obj);
|
|
|
|
if (!proto)
|
2001-02-07 02:19:44 +03:00
|
|
|
return js_CheckAccess(cx, obj, id, mode, vp, attrsp);
|
1998-04-24 04:31:11 +04:00
|
|
|
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)
|
2001-02-07 02:19:44 +03:00
|
|
|
return obj;
|
1998-04-24 04:31:11 +04:00
|
|
|
return OBJ_THIS_OBJECT(cx, proto);
|
|
|
|
}
|
|
|
|
|
1998-10-14 14:22:38 +04:00
|
|
|
JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = {
|
1998-04-24 04:31:11 +04:00
|
|
|
js_NewObjectMap, js_DestroyObjectMap,
|
|
|
|
with_LookupProperty, js_DefineProperty,
|
|
|
|
with_GetProperty, with_SetProperty,
|
|
|
|
with_GetAttributes, with_SetAttributes,
|
|
|
|
with_DeleteProperty, with_DefaultValue,
|
|
|
|
with_Enumerate, with_CheckAccess,
|
1999-11-12 00:52:35 +03:00
|
|
|
with_ThisObject, NATIVE_DROP_PROPERTY,
|
2000-06-27 06:37:25 +04:00
|
|
|
NULL, NULL,
|
|
|
|
NULL, NULL,
|
|
|
|
js_SetProtoOrParent, js_SetProtoOrParent,
|
2000-08-26 06:30:22 +04:00
|
|
|
js_Mark, js_Clear,
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
NULL, NULL
|
1998-04-24 04:31:11 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static JSObjectOps *
|
|
|
|
with_getObjectOps(JSContext *cx, JSClass *clasp)
|
|
|
|
{
|
1998-10-14 14:22:38 +04:00
|
|
|
return &js_WithObjectOps;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
JSClass js_WithClass = {
|
|
|
|
"With",
|
2006-05-21 02:27:28 +04:00
|
|
|
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
|
1998-04-24 04:31:11 +04:00
|
|
|
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
|
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
|
1999-11-12 00:52:35 +03:00
|
|
|
with_getObjectOps,
|
2000-08-26 06:30:22 +04:00
|
|
|
0,0,0,0,0,0,0
|
1998-03-28 05:44:41 +03:00
|
|
|
};
|
|
|
|
|
2006-05-21 02:27:28 +04:00
|
|
|
JSObject *
|
|
|
|
js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
|
|
|
|
{
|
|
|
|
JSObject *obj;
|
|
|
|
|
|
|
|
obj = js_NewObject(cx, &js_WithClass, proto, parent);
|
|
|
|
if (!obj)
|
|
|
|
return NULL;
|
2006-12-09 18:02:37 +03:00
|
|
|
STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(cx->fp));
|
2006-05-21 02:27:28 +04:00
|
|
|
OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSObject *
|
|
|
|
js_NewBlockObject(JSContext *cx)
|
|
|
|
{
|
|
|
|
JSObject *obj;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
if (!obj || !js_GetMutableScope(cx, obj))
|
|
|
|
return NULL;
|
|
|
|
OBJ_SET_PROTO(cx, obj, NULL);
|
|
|
|
return obj;
|
|
|
|
}
|
2006-06-04 06:19:09 +04:00
|
|
|
|
2006-05-21 02:27:28 +04:00
|
|
|
JSObject *
|
|
|
|
js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent,
|
|
|
|
JSStackFrame *fp)
|
|
|
|
{
|
|
|
|
JSObject *clone;
|
2006-06-04 06:19:09 +04:00
|
|
|
|
2006-05-21 02:27:28 +04:00
|
|
|
clone = js_NewObject(cx, &js_BlockClass, proto, parent);
|
|
|
|
if (!clone)
|
|
|
|
return NULL;
|
2006-12-09 18:02:37 +03:00
|
|
|
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));
|
2006-05-21 02:27:28 +04:00
|
|
|
return clone;
|
|
|
|
}
|
|
|
|
|
2006-08-01 05:52:37 +04:00
|
|
|
/*
|
|
|
|
* XXXblock this reverses a path in the property tree -- try to share
|
|
|
|
* the prototype's scope harder!
|
|
|
|
*/
|
2006-07-07 00:47:02 +04:00
|
|
|
JSBool
|
|
|
|
js_PutBlockObject(JSContext *cx, JSObject *obj)
|
|
|
|
{
|
|
|
|
JSScopeProperty *sprop;
|
|
|
|
jsval v;
|
|
|
|
|
|
|
|
for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) {
|
2006-07-12 03:29:41 +04:00
|
|
|
if (sprop->getter != js_BlockClass.getProperty)
|
|
|
|
continue;
|
|
|
|
if (!(sprop->flags & SPROP_HAS_SHORTID))
|
|
|
|
continue;
|
|
|
|
if (!sprop->getter(cx, obj, INT_TO_JSVAL(sprop->shortid), &v) ||
|
|
|
|
!js_DefineNativeProperty(cx, obj, sprop->id,
|
|
|
|
v, NULL, NULL,
|
|
|
|
JSPROP_ENUMERATE | JSPROP_PERMANENT,
|
|
|
|
SPROP_HAS_SHORTID, sprop->shortid,
|
|
|
|
NULL)) {
|
2006-07-07 00:47:02 +04:00
|
|
|
JS_SetPrivate(cx, obj, NULL);
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return JS_SetPrivate(cx, obj, NULL);
|
|
|
|
}
|
|
|
|
|
2006-05-21 02:27:28 +04:00
|
|
|
static JSBool
|
|
|
|
block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|
|
|
{
|
|
|
|
JSStackFrame *fp;
|
|
|
|
jsint slot;
|
|
|
|
|
|
|
|
JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL));
|
|
|
|
if (!JSVAL_IS_INT(id))
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
|
|
|
|
if (!fp)
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
slot = OBJ_BLOCK_DEPTH(cx, obj) + (uint16) JSVAL_TO_INT(id);
|
|
|
|
JS_ASSERT((uintN)slot < fp->script->depth);
|
|
|
|
*vp = fp->spbase[slot];
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
block_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|
|
|
{
|
|
|
|
JSStackFrame *fp;
|
|
|
|
jsint slot;
|
|
|
|
|
|
|
|
JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL));
|
|
|
|
if (!JSVAL_IS_INT(id))
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
|
|
|
|
if (!fp)
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
slot = OBJ_BLOCK_DEPTH(cx, obj) + (uint16) JSVAL_TO_INT(id);
|
|
|
|
JS_ASSERT((uintN)slot < fp->script->depth);
|
|
|
|
fp->spbase[slot] = *vp;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2006-08-01 02:52:27 +04:00
|
|
|
#if JS_HAS_XDR
|
|
|
|
|
|
|
|
#define NO_PARENT_INDEX (jsatomid)-1
|
|
|
|
|
|
|
|
jsatomid
|
|
|
|
FindObjectAtomIndex(JSAtomMap *map, JSObject *obj)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
JSAtom *atom;
|
|
|
|
|
|
|
|
for (i = 0; i < map->length; i++) {
|
|
|
|
atom = map->vector[i];
|
|
|
|
if (ATOM_KEY(atom) == OBJECT_TO_JSVAL(obj))
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NO_PARENT_INDEX;
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
block_xdrObject(JSXDRState *xdr, JSObject **objp)
|
|
|
|
{
|
|
|
|
JSContext *cx;
|
|
|
|
jsatomid parentId;
|
|
|
|
JSAtomMap *atomMap;
|
2006-08-15 10:37:52 +04:00
|
|
|
JSObject *obj, *parent;
|
2006-08-01 02:52:27 +04:00
|
|
|
uint16 depth, count, i;
|
|
|
|
uint32 tmp;
|
|
|
|
JSTempValueRooter tvr;
|
|
|
|
JSScopeProperty *sprop;
|
|
|
|
jsid propid;
|
|
|
|
JSAtom *atom;
|
|
|
|
int16 shortid;
|
|
|
|
JSBool ok;
|
|
|
|
|
|
|
|
cx = xdr->cx;
|
2006-08-17 12:13:18 +04:00
|
|
|
#ifdef __GNUC__
|
|
|
|
obj = NULL; /* quell GCC overwarning */
|
|
|
|
#endif
|
2006-08-01 02:52:27 +04:00
|
|
|
|
|
|
|
atomMap = &xdr->script->atomMap;
|
|
|
|
if (xdr->mode == JSXDR_ENCODE) {
|
2006-08-15 10:37:52 +04:00
|
|
|
obj = *objp;
|
|
|
|
parent = OBJ_GET_PARENT(cx, obj);
|
2006-08-01 02:52:27 +04:00
|
|
|
parentId = FindObjectAtomIndex(atomMap, parent);
|
2006-08-15 10:37:52 +04:00
|
|
|
depth = OBJ_BLOCK_DEPTH(cx, obj);
|
|
|
|
count = OBJ_BLOCK_COUNT(cx, obj);
|
2006-08-01 02:52:27 +04:00
|
|
|
tmp = (uint32)(depth << 16) | count;
|
|
|
|
}
|
2006-08-01 05:52:37 +04:00
|
|
|
#ifdef __GNUC__ /* suppress bogus gcc warnings */
|
|
|
|
else count = 0;
|
|
|
|
#endif
|
2006-08-01 02:52:27 +04:00
|
|
|
|
|
|
|
/* First, XDR the parent atomid. */
|
|
|
|
if (!JS_XDRUint32(xdr, &parentId))
|
|
|
|
return JS_FALSE;
|
|
|
|
|
|
|
|
if (xdr->mode == JSXDR_DECODE) {
|
2006-08-15 10:37:52 +04:00
|
|
|
obj = js_NewBlockObject(cx);
|
|
|
|
if (!obj)
|
2006-08-01 02:52:27 +04:00
|
|
|
return JS_FALSE;
|
2006-08-15 10:37:52 +04:00
|
|
|
*objp = obj;
|
2006-08-01 02:52:27 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If there's a parent id, then get the parent out of our script's
|
|
|
|
* atomMap. We know that we XDR block object in outer-to-inner order,
|
|
|
|
* which means that getting the parent now will work.
|
|
|
|
*/
|
2006-08-15 10:37:52 +04:00
|
|
|
if (parentId == NO_PARENT_INDEX) {
|
|
|
|
parent = NULL;
|
|
|
|
} else {
|
2006-08-01 02:52:27 +04:00
|
|
|
atom = js_GetAtom(cx, atomMap, parentId);
|
|
|
|
JS_ASSERT(ATOM_IS_OBJECT(atom));
|
|
|
|
parent = ATOM_TO_OBJECT(atom);
|
|
|
|
}
|
2006-12-09 18:02:37 +03:00
|
|
|
STOBJ_SET_PARENT(obj, parent);
|
2006-08-01 02:52:27 +04:00
|
|
|
}
|
|
|
|
|
2006-08-15 10:37:52 +04:00
|
|
|
JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(obj), &tvr);
|
2006-08-01 02:52:27 +04:00
|
|
|
|
|
|
|
if (!JS_XDRUint32(xdr, &tmp)) {
|
|
|
|
JS_POP_TEMP_ROOT(cx, &tvr);
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (xdr->mode == JSXDR_DECODE) {
|
|
|
|
depth = (uint16)(tmp >> 16);
|
|
|
|
count = (uint16)tmp;
|
2006-12-09 18:02:37 +03:00
|
|
|
STOBJ_SET_SLOT(obj, JSSLOT_BLOCK_DEPTH, INT_TO_JSVAL(depth));
|
2006-08-01 02:52:27 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XDR the block object's properties. We know that there are 'count'
|
|
|
|
* properties to XDR, stored as id/shortid pairs. We do not XDR any
|
|
|
|
* non-native properties, only those that the compiler created.
|
|
|
|
*/
|
|
|
|
sprop = NULL;
|
|
|
|
ok = JS_TRUE;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
if (xdr->mode == JSXDR_ENCODE) {
|
|
|
|
/* Find a property to XDR. */
|
|
|
|
do {
|
|
|
|
/* If sprop is NULL, this is the first property. */
|
2006-08-15 10:37:52 +04:00
|
|
|
sprop = sprop ? sprop->parent : OBJ_SCOPE(obj)->lastProp;
|
2006-08-01 02:52:27 +04:00
|
|
|
} while (!(sprop->flags & SPROP_HAS_SHORTID));
|
|
|
|
|
|
|
|
JS_ASSERT(sprop->getter == js_BlockClass.getProperty);
|
|
|
|
propid = sprop->id;
|
|
|
|
JS_ASSERT(JSID_IS_ATOM(propid));
|
|
|
|
atom = JSID_TO_ATOM(propid);
|
|
|
|
shortid = sprop->shortid;
|
|
|
|
JS_ASSERT(shortid >= 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XDR the real id, then the shortid. */
|
|
|
|
if (!js_XDRStringAtom(xdr, &atom) ||
|
|
|
|
!JS_XDRUint16(xdr, (uint16 *)&shortid)) {
|
|
|
|
ok = JS_FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (xdr->mode == JSXDR_DECODE) {
|
2006-08-15 10:37:52 +04:00
|
|
|
if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom),
|
2006-08-01 02:52:27 +04:00
|
|
|
JSVAL_VOID, NULL, NULL,
|
|
|
|
JSPROP_ENUMERATE | JSPROP_PERMANENT,
|
|
|
|
SPROP_HAS_SHORTID, shortid, NULL)) {
|
|
|
|
ok = JS_FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
JS_POP_TEMP_ROOT(cx, &tvr);
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
# define block_xdrObject NULL
|
|
|
|
#endif
|
|
|
|
|
2006-05-21 02:27:28 +04:00
|
|
|
JSClass js_BlockClass = {
|
|
|
|
"Block",
|
2006-08-01 02:52:27 +04:00
|
|
|
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) |
|
|
|
|
JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_CACHED_PROTO(JSProto_Block),
|
2006-05-21 02:27:28 +04:00
|
|
|
JS_PropertyStub, JS_PropertyStub, block_getProperty, block_setProperty,
|
|
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
|
2006-08-01 02:52:27 +04:00
|
|
|
NULL, NULL, NULL, NULL, block_xdrObject, NULL, NULL, NULL
|
2006-05-21 02:27:28 +04:00
|
|
|
};
|
|
|
|
|
2006-08-01 02:52:27 +04:00
|
|
|
JSObject*
|
|
|
|
js_InitBlockClass(JSContext *cx, JSObject* obj)
|
|
|
|
{
|
|
|
|
JSObject *proto;
|
|
|
|
|
|
|
|
proto = JS_InitClass(cx, obj, NULL, &js_BlockClass, NULL, 0, NULL,
|
|
|
|
NULL, NULL, NULL);
|
|
|
|
if (!proto)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
OBJ_SET_PROTO(cx, proto, NULL);
|
|
|
|
return proto;
|
|
|
|
}
|
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
JSObject *
|
|
|
|
js_InitObjectClass(JSContext *cx, JSObject *obj)
|
|
|
|
{
|
|
|
|
JSObject *proto;
|
2000-08-19 12:37:07 +04:00
|
|
|
jsval eval;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1,
|
2001-02-07 02:19:44 +03:00
|
|
|
object_props, object_methods, NULL, NULL);
|
2002-02-06 10:01:37 +03:00
|
|
|
if (!proto)
|
|
|
|
return NULL;
|
|
|
|
|
2000-05-02 01:55:52 +04:00
|
|
|
/* ECMA (15.1.2.1) says 'eval' is also a property of the global object. */
|
2004-10-05 14:19:07 +04:00
|
|
|
if (!OBJ_GET_PROPERTY(cx, proto,
|
|
|
|
ATOM_TO_JSID(cx->runtime->atomState.evalAtom),
|
2000-08-19 12:37:07 +04:00
|
|
|
&eval)) {
|
2000-05-02 01:55:52 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
2004-10-05 14:19:07 +04:00
|
|
|
if (!OBJ_DEFINE_PROPERTY(cx, obj,
|
|
|
|
ATOM_TO_JSID(cx->runtime->atomState.evalAtom),
|
2000-08-19 12:37:07 +04:00
|
|
|
eval, NULL, NULL, 0, NULL)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
return NULL;
|
2000-05-02 01:55:52 +04:00
|
|
|
}
|
2000-06-27 06:37:25 +04:00
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
return proto;
|
|
|
|
}
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
void
|
|
|
|
js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops,
|
2001-02-07 02:19:44 +03:00
|
|
|
JSClass *clasp)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
1998-04-24 04:31:11 +04:00
|
|
|
map->nrefs = nrefs;
|
|
|
|
map->ops = ops;
|
|
|
|
map->freeslot = JSSLOT_FREE(clasp);
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
JSObjectMap *
|
|
|
|
js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,
|
2001-02-07 02:19:44 +03:00
|
|
|
JSClass *clasp, JSObject *obj)
|
1998-04-24 04:31:11 +04:00
|
|
|
{
|
|
|
|
return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj);
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
void
|
|
|
|
js_DestroyObjectMap(JSContext *cx, JSObjectMap *map)
|
|
|
|
{
|
|
|
|
js_DestroyScope(cx, (JSScope *)map);
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
JSObjectMap *
|
|
|
|
js_HoldObjectMap(JSContext *cx, JSObjectMap *map)
|
|
|
|
{
|
1998-10-14 14:22:38 +04:00
|
|
|
JS_ASSERT(map->nrefs >= 0);
|
All this r=mccabe, r=beard, and sr=jband -- many thanks to all who helped,
especially to jband for his great stress-test setup and particularly helpful
(in terms of reproducing bugs in draft patches) MP and laptop machines.
- Radical(*) object (scope) locking optimization: don't lock if a scope is
accessed on the context that exclusively owns it (initially, the context
on which the scope was created). Once a scope becomes shared among more
than one owner-context, give it the usual thin or fat lock, per existing
jslock.c code.
I did this at the memory cost of another word per JSScope, ownercx, which
raised scope size from 12 to 13 words if !DEBUG. I also added a linked
list head pointer, rt->scopeSharingTodo, and a scopeSharingDone condition
variable to JSRuntime, and a scopeToShare pointer to JSContext that's
necessary for deadlock avoidance.
The rt->scopeSharingTodo list links JSScopes through the scope->u.link
union arm, which overlays the pre-existing scope->count (now u.count)
member. This list holds scopes still exclusively owned by a context, but
wanted by js_LockScope calls active on other threads. Those calls wait
on the rt->scopeSharingDone condition, which is notified every time an
owner-context ends the request running on it, in which code active on
that context may be using scope freely until end of request.
The code that waits on rt->scopeSharingDone must first suspend any and
all requests active on the calling context, and resume those contexts
after the wait is notified. This means a GC could happen while the
thread locking a scope owned by another thread's context blocks; all
calls to JS_LOCK_OBJ must therefore first home fp->sp above any live
operands, e.g. The interpreter takes care to do that already.
To avoid AB-BA deadlocks, if a js_LockScope attempt on one context finds
that the owner-context of the scope is already waiting on a scope owned
by the current context (or indirectly depending on such a scope lock),
the attempt converts the scope from lock-free exclusive ownership to
shared ownership (thin or fat lock).
- Fix js_SetupLocks and the js_LockGlobal/js_UnlockGlobal code to avoid
divmod instruction costs, strength-reducing to bit-mask instructions.
- The radical lock-free scope change required care in handling the 0=>1
and 1=>0 transitions of cx->requestDepth, which was till now thread-local
because part of the JSContext not manipulated by other threads. It's
still updated only by cx's thread, but it is read by other threads in
the course of attempting to claim exclusive ownership of a scope for more
lock-free JS object operations.
- The JS_SuspendRequest and JS_ResumeRequest APIs have changed incompatibly
to require their caller to save and restore the requestCount found when
JS_SuspendRequest is called. This is necessary to avoid deadlock; sorry
for the incompatible change.
- Fixed various nits in jslock.[ch], including using Init/Finish rather
than New/Destroy for the methods that take a JSThinLock and initialize
and finish/free its members. Another example: JS_ATOMIC_ADDREF is now
JS_ATOMIC_INCREMENT and JS_ATOMIC_DECREMENT, so the two cases can be
mapped to PR_AtomicIncrement and PR_AtomicDecrement. This entailed
changing jsrefcount from jsword to int32 (PRInt32).
- No need to use JS_ATOMIC_INCREMENT on JSScopeProperty.nrefs, as it is
always and everywhere protected by the property's JSScope.lock.
- Cleaned up gratuitous casts in jscntxt.c by using &cx->links, etc.
- The lock used for mutual exclusion around both request begin and end vs.
GC synchronization is rt->gcLock, and this lock now also protects all
scope->ownercx pointer changes from non-null (exclusive) to null (shared),
the rt->scopeSharingTodo/scope->u.link list operations, and of course the
rt->scopeSharingDone condition.
But this means that js_GC cannot hold rt->gcLock across the bulk of its
body, in particular the mark phase, during which JS_GetPrivate calls,
e.g., may need to "promote" scope locks from lock-free to thin or fat,
because doing so would double-trip. There never was any good reason to
hold rt->gcLock so long, of course -- locks are for mutual exclusion, not
for waiting or notifying a thread -- those operations require a condition,
rt->gcDone, which we already use along with rt->gcLevel to keep racing GC
attempts at bay.
So now that rt->gcLock does not protect the mark phase, the enumeration
of rt->gcRootsHash can race badly with JS_RemoveRootRT, an API that may
legitimately be called outside of a request, without even a context. It
turns out that people may be cheating on the request model even with
JS_AddRoot, JS_AddNamedRoot, and JS_RemoveRoot calls, so we must make
all of those interlock with the GC using gcLevel and gcDone, unless they
are called on the gcThread.
Also, since bug 49816 was fixed, there has been no need for a separate
finalize phase, or for rt->gcFinalVec. Finalizers can no longer allocate
newborn GC-things that might be swept (because unmarked), or double-trip
on rt->gcLock (which is no longer held). So js_GC finalizes as it sweeps,
just as it did in days of old.
- I added comments to jslock.h making it plain that callers of JS_LOCK_OBJ
and JS_UNLOCK_OBJ must either be implementations of js_ObjectOps hooks,
or code reachable only from those hooks; or else must be predicated on
OBJ_IS_NATIVE tests. It turns out jsinterp.c's CACHED_GET and CACHED_SET
macros neglected to do such tests, limiting the ability of JS embeddings
to implement JSObjectOps with their own non-JSScope JSObjectMap subclass.
Fixed, small performance hit that the lock-free optimization should more
than make up for.
- jslock.c now gives a #error if you try to compile it on a platform that
lacks a compare-and-swap instruction. The #error says to use NSPR locks.
Before this change, some platforms would emulate compare-and-swap using
a global PRLock, which is always worse in runtime than using per-scope
PRLocks.
2000-12-04 05:43:31 +03:00
|
|
|
JS_ATOMIC_INCREMENT(&map->nrefs);
|
1998-04-24 04:31:11 +04:00
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSObjectMap *
|
|
|
|
js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj)
|
|
|
|
{
|
1998-10-14 14:22:38 +04:00
|
|
|
JS_ASSERT(map->nrefs > 0);
|
All this r=mccabe, r=beard, and sr=jband -- many thanks to all who helped,
especially to jband for his great stress-test setup and particularly helpful
(in terms of reproducing bugs in draft patches) MP and laptop machines.
- Radical(*) object (scope) locking optimization: don't lock if a scope is
accessed on the context that exclusively owns it (initially, the context
on which the scope was created). Once a scope becomes shared among more
than one owner-context, give it the usual thin or fat lock, per existing
jslock.c code.
I did this at the memory cost of another word per JSScope, ownercx, which
raised scope size from 12 to 13 words if !DEBUG. I also added a linked
list head pointer, rt->scopeSharingTodo, and a scopeSharingDone condition
variable to JSRuntime, and a scopeToShare pointer to JSContext that's
necessary for deadlock avoidance.
The rt->scopeSharingTodo list links JSScopes through the scope->u.link
union arm, which overlays the pre-existing scope->count (now u.count)
member. This list holds scopes still exclusively owned by a context, but
wanted by js_LockScope calls active on other threads. Those calls wait
on the rt->scopeSharingDone condition, which is notified every time an
owner-context ends the request running on it, in which code active on
that context may be using scope freely until end of request.
The code that waits on rt->scopeSharingDone must first suspend any and
all requests active on the calling context, and resume those contexts
after the wait is notified. This means a GC could happen while the
thread locking a scope owned by another thread's context blocks; all
calls to JS_LOCK_OBJ must therefore first home fp->sp above any live
operands, e.g. The interpreter takes care to do that already.
To avoid AB-BA deadlocks, if a js_LockScope attempt on one context finds
that the owner-context of the scope is already waiting on a scope owned
by the current context (or indirectly depending on such a scope lock),
the attempt converts the scope from lock-free exclusive ownership to
shared ownership (thin or fat lock).
- Fix js_SetupLocks and the js_LockGlobal/js_UnlockGlobal code to avoid
divmod instruction costs, strength-reducing to bit-mask instructions.
- The radical lock-free scope change required care in handling the 0=>1
and 1=>0 transitions of cx->requestDepth, which was till now thread-local
because part of the JSContext not manipulated by other threads. It's
still updated only by cx's thread, but it is read by other threads in
the course of attempting to claim exclusive ownership of a scope for more
lock-free JS object operations.
- The JS_SuspendRequest and JS_ResumeRequest APIs have changed incompatibly
to require their caller to save and restore the requestCount found when
JS_SuspendRequest is called. This is necessary to avoid deadlock; sorry
for the incompatible change.
- Fixed various nits in jslock.[ch], including using Init/Finish rather
than New/Destroy for the methods that take a JSThinLock and initialize
and finish/free its members. Another example: JS_ATOMIC_ADDREF is now
JS_ATOMIC_INCREMENT and JS_ATOMIC_DECREMENT, so the two cases can be
mapped to PR_AtomicIncrement and PR_AtomicDecrement. This entailed
changing jsrefcount from jsword to int32 (PRInt32).
- No need to use JS_ATOMIC_INCREMENT on JSScopeProperty.nrefs, as it is
always and everywhere protected by the property's JSScope.lock.
- Cleaned up gratuitous casts in jscntxt.c by using &cx->links, etc.
- The lock used for mutual exclusion around both request begin and end vs.
GC synchronization is rt->gcLock, and this lock now also protects all
scope->ownercx pointer changes from non-null (exclusive) to null (shared),
the rt->scopeSharingTodo/scope->u.link list operations, and of course the
rt->scopeSharingDone condition.
But this means that js_GC cannot hold rt->gcLock across the bulk of its
body, in particular the mark phase, during which JS_GetPrivate calls,
e.g., may need to "promote" scope locks from lock-free to thin or fat,
because doing so would double-trip. There never was any good reason to
hold rt->gcLock so long, of course -- locks are for mutual exclusion, not
for waiting or notifying a thread -- those operations require a condition,
rt->gcDone, which we already use along with rt->gcLevel to keep racing GC
attempts at bay.
So now that rt->gcLock does not protect the mark phase, the enumeration
of rt->gcRootsHash can race badly with JS_RemoveRootRT, an API that may
legitimately be called outside of a request, without even a context. It
turns out that people may be cheating on the request model even with
JS_AddRoot, JS_AddNamedRoot, and JS_RemoveRoot calls, so we must make
all of those interlock with the GC using gcLevel and gcDone, unless they
are called on the gcThread.
Also, since bug 49816 was fixed, there has been no need for a separate
finalize phase, or for rt->gcFinalVec. Finalizers can no longer allocate
newborn GC-things that might be swept (because unmarked), or double-trip
on rt->gcLock (which is no longer held). So js_GC finalizes as it sweeps,
just as it did in days of old.
- I added comments to jslock.h making it plain that callers of JS_LOCK_OBJ
and JS_UNLOCK_OBJ must either be implementations of js_ObjectOps hooks,
or code reachable only from those hooks; or else must be predicated on
OBJ_IS_NATIVE tests. It turns out jsinterp.c's CACHED_GET and CACHED_SET
macros neglected to do such tests, limiting the ability of JS embeddings
to implement JSObjectOps with their own non-JSScope JSObjectMap subclass.
Fixed, small performance hit that the lock-free optimization should more
than make up for.
- jslock.c now gives a #error if you try to compile it on a platform that
lacks a compare-and-swap instruction. The #error says to use NSPR locks.
Before this change, some platforms would emulate compare-and-swap using
a global PRLock, which is always worse in runtime than using per-scope
PRLocks.
2000-12-04 05:43:31 +03:00
|
|
|
JS_ATOMIC_DECREMENT(&map->nrefs);
|
1998-04-24 04:31:11 +04:00
|
|
|
if (map->nrefs == 0) {
|
2001-02-07 02:19:44 +03:00
|
|
|
map->ops->destroyObjectMap(cx, map);
|
|
|
|
return NULL;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj)
|
2001-02-07 02:19:44 +03:00
|
|
|
((JSScope *)map)->object = NULL;
|
1998-04-24 04:31:11 +04:00
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
2006-12-12 13:45:06 +03:00
|
|
|
static void
|
|
|
|
FreeSlots(JSContext *cx, JSObject *obj)
|
2005-01-05 09:15:03 +03:00
|
|
|
{
|
2006-12-12 13:45:06 +03:00
|
|
|
if (obj->dslots) {
|
|
|
|
JS_ASSERT((uint32)obj->dslots[-1] > JS_INITIAL_NSLOTS);
|
|
|
|
JS_free(cx, obj->dslots - 1);
|
|
|
|
obj->dslots = NULL;
|
2005-01-05 09:15:03 +03:00
|
|
|
}
|
2006-12-12 13:45:06 +03:00
|
|
|
}
|
2005-01-05 09:15:03 +03:00
|
|
|
|
2007-01-07 01:03:06 +03:00
|
|
|
#define SLOTS_TO_DYNAMIC_WORDS(nslots) \
|
|
|
|
(JS_ASSERT((nslots) > JS_INITIAL_NSLOTS), (nslots) + 1 - JS_INITIAL_NSLOTS)
|
|
|
|
|
|
|
|
#define DYNAMIC_WORDS_TO_SLOTS(words) \
|
|
|
|
(JS_ASSERT((words) > 1), (words) - 1 + JS_INITIAL_NSLOTS)
|
|
|
|
|
2006-12-12 13:45:06 +03:00
|
|
|
static JSBool
|
2007-01-07 01:03:06 +03:00
|
|
|
ReallocSlots(JSContext *cx, JSObject *obj, uint32 nslots,
|
|
|
|
JSBool exactAllocation)
|
2006-12-12 13:45:06 +03:00
|
|
|
{
|
2007-01-07 01:03:06 +03:00
|
|
|
jsval *old, *slots;
|
|
|
|
uint32 oslots, nwords, owords, log, i;
|
2006-12-12 13:45:06 +03:00
|
|
|
|
2007-01-07 01:03:06 +03:00
|
|
|
/*
|
|
|
|
* Minimal number of dynamic slots to allocate.
|
|
|
|
*/
|
|
|
|
#define MIN_DYNAMIC_WORDS 4
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The limit to switch to linear allocation strategy from the power of 2
|
|
|
|
* growth no to waste too much memory.
|
|
|
|
*/
|
|
|
|
#define LINEAR_GROWTH_STEP JS_BIT(16)
|
|
|
|
|
|
|
|
old = obj->dslots;
|
2006-12-12 13:45:06 +03:00
|
|
|
if (nslots <= JS_INITIAL_NSLOTS) {
|
2007-01-07 01:03:06 +03:00
|
|
|
if (old &&
|
|
|
|
(exactAllocation ||
|
|
|
|
SLOTS_TO_DYNAMIC_WORDS((uint32)old[-1]) != MIN_DYNAMIC_WORDS ||
|
|
|
|
nslots <= (JS_INITIAL_NSLOTS +
|
|
|
|
JSSLOT_FREE(STOBJ_GET_CLASS(obj))) / 2)) {
|
|
|
|
/*
|
|
|
|
* We do not want to free dynamic slots when allocation is a hint,
|
|
|
|
* we reached minimal allocation and almost all fixed slots are
|
|
|
|
* used. It avoids allocating dynamic slots again when properties
|
|
|
|
* are added to the object.
|
|
|
|
*
|
|
|
|
* If there were no private or reserved slots, the condition to
|
|
|
|
* free the slots would be
|
|
|
|
*
|
|
|
|
* nslots <= JS_INITIAL_NSLOTS / 2
|
|
|
|
*
|
|
|
|
* but to account for never removed slots before JSSLOT_FREE(class)
|
|
|
|
* we need to subtract it from the slot counts which gives
|
|
|
|
*
|
|
|
|
* nslots - JSSLOT_FREE <= (JS_INITIAL_NSLOTS - JSSLOT_FREE) / 2
|
|
|
|
*
|
|
|
|
* or
|
|
|
|
*
|
|
|
|
* nslots <= (JS_INITIAL_NSLOTS + JSSLOT_FREE) / 2
|
|
|
|
*/
|
|
|
|
FreeSlots(cx, obj);
|
|
|
|
}
|
2006-12-12 13:45:06 +03:00
|
|
|
return JS_TRUE;
|
2005-01-05 09:15:03 +03:00
|
|
|
}
|
|
|
|
|
2007-01-07 01:03:06 +03:00
|
|
|
oslots = (old) ? (uint32)*--old : JS_INITIAL_NSLOTS;
|
|
|
|
nwords = SLOTS_TO_DYNAMIC_WORDS(nslots);
|
2005-01-05 09:15:03 +03:00
|
|
|
|
2007-01-07 01:03:06 +03:00
|
|
|
if (nslots > oslots) {
|
|
|
|
if (!exactAllocation) {
|
|
|
|
/*
|
|
|
|
* Round up nslots so the number of bytes in dslots array is power
|
|
|
|
* of 2 to ensure exponential grouth.
|
|
|
|
*/
|
|
|
|
if (nwords <= MIN_DYNAMIC_WORDS) {
|
|
|
|
nwords = MIN_DYNAMIC_WORDS;
|
|
|
|
} else if (nwords < LINEAR_GROWTH_STEP) {
|
|
|
|
JS_CEILING_LOG2(log, nwords);
|
|
|
|
nwords = JS_BIT(log);
|
|
|
|
} else {
|
|
|
|
nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
slots = (jsval *)JS_realloc(cx, old, nwords * sizeof(jsval));
|
|
|
|
if (!slots)
|
2006-12-12 13:45:06 +03:00
|
|
|
return JS_FALSE;
|
2007-01-07 01:03:06 +03:00
|
|
|
} else {
|
|
|
|
JS_ASSERT(nslots < oslots);
|
|
|
|
if (!exactAllocation) {
|
|
|
|
owords = DYNAMIC_WORDS_TO_SLOTS(oslots);
|
|
|
|
if (owords <= MIN_DYNAMIC_WORDS)
|
|
|
|
return JS_TRUE;
|
|
|
|
if (owords < LINEAR_GROWTH_STEP * 2) {
|
|
|
|
/*
|
|
|
|
* Shrink only if 1/4 of slots are left and we need to grow
|
|
|
|
* the array at least twice to reach the current capacity. It
|
|
|
|
* prevents frequent capacity growth/shrinking when slots are
|
|
|
|
* often removed and added.
|
|
|
|
*/
|
|
|
|
if (nwords > owords / 4)
|
|
|
|
return JS_TRUE;
|
|
|
|
JS_CEILING_LOG2(log, nwords);
|
|
|
|
nwords = JS_BIT(log);
|
|
|
|
if (nwords < MIN_DYNAMIC_WORDS)
|
|
|
|
nwords = MIN_DYNAMIC_WORDS;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Shrink only if we free at least 2 linear allocation
|
|
|
|
* segments, to prevent growth/shrinking resonance.
|
|
|
|
*/
|
|
|
|
if (nwords > owords - LINEAR_GROWTH_STEP * 2)
|
|
|
|
return JS_TRUE;
|
|
|
|
nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP);
|
|
|
|
}
|
|
|
|
}
|
2005-01-05 09:15:03 +03:00
|
|
|
|
2007-01-07 01:03:06 +03:00
|
|
|
/* We avoid JS_realloc not to report a failed shrink attempt. */
|
|
|
|
slots = (jsval *)realloc(old, nwords * sizeof(jsval));
|
|
|
|
if (!slots)
|
|
|
|
slots = old;
|
2005-01-05 09:15:03 +03:00
|
|
|
}
|
|
|
|
|
2007-01-07 01:03:06 +03:00
|
|
|
nslots = DYNAMIC_WORDS_TO_SLOTS(nwords);
|
|
|
|
*slots++ = (jsval)nslots;
|
|
|
|
obj->dslots = slots;
|
2005-01-05 09:15:03 +03:00
|
|
|
|
2006-12-12 13:45:06 +03:00
|
|
|
/* If we're extending an allocation, initialize free slots. */
|
|
|
|
for (i = oslots; i < nslots; i++)
|
2007-01-07 01:03:06 +03:00
|
|
|
slots[i - JS_INITIAL_NSLOTS] = JSVAL_VOID;
|
2005-01-05 09:15:03 +03:00
|
|
|
|
2006-12-12 13:45:06 +03:00
|
|
|
return JS_TRUE;
|
2007-01-07 01:03:06 +03:00
|
|
|
|
|
|
|
#undef LINEAR_GROWTH_STEP
|
|
|
|
#undef MIN_DYNAMIC_WORDS
|
2005-01-05 09:15:03 +03:00
|
|
|
}
|
|
|
|
|
2006-04-19 04:22:43 +04:00
|
|
|
extern JSBool
|
|
|
|
js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp)
|
|
|
|
{
|
|
|
|
JSProtoKey key;
|
|
|
|
JSAtom *atom;
|
|
|
|
|
|
|
|
key = JSCLASS_CACHED_PROTO_KEY(clasp);
|
|
|
|
if (key != JSProto_Null) {
|
|
|
|
*idp = INT_TO_JSID(key);
|
2006-05-21 02:27:28 +04:00
|
|
|
} else if (clasp->flags & JSCLASS_IS_ANONYMOUS) {
|
|
|
|
*idp = INT_TO_JSID(JSProto_Object);
|
2006-04-19 04:22:43 +04:00
|
|
|
} else {
|
|
|
|
atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
|
|
|
|
if (!atom)
|
|
|
|
return JS_FALSE;
|
|
|
|
*idp = ATOM_TO_JSID(atom);
|
|
|
|
}
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
JSObject *
|
|
|
|
js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
|
|
|
|
{
|
2006-04-19 04:22:43 +04:00
|
|
|
jsid id;
|
2004-10-05 14:19:07 +04:00
|
|
|
JSObject *obj;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSObjectOps *ops;
|
|
|
|
JSObjectMap *map;
|
2004-04-13 05:25:17 +04:00
|
|
|
JSClass *protoclasp;
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
uint32 nslots, i;
|
2006-03-13 03:24:06 +03:00
|
|
|
JSTempValueRooter tvr;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
/* Bootstrap the ur-object, and make it the default prototype object. */
|
|
|
|
if (!proto) {
|
2006-04-19 04:22:43 +04:00
|
|
|
if (!js_GetClassId(cx, clasp, &id))
|
2006-02-14 12:33:45 +03:00
|
|
|
return NULL;
|
2006-04-19 04:22:43 +04:00
|
|
|
if (!js_GetClassPrototype(cx, parent, id, &proto))
|
2005-01-19 05:25:40 +03:00
|
|
|
return NULL;
|
2006-02-14 12:33:45 +03:00
|
|
|
if (!proto &&
|
2006-04-19 04:22:43 +04:00
|
|
|
!js_GetClassPrototype(cx, parent, INT_TO_JSID(JSProto_Object),
|
2006-02-14 12:33:45 +03:00
|
|
|
&proto)) {
|
2005-01-19 05:25:40 +03:00
|
|
|
return NULL;
|
2006-02-14 12:33:45 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Always call the class's getObjectOps hook if it has one. */
|
|
|
|
ops = clasp->getObjectOps
|
2001-02-07 02:19:44 +03:00
|
|
|
? clasp->getObjectOps(cx, clasp)
|
|
|
|
: &js_ObjectOps;
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2005-01-19 05:25:40 +03:00
|
|
|
/*
|
|
|
|
* Allocate a zeroed object from the GC heap. Do this *after* any other
|
2006-04-19 04:22:43 +04:00
|
|
|
* GC-thing allocations under js_GetClassPrototype or clasp->getObjectOps,
|
2005-01-19 05:25:40 +03:00
|
|
|
* to avoid displacing the newborn root for obj.
|
|
|
|
*/
|
|
|
|
obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject));
|
|
|
|
if (!obj)
|
|
|
|
return NULL;
|
|
|
|
|
2006-12-12 13:45:06 +03:00
|
|
|
obj->dslots = NULL;
|
|
|
|
|
2006-03-13 03:24:06 +03:00
|
|
|
/*
|
2006-12-12 13:45:06 +03:00
|
|
|
* Root obj to prevent it from being collected out from under this call to
|
|
|
|
* js_NewObject. There's a possibilty of GC under the objectHook call-out
|
|
|
|
* further below.
|
2006-03-13 03:24:06 +03:00
|
|
|
*/
|
2006-10-27 23:38:33 +04:00
|
|
|
JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
|
2006-03-13 03:24:06 +03:00
|
|
|
|
2006-12-12 13:45:06 +03:00
|
|
|
/* Set the proto, parent, and class properties. */
|
|
|
|
STOBJ_SET_PROTO(obj, proto);
|
|
|
|
STOBJ_SET_PARENT(obj, parent);
|
|
|
|
STOBJ_SET_SLOT(obj, JSSLOT_CLASS, PRIVATE_TO_JSVAL(clasp));
|
|
|
|
|
|
|
|
/* Initialize the remaining fixed slots. */
|
|
|
|
for (i = JSSLOT_PRIVATE; i != JS_INITIAL_NSLOTS; ++i)
|
|
|
|
obj->fslots[i] = JSVAL_VOID;
|
|
|
|
|
2002-08-09 01:51:54 +04:00
|
|
|
/*
|
|
|
|
* Share proto's map only if it has the same JSObjectOps, and only if
|
2004-04-13 05:25:17 +04:00
|
|
|
* proto's class has the same private and reserved slots as obj's map
|
|
|
|
* and class have. We assume that if prototype and object are of the
|
|
|
|
* same class, they always have the same number of computed reserved
|
|
|
|
* slots (returned via clasp->reserveSlots); otherwise, prototype and
|
|
|
|
* object classes must have the same (null or not) reserveSlots hook.
|
2002-08-09 01:51:54 +04:00
|
|
|
*/
|
|
|
|
if (proto &&
|
|
|
|
(map = proto->map)->ops == ops &&
|
2004-04-13 05:25:17 +04:00
|
|
|
((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp ||
|
|
|
|
(!((protoclasp->flags ^ clasp->flags) &
|
|
|
|
(JSCLASS_HAS_PRIVATE |
|
|
|
|
(JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) &&
|
|
|
|
protoclasp->reserveSlots == clasp->reserveSlots)))
|
|
|
|
{
|
2004-10-05 14:19:07 +04:00
|
|
|
/*
|
|
|
|
* Default parent to the parent of the prototype, which was set from
|
|
|
|
* the parent of the prototype's constructor.
|
|
|
|
*/
|
|
|
|
if (!parent)
|
2006-12-12 13:45:06 +03:00
|
|
|
STOBJ_SET_PARENT(obj, OBJ_GET_PARENT(cx, proto));
|
2001-02-07 02:19:44 +03:00
|
|
|
|
|
|
|
/* Share the given prototype's map. */
|
|
|
|
obj->map = js_HoldObjectMap(cx, map);
|
1998-04-24 04:31:11 +04:00
|
|
|
} else {
|
2001-02-07 02:19:44 +03:00
|
|
|
/* Leave parent alone. Allocate a new map for obj. */
|
|
|
|
map = ops->newObjectMap(cx, 1, ops, clasp, obj);
|
|
|
|
if (!map)
|
|
|
|
goto bad;
|
|
|
|
obj->map = map;
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
|
2007-01-07 01:03:06 +03:00
|
|
|
/* Let ops->newObjectMap set freeslot so as to reserve slots. */
|
|
|
|
nslots = map->freeslot;
|
2006-12-12 13:45:06 +03:00
|
|
|
JS_ASSERT(nslots >= JSSLOT_PRIVATE);
|
2007-01-07 01:03:06 +03:00
|
|
|
if (nslots > JS_INITIAL_NSLOTS &&
|
|
|
|
!ReallocSlots(cx, obj, nslots, JS_TRUE)) {
|
2006-12-12 13:45:06 +03:00
|
|
|
js_DropObjectMap(cx, map, obj);
|
|
|
|
obj->map = NULL;
|
|
|
|
goto bad;
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2004-08-19 21:57:36 +04:00
|
|
|
if (cx->runtime->objectHook) {
|
|
|
|
JS_KEEP_ATOMS(cx->runtime);
|
1998-10-14 14:22:38 +04:00
|
|
|
cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData);
|
2004-08-19 21:57:36 +04:00
|
|
|
JS_UNKEEP_ATOMS(cx->runtime);
|
|
|
|
}
|
1998-10-14 14:22:38 +04:00
|
|
|
|
2006-03-13 03:24:06 +03:00
|
|
|
out:
|
|
|
|
JS_POP_TEMP_ROOT(cx, &tvr);
|
|
|
|
cx->newborn[GCX_OBJECT] = (JSGCThing *) obj;
|
1998-03-28 05:44:41 +03:00
|
|
|
return obj;
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
bad:
|
2006-03-13 03:24:06 +03:00
|
|
|
obj = NULL;
|
|
|
|
goto out;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
2006-05-21 02:27:28 +04:00
|
|
|
JS_STATIC_DLL_CALLBACK(JSObject *)
|
|
|
|
js_InitNullClass(JSContext *cx, JSObject *obj)
|
|
|
|
{
|
|
|
|
JS_ASSERT(0);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
|
|
|
|
#include "jsproto.tbl"
|
|
|
|
#undef JS_PROTO
|
|
|
|
|
|
|
|
static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = {
|
|
|
|
#define JS_PROTO(name,code,init) init,
|
|
|
|
#include "jsproto.tbl"
|
|
|
|
#undef JS_PROTO
|
|
|
|
};
|
|
|
|
|
|
|
|
JSBool
|
|
|
|
js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
|
|
|
|
JSObject **objp)
|
|
|
|
{
|
|
|
|
JSBool ok;
|
|
|
|
JSObject *tmp, *cobj;
|
|
|
|
JSResolvingKey rkey;
|
|
|
|
JSResolvingEntry *rentry;
|
|
|
|
uint32 generation;
|
|
|
|
JSObjectOp init;
|
|
|
|
jsval v;
|
|
|
|
|
|
|
|
while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
|
|
|
|
obj = tmp;
|
|
|
|
if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) {
|
|
|
|
*objp = NULL;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ok = JS_GetReservedSlot(cx, obj, key, &v);
|
|
|
|
if (!ok)
|
|
|
|
return JS_FALSE;
|
|
|
|
if (!JSVAL_IS_PRIMITIVE(v)) {
|
|
|
|
*objp = JSVAL_TO_OBJECT(v);
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
rkey.obj = obj;
|
|
|
|
rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
|
|
|
|
if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry))
|
|
|
|
return JS_FALSE;
|
|
|
|
if (!rentry) {
|
|
|
|
/* Already caching key in obj -- suppress recursion. */
|
|
|
|
*objp = NULL;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
generation = cx->resolvingTable->generation;
|
|
|
|
|
|
|
|
cobj = NULL;
|
|
|
|
init = lazy_prototype_init[key];
|
|
|
|
if (init) {
|
|
|
|
if (!init(cx, obj)) {
|
|
|
|
ok = JS_FALSE;
|
|
|
|
} else {
|
|
|
|
ok = JS_GetReservedSlot(cx, obj, key, &v);
|
|
|
|
if (ok && !JSVAL_IS_PRIMITIVE(v))
|
|
|
|
cobj = JSVAL_TO_OBJECT(v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation);
|
|
|
|
*objp = cobj;
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
|
|
|
js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj)
|
|
|
|
{
|
|
|
|
JS_ASSERT(!OBJ_GET_PARENT(cx, obj));
|
|
|
|
if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL))
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
return JS_SetReservedSlot(cx, obj, key, OBJECT_TO_JSVAL(cobj));
|
|
|
|
}
|
|
|
|
|
2004-10-05 14:19:07 +04:00
|
|
|
JSBool
|
2006-04-19 04:22:43 +04:00
|
|
|
js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
2006-04-20 03:36:16 +04:00
|
|
|
JSObject *obj, *cobj, *pobj;
|
2006-04-19 04:22:43 +04:00
|
|
|
JSProtoKey key;
|
2004-04-20 22:02:49 +04:00
|
|
|
JSProperty *prop;
|
|
|
|
JSScopeProperty *sprop;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2004-04-13 05:25:17 +04:00
|
|
|
if (start || (cx->fp && (start = cx->fp->scopeChain) != NULL)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
/* Find the topmost object in the scope chain. */
|
|
|
|
do {
|
2004-04-13 05:25:17 +04:00
|
|
|
obj = start;
|
|
|
|
start = OBJ_GET_PARENT(cx, obj);
|
|
|
|
} while (start);
|
1998-03-28 05:44:41 +03:00
|
|
|
} else {
|
2001-02-07 02:19:44 +03:00
|
|
|
obj = cx->globalObject;
|
|
|
|
if (!obj) {
|
|
|
|
*vp = JSVAL_VOID;
|
2004-04-15 13:05:46 +04:00
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-10-28 04:21:53 +04:00
|
|
|
OBJ_TO_INNER_OBJECT(cx, obj);
|
|
|
|
if (!obj)
|
|
|
|
return JS_FALSE;
|
|
|
|
|
2006-04-19 04:22:43 +04:00
|
|
|
if (JSID_IS_INT(id)) {
|
|
|
|
key = JSID_TO_INT(id);
|
|
|
|
JS_ASSERT(key != JSProto_Null);
|
2006-04-20 03:36:16 +04:00
|
|
|
if (!js_GetClassObject(cx, obj, key, &cobj))
|
2006-04-19 04:22:43 +04:00
|
|
|
return JS_FALSE;
|
2006-04-20 03:36:16 +04:00
|
|
|
if (cobj) {
|
|
|
|
*vp = OBJECT_TO_JSVAL(cobj);
|
2006-04-19 04:22:43 +04:00
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
|
|
|
|
}
|
|
|
|
|
2004-09-02 00:51:39 +04:00
|
|
|
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
2006-04-19 04:22:43 +04:00
|
|
|
if (!js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME,
|
|
|
|
&pobj, &prop)) {
|
2004-04-20 22:02:49 +04:00
|
|
|
return JS_FALSE;
|
2005-09-30 04:39:18 +04:00
|
|
|
}
|
2004-04-20 22:02:49 +04:00
|
|
|
if (!prop) {
|
|
|
|
*vp = JSVAL_VOID;
|
|
|
|
return JS_TRUE;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
2004-04-20 22:02:49 +04:00
|
|
|
JS_ASSERT(OBJ_IS_NATIVE(pobj));
|
|
|
|
sprop = (JSScopeProperty *) prop;
|
|
|
|
JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
|
|
|
|
*vp = OBJ_GET_SLOT(cx, pobj, sprop->slot);
|
|
|
|
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
|
|
|
return JS_TRUE;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
JSObject *
|
|
|
|
js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
|
2002-02-14 10:25:34 +03:00
|
|
|
JSObject *parent, uintN argc, jsval *argv)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
2006-04-19 04:22:43 +04:00
|
|
|
jsid id;
|
2000-01-18 14:06:05 +03:00
|
|
|
jsval cval, rval;
|
2006-02-07 23:09:53 +03:00
|
|
|
JSTempValueRooter argtvr, tvr;
|
1998-08-26 10:14:55 +04:00
|
|
|
JSObject *obj, *ctor;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2006-02-07 23:09:53 +03:00
|
|
|
JS_PUSH_TEMP_ROOT(cx, argc, argv, &argtvr);
|
|
|
|
|
2006-04-19 04:22:43 +04:00
|
|
|
if (!js_GetClassId(cx, clasp, &id) ||
|
|
|
|
!js_FindClassObject(cx, parent, id, &cval)) {
|
2006-02-07 23:09:53 +03:00
|
|
|
JS_POP_TEMP_ROOT(cx, &argtvr);
|
2001-02-07 02:19:44 +03:00
|
|
|
return NULL;
|
2006-02-07 23:09:53 +03:00
|
|
|
}
|
2006-04-19 04:22:43 +04:00
|
|
|
|
2002-03-03 03:59:16 +03:00
|
|
|
if (JSVAL_IS_PRIMITIVE(cval)) {
|
2003-10-22 10:26:03 +04:00
|
|
|
js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
|
2006-02-07 23:09:53 +03:00
|
|
|
JS_POP_TEMP_ROOT(cx, &argtvr);
|
2002-03-03 03:59:16 +03:00
|
|
|
return NULL;
|
|
|
|
}
|
1998-08-26 10:14:55 +04:00
|
|
|
|
2006-02-02 09:46:32 +03:00
|
|
|
/*
|
|
|
|
* Protect cval in case a crazy getter for .prototype uproots it. After
|
|
|
|
* this point, all control flow must exit through label out with obj set.
|
|
|
|
*/
|
|
|
|
JS_PUSH_SINGLE_TEMP_ROOT(cx, cval, &tvr);
|
|
|
|
|
1998-08-26 10:14:55 +04:00
|
|
|
/*
|
|
|
|
* 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)
|
2001-02-07 02:19:44 +03:00
|
|
|
parent = OBJ_GET_PARENT(cx, ctor);
|
1998-08-26 10:14:55 +04:00
|
|
|
if (!proto) {
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!OBJ_GET_PROPERTY(cx, ctor,
|
2004-10-05 14:19:07 +04:00
|
|
|
ATOM_TO_JSID(cx->runtime->atomState
|
|
|
|
.classPrototypeAtom),
|
2001-02-07 02:19:44 +03:00
|
|
|
&rval)) {
|
2006-02-07 23:09:53 +03:00
|
|
|
obj = NULL;
|
2006-02-02 09:46:32 +03:00
|
|
|
goto out;
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
|
|
|
if (JSVAL_IS_OBJECT(rval))
|
|
|
|
proto = JSVAL_TO_OBJECT(rval);
|
1998-08-26 10:14:55 +04:00
|
|
|
}
|
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
obj = js_NewObject(cx, clasp, proto, parent);
|
|
|
|
if (!obj)
|
2006-02-02 09:46:32 +03:00
|
|
|
goto out;
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2006-02-07 23:09:53 +03:00
|
|
|
if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval))
|
|
|
|
goto bad;
|
|
|
|
|
|
|
|
if (JSVAL_IS_PRIMITIVE(rval))
|
2006-02-02 09:46:32 +03:00
|
|
|
goto out;
|
2006-02-07 23:09:53 +03:00
|
|
|
obj = JSVAL_TO_OBJECT(rval);
|
|
|
|
|
|
|
|
/*
|
2006-05-21 02:27:28 +04:00
|
|
|
* If the instance's class differs from what was requested, throw a type
|
|
|
|
* error. If the given class has both the JSCLASS_HAS_PRIVATE and the
|
|
|
|
* JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its
|
|
|
|
* private data set at this point, then the constructor was replaced and
|
|
|
|
* we should throw a type error.
|
2006-02-07 23:09:53 +03:00
|
|
|
*/
|
|
|
|
if (OBJ_GET_CLASS(cx, obj) != clasp ||
|
|
|
|
(!(~clasp->flags & (JSCLASS_HAS_PRIVATE |
|
|
|
|
JSCLASS_CONSTRUCT_PROTOTYPE)) &&
|
|
|
|
!JS_GetPrivate(cx, obj))) {
|
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_WRONG_CONSTRUCTOR, clasp->name);
|
|
|
|
goto bad;
|
2006-02-02 09:46:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
JS_POP_TEMP_ROOT(cx, &tvr);
|
2006-02-07 23:09:53 +03:00
|
|
|
JS_POP_TEMP_ROOT(cx, &argtvr);
|
2006-02-02 09:46:32 +03:00
|
|
|
return obj;
|
2006-02-07 23:09:53 +03:00
|
|
|
|
|
|
|
bad:
|
|
|
|
cx->newborn[GCX_OBJECT] = NULL;
|
|
|
|
obj = NULL;
|
|
|
|
goto out;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
js_FinalizeObject(JSContext *cx, JSObject *obj)
|
|
|
|
{
|
1998-04-24 04:31:11 +04:00
|
|
|
JSObjectMap *map;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
/* Cope with stillborn objects that have no map. */
|
|
|
|
map = obj->map;
|
|
|
|
if (!map)
|
2001-02-07 02:19:44 +03:00
|
|
|
return;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
if (cx->runtime->objectHook)
|
1998-10-14 14:22:38 +04:00
|
|
|
cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData);
|
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
/* Remove all watchpoints with weak links to obj. */
|
|
|
|
JS_ClearWatchPointsForObject(cx, obj);
|
|
|
|
|
2006-12-12 13:45:06 +03:00
|
|
|
/* Finalize obj first, in case it needs map and slots. */
|
|
|
|
GC_AWARE_GET_CLASS(cx, obj)->finalize(cx, obj);
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
/* Drop map and free slots. */
|
|
|
|
js_DropObjectMap(cx, map, obj);
|
1998-03-28 05:44:41 +03:00
|
|
|
obj->map = NULL;
|
2006-12-12 13:45:06 +03:00
|
|
|
FreeSlots(cx, obj);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
2002-02-23 06:49:27 +03:00
|
|
|
/* XXXbe if one adds props, deletes earlier props, adds more, the last added
|
|
|
|
won't recycle the deleted props' slots. */
|
1998-03-28 05:44:41 +03:00
|
|
|
JSBool
|
|
|
|
js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)
|
|
|
|
{
|
|
|
|
JSObjectMap *map;
|
2004-04-13 05:25:17 +04:00
|
|
|
JSClass *clasp;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
|
|
|
map = obj->map;
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
|
2004-04-13 05:25:17 +04:00
|
|
|
clasp = LOCKED_OBJ_GET_CLASS(obj);
|
2007-01-07 01:03:06 +03:00
|
|
|
if (map->freeslot == JSSLOT_FREE(clasp) && clasp->reserveSlots) {
|
2004-04-13 05:25:17 +04:00
|
|
|
/* Adjust map->freeslot to include computed reserved slots, if any. */
|
2007-01-07 01:03:06 +03:00
|
|
|
map->freeslot += clasp->reserveSlots(cx, obj);
|
2004-04-13 05:25:17 +04:00
|
|
|
}
|
2007-01-07 01:03:06 +03:00
|
|
|
|
|
|
|
if (map->freeslot >= STOBJ_NSLOTS(obj) &&
|
|
|
|
!ReallocSlots(cx, obj, map->freeslot + 1, JS_FALSE)) {
|
|
|
|
return JS_FALSE;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef TOO_MUCH_GC
|
2006-12-09 18:02:37 +03:00
|
|
|
STOBJ_SET_SLOT(obj, map->freeslot, JSVAL_VOID);
|
1998-03-28 05:44:41 +03:00
|
|
|
#endif
|
|
|
|
*slotp = map->freeslot++;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
|
|
|
|
{
|
|
|
|
JSObjectMap *map;
|
|
|
|
|
|
|
|
map = obj->map;
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
|
2007-01-07 01:03:06 +03:00
|
|
|
LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_VOID);
|
|
|
|
if (map->freeslot == slot + 1) {
|
|
|
|
/* When shrinking ReallocSlots always returns true. */
|
|
|
|
ReallocSlots(cx, obj, slot, JS_FALSE);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-07-31 04:07:22 +04:00
|
|
|
/* JSVAL_INT_MAX as a string */
|
|
|
|
#define JSVAL_INT_MAX_STRING "1073741823"
|
|
|
|
|
2005-05-15 22:24:31 +04:00
|
|
|
#define CHECK_FOR_STRING_INDEX(id) \
|
1998-10-14 14:22:38 +04:00
|
|
|
JS_BEGIN_MACRO \
|
2004-10-05 14:19:07 +04:00
|
|
|
if (JSID_IS_ATOM(id)) { \
|
|
|
|
JSAtom *atom_ = JSID_TO_ATOM(id); \
|
2001-10-29 01:37:25 +03:00
|
|
|
JSString *str_ = ATOM_TO_STRING(atom_); \
|
|
|
|
const jschar *cp_ = str_->chars; \
|
|
|
|
JSBool negative_ = (*cp_ == '-'); \
|
|
|
|
if (negative_) cp_++; \
|
2006-11-21 01:37:08 +03:00
|
|
|
if (JS7_ISDEC(*cp_)) { \
|
|
|
|
size_t n_ = str_->length - negative_; \
|
|
|
|
if (n_ <= sizeof(JSVAL_INT_MAX_STRING) - 1) \
|
|
|
|
id = CheckForStringIndex(id, cp_, cp_ + n_, negative_); \
|
2000-10-29 04:30:16 +03:00
|
|
|
} \
|
|
|
|
} \
|
1998-10-14 14:22:38 +04:00
|
|
|
JS_END_MACRO
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2003-07-27 02:37:11 +04:00
|
|
|
static jsid
|
2006-11-21 01:37:08 +03:00
|
|
|
CheckForStringIndex(jsid id, const jschar *cp, const jschar *end,
|
|
|
|
JSBool negative)
|
2003-07-27 02:37:11 +04:00
|
|
|
{
|
|
|
|
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++;
|
|
|
|
}
|
|
|
|
}
|
2006-11-21 01:37:08 +03:00
|
|
|
if (cp == end &&
|
2003-07-27 02:37:11 +04:00
|
|
|
(oldIndex < (JSVAL_INT_MAX / 10) ||
|
|
|
|
(oldIndex == (JSVAL_INT_MAX / 10) &&
|
|
|
|
c <= (JSVAL_INT_MAX % 10)))) {
|
|
|
|
if (negative)
|
|
|
|
index = 0 - index;
|
2004-10-05 14:19:07 +04:00
|
|
|
id = INT_TO_JSID((jsint)index);
|
2003-07-27 02:37:11 +04:00
|
|
|
}
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2005-05-17 05:06:35 +04:00
|
|
|
static JSBool
|
|
|
|
HidePropertyName(JSContext *cx, jsid *idp)
|
|
|
|
{
|
|
|
|
jsid id;
|
|
|
|
JSAtom *atom, *hidden;
|
2005-07-31 00:57:07 +04:00
|
|
|
|
2005-05-17 05:06:35 +04:00
|
|
|
id = *idp;
|
|
|
|
JS_ASSERT(JSID_IS_ATOM(id));
|
|
|
|
|
|
|
|
atom = JSID_TO_ATOM(id);
|
|
|
|
JS_ASSERT(!(atom->flags & ATOM_HIDDEN));
|
|
|
|
JS_ASSERT(ATOM_IS_STRING(atom));
|
|
|
|
|
|
|
|
hidden = js_AtomizeString(cx, ATOM_TO_STRING(atom), ATOM_HIDDEN);
|
|
|
|
if (!hidden)
|
|
|
|
return JS_FALSE;
|
|
|
|
|
2005-05-17 21:44:57 +04:00
|
|
|
/*
|
|
|
|
* Link hidden to unhidden atom to optimize call_enumerate -- this means
|
|
|
|
* the GC must mark a hidden atom's unhidden counterpart (see js_MarkAtom
|
2006-09-02 22:27:35 +04:00
|
|
|
* in jsgc.c). It uses the atom's entry.value member for this linkage.
|
2005-05-17 21:44:57 +04:00
|
|
|
*/
|
|
|
|
hidden->entry.value = atom;
|
2005-05-17 05:06:35 +04:00
|
|
|
*idp = ATOM_TO_JSID(hidden);
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSScopeProperty *
|
|
|
|
js_AddHiddenProperty(JSContext *cx, JSObject *obj, jsid id,
|
|
|
|
JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
|
|
|
|
uintN attrs, uintN flags, intN shortid)
|
|
|
|
{
|
|
|
|
if (!HidePropertyName(cx, &id))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
flags |= SPROP_IS_HIDDEN;
|
|
|
|
return js_AddNativeProperty(cx, obj, id, getter, setter, slot, attrs,
|
|
|
|
flags, shortid);
|
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
|
|
|
js_LookupHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
|
|
|
|
JSProperty **propp)
|
|
|
|
{
|
|
|
|
return HidePropertyName(cx, &id) &&
|
|
|
|
js_LookupProperty(cx, obj, id, objp, propp);
|
|
|
|
}
|
|
|
|
|
2002-02-23 06:49:27 +03:00
|
|
|
JSScopeProperty *
|
|
|
|
js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
|
|
|
|
JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
|
|
|
|
uintN attrs, uintN flags, intN shortid)
|
|
|
|
{
|
|
|
|
JSScope *scope;
|
2002-03-12 10:17:30 +03:00
|
|
|
JSScopeProperty *sprop;
|
2002-02-23 06:49:27 +03:00
|
|
|
|
2002-03-12 10:17:30 +03:00
|
|
|
JS_LOCK_OBJ(cx, obj);
|
2002-02-23 06:49:27 +03:00
|
|
|
scope = js_GetMutableScope(cx, obj);
|
2002-03-12 10:17:30 +03:00
|
|
|
if (!scope) {
|
|
|
|
sprop = NULL;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Handle old bug that took empty string as zero index. Also convert
|
|
|
|
* string indices to integers if appropriate.
|
|
|
|
*/
|
2005-05-15 22:24:31 +04:00
|
|
|
CHECK_FOR_STRING_INDEX(id);
|
2002-03-12 10:17:30 +03:00
|
|
|
sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs,
|
|
|
|
flags, shortid);
|
|
|
|
}
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
return sprop;
|
2002-02-23 06:49:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
JSScopeProperty *
|
|
|
|
js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
|
|
|
|
JSScopeProperty *sprop, uintN attrs, uintN mask,
|
|
|
|
JSPropertyOp getter, JSPropertyOp setter)
|
|
|
|
{
|
|
|
|
JSScope *scope;
|
2003-04-14 08:14:57 +04:00
|
|
|
|
2002-03-12 10:17:30 +03:00
|
|
|
JS_LOCK_OBJ(cx, obj);
|
2002-02-23 06:49:27 +03:00
|
|
|
scope = js_GetMutableScope(cx, obj);
|
2002-03-12 10:17:30 +03:00
|
|
|
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);
|
2002-02-23 06:49:27 +03:00
|
|
|
return sprop;
|
|
|
|
}
|
1998-07-31 04:07:22 +04:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
JSBool
|
|
|
|
js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
2001-02-07 02:19:44 +03:00
|
|
|
JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
|
|
|
|
JSProperty **propp)
|
2002-02-23 06:49:27 +03:00
|
|
|
{
|
|
|
|
return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs,
|
|
|
|
0, 0, propp);
|
|
|
|
}
|
|
|
|
|
2005-07-20 01:13:19 +04:00
|
|
|
/*
|
|
|
|
* Backward compatibility requires allowing addProperty hooks to mutate the
|
|
|
|
* nominal initial value of a slot-full property, while GC safety wants that
|
|
|
|
* value to be stored before the call-out through the hook. Optimize to do
|
|
|
|
* both while saving cycles for classes that stub their addProperty hook.
|
|
|
|
*/
|
|
|
|
#define ADD_PROPERTY_HELPER(cx,clasp,obj,scope,sprop,vp,cleanup) \
|
|
|
|
JS_BEGIN_MACRO \
|
|
|
|
if ((clasp)->addProperty != JS_PropertyStub) { \
|
|
|
|
jsval nominal_ = *(vp); \
|
|
|
|
if (!(clasp)->addProperty(cx, obj, SPROP_USERID(sprop), vp)) { \
|
|
|
|
cleanup; \
|
|
|
|
} \
|
|
|
|
if (*(vp) != nominal_) { \
|
|
|
|
if (SPROP_HAS_VALID_SLOT(sprop, scope)) \
|
|
|
|
LOCKED_OBJ_SET_SLOT(obj, (sprop)->slot, *(vp)); \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
JS_END_MACRO
|
|
|
|
|
2002-02-23 06:49:27 +03:00
|
|
|
JSBool
|
|
|
|
js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
|
|
|
JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
|
|
|
|
uintN flags, intN shortid, JSProperty **propp)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
1998-04-24 04:31:11 +04:00
|
|
|
JSClass *clasp;
|
1998-03-28 05:44:41 +03:00
|
|
|
JSScope *scope;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSScopeProperty *sprop;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1999-07-01 06:24:32 +04:00
|
|
|
/*
|
2000-06-27 06:37:25 +04:00
|
|
|
* Handle old bug that took empty string as zero index. Also convert
|
|
|
|
* string indices to integers if appropriate.
|
1999-07-01 06:24:32 +04:00
|
|
|
*/
|
2005-05-15 22:24:31 +04:00
|
|
|
CHECK_FOR_STRING_INDEX(id);
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1999-09-21 04:13:48 +04:00
|
|
|
#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;
|
2006-05-21 02:27:28 +04:00
|
|
|
JSProperty *prop;
|
1999-09-21 04:13:48 +04:00
|
|
|
|
2002-02-23 06:49:27 +03:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2004-04-13 05:25:17 +04:00
|
|
|
if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
|
2002-02-23 06:49:27 +03:00
|
|
|
return JS_FALSE;
|
2004-04-13 05:25:17 +04:00
|
|
|
sprop = (JSScopeProperty *) prop;
|
1999-09-21 04:13:48 +04:00
|
|
|
if (sprop &&
|
|
|
|
pobj == obj &&
|
|
|
|
(sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
|
2002-02-23 06:49:27 +03:00
|
|
|
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;
|
1999-09-21 04:13:48 +04:00
|
|
|
}
|
2000-04-01 08:23:04 +04:00
|
|
|
|
2004-04-13 05:25:17 +04:00
|
|
|
if (prop) {
|
2000-04-01 08:23:04 +04:00
|
|
|
/* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */
|
2004-04-13 05:25:17 +04:00
|
|
|
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
|
|
|
prop = NULL;
|
2000-04-01 08:23:04 +04:00
|
|
|
}
|
1999-09-21 04:13:48 +04:00
|
|
|
}
|
|
|
|
#endif /* JS_HAS_GETTER_SETTER */
|
|
|
|
|
2002-02-23 06:49:27 +03:00
|
|
|
/* Lock if object locking is required by this implementation. */
|
|
|
|
JS_LOCK_OBJ(cx, obj);
|
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
/* Use the object's class getter and setter by default. */
|
1998-04-24 04:31:11 +04:00
|
|
|
clasp = LOCKED_OBJ_GET_CLASS(obj);
|
1998-03-28 05:44:41 +03:00
|
|
|
if (!getter)
|
2001-02-07 02:19:44 +03:00
|
|
|
getter = clasp->getProperty;
|
1998-03-28 05:44:41 +03:00
|
|
|
if (!setter)
|
2001-02-07 02:19:44 +03:00
|
|
|
setter = clasp->setProperty;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2002-02-23 06:49:27 +03:00
|
|
|
/* Get obj's own scope if it has one, or create a new one for obj. */
|
|
|
|
scope = js_GetMutableScope(cx, obj);
|
1998-03-28 05:44:41 +03:00
|
|
|
if (!scope)
|
2001-02-07 02:19:44 +03:00
|
|
|
goto bad;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2002-02-23 06:49:27 +03:00
|
|
|
/* 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;
|
|
|
|
|
2005-03-29 23:24:33 +04:00
|
|
|
/* Store value before calling addProperty, in case the latter GC's. */
|
|
|
|
if (SPROP_HAS_VALID_SLOT(sprop, scope))
|
|
|
|
LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value);
|
|
|
|
|
2002-02-23 06:49:27 +03:00
|
|
|
/* XXXbe called with lock held */
|
2005-07-20 01:13:19 +04:00
|
|
|
ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value,
|
|
|
|
js_RemoveScopeProperty(cx, scope, id);
|
|
|
|
goto bad);
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2003-06-08 01:08:45 +04:00
|
|
|
#if JS_HAS_GETTER_SETTER
|
2002-02-23 06:49:27 +03:00
|
|
|
out:
|
2003-06-08 01:08:45 +04:00
|
|
|
#endif
|
2003-07-27 02:37:11 +04:00
|
|
|
PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
|
2002-02-23 06:49:27 +03:00
|
|
|
if (propp)
|
2001-02-07 02:19:44 +03:00
|
|
|
*propp = (JSProperty *) sprop;
|
2002-02-23 06:49:27 +03:00
|
|
|
else
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
1998-04-24 04:31:11 +04:00
|
|
|
return JS_TRUE;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
bad:
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
return JS_FALSE;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
2004-07-30 00:44:20 +04:00
|
|
|
/*
|
|
|
|
* Given pc pointing after a property accessing bytecode, return true if the
|
2005-12-08 20:59:45 +03:00
|
|
|
* access is "object-detecting" in the sense used by web scripts, e.g., when
|
2004-07-30 00:44:20 +04:00
|
|
|
* checking whether document.all is defined.
|
|
|
|
*/
|
|
|
|
static JSBool
|
|
|
|
Detecting(JSContext *cx, jsbytecode *pc)
|
|
|
|
{
|
|
|
|
JSScript *script;
|
|
|
|
jsbytecode *endpc;
|
|
|
|
JSOp op;
|
|
|
|
JSAtom *atom;
|
|
|
|
|
2005-02-15 22:20:00 +03:00
|
|
|
if (!cx->fp)
|
|
|
|
return JS_FALSE;
|
2004-07-30 00:44:20 +04:00
|
|
|
script = cx->fp->script;
|
|
|
|
for (endpc = script->code + script->length; pc < endpc; pc++) {
|
|
|
|
/* General case: a branch or equality op follows the access. */
|
|
|
|
op = (JSOp) *pc;
|
|
|
|
if (js_CodeSpec[op].format & JOF_DETECTING)
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Special case #1: handle (document.all == null). Don't sweat about
|
|
|
|
* JS1.2's revision of the equality operators here.
|
|
|
|
*/
|
|
|
|
if (op == JSOP_NULL) {
|
|
|
|
if (++pc < endpc)
|
|
|
|
return *pc == JSOP_EQ || *pc == JSOP_NE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Special case #2: handle (document.all == undefined). Don't worry
|
|
|
|
* about someone redefining undefined, which was added by Edition 3,
|
2005-12-08 20:59:45 +03:00
|
|
|
* so is read/write for backward compatibility.
|
2004-07-30 00:44:20 +04:00
|
|
|
*/
|
|
|
|
if (op == JSOP_NAME) {
|
|
|
|
atom = GET_ATOM(cx, script, pc);
|
|
|
|
if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] &&
|
|
|
|
(pc += js_CodeSpec[op].length) < endpc) {
|
|
|
|
op = (JSOp) *pc;
|
|
|
|
return op == JSOP_EQ || op == JSOP_NE ||
|
2006-12-20 11:12:18 +03:00
|
|
|
op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
|
2004-07-30 00:44:20 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* At this point, anything but grouping means we're not detecting. */
|
|
|
|
if (op != JSOP_GROUP)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
|
2004-10-05 14:19:07 +04:00
|
|
|
JS_FRIEND_API(JSBool)
|
|
|
|
js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
|
|
|
|
JSProperty **propp)
|
|
|
|
{
|
|
|
|
return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp);
|
|
|
|
}
|
|
|
|
|
2004-09-02 00:51:39 +04:00
|
|
|
JSBool
|
|
|
|
js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
2004-10-05 14:19:07 +04:00
|
|
|
JSObject **objp, JSProperty **propp)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
2003-03-12 23:29:16 +03:00
|
|
|
JSObject *start, *obj2, *proto;
|
2000-04-15 06:01:02 +04:00
|
|
|
JSScope *scope;
|
2002-02-23 06:49:27 +03:00
|
|
|
JSScopeProperty *sprop;
|
1998-03-28 05:44:41 +03:00
|
|
|
JSClass *clasp;
|
|
|
|
JSResolveOp resolve;
|
2001-05-17 05:43:58 +04:00
|
|
|
JSResolvingKey key;
|
2003-06-05 22:49:13 +04:00
|
|
|
JSResolvingEntry *entry;
|
2001-05-25 07:05:38 +04:00
|
|
|
uint32 generation;
|
1998-03-28 05:44:41 +03:00
|
|
|
JSNewResolveOp newresolve;
|
2004-06-17 01:15:35 +04:00
|
|
|
jsbytecode *pc;
|
|
|
|
const JSCodeSpec *cs;
|
1998-04-24 04:31:11 +04:00
|
|
|
uint32 format;
|
2001-05-17 05:43:58 +04:00
|
|
|
JSBool ok;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1999-07-01 06:24:32 +04:00
|
|
|
/*
|
2000-06-27 06:37:25 +04:00
|
|
|
* Handle old bug that took empty string as zero index. Also convert
|
|
|
|
* string indices to integers if appropriate.
|
1999-07-01 06:24:32 +04:00
|
|
|
*/
|
2005-05-15 22:24:31 +04:00
|
|
|
CHECK_FOR_STRING_INDEX(id);
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2006-12-24 14:31:37 +03:00
|
|
|
JS_COUNT_OPERATION(cx, JSOW_LOOKUP_PROPERTY);
|
1998-03-28 05:44:41 +03:00
|
|
|
/* Search scopes starting with obj and following the prototype link. */
|
2003-03-12 23:29:16 +03:00
|
|
|
start = obj;
|
1998-04-24 04:31:11 +04:00
|
|
|
for (;;) {
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_LOCK_OBJ(cx, obj);
|
|
|
|
scope = OBJ_SCOPE(obj);
|
2000-04-15 06:01:02 +04:00
|
|
|
if (scope->object == obj) {
|
2002-02-23 06:49:27 +03:00
|
|
|
sprop = SCOPE_GET_PROPERTY(scope, id);
|
2000-04-15 06:01:02 +04:00
|
|
|
} else {
|
|
|
|
/* Shared prototype scope: try resolve before lookup. */
|
2002-02-23 06:49:27 +03:00
|
|
|
sprop = NULL;
|
2000-04-15 06:01:02 +04:00
|
|
|
}
|
2001-05-17 05:43:58 +04:00
|
|
|
|
|
|
|
/* Try obj's class resolve hook if id was not found in obj's scope. */
|
2002-02-23 06:49:27 +03:00
|
|
|
if (!sprop) {
|
2001-02-07 02:19:44 +03:00
|
|
|
clasp = LOCKED_OBJ_GET_CLASS(obj);
|
|
|
|
resolve = clasp->resolve;
|
|
|
|
if (resolve != JS_ResolveStub) {
|
2001-05-17 05:43:58 +04:00
|
|
|
/* Avoid recursion on (obj, id) already being resolved on cx. */
|
|
|
|
key.obj = obj;
|
|
|
|
key.id = id;
|
2001-09-14 07:37:15 +04:00
|
|
|
|
|
|
|
/*
|
2003-06-12 03:43:56 +04:00
|
|
|
* Once we have successfully added an entry for (obj, key) to
|
|
|
|
* cx->resolvingTable, control must go through cleanup: before
|
2003-06-05 22:49:13 +04:00
|
|
|
* returning. But note that JS_DHASH_ADD may find an existing
|
|
|
|
* entry, in which case we bail to suppress runaway recursion.
|
2001-09-14 07:37:15 +04:00
|
|
|
*/
|
2004-08-19 21:57:36 +04:00
|
|
|
if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) {
|
2001-05-17 05:43:58 +04:00
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
2003-08-04 01:59:50 +04:00
|
|
|
if (!entry) {
|
2006-04-19 04:22:43 +04:00
|
|
|
/* Already resolving id in obj -- suppress recursion. */
|
2003-06-05 22:49:13 +04:00
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
goto out;
|
|
|
|
}
|
2003-08-04 01:59:50 +04:00
|
|
|
generation = cx->resolvingTable->generation;
|
2001-05-17 05:43:58 +04:00
|
|
|
|
|
|
|
/* Null *propp here so we can test it at cleanup: safely. */
|
|
|
|
*propp = NULL;
|
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
if (clasp->flags & JSCLASS_NEW_RESOLVE) {
|
|
|
|
newresolve = (JSNewResolveOp)resolve;
|
2004-11-17 10:43:01 +03:00
|
|
|
if (!(flags & JSRESOLVE_CLASSNAME) &&
|
|
|
|
cx->fp &&
|
|
|
|
(pc = cx->fp->pc)) {
|
2004-06-17 01:15:35 +04:00
|
|
|
cs = &js_CodeSpec[*pc];
|
|
|
|
format = cs->format;
|
2001-02-07 02:19:44 +03:00
|
|
|
if ((format & JOF_MODEMASK) != JOF_NAME)
|
|
|
|
flags |= JSRESOLVE_QUALIFIED;
|
Fix for bug 99663 (for loop resolves properties of the object being enumerated
with JSRESOLVE_ASSIGNING, wrongly), plus a few miscellaneous bugfixes.
- Combine the JSStackFrame members constructing, special, overrides, and
reserved into a uint32 flags member.
- Separate JOF_ASSIGNING from the JOF_SET bytecode format flag, and impute
JSRESOLVE_ASSIGNING from the presence of JOF_ASSIGNING among the current
opcode's format flags. To handle the for-in loop opcodes, which do more
than simply assign -- in particular, they do property lookups whose resolve
hook outcalls should not be flagged with JSRESOLVE_ASSIGNING -- a new frame
flag, JSFRAME_ASSIGNING, has been added.
- Fix interpreter version selection to respect JS_SetVersion, whose effect on
cx->version is "sticky".
- Fix js_DecompileValueGenerator to deal with JSOP_ENUMELEM -- it never had,
as this testcase shows (it crashes without this patch):
version(120);
eval("function fe(s) { for (it[s] in this); }");
try { fe('rdonly'); } catch (e) { print(e); }
2001-10-03 10:39:30 +04:00
|
|
|
if ((format & JOF_ASSIGNING) ||
|
|
|
|
(cx->fp->flags & JSFRAME_ASSIGNING)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
flags |= JSRESOLVE_ASSIGNING;
|
2004-06-17 01:15:35 +04:00
|
|
|
} else {
|
|
|
|
pc += cs->length;
|
2004-07-30 00:44:20 +04:00
|
|
|
if (Detecting(cx, pc))
|
2004-06-17 01:15:35 +04:00
|
|
|
flags |= JSRESOLVE_DETECTING;
|
Fix for bug 99663 (for loop resolves properties of the object being enumerated
with JSRESOLVE_ASSIGNING, wrongly), plus a few miscellaneous bugfixes.
- Combine the JSStackFrame members constructing, special, overrides, and
reserved into a uint32 flags member.
- Separate JOF_ASSIGNING from the JOF_SET bytecode format flag, and impute
JSRESOLVE_ASSIGNING from the presence of JOF_ASSIGNING among the current
opcode's format flags. To handle the for-in loop opcodes, which do more
than simply assign -- in particular, they do property lookups whose resolve
hook outcalls should not be flagged with JSRESOLVE_ASSIGNING -- a new frame
flag, JSFRAME_ASSIGNING, has been added.
- Fix interpreter version selection to respect JS_SetVersion, whose effect on
cx->version is "sticky".
- Fix js_DecompileValueGenerator to deal with JSOP_ENUMELEM -- it never had,
as this testcase shows (it crashes without this patch):
version(120);
eval("function fe(s) { for (it[s] in this); }");
try { fe('rdonly'); } catch (e) { print(e); }
2001-10-03 10:39:30 +04:00
|
|
|
}
|
2004-09-02 00:51:39 +04:00
|
|
|
if (format & JOF_DECLARING)
|
|
|
|
flags |= JSRESOLVE_DECLARING;
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
2003-03-12 23:29:16 +03:00
|
|
|
obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START)
|
|
|
|
? start
|
|
|
|
: NULL;
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
2003-08-04 01:59:50 +04:00
|
|
|
|
|
|
|
/* Protect id and all atoms from a GC nested in resolve. */
|
|
|
|
JS_KEEP_ATOMS(cx->runtime);
|
2002-02-23 06:49:27 +03:00
|
|
|
ok = newresolve(cx, obj, ID_TO_VALUE(id), flags, &obj2);
|
2003-08-04 01:59:50 +04:00
|
|
|
JS_UNKEEP_ATOMS(cx->runtime);
|
2001-05-17 05:43:58 +04:00
|
|
|
if (!ok)
|
|
|
|
goto cleanup;
|
2003-08-04 01:59:50 +04:00
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_LOCK_OBJ(cx, obj);
|
|
|
|
if (obj2) {
|
2001-05-17 05:43:58 +04:00
|
|
|
/* Resolved: juggle locks and lookup id again. */
|
|
|
|
if (obj2 != obj) {
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
JS_LOCK_OBJ(cx, obj2);
|
|
|
|
}
|
2001-02-07 02:19:44 +03:00
|
|
|
scope = OBJ_SCOPE(obj2);
|
2001-05-17 05:43:58 +04:00
|
|
|
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 {
|
2001-03-11 05:35:10 +03:00
|
|
|
/*
|
2001-05-17 05:43:58 +04:00
|
|
|
* 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.
|
2001-03-11 05:35:10 +03:00
|
|
|
*/
|
2001-05-17 05:43:58 +04:00
|
|
|
if (scope->object == obj2)
|
2002-02-23 06:49:27 +03:00
|
|
|
sprop = SCOPE_GET_PROPERTY(scope, id);
|
2001-05-17 05:43:58 +04:00
|
|
|
}
|
2005-03-25 01:28:09 +03:00
|
|
|
if (sprop) {
|
|
|
|
JS_ASSERT(obj2 == scope->object);
|
|
|
|
obj = obj2;
|
|
|
|
} else if (obj2 != obj) {
|
2001-05-17 05:43:58 +04:00
|
|
|
JS_UNLOCK_OBJ(cx, obj2);
|
|
|
|
JS_LOCK_OBJ(cx, obj);
|
2001-03-11 05:35:10 +03:00
|
|
|
}
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
|
|
|
} else {
|
2001-05-17 05:43:58 +04:00
|
|
|
/*
|
|
|
|
* Old resolve always requires id re-lookup if obj owns
|
|
|
|
* its scope after resolve returns.
|
|
|
|
*/
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
2002-02-23 06:49:27 +03:00
|
|
|
ok = resolve(cx, obj, ID_TO_VALUE(id));
|
2001-05-17 05:43:58 +04:00
|
|
|
if (!ok)
|
|
|
|
goto cleanup;
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_LOCK_OBJ(cx, obj);
|
|
|
|
scope = OBJ_SCOPE(obj);
|
2001-05-17 05:43:58 +04:00
|
|
|
JS_ASSERT(MAP_IS_NATIVE(&scope->map));
|
|
|
|
if (scope->object == obj)
|
2002-02-23 06:49:27 +03:00
|
|
|
sprop = SCOPE_GET_PROPERTY(scope, id);
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
2001-05-17 05:43:58 +04:00
|
|
|
|
|
|
|
cleanup:
|
2004-08-19 21:57:36 +04:00
|
|
|
js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
|
2001-05-17 05:43:58 +04:00
|
|
|
if (!ok || *propp)
|
|
|
|
return ok;
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
|
|
|
}
|
2001-05-17 05:43:58 +04:00
|
|
|
|
2005-05-17 05:06:35 +04:00
|
|
|
if (sprop) {
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_ASSERT(OBJ_SCOPE(obj) == scope);
|
2001-04-14 11:34:58 +04:00
|
|
|
*objp = scope->object; /* XXXbe hide in jsscope.[ch] */
|
2002-02-23 06:49:27 +03:00
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
*propp = (JSProperty *) sprop;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
2002-02-23 06:49:27 +03:00
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
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;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
2001-05-17 05:43:58 +04:00
|
|
|
|
|
|
|
out:
|
1998-04-24 04:31:11 +04:00
|
|
|
*objp = NULL;
|
1998-03-28 05:44:41 +03:00
|
|
|
*propp = NULL;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
JS_FRIEND_API(JSBool)
|
|
|
|
js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
|
2001-02-07 02:19:44 +03:00
|
|
|
JSProperty **propp)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
|
|
|
JSRuntime *rt;
|
r=brendan@mozilla.org
Bug #14462, lot's of discussin there about these changes, but here's
Brendan's description :
In order, changes in the patch are:
- Rename JSSLOT_ITR_STATE to be JSSLOT_ITER_STATE (avoid cybercrud abbreviation
as cbrcrd, no more six-char id limits!).
- Property cache tests must occur with the object's scope-lock held, to close a
race with delete (js_DestroyProperty, always called with the property's scope
locked). Once the cache has been hit, and before the lock is released, the
property's refcount must be bumped. This requires re-acquisition of the lock
and js_DropScopeProperty afterward.
- Reworked js_FindProperty to use a do-while loop, as cx->fp->scopeChain must be
non-null. This avoids a gratuitous lastobj init done to "Suppress gcc warning"
in the old revision.
- Akin to the property cache hit cases in jsinterp.c and jsobj.c's
js_FindProperty, code to hold and drop the scope-property by its refcount that
was #ifdef JS_THREADSAFE must be unconditional, now that user-defined getters
and setters may delete the property id they're getting or setting.
- Fixed overlong continuation line in jsobj.h.
/be
2000-02-08 04:24:53 +03:00
|
|
|
JSObject *obj, *pobj, *lastobj;
|
2002-02-23 06:49:27 +03:00
|
|
|
JSScopeProperty *sprop;
|
1998-03-28 05:44:41 +03:00
|
|
|
JSProperty *prop;
|
|
|
|
|
|
|
|
rt = cx->runtime;
|
r=brendan@mozilla.org
Bug #14462, lot's of discussin there about these changes, but here's
Brendan's description :
In order, changes in the patch are:
- Rename JSSLOT_ITR_STATE to be JSSLOT_ITER_STATE (avoid cybercrud abbreviation
as cbrcrd, no more six-char id limits!).
- Property cache tests must occur with the object's scope-lock held, to close a
race with delete (js_DestroyProperty, always called with the property's scope
locked). Once the cache has been hit, and before the lock is released, the
property's refcount must be bumped. This requires re-acquisition of the lock
and js_DropScopeProperty afterward.
- Reworked js_FindProperty to use a do-while loop, as cx->fp->scopeChain must be
non-null. This avoids a gratuitous lastobj init done to "Suppress gcc warning"
in the old revision.
- Akin to the property cache hit cases in jsinterp.c and jsobj.c's
js_FindProperty, code to hold and drop the scope-property by its refcount that
was #ifdef JS_THREADSAFE must be unconditional, now that user-defined getters
and setters may delete the property id they're getting or setting.
- Fixed overlong continuation line in jsobj.h.
/be
2000-02-08 04:24:53 +03:00
|
|
|
obj = cx->fp->scopeChain;
|
|
|
|
do {
|
2001-02-07 02:19:44 +03:00
|
|
|
/* Try the property cache and return immediately on cache hit. */
|
2002-02-23 06:49:27 +03:00
|
|
|
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);
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
/* If cache miss, take the slow path. */
|
2000-10-09 18:38:21 +04:00
|
|
|
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
|
|
|
|
return JS_FALSE;
|
|
|
|
if (prop) {
|
2002-02-23 06:49:27 +03:00
|
|
|
if (OBJ_IS_NATIVE(pobj)) {
|
|
|
|
sprop = (JSScopeProperty *) prop;
|
|
|
|
PROPERTY_CACHE_FILL(&rt->propertyCache, pobj, id, sprop);
|
|
|
|
}
|
2000-10-09 18:38:21 +04:00
|
|
|
*objp = obj;
|
|
|
|
*pobjp = pobj;
|
|
|
|
*propp = prop;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
2001-02-07 02:19:44 +03:00
|
|
|
lastobj = obj;
|
r=brendan@mozilla.org
Bug #14462, lot's of discussin there about these changes, but here's
Brendan's description :
In order, changes in the patch are:
- Rename JSSLOT_ITR_STATE to be JSSLOT_ITER_STATE (avoid cybercrud abbreviation
as cbrcrd, no more six-char id limits!).
- Property cache tests must occur with the object's scope-lock held, to close a
race with delete (js_DestroyProperty, always called with the property's scope
locked). Once the cache has been hit, and before the lock is released, the
property's refcount must be bumped. This requires re-acquisition of the lock
and js_DropScopeProperty afterward.
- Reworked js_FindProperty to use a do-while loop, as cx->fp->scopeChain must be
non-null. This avoids a gratuitous lastobj init done to "Suppress gcc warning"
in the old revision.
- Akin to the property cache hit cases in jsinterp.c and jsobj.c's
js_FindProperty, code to hold and drop the scope-property by its refcount that
was #ifdef JS_THREADSAFE must be unconditional, now that user-defined getters
and setters may delete the property id they're getting or setting.
- Fixed overlong continuation line in jsobj.h.
/be
2000-02-08 04:24:53 +03:00
|
|
|
} while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);
|
2000-04-04 12:17:05 +04:00
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
*objp = lastobj;
|
1998-04-24 04:31:11 +04:00
|
|
|
*pobjp = NULL;
|
1998-03-28 05:44:41 +03:00
|
|
|
*propp = NULL;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2003-05-15 00:57:47 +04:00
|
|
|
JSObject *
|
|
|
|
js_FindIdentifierBase(JSContext *cx, jsid id)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
2003-05-15 00:57:47 +04:00
|
|
|
JSObject *obj, *pobj;
|
1998-03-28 05:44:41 +03:00
|
|
|
JSProperty *prop;
|
|
|
|
|
|
|
|
/*
|
2003-05-15 00:57:47 +04:00
|
|
|
* Look for id's property along the "with" statement chain and the
|
|
|
|
* statically-linked scope chain.
|
1998-03-28 05:44:41 +03:00
|
|
|
*/
|
2003-05-15 00:57:47 +04:00
|
|
|
if (!js_FindProperty(cx, id, &obj, &pobj, &prop))
|
|
|
|
return NULL;
|
|
|
|
if (prop) {
|
|
|
|
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
|
|
|
return obj;
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Use the top-level scope from the scope chain, which won't end in the
|
1998-04-24 04:31:11 +04:00
|
|
|
* same scope as cx->globalObject for cross-context function calls.
|
1998-03-28 05:44:41 +03:00
|
|
|
*/
|
1998-10-14 14:22:38 +04:00
|
|
|
JS_ASSERT(obj);
|
1998-03-28 05:44:41 +03:00
|
|
|
|
|
|
|
/*
|
2003-05-15 00:57:47 +04:00
|
|
|
* Property not found. Give a strict warning if binding an undeclared
|
|
|
|
* top-level variable.
|
1998-03-28 05:44:41 +03:00
|
|
|
*/
|
1999-11-12 09:03:40 +03:00
|
|
|
if (JS_HAS_STRICT_OPTION(cx)) {
|
2002-02-23 06:49:27 +03:00
|
|
|
JSString *str = JSVAL_TO_STRING(ID_TO_VALUE(id));
|
2000-01-18 14:06:05 +03:00
|
|
|
if (!JS_ReportErrorFlagsAndNumber(cx,
|
|
|
|
JSREPORT_WARNING | JSREPORT_STRICT,
|
|
|
|
js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_UNDECLARED_VAR,
|
|
|
|
JS_GetStringBytes(str))) {
|
2003-05-15 00:57:47 +04:00
|
|
|
return NULL;
|
2000-01-18 14:06:05 +03:00
|
|
|
}
|
1999-11-12 09:03:40 +03:00
|
|
|
}
|
2003-05-15 00:57:47 +04:00
|
|
|
return obj;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
2007-01-05 00:15:09 +03:00
|
|
|
JSBool
|
|
|
|
js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj,
|
|
|
|
JSScopeProperty *sprop, jsval *vp)
|
|
|
|
{
|
|
|
|
JSScope *scope;
|
|
|
|
uint32 slot;
|
|
|
|
int32 sample;
|
|
|
|
JSTempValueRooter tvr;
|
|
|
|
JSBool ok;
|
|
|
|
|
|
|
|
JS_ASSERT(OBJ_IS_NATIVE(pobj));
|
|
|
|
JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj));
|
|
|
|
scope = OBJ_SCOPE(pobj);
|
|
|
|
JS_ASSERT(scope->object == pobj);
|
|
|
|
|
|
|
|
slot = sprop->slot;
|
|
|
|
if (slot != SPROP_INVALID_SLOT) {
|
|
|
|
*vp = LOCKED_OBJ_GET_SLOT(pobj, slot);
|
|
|
|
|
|
|
|
/* If sprop has a stub getter, we're done. */
|
|
|
|
if (SPROP_HAS_STUB_GETTER(sprop))
|
|
|
|
return JS_TRUE;
|
|
|
|
} else {
|
|
|
|
JS_ASSERT(!SPROP_HAS_STUB_GETTER(sprop));
|
|
|
|
*vp = JSVAL_VOID;
|
|
|
|
}
|
|
|
|
|
|
|
|
sample = cx->runtime->propertyRemovals;
|
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
|
|
|
JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);
|
|
|
|
ok = SPROP_GET(cx, sprop, obj, pobj, vp);
|
|
|
|
JS_POP_TEMP_ROOT(cx, &tvr);
|
|
|
|
if (!ok)
|
|
|
|
return JS_FALSE;
|
|
|
|
|
|
|
|
JS_LOCK_SCOPE(cx, scope);
|
|
|
|
JS_ASSERT(scope->object == pobj);
|
|
|
|
if (SLOT_IN_SCOPE(slot, scope) &&
|
|
|
|
(JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
|
|
|
|
SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) {
|
|
|
|
LOCKED_OBJ_SET_SLOT(pobj, slot, *vp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
|
|
|
js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp)
|
|
|
|
{
|
|
|
|
JSScope *scope;
|
|
|
|
uint32 slot;
|
|
|
|
jsval pval;
|
|
|
|
int32 sample;
|
|
|
|
JSTempValueRooter tvr;
|
|
|
|
JSBool ok;
|
|
|
|
|
|
|
|
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
|
|
|
JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj));
|
|
|
|
scope = OBJ_SCOPE(obj);
|
|
|
|
JS_ASSERT(scope->object == obj);
|
|
|
|
|
|
|
|
slot = sprop->slot;
|
|
|
|
if (slot != SPROP_INVALID_SLOT) {
|
|
|
|
pval = LOCKED_OBJ_GET_SLOT(obj, slot);
|
|
|
|
|
|
|
|
/* If sprop has a stub setter, keep scope locked and just store *vp. */
|
|
|
|
if (SPROP_HAS_STUB_SETTER(sprop))
|
|
|
|
goto set_slot;
|
|
|
|
} else {
|
|
|
|
JS_ASSERT(!SPROP_HAS_STUB_GETTER(sprop));
|
|
|
|
pval = JSVAL_VOID;
|
|
|
|
}
|
|
|
|
|
|
|
|
sample = cx->runtime->propertyRemovals;
|
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
|
|
|
JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);
|
|
|
|
ok = SPROP_SET(cx, sprop, obj, obj, vp);
|
|
|
|
JS_POP_TEMP_ROOT(cx, &tvr);
|
|
|
|
if (!ok)
|
|
|
|
return JS_FALSE;
|
|
|
|
|
|
|
|
JS_LOCK_SCOPE(cx, scope);
|
|
|
|
JS_ASSERT(scope->object == obj);
|
|
|
|
if (SLOT_IN_SCOPE(slot, scope) &&
|
|
|
|
(JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
|
|
|
|
SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) {
|
|
|
|
set_slot:
|
|
|
|
GC_POKE(cx, pval);
|
|
|
|
LOCKED_OBJ_SET_SLOT(obj, slot, *vp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
JSBool
|
|
|
|
js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
|
|
|
JSObject *obj2;
|
2004-04-13 05:25:17 +04:00
|
|
|
JSProperty *prop;
|
|
|
|
JSScopeProperty *sprop;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
/*
|
|
|
|
* Handle old bug that took empty string as zero index. Also convert
|
|
|
|
* string indices to integers if appropriate.
|
|
|
|
*/
|
2005-05-15 22:24:31 +04:00
|
|
|
CHECK_FOR_STRING_INDEX(id);
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
|
2006-12-24 14:31:37 +03:00
|
|
|
JS_COUNT_OPERATION(cx, JSOW_GET_PROPERTY);
|
2004-04-13 05:25:17 +04:00
|
|
|
if (!js_LookupProperty(cx, obj, id, &obj2, &prop))
|
2000-04-04 12:17:05 +04:00
|
|
|
return JS_FALSE;
|
2004-04-13 05:25:17 +04:00
|
|
|
if (!prop) {
|
2006-01-31 23:53:30 +03:00
|
|
|
jsbytecode *pc;
|
2000-09-19 06:24:11 +04:00
|
|
|
|
2006-04-27 01:33:01 +04:00
|
|
|
*vp = JSVAL_VOID;
|
2000-09-19 06:24:11 +04:00
|
|
|
|
2002-02-23 06:49:27 +03:00
|
|
|
if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp))
|
2000-09-19 06:24:11 +04:00
|
|
|
return JS_FALSE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Give a strict warning if foo.bar is evaluated by a script for an
|
|
|
|
* object foo with no property named 'bar'.
|
|
|
|
*/
|
2006-04-27 01:33:01 +04:00
|
|
|
if (JSVAL_IS_VOID(*vp) && cx->fp && (pc = cx->fp->pc)) {
|
2006-01-31 23:53:30 +03:00
|
|
|
JSOp op;
|
|
|
|
uintN flags;
|
2000-09-19 06:24:11 +04:00
|
|
|
JSString *str;
|
|
|
|
|
2006-01-31 23:53:30 +03:00
|
|
|
op = *pc;
|
|
|
|
if (op == JSOP_GETXPROP || op == JSOP_GETXELEM) {
|
|
|
|
flags = JSREPORT_ERROR;
|
|
|
|
} else {
|
|
|
|
if (!JS_HAS_STRICT_OPTION(cx) ||
|
|
|
|
(op != JSOP_GETPROP && op != JSOP_GETELEM)) {
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2006-10-04 02:47:23 +04:00
|
|
|
/*
|
|
|
|
* XXX do not warn about missing __iterator__ as the function
|
|
|
|
* may be called from JS_GetMethodById. See bug 355145.
|
|
|
|
*/
|
|
|
|
if (id == ATOM_TO_JSID(cx->runtime->atomState.iteratorAtom))
|
|
|
|
return JS_TRUE;
|
|
|
|
|
2006-01-31 23:53:30 +03:00
|
|
|
/* Kludge to allow (typeof foo == "undefined") tests. */
|
|
|
|
JS_ASSERT(cx->fp->script);
|
|
|
|
pc += js_CodeSpec[op].length;
|
|
|
|
if (Detecting(cx, pc))
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
flags = JSREPORT_WARNING | JSREPORT_STRICT;
|
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2000-09-19 06:24:11 +04:00
|
|
|
/* Ok, bad undefined property reference: whine about it. */
|
2004-04-14 06:36:37 +04:00
|
|
|
str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
|
|
|
|
ID_TO_VALUE(id), NULL);
|
2000-09-19 06:24:11 +04:00
|
|
|
if (!str ||
|
2006-01-31 23:53:30 +03:00
|
|
|
!JS_ReportErrorFlagsAndNumber(cx, flags,
|
2000-09-19 06:24:11 +04:00
|
|
|
js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_UNDEFINED_PROP,
|
|
|
|
JS_GetStringBytes(str))) {
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return JS_TRUE;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
if (!OBJ_IS_NATIVE(obj2)) {
|
2004-04-13 05:25:17 +04:00
|
|
|
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
2000-04-04 12:17:05 +04:00
|
|
|
return OBJ_GET_PROPERTY(cx, obj2, id, vp);
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
|
2004-04-13 05:25:17 +04:00
|
|
|
sprop = (JSScopeProperty *) prop;
|
2007-01-05 00:15:09 +03:00
|
|
|
if (!js_NativeGet(cx, obj, obj2, sprop, vp))
|
2000-04-04 12:17:05 +04:00
|
|
|
return JS_FALSE;
|
2002-02-23 06:49:27 +03:00
|
|
|
|
2007-01-05 00:15:09 +03:00
|
|
|
PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj2, id, sprop);
|
|
|
|
JS_UNLOCK_OBJ(cx, obj2);
|
1998-04-24 04:31:11 +04:00
|
|
|
return JS_TRUE;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
JSBool
|
|
|
|
js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
2001-05-17 05:43:58 +04:00
|
|
|
JSObject *pobj;
|
2004-04-13 05:25:17 +04:00
|
|
|
JSProperty *prop;
|
2001-05-17 05:43:58 +04:00
|
|
|
JSScopeProperty *sprop;
|
|
|
|
JSScope *scope;
|
2002-02-23 06:49:27 +03:00
|
|
|
uintN attrs, flags;
|
|
|
|
intN shortid;
|
2003-04-14 08:14:57 +04:00
|
|
|
JSClass *clasp;
|
2000-06-27 06:37:25 +04:00
|
|
|
JSPropertyOp getter, setter;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1999-07-01 06:24:32 +04:00
|
|
|
/*
|
2000-06-27 06:37:25 +04:00
|
|
|
* Handle old bug that took empty string as zero index. Also convert
|
|
|
|
* string indices to integers if appropriate.
|
1999-07-01 06:24:32 +04:00
|
|
|
*/
|
2005-05-15 22:24:31 +04:00
|
|
|
CHECK_FOR_STRING_INDEX(id);
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2006-12-24 14:31:37 +03:00
|
|
|
JS_COUNT_OPERATION(cx, JSOW_SET_PROPERTY);
|
2004-04-13 05:25:17 +04:00
|
|
|
if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
|
Landing the XPCDOM_20010329_BRANCH branch, changes mostly done by jband@netscape.com and jst@netscape.com, also some changes done by shaver@mozilla.org, peterv@netscape.com and markh@activestate.com. r= and sr= by vidur@netscape.com, jband@netscape.com, jst@netscpae.com, danm@netscape.com, hyatt@netscape.com, shaver@mozilla.org, dbradley@netscape.com, rpotts@netscape.com.
2001-05-08 21:42:36 +04:00
|
|
|
return JS_FALSE;
|
2001-09-04 02:29:12 +04:00
|
|
|
|
2004-04-13 05:25:17 +04:00
|
|
|
if (prop && !OBJ_IS_NATIVE(pobj)) {
|
|
|
|
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
|
|
|
prop = NULL;
|
2001-09-04 02:29:12 +04:00
|
|
|
}
|
2004-04-13 05:25:17 +04:00
|
|
|
sprop = (JSScopeProperty *) prop;
|
2001-09-04 02:29:12 +04:00
|
|
|
|
2001-05-17 05:43:58 +04:00
|
|
|
/*
|
|
|
|
* 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
|
2002-02-23 06:49:27 +03:00
|
|
|
* 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).
|
2001-05-17 05:43:58 +04:00
|
|
|
*/
|
2002-02-23 06:49:27 +03:00
|
|
|
attrs = JSPROP_ENUMERATE;
|
|
|
|
flags = 0;
|
|
|
|
shortid = 0;
|
2003-04-14 08:14:57 +04:00
|
|
|
clasp = OBJ_GET_CLASS(cx, obj);
|
2002-02-23 06:49:27 +03:00
|
|
|
getter = clasp->getProperty;
|
|
|
|
setter = clasp->setProperty;
|
2003-04-14 08:14:57 +04:00
|
|
|
|
2002-02-23 06:49:27 +03:00
|
|
|
if (sprop) {
|
2003-03-14 08:24:58 +03:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
2001-05-17 05:43:58 +04:00
|
|
|
attrs = sprop->attrs;
|
2003-11-11 04:28:17 +03:00
|
|
|
if ((attrs & JSPROP_READONLY) ||
|
|
|
|
(SCOPE_IS_SEALED(scope) && pobj == obj)) {
|
2003-04-14 07:26:47 +04:00
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
2005-12-02 00:05:12 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Here, we'll either return true or goto read_only_error, which
|
|
|
|
* reports a strict warning or throws an error. So we redefine
|
|
|
|
* the |flags| local variable to be JSREPORT_* flags to pass to
|
|
|
|
* JS_ReportErrorFlagsAndNumberUC at label read_only_error. We
|
|
|
|
* must likewise re-task flags further below for the other 'goto
|
|
|
|
* read_only_error;' case.
|
|
|
|
*/
|
|
|
|
flags = JSREPORT_ERROR;
|
2006-09-20 14:09:28 +04:00
|
|
|
if (attrs & JSPROP_READONLY) {
|
2005-12-02 00:05:12 +03:00
|
|
|
if (!JS_HAS_STRICT_OPTION(cx)) {
|
|
|
|
/* Just return true per ECMA if not in strict mode. */
|
|
|
|
return JS_TRUE;
|
2005-12-01 05:26:35 +03:00
|
|
|
}
|
2005-12-02 00:05:12 +03:00
|
|
|
|
|
|
|
/* Strict mode: report a read-only strict warning. */
|
|
|
|
flags = JSREPORT_STRICT | JSREPORT_WARNING;
|
2005-12-01 05:26:35 +03:00
|
|
|
}
|
2003-04-12 01:02:29 +04:00
|
|
|
goto read_only_error;
|
2001-05-17 05:43:58 +04:00
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2001-05-17 05:43:58 +04:00
|
|
|
if (pobj != obj) {
|
2003-04-14 03:33:34 +04:00
|
|
|
/*
|
|
|
|
* We found id in a prototype object: prepare to share or shadow.
|
2003-04-14 07:32:55 +04:00
|
|
|
* NB: Thanks to the immutable, garbage-collected property tree
|
2003-04-14 08:14:57 +04:00
|
|
|
* maintained by jsscope.c in cx->runtime, we needn't worry about
|
|
|
|
* sprop going away behind our back after we've unlocked scope.
|
2003-04-14 03:33:34 +04:00
|
|
|
*/
|
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
2000-06-27 06:37:25 +04:00
|
|
|
|
2003-04-14 03:33:34 +04:00
|
|
|
/* Don't clone a shared prototype property. */
|
2007-01-05 00:15:09 +03:00
|
|
|
if (attrs & JSPROP_SHARED) {
|
|
|
|
JS_ASSERT(!SPROP_HAS_STUB_SETTER(sprop));
|
2002-02-23 06:49:27 +03:00
|
|
|
return SPROP_SET(cx, sprop, obj, pobj, vp);
|
2007-01-05 00:15:09 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2003-04-14 03:33:34 +04:00
|
|
|
/* Restore attrs to the ECMA default for new properties. */
|
|
|
|
attrs = JSPROP_ENUMERATE;
|
2002-03-09 01:46:13 +03:00
|
|
|
|
2003-04-14 03:33:34 +04:00
|
|
|
/*
|
2003-05-02 21:36:58 +04:00
|
|
|
* 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.
|
2003-04-14 03:33:34 +04:00
|
|
|
*/
|
2003-05-02 21:36:58 +04:00
|
|
|
if (sprop->flags & SPROP_HAS_SHORTID) {
|
|
|
|
flags = SPROP_HAS_SHORTID;
|
2003-04-14 03:33:34 +04:00
|
|
|
shortid = sprop->shortid;
|
2003-05-02 21:36:58 +04:00
|
|
|
getter = sprop->getter;
|
|
|
|
setter = sprop->setter;
|
2002-03-09 01:46:13 +03:00
|
|
|
}
|
2003-04-14 03:33:34 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Forget we found the proto-property now that we've copied any
|
|
|
|
* needed member values.
|
|
|
|
*/
|
2000-06-27 06:37:25 +04:00
|
|
|
sprop = NULL;
|
|
|
|
}
|
2004-04-13 05:25:17 +04:00
|
|
|
#ifdef __GNUC__ /* suppress bogus gcc warnings */
|
2002-02-23 06:49:27 +03:00
|
|
|
} else {
|
|
|
|
scope = NULL;
|
|
|
|
#endif
|
2001-05-17 05:43:58 +04:00
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2001-05-17 05:43:58 +04:00
|
|
|
if (!sprop) {
|
2005-12-02 00:05:12 +03:00
|
|
|
if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj) {
|
|
|
|
flags = JSREPORT_ERROR;
|
2003-04-14 03:33:34 +04:00
|
|
|
goto read_only_error;
|
2005-12-02 00:05:12 +03:00
|
|
|
}
|
2003-03-19 03:53:45 +03:00
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
/* Find or make a property descriptor with the right heritage. */
|
2001-05-17 05:43:58 +04:00
|
|
|
JS_LOCK_OBJ(cx, obj);
|
2002-02-23 06:49:27 +03:00
|
|
|
scope = js_GetMutableScope(cx, obj);
|
2000-06-27 06:37:25 +04:00
|
|
|
if (!scope) {
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
return JS_FALSE;
|
2000-06-27 06:37:25 +04:00
|
|
|
}
|
2002-02-23 06:49:27 +03:00
|
|
|
if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
|
|
|
|
attrs |= JSPROP_SHARED;
|
|
|
|
sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
|
|
|
|
SPROP_INVALID_SLOT, attrs, flags, shortid);
|
2000-06-27 06:37:25 +04:00
|
|
|
if (!sprop) {
|
2002-02-23 06:49:27 +03:00
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
|
|
|
return JS_FALSE;
|
2000-06-27 06:37:25 +04:00
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2005-03-29 23:24:33 +04:00
|
|
|
/*
|
|
|
|
* Initialize the new property value (passed to setter) to undefined.
|
|
|
|
* Note that we store before calling addProperty, to match the order
|
|
|
|
* in js_DefineNativeProperty.
|
|
|
|
*/
|
|
|
|
if (SPROP_HAS_VALID_SLOT(sprop, scope))
|
|
|
|
LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID);
|
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
/* XXXbe called with obj locked */
|
2005-07-20 01:13:19 +04:00
|
|
|
ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, vp,
|
|
|
|
js_RemoveScopeProperty(cx, scope, id);
|
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
|
|
|
return JS_FALSE);
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2003-04-14 08:14:57 +04:00
|
|
|
PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
2007-01-05 00:15:09 +03:00
|
|
|
if (!js_NativeSet(cx, obj, sprop, vp))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
2002-02-23 06:49:27 +03:00
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
1998-04-24 04:31:11 +04:00
|
|
|
return JS_TRUE;
|
2003-04-14 03:33:34 +04:00
|
|
|
|
|
|
|
read_only_error: {
|
|
|
|
JSString *str = js_DecompileValueGenerator(cx,
|
|
|
|
JSDVG_IGNORE_STACK,
|
|
|
|
ID_TO_VALUE(id),
|
|
|
|
NULL);
|
2005-12-02 00:05:12 +03:00
|
|
|
if (!str)
|
|
|
|
return JS_FALSE;
|
|
|
|
return JS_ReportErrorFlagsAndNumberUC(cx, flags, js_GetErrorMessage,
|
|
|
|
NULL, JSMSG_READ_ONLY,
|
|
|
|
JS_GetStringChars(str));
|
2003-04-14 03:33:34 +04:00
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
1998-04-24 04:31:11 +04:00
|
|
|
js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
|
2001-02-07 02:19:44 +03:00
|
|
|
uintN *attrsp)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
1998-04-24 04:31:11 +04:00
|
|
|
JSBool noprop, ok;
|
|
|
|
JSScopeProperty *sprop;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
noprop = !prop;
|
|
|
|
if (noprop) {
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!js_LookupProperty(cx, obj, id, &obj, &prop))
|
|
|
|
return JS_FALSE;
|
1999-09-22 09:54:44 +04:00
|
|
|
if (!prop) {
|
|
|
|
*attrsp = 0;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!OBJ_IS_NATIVE(obj)) {
|
|
|
|
ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp);
|
|
|
|
OBJ_DROP_PROPERTY(cx, obj, prop);
|
|
|
|
return ok;
|
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
sprop = (JSScopeProperty *)prop;
|
|
|
|
*attrsp = sprop->attrs;
|
|
|
|
if (noprop)
|
2001-02-07 02:19:44 +03:00
|
|
|
OBJ_DROP_PROPERTY(cx, obj, prop);
|
1998-04-24 04:31:11 +04:00
|
|
|
return JS_TRUE;
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
JSBool
|
|
|
|
js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
|
2001-02-07 02:19:44 +03:00
|
|
|
uintN *attrsp)
|
1998-04-24 04:31:11 +04:00
|
|
|
{
|
|
|
|
JSBool noprop, ok;
|
|
|
|
JSScopeProperty *sprop;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
noprop = !prop;
|
|
|
|
if (noprop) {
|
2001-02-07 02:19:44 +03:00
|
|
|
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;
|
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
sprop = (JSScopeProperty *)prop;
|
2006-09-19 23:23:18 +04:00
|
|
|
sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, *attrsp, 0,
|
2002-02-23 06:49:27 +03:00
|
|
|
sprop->getter, sprop->setter);
|
1998-04-24 04:31:11 +04:00
|
|
|
if (noprop)
|
2001-02-07 02:19:44 +03:00
|
|
|
OBJ_DROP_PROPERTY(cx, obj, prop);
|
2002-02-23 06:49:27 +03:00
|
|
|
return (sprop != NULL);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
1998-04-24 04:31:11 +04:00
|
|
|
js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
1998-04-24 04:31:11 +04:00
|
|
|
JSObject *proto;
|
|
|
|
JSProperty *prop;
|
|
|
|
JSScopeProperty *sprop;
|
1998-03-28 05:44:41 +03:00
|
|
|
JSScope *scope;
|
2002-02-23 06:49:27 +03:00
|
|
|
JSBool ok;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2006-04-27 01:33:01 +04:00
|
|
|
*rval = JSVAL_TRUE;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1999-07-01 06:24:32 +04:00
|
|
|
/*
|
2000-06-27 06:37:25 +04:00
|
|
|
* Handle old bug that took empty string as zero index. Also convert
|
|
|
|
* string indices to integers if appropriate.
|
1999-07-01 06:24:32 +04:00
|
|
|
*/
|
2005-05-15 22:24:31 +04:00
|
|
|
CHECK_FOR_STRING_INDEX(id);
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2006-12-24 14:31:37 +03:00
|
|
|
JS_COUNT_OPERATION(cx, JSOW_DELETE_PROPERTY);
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!js_LookupProperty(cx, obj, id, &proto, &prop))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!prop || proto != obj) {
|
- [jsemit.c] Fix horrid stupid bugs generating JSOP_ARGCNT and JSOP_ARGSUB,
where any occurrence of arguments.length or arguments[0], e.g., would be
"optimized" to use those bytecodes. This is just wrong if the occurrence
is an operand of delete, ++, --, or the left-hand-side of an assignment
operator!
- [jsfun.c, jsinterp.c] args_getProperty etc. must use JS_GetInstancePrivate,
not JS_GetPrivate, as the arguments object is exposed, and can be made a
prototype of other objects that do not have private data, or private data
that's a JSStackFrame*. Same goes for fun_getProperty, js_GetArgument, etc.
- [jsfun.c, jsobj.c, jsstr.c] No need to specialize fun_delProperty and
str_delProperty to help convince users and ECMA conformance tests that
fun.length and str.length are direct properties of instances, instead of
being delegated to Function.prototype.length and String.prototype.length.
This special case is done universally in js_DeleteProperty for all SHARED
and PERMANENT proto-properties.
- [jshash.c] Sneaking this followup-fix for bug 69271 in: use JS_HASH_BITS
rather than hardcoded 32.
- [jsobj.c, jsscope.[ch]] Fix misnamed js_HashValue (it takes a jsid, so it
is now js_HashId).
- [jsscript.c] script_compile needs to call JS_InstanceOf, to ensure that obj
is a Script object.
2001-03-22 05:52:42 +03:00
|
|
|
/*
|
|
|
|
* 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;
|
2002-09-12 23:34:59 +04:00
|
|
|
if (SPROP_IS_SHARED_PERMANENT(sprop))
|
- [jsemit.c] Fix horrid stupid bugs generating JSOP_ARGCNT and JSOP_ARGSUB,
where any occurrence of arguments.length or arguments[0], e.g., would be
"optimized" to use those bytecodes. This is just wrong if the occurrence
is an operand of delete, ++, --, or the left-hand-side of an assignment
operator!
- [jsfun.c, jsinterp.c] args_getProperty etc. must use JS_GetInstancePrivate,
not JS_GetPrivate, as the arguments object is exposed, and can be made a
prototype of other objects that do not have private data, or private data
that's a JSStackFrame*. Same goes for fun_getProperty, js_GetArgument, etc.
- [jsfun.c, jsobj.c, jsstr.c] No need to specialize fun_delProperty and
str_delProperty to help convince users and ECMA conformance tests that
fun.length and str.length are direct properties of instances, instead of
being delegated to Function.prototype.length and String.prototype.length.
This special case is done universally in js_DeleteProperty for all SHARED
and PERMANENT proto-properties.
- [jshash.c] Sneaking this followup-fix for bug 69271 in: use JS_HASH_BITS
rather than hardcoded 32.
- [jsobj.c, jsscope.[ch]] Fix misnamed js_HashValue (it takes a jsid, so it
is now js_HashId).
- [jsscript.c] script_compile needs to call JS_InstanceOf, to ensure that obj
is a Script object.
2001-03-22 05:52:42 +03:00
|
|
|
*rval = JSVAL_FALSE;
|
|
|
|
}
|
2001-02-07 02:19:44 +03:00
|
|
|
OBJ_DROP_PROPERTY(cx, proto, prop);
|
- [jsemit.c] Fix horrid stupid bugs generating JSOP_ARGCNT and JSOP_ARGSUB,
where any occurrence of arguments.length or arguments[0], e.g., would be
"optimized" to use those bytecodes. This is just wrong if the occurrence
is an operand of delete, ++, --, or the left-hand-side of an assignment
operator!
- [jsfun.c, jsinterp.c] args_getProperty etc. must use JS_GetInstancePrivate,
not JS_GetPrivate, as the arguments object is exposed, and can be made a
prototype of other objects that do not have private data, or private data
that's a JSStackFrame*. Same goes for fun_getProperty, js_GetArgument, etc.
- [jsfun.c, jsobj.c, jsstr.c] No need to specialize fun_delProperty and
str_delProperty to help convince users and ECMA conformance tests that
fun.length and str.length are direct properties of instances, instead of
being delegated to Function.prototype.length and String.prototype.length.
This special case is done universally in js_DeleteProperty for all SHARED
and PERMANENT proto-properties.
- [jshash.c] Sneaking this followup-fix for bug 69271 in: use JS_HASH_BITS
rather than hardcoded 32.
- [jsobj.c, jsscope.[ch]] Fix misnamed js_HashValue (it takes a jsid, so it
is now js_HashId).
- [jsscript.c] script_compile needs to call JS_InstanceOf, to ensure that obj
is a Script object.
2001-03-22 05:52:42 +03:00
|
|
|
if (*rval == JSVAL_FALSE)
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
/*
|
- [jsemit.c] Fix horrid stupid bugs generating JSOP_ARGCNT and JSOP_ARGSUB,
where any occurrence of arguments.length or arguments[0], e.g., would be
"optimized" to use those bytecodes. This is just wrong if the occurrence
is an operand of delete, ++, --, or the left-hand-side of an assignment
operator!
- [jsfun.c, jsinterp.c] args_getProperty etc. must use JS_GetInstancePrivate,
not JS_GetPrivate, as the arguments object is exposed, and can be made a
prototype of other objects that do not have private data, or private data
that's a JSStackFrame*. Same goes for fun_getProperty, js_GetArgument, etc.
- [jsfun.c, jsobj.c, jsstr.c] No need to specialize fun_delProperty and
str_delProperty to help convince users and ECMA conformance tests that
fun.length and str.length are direct properties of instances, instead of
being delegated to Function.prototype.length and String.prototype.length.
This special case is done universally in js_DeleteProperty for all SHARED
and PERMANENT proto-properties.
- [jshash.c] Sneaking this followup-fix for bug 69271 in: use JS_HASH_BITS
rather than hardcoded 32.
- [jsobj.c, jsscope.[ch]] Fix misnamed js_HashValue (it takes a jsid, so it
is now js_HashId).
- [jsscript.c] script_compile needs to call JS_InstanceOf, to ensure that obj
is a Script object.
2001-03-22 05:52:42 +03:00
|
|
|
* 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.
|
2001-02-07 02:19:44 +03:00
|
|
|
*/
|
2002-02-23 06:49:27 +03:00
|
|
|
return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, ID_TO_VALUE(id),
|
2001-02-07 02:19:44 +03:00
|
|
|
rval);
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
sprop = (JSScopeProperty *)prop;
|
|
|
|
if (sprop->attrs & JSPROP_PERMANENT) {
|
2001-02-07 02:19:44 +03:00
|
|
|
OBJ_DROP_PROPERTY(cx, obj, prop);
|
2006-09-20 14:09:28 +04:00
|
|
|
*rval = JSVAL_FALSE;
|
|
|
|
return JS_TRUE;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
/* XXXbe called with obj locked */
|
2002-02-23 06:49:27 +03:00
|
|
|
if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, SPROP_USERID(sprop),
|
|
|
|
rval)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
OBJ_DROP_PROPERTY(cx, obj, prop);
|
|
|
|
return JS_FALSE;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
1999-11-02 05:23:50 +03:00
|
|
|
scope = OBJ_SCOPE(obj);
|
2002-02-23 06:49:27 +03:00
|
|
|
if (SPROP_HAS_VALID_SLOT(sprop, scope))
|
|
|
|
GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot));
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2002-02-23 06:49:27 +03:00
|
|
|
PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, NULL);
|
|
|
|
ok = js_RemoveScopeProperty(cx, scope, id);
|
1998-04-24 04:31:11 +04:00
|
|
|
OBJ_DROP_PROPERTY(cx, obj, prop);
|
2002-02-23 06:49:27 +03:00
|
|
|
return ok;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
1998-04-24 04:31:11 +04:00
|
|
|
js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
2006-09-18 15:04:30 +04:00
|
|
|
jsval v, save;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSString *str;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2006-09-18 15:04:30 +04:00
|
|
|
v = save = OBJECT_TO_JSVAL(obj);
|
1998-04-24 04:31:11 +04:00
|
|
|
switch (hint) {
|
|
|
|
case JSTYPE_STRING:
|
1998-10-14 14:22:38 +04:00
|
|
|
/*
|
|
|
|
* 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,
|
2004-10-05 14:19:07 +04:00
|
|
|
&v)) {
|
1998-10-14 14:22:38 +04:00
|
|
|
return JS_FALSE;
|
2004-10-05 14:19:07 +04:00
|
|
|
}
|
1998-10-14 14:22:38 +04:00
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!JSVAL_IS_PRIMITIVE(v)) {
|
|
|
|
if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
break;
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
default:
|
2001-02-07 02:19:44 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0,
|
2006-04-27 01:33:01 +04:00
|
|
|
NULL, &v)) {
|
1998-10-14 14:22:38 +04:00
|
|
|
return JS_FALSE;
|
2006-04-27 01:33:01 +04:00
|
|
|
}
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
|
|
|
break;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!JSVAL_IS_PRIMITIVE(v)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
/* 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);
|
2006-09-18 15:04:30 +04:00
|
|
|
str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, save, str);
|
2001-02-07 02:19:44 +03:00
|
|
|
if (str) {
|
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_CANT_CONVERT_TO,
|
|
|
|
JS_GetStringBytes(str),
|
|
|
|
(hint == JSTYPE_VOID)
|
|
|
|
? "primitive type"
|
2006-01-26 11:47:51 +03:00
|
|
|
: js_type_strs[hint]);
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
|
|
|
return JS_FALSE;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
out:
|
1998-04-24 04:31:11 +04:00
|
|
|
*vp = v;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2002-03-12 10:17:30 +03:00
|
|
|
JSIdArray *
|
1998-04-24 04:31:11 +04:00
|
|
|
js_NewIdArray(JSContext *cx, jsint length)
|
|
|
|
{
|
|
|
|
JSIdArray *ida;
|
|
|
|
|
2000-02-04 05:01:49 +03:00
|
|
|
ida = (JSIdArray *)
|
2005-06-16 08:24:03 +04:00
|
|
|
JS_malloc(cx, sizeof(JSIdArray) + (length-1) * sizeof(jsval));
|
1998-04-24 04:31:11 +04:00
|
|
|
if (ida)
|
2001-02-07 02:19:44 +03:00
|
|
|
ida->length = length;
|
1998-04-24 04:31:11 +04:00
|
|
|
return ida;
|
|
|
|
}
|
|
|
|
|
2002-03-12 10:17:30 +03:00
|
|
|
JSIdArray *
|
2005-06-16 08:24:03 +04:00
|
|
|
js_SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length)
|
1999-10-25 23:24:03 +04:00
|
|
|
{
|
2005-06-16 08:24:03 +04:00
|
|
|
JSIdArray *rida;
|
|
|
|
|
|
|
|
rida = (JSIdArray *)
|
|
|
|
JS_realloc(cx, ida, sizeof(JSIdArray) + (length-1) * sizeof(jsval));
|
|
|
|
if (!rida)
|
|
|
|
JS_DestroyIdArray(cx, ida);
|
|
|
|
else
|
|
|
|
rida->length = length;
|
|
|
|
return rida;
|
1999-10-25 23:24:03 +04:00
|
|
|
}
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
/* Private type used to iterate over all properties of a native JS object */
|
2006-06-21 13:13:24 +04:00
|
|
|
struct JSNativeIteratorState {
|
|
|
|
jsint next_index; /* index into jsid array */
|
|
|
|
JSIdArray *ida; /* all property ids in enumeration */
|
|
|
|
JSNativeIteratorState *next; /* double-linked list support */
|
|
|
|
JSNativeIteratorState **prevp;
|
|
|
|
};
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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,
|
2001-02-07 02:19:44 +03:00
|
|
|
jsval *statep, jsid *idp)
|
1998-04-24 04:31:11 +04:00
|
|
|
{
|
2006-06-21 13:13:24 +04:00
|
|
|
JSRuntime *rt;
|
2004-11-17 10:43:01 +03:00
|
|
|
JSObject *proto;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSClass *clasp;
|
|
|
|
JSEnumerateOp enumerate;
|
2002-02-23 06:49:27 +03:00
|
|
|
JSScopeProperty *sprop, *lastProp;
|
1998-04-24 04:31:11 +04:00
|
|
|
jsint i, length;
|
|
|
|
JSScope *scope;
|
|
|
|
JSIdArray *ida;
|
|
|
|
JSNativeIteratorState *state;
|
|
|
|
|
2006-06-21 13:13:24 +04:00
|
|
|
rt = cx->runtime;
|
1998-04-24 04:31:11 +04:00
|
|
|
clasp = OBJ_GET_CLASS(cx, obj);
|
|
|
|
enumerate = clasp->enumerate;
|
|
|
|
if (clasp->flags & JSCLASS_NEW_ENUMERATE)
|
2001-02-07 02:19:44 +03:00
|
|
|
return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
|
1998-07-31 04:07:22 +04:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
switch (enum_op) {
|
2004-11-17 10:43:01 +03:00
|
|
|
case JSENUMERATE_INIT:
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!enumerate(cx, obj))
|
2004-11-17 10:43:01 +03:00
|
|
|
return JS_FALSE;
|
2001-02-07 02:19:44 +03:00
|
|
|
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.
|
|
|
|
*/
|
2004-11-17 10:43:01 +03:00
|
|
|
proto = OBJ_GET_PROTO(cx, obj);
|
|
|
|
if (proto && scope == OBJ_SCOPE(proto)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
ida = js_NewIdArray(cx, 0);
|
|
|
|
if (!ida) {
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
2004-11-17 10:43:01 +03:00
|
|
|
return JS_FALSE;
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Object has a private scope; Enumerate all props in scope. */
|
2002-02-23 06:49:27 +03:00
|
|
|
for (sprop = lastProp = SCOPE_LAST_PROP(scope); sprop;
|
|
|
|
sprop = sprop->parent) {
|
2004-04-13 05:25:17 +04:00
|
|
|
if ((
|
|
|
|
#ifdef DUMP_CALL_TABLE
|
|
|
|
(cx->options & JSOPTION_LOGCALL_TOSOURCE) ||
|
|
|
|
#endif
|
|
|
|
(sprop->attrs & JSPROP_ENUMERATE)) &&
|
2002-02-23 06:49:27 +03:00
|
|
|
!(sprop->flags & SPROP_IS_ALIAS) &&
|
|
|
|
(!SCOPE_HAD_MIDDLE_DELETE(scope) ||
|
|
|
|
SCOPE_HAS_PROPERTY(scope, sprop))) {
|
2001-02-07 02:19:44 +03:00
|
|
|
length++;
|
2002-02-23 06:49:27 +03:00
|
|
|
}
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
|
|
|
ida = js_NewIdArray(cx, length);
|
|
|
|
if (!ida) {
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
2004-11-17 10:43:01 +03:00
|
|
|
return JS_FALSE;
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
2002-02-23 06:49:27 +03:00
|
|
|
i = length;
|
|
|
|
for (sprop = lastProp; sprop; sprop = sprop->parent) {
|
2004-04-13 05:25:17 +04:00
|
|
|
if ((
|
|
|
|
#ifdef DUMP_CALL_TABLE
|
|
|
|
(cx->options & JSOPTION_LOGCALL_TOSOURCE) ||
|
|
|
|
#endif
|
|
|
|
(sprop->attrs & JSPROP_ENUMERATE)) &&
|
2002-02-23 06:49:27 +03:00
|
|
|
!(sprop->flags & SPROP_IS_ALIAS) &&
|
|
|
|
(!SCOPE_HAD_MIDDLE_DELETE(scope) ||
|
|
|
|
SCOPE_HAS_PROPERTY(scope, sprop))) {
|
|
|
|
JS_ASSERT(i > 0);
|
|
|
|
ida->vector[--i] = sprop->id;
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
|
|
|
|
state = (JSNativeIteratorState *)
|
2000-02-04 05:01:49 +03:00
|
|
|
JS_malloc(cx, sizeof(JSNativeIteratorState));
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!state) {
|
|
|
|
JS_DestroyIdArray(cx, ida);
|
2004-11-17 10:43:01 +03:00
|
|
|
return JS_FALSE;
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
|
|
|
state->ida = ida;
|
|
|
|
state->next_index = 0;
|
2006-06-21 13:13:24 +04:00
|
|
|
|
|
|
|
JS_LOCK_RUNTIME(rt);
|
|
|
|
state->next = rt->nativeIteratorStates;
|
|
|
|
if (state->next)
|
|
|
|
state->next->prevp = &state->next;
|
|
|
|
state->prevp = &rt->nativeIteratorStates;
|
|
|
|
*state->prevp = state;
|
|
|
|
JS_UNLOCK_RUNTIME(rt);
|
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
*statep = PRIVATE_TO_JSVAL(state);
|
|
|
|
if (idp)
|
|
|
|
*idp = INT_TO_JSVAL(length);
|
2004-11-17 10:43:01 +03:00
|
|
|
break;
|
1998-07-31 04:07:22 +04:00
|
|
|
|
2004-11-17 10:43:01 +03:00
|
|
|
case JSENUMERATE_NEXT:
|
2001-02-07 02:19:44 +03:00
|
|
|
state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
|
|
|
|
ida = state->ida;
|
|
|
|
length = ida->length;
|
|
|
|
if (state->next_index != length) {
|
|
|
|
*idp = ida->vector[state->next_index++];
|
2004-11-17 10:43:01 +03:00
|
|
|
break;
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
2004-11-17 10:43:01 +03:00
|
|
|
/* FALL THROUGH */
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2004-11-17 10:43:01 +03:00
|
|
|
case JSENUMERATE_DESTROY:
|
2001-02-07 02:19:44 +03:00
|
|
|
state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
|
2006-06-21 13:13:24 +04:00
|
|
|
|
|
|
|
JS_LOCK_RUNTIME(rt);
|
|
|
|
JS_ASSERT(rt->nativeIteratorStates);
|
|
|
|
JS_ASSERT(*state->prevp == state);
|
|
|
|
if (state->next) {
|
|
|
|
JS_ASSERT(state->next->prevp == &state->next);
|
|
|
|
state->next->prevp = state->prevp;
|
|
|
|
}
|
|
|
|
*state->prevp = state->next;
|
|
|
|
JS_UNLOCK_RUNTIME(rt);
|
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_DestroyIdArray(cx, state->ida);
|
|
|
|
JS_free(cx, state);
|
|
|
|
*statep = JSVAL_NULL;
|
2004-11-17 10:43:01 +03:00
|
|
|
break;
|
2006-06-21 13:13:24 +04:00
|
|
|
}
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
2006-06-18 06:04:40 +04:00
|
|
|
|
2006-06-21 13:13:24 +04:00
|
|
|
void
|
|
|
|
js_MarkNativeIteratorStates(JSContext *cx)
|
|
|
|
{
|
|
|
|
JSNativeIteratorState *state;
|
|
|
|
jsid *cursor, *end, id;
|
2006-06-20 02:53:51 +04:00
|
|
|
|
2006-06-21 13:13:24 +04:00
|
|
|
state = cx->runtime->nativeIteratorStates;
|
|
|
|
if (!state)
|
|
|
|
return;
|
|
|
|
|
|
|
|
do {
|
|
|
|
JS_ASSERT(*state->prevp == state);
|
|
|
|
cursor = state->ida->vector;
|
|
|
|
end = cursor + state->ida->length;
|
|
|
|
for (; cursor != end; ++cursor) {
|
|
|
|
id = *cursor;
|
2007-01-05 03:14:39 +03:00
|
|
|
MARK_ID(cx, id);
|
2006-06-18 06:04:40 +04:00
|
|
|
}
|
2006-06-21 13:13:24 +04:00
|
|
|
} while ((state = state->next) != NULL);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
1998-04-24 04:31:11 +04:00
|
|
|
js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
|
2001-02-07 02:19:44 +03:00
|
|
|
jsval *vp, uintN *attrsp)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
2006-05-16 18:47:28 +04:00
|
|
|
JSBool writing;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSObject *pobj;
|
|
|
|
JSProperty *prop;
|
|
|
|
JSClass *clasp;
|
2006-05-16 18:47:28 +04:00
|
|
|
JSScopeProperty *sprop;
|
2005-06-22 01:31:26 +04:00
|
|
|
JSCheckAccessOp check;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2006-05-16 18:47:28 +04:00
|
|
|
writing = (mode & JSACC_WRITE) != 0;
|
|
|
|
switch (mode & JSACC_TYPEMASK) {
|
|
|
|
case JSACC_PROTO:
|
|
|
|
pobj = obj;
|
|
|
|
if (!writing)
|
2006-12-09 18:02:37 +03:00
|
|
|
*vp = OBJECT_TO_JSVAL(OBJ_GET_PROTO(cx, obj));
|
2006-05-16 18:47:28 +04:00
|
|
|
*attrsp = JSPROP_PERMANENT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JSACC_PARENT:
|
|
|
|
JS_ASSERT(!writing);
|
|
|
|
pobj = obj;
|
2006-12-09 18:02:37 +03:00
|
|
|
*vp = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, obj));
|
2006-05-16 18:47:28 +04:00
|
|
|
*attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
|
|
|
|
return JS_FALSE;
|
|
|
|
if (!prop) {
|
|
|
|
if (!writing)
|
|
|
|
*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;
|
|
|
|
*attrsp = sprop->attrs;
|
|
|
|
if (!writing) {
|
|
|
|
*vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)))
|
|
|
|
? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
|
|
|
|
: JSVAL_VOID;
|
|
|
|
}
|
2001-02-07 02:19:44 +03:00
|
|
|
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
2005-06-22 01:31:26 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If obj's class has a stub (null) checkAccess hook, use the per-runtime
|
|
|
|
* checkObjectAccess callback, if configured.
|
|
|
|
*
|
|
|
|
* We don't want to require all classes to supply a checkAccess hook; we
|
|
|
|
* need that hook only for certain classes used when precompiling scripts
|
|
|
|
* and functions ("brutal sharing"). But for general safety of built-in
|
|
|
|
* magic properties such as __proto__ and __parent__, we route all access
|
|
|
|
* checks, even for classes that stub out checkAccess, through the global
|
|
|
|
* checkObjectAccess hook. This covers precompilation-based sharing and
|
|
|
|
* (possibly unintended) runtime sharing across trust boundaries.
|
|
|
|
*/
|
2006-05-16 18:47:28 +04:00
|
|
|
clasp = OBJ_GET_CLASS(cx, pobj);
|
2005-06-22 01:31:26 +04:00
|
|
|
check = clasp->checkAccess;
|
|
|
|
if (!check)
|
|
|
|
check = cx->runtime->checkObjectAccess;
|
2006-05-16 18:47:28 +04:00
|
|
|
return !check || check(cx, pobj, ID_TO_VALUE(id), mode, vp);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
2003-04-14 08:14:57 +04:00
|
|
|
|
2002-02-23 06:49:27 +03:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
void
|
|
|
|
js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
|
|
|
|
{
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
}
|
|
|
|
#endif
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2001-09-06 01:25:09 +04:00
|
|
|
static void
|
2003-10-22 10:26:03 +04:00
|
|
|
ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
|
2001-09-06 01:25:09 +04:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2003-10-22 10:26:03 +04:00
|
|
|
js_ReportIsNotFunction(cx, vp, flags);
|
2001-09-06 01:25:09 +04:00
|
|
|
|
|
|
|
if (fp->down) {
|
|
|
|
JS_ASSERT(cx->dormantFrameChain == fp);
|
|
|
|
cx->dormantFrameChain = fp->dormantNext;
|
|
|
|
fp->dormantNext = NULL;
|
|
|
|
cx->fp = fp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-02-11 10:21:59 +03:00
|
|
|
#ifdef NARCISSUS
|
|
|
|
static JSBool
|
|
|
|
GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval)
|
|
|
|
{
|
|
|
|
JSObject *tmp;
|
|
|
|
jsval xcval;
|
|
|
|
|
|
|
|
while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
|
|
|
|
obj = tmp;
|
|
|
|
if (!OBJ_GET_PROPERTY(cx, obj,
|
2004-10-05 14:19:07 +04:00
|
|
|
ATOM_TO_JSID(cx->runtime->atomState
|
|
|
|
.ExecutionContextAtom),
|
2004-02-11 10:21:59 +03:00
|
|
|
&xcval)) {
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
if (JSVAL_IS_PRIMITIVE(xcval)) {
|
|
|
|
JS_ReportError(cx, "invalid ExecutionContext in global object");
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(xcval),
|
2004-10-05 14:19:07 +04:00
|
|
|
ATOM_TO_JSID(cx->runtime->atomState.currentAtom),
|
2004-02-11 10:21:59 +03:00
|
|
|
rval)) {
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
JSBool
|
|
|
|
js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
1998-04-24 04:31:11 +04:00
|
|
|
JSClass *clasp;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
|
|
|
|
if (!clasp->call) {
|
2004-02-11 10:21:59 +03:00
|
|
|
#ifdef NARCISSUS
|
|
|
|
JSObject *callee, *args;
|
|
|
|
jsval fval, nargv[3];
|
|
|
|
JSBool ok;
|
|
|
|
|
|
|
|
callee = JSVAL_TO_OBJECT(argv[-2]);
|
|
|
|
if (!OBJ_GET_PROPERTY(cx, callee,
|
2004-10-05 14:19:07 +04:00
|
|
|
ATOM_TO_JSID(cx->runtime->atomState.callAtom),
|
2004-02-11 10:21:59 +03:00
|
|
|
&fval)) {
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
2006-04-27 04:39:43 +04:00
|
|
|
if (VALUE_IS_FUNCTION(cx, fval)) {
|
2004-02-11 10:21:59 +03:00
|
|
|
if (!GetCurrentExecutionContext(cx, obj, &nargv[2]))
|
|
|
|
return JS_FALSE;
|
|
|
|
args = js_GetArgsObject(cx, cx->fp);
|
|
|
|
if (!args)
|
|
|
|
return JS_FALSE;
|
|
|
|
nargv[0] = OBJECT_TO_JSVAL(obj);
|
|
|
|
nargv[1] = OBJECT_TO_JSVAL(args);
|
|
|
|
return js_InternalCall(cx, callee, fval, 3, nargv, rval);
|
|
|
|
}
|
|
|
|
if (JSVAL_IS_OBJECT(fval) && JSVAL_TO_OBJECT(fval) != callee) {
|
|
|
|
argv[-2] = fval;
|
|
|
|
ok = js_Call(cx, obj, argc, argv, rval);
|
|
|
|
argv[-2] = OBJECT_TO_JSVAL(callee);
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
#endif
|
2006-05-21 02:27:28 +04:00
|
|
|
ReportIsNotFunction(cx, &argv[-2], cx->fp->flags & JSFRAME_ITERATOR);
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
return clasp->call(cx, obj, argc, argv, rval);
|
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
|
|
|
js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
2001-02-07 02:19:44 +03:00
|
|
|
jsval *rval)
|
1998-04-24 04:31:11 +04:00
|
|
|
{
|
|
|
|
JSClass *clasp;
|
|
|
|
|
|
|
|
clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
|
|
|
|
if (!clasp->construct) {
|
2004-02-11 10:21:59 +03:00
|
|
|
#ifdef NARCISSUS
|
|
|
|
JSObject *callee, *args;
|
|
|
|
jsval cval, nargv[2];
|
|
|
|
JSBool ok;
|
|
|
|
|
|
|
|
callee = JSVAL_TO_OBJECT(argv[-2]);
|
|
|
|
if (!OBJ_GET_PROPERTY(cx, callee,
|
2004-10-05 14:19:07 +04:00
|
|
|
ATOM_TO_JSID(cx->runtime->atomState
|
|
|
|
.constructAtom),
|
2004-02-11 10:21:59 +03:00
|
|
|
&cval)) {
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
2006-04-27 04:39:43 +04:00
|
|
|
if (VALUE_IS_FUNCTION(cx, cval)) {
|
2004-02-11 10:21:59 +03:00
|
|
|
if (!GetCurrentExecutionContext(cx, obj, &nargv[1]))
|
|
|
|
return JS_FALSE;
|
|
|
|
args = js_GetArgsObject(cx, cx->fp);
|
|
|
|
if (!args)
|
|
|
|
return JS_FALSE;
|
|
|
|
nargv[0] = OBJECT_TO_JSVAL(args);
|
|
|
|
return js_InternalCall(cx, callee, cval, 2, nargv, rval);
|
|
|
|
}
|
|
|
|
if (JSVAL_IS_OBJECT(cval) && JSVAL_TO_OBJECT(cval) != callee) {
|
|
|
|
argv[-2] = cval;
|
|
|
|
ok = js_Call(cx, obj, argc, argv, rval);
|
|
|
|
argv[-2] = OBJECT_TO_JSVAL(callee);
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
#endif
|
2003-10-22 10:26:03 +04:00
|
|
|
ReportIsNotFunction(cx, &argv[-2], JSV2F_CONSTRUCT);
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
return clasp->construct(cx, obj, argc, argv, rval);
|
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
|
|
|
js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
|
|
|
|
{
|
|
|
|
JSClass *clasp;
|
2005-02-24 03:06:43 +03:00
|
|
|
JSString *str;
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
clasp = OBJ_GET_CLASS(cx, obj);
|
|
|
|
if (clasp->hasInstance)
|
2001-02-07 02:19:44 +03:00
|
|
|
return clasp->hasInstance(cx, obj, v, bp);
|
2004-02-11 10:21:59 +03:00
|
|
|
#ifdef NARCISSUS
|
|
|
|
{
|
|
|
|
jsval fval, rval;
|
|
|
|
|
|
|
|
if (!OBJ_GET_PROPERTY(cx, obj,
|
2004-10-05 14:19:07 +04:00
|
|
|
ATOM_TO_JSID(cx->runtime->atomState
|
|
|
|
.hasInstanceAtom),
|
2004-02-11 10:21:59 +03:00
|
|
|
&fval)) {
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
2006-04-27 04:39:43 +04:00
|
|
|
if (VALUE_IS_FUNCTION(cx, fval)) {
|
2004-02-11 10:21:59 +03:00
|
|
|
return js_InternalCall(cx, obj, fval, 1, &v, &rval) &&
|
|
|
|
js_ValueToBoolean(cx, rval, bp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2005-02-24 03:06:43 +03:00
|
|
|
str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
|
|
|
|
OBJECT_TO_JSVAL(obj), NULL);
|
|
|
|
if (str) {
|
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_BAD_INSTANCEOF_RHS,
|
|
|
|
JS_GetStringBytes(str));
|
|
|
|
}
|
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
|
|
|
js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
|
|
|
|
{
|
|
|
|
JSObject *obj2;
|
|
|
|
|
|
|
|
*bp = JS_FALSE;
|
|
|
|
if (JSVAL_IS_PRIMITIVE(v))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_TRUE;
|
1998-04-24 04:31:11 +04:00
|
|
|
obj2 = JSVAL_TO_OBJECT(v);
|
1999-06-16 03:44:30 +04:00
|
|
|
while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL) {
|
2001-02-07 02:19:44 +03:00
|
|
|
if (obj2 == obj) {
|
|
|
|
*bp = JS_TRUE;
|
|
|
|
break;
|
|
|
|
}
|
1999-06-16 03:44:30 +04:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
2006-04-19 04:22:43 +04:00
|
|
|
js_GetClassPrototype(JSContext *cx, JSObject *scope, jsid id,
|
2006-02-14 12:33:45 +03:00
|
|
|
JSObject **protop)
|
1998-04-24 04:31:11 +04:00
|
|
|
{
|
|
|
|
jsval v;
|
|
|
|
JSObject *ctor;
|
|
|
|
|
2006-04-19 04:22:43 +04:00
|
|
|
if (!js_FindClassObject(cx, scope, id, &v))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
2006-04-27 04:39:43 +04:00
|
|
|
if (VALUE_IS_FUNCTION(cx, v)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
ctor = JSVAL_TO_OBJECT(v);
|
|
|
|
if (!OBJ_GET_PROPERTY(cx, ctor,
|
2004-10-05 14:19:07 +04:00
|
|
|
ATOM_TO_JSID(cx->runtime->atomState
|
|
|
|
.classPrototypeAtom),
|
2001-02-07 02:19:44 +03:00
|
|
|
&v)) {
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
2005-10-24 01:42:07 +04:00
|
|
|
if (!JSVAL_IS_PRIMITIVE(v)) {
|
|
|
|
/*
|
|
|
|
* Set the newborn root in case v is otherwise unreferenced.
|
|
|
|
* It's ok to overwrite newborn roots here, since the getter
|
|
|
|
* called just above could have. Unlike the common GC rooting
|
|
|
|
* model, our callers do not have to protect protop thanks to
|
|
|
|
* this newborn root, since they all immediately create a new
|
|
|
|
* instance that delegates to this object, or just query the
|
|
|
|
* prototype for its class.
|
|
|
|
*/
|
|
|
|
cx->newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(v);
|
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
*protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2005-06-22 01:31:26 +04:00
|
|
|
/*
|
|
|
|
* For shared precompilation of function objects, we support cloning on entry
|
|
|
|
* to an execution context in which the function declaration or expression
|
|
|
|
* should be processed as if it were not precompiled, where the precompiled
|
|
|
|
* function's scope chain does not match the execution context's. The cloned
|
|
|
|
* function object carries its execution-context scope in its parent slot; it
|
|
|
|
* links to the precompiled function (the "clone-parent") via its proto slot.
|
|
|
|
*
|
|
|
|
* Note that this prototype-based delegation leaves an unchecked access path
|
|
|
|
* from the clone to the clone-parent's 'constructor' property. If the clone
|
|
|
|
* lives in a less privileged or shared scope than the clone-parent, this is
|
|
|
|
* a security hole, a sharing hazard, or both. Therefore we check all such
|
|
|
|
* accesses with the following getter/setter pair, which we use when defining
|
|
|
|
* 'constructor' in f.prototype for all function objects f.
|
|
|
|
*/
|
|
|
|
static JSBool
|
|
|
|
CheckCtorGetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|
|
|
{
|
|
|
|
JSAtom *atom;
|
|
|
|
uintN attrs;
|
|
|
|
|
|
|
|
atom = cx->runtime->atomState.constructorAtom;
|
|
|
|
JS_ASSERT(id == ATOM_KEY(atom));
|
|
|
|
return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_READ,
|
|
|
|
vp, &attrs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
CheckCtorSetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|
|
|
{
|
|
|
|
JSAtom *atom;
|
|
|
|
uintN attrs;
|
|
|
|
|
|
|
|
atom = cx->runtime->atomState.constructorAtom;
|
|
|
|
JS_ASSERT(id == ATOM_KEY(atom));
|
|
|
|
return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_WRITE,
|
2005-10-25 04:29:28 +04:00
|
|
|
vp, &attrs);
|
2005-06-22 01:31:26 +04:00
|
|
|
}
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
JSBool
|
|
|
|
js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,
|
2001-02-07 02:19:44 +03:00
|
|
|
uintN attrs)
|
1998-04-24 04:31:11 +04:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Use the given attributes for the prototype property of the constructor,
|
2004-02-11 10:21:59 +03:00
|
|
|
* as user-defined constructors have a DontDelete prototype (which may be
|
|
|
|
* reset), while native or "system" constructors have DontEnum | ReadOnly |
|
|
|
|
* DontDelete.
|
1998-04-24 04:31:11 +04:00
|
|
|
*/
|
|
|
|
if (!OBJ_DEFINE_PROPERTY(cx, ctor,
|
2004-10-05 14:19:07 +04:00
|
|
|
ATOM_TO_JSID(cx->runtime->atomState
|
|
|
|
.classPrototypeAtom),
|
|
|
|
OBJECT_TO_JSVAL(proto),
|
|
|
|
JS_PropertyStub, JS_PropertyStub,
|
2001-02-07 02:19:44 +03:00
|
|
|
attrs, NULL)) {
|
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ECMA says that Object.prototype.constructor, or f.prototype.constructor
|
|
|
|
* for a user-defined function f, is DontEnum.
|
|
|
|
*/
|
|
|
|
return OBJ_DEFINE_PROPERTY(cx, proto,
|
2004-10-05 14:19:07 +04:00
|
|
|
ATOM_TO_JSID(cx->runtime->atomState
|
|
|
|
.constructorAtom),
|
|
|
|
OBJECT_TO_JSVAL(ctor),
|
2005-06-22 01:31:26 +04:00
|
|
|
CheckCtorGetAccess, CheckCtorSetAccess,
|
2001-02-07 02:19:44 +03:00
|
|
|
0, NULL);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
|
|
|
js_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
|
|
|
|
{
|
|
|
|
JSObject *obj;
|
|
|
|
|
|
|
|
if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
obj = NULL;
|
1998-03-28 05:44:41 +03:00
|
|
|
} else if (JSVAL_IS_OBJECT(v)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
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);
|
1998-03-28 05:44:41 +03:00
|
|
|
} else {
|
2001-02-07 02:19:44 +03:00
|
|
|
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;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
*objp = obj;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSObject *
|
|
|
|
js_ValueToNonNullObject(JSContext *cx, jsval v)
|
|
|
|
{
|
|
|
|
JSObject *obj;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSString *str;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
|
|
|
if (!js_ValueToObject(cx, v, &obj))
|
2001-02-07 02:19:44 +03:00
|
|
|
return NULL;
|
1998-03-28 05:44:41 +03:00
|
|
|
if (!obj) {
|
2001-02-07 02:19:44 +03:00
|
|
|
str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
|
|
|
|
if (str) {
|
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_NO_PROPERTIES, JS_GetStringBytes(str));
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
1998-10-14 14:22:38 +04:00
|
|
|
JSBool
|
1998-03-28 05:44:41 +03:00
|
|
|
js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval)
|
|
|
|
{
|
|
|
|
jsval argv[1];
|
|
|
|
|
|
|
|
argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]);
|
1998-10-14 14:22:38 +04:00
|
|
|
return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv,
|
|
|
|
rval);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
1998-10-14 14:22:38 +04:00
|
|
|
JSBool
|
1998-03-28 05:44:41 +03:00
|
|
|
js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
|
2001-02-07 02:19:44 +03:00
|
|
|
uintN argc, jsval *argv, jsval *rval)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
|
|
|
JSErrorReporter older;
|
2006-04-07 00:15:24 +04:00
|
|
|
jsid id;
|
1998-03-28 05:44:41 +03:00
|
|
|
jsval fval;
|
1998-10-14 14:22:38 +04:00
|
|
|
JSBool ok;
|
2005-07-06 01:18:13 +04:00
|
|
|
int stackDummy;
|
|
|
|
|
|
|
|
if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
|
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
1998-10-14 14:22:38 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
1998-03-28 05:44:41 +03:00
|
|
|
older = JS_SetErrorReporter(cx, NULL);
|
2006-04-07 00:15:24 +04:00
|
|
|
id = ATOM_TO_JSID(atom);
|
|
|
|
fval = JSVAL_VOID;
|
|
|
|
#if JS_HAS_XML_SUPPORT
|
|
|
|
if (OBJECT_IS_XML(cx, obj)) {
|
|
|
|
JSXMLObjectOps *ops;
|
|
|
|
|
|
|
|
ops = (JSXMLObjectOps *) obj->map->ops;
|
|
|
|
obj = ops->getMethod(cx, obj, id, &fval);
|
|
|
|
ok = (obj != NULL);
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
ok = OBJ_GET_PROPERTY(cx, obj, id, &fval);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
2006-04-07 00:15:24 +04:00
|
|
|
if (!ok)
|
|
|
|
JS_ClearPendingException(cx);
|
1998-03-28 05:44:41 +03:00
|
|
|
JS_SetErrorReporter(cx, older);
|
2006-10-06 16:47:30 +04:00
|
|
|
|
|
|
|
return JSVAL_IS_PRIMITIVE(fval) ||
|
|
|
|
js_InternalCall(cx, obj, fval, argc, argv, rval);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
#if JS_HAS_XDR
|
|
|
|
|
|
|
|
JSBool
|
|
|
|
js_XDRObject(JSXDRState *xdr, JSObject **objp)
|
|
|
|
{
|
|
|
|
JSContext *cx;
|
2006-04-20 05:17:06 +04:00
|
|
|
JSAtom *atom;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSClass *clasp;
|
|
|
|
uint32 classId, classDef;
|
2006-04-19 04:22:43 +04:00
|
|
|
JSProtoKey protoKey;
|
|
|
|
jsid classKey;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSObject *proto;
|
|
|
|
|
|
|
|
cx = xdr->cx;
|
2006-04-20 05:17:06 +04:00
|
|
|
atom = NULL;
|
1998-04-24 04:31:11 +04:00
|
|
|
if (xdr->mode == JSXDR_ENCODE) {
|
2001-02-07 02:19:44 +03:00
|
|
|
clasp = OBJ_GET_CLASS(cx, *objp);
|
2006-02-14 12:33:45 +03:00
|
|
|
classId = JS_XDRFindClassIdByName(xdr, clasp->name);
|
2001-02-07 02:19:44 +03:00
|
|
|
classDef = !classId;
|
2006-04-20 09:18:09 +04:00
|
|
|
if (classDef) {
|
|
|
|
if (!JS_XDRRegisterClass(xdr, clasp, &classId))
|
2006-04-19 04:22:43 +04:00
|
|
|
return JS_FALSE;
|
2006-04-20 09:18:09 +04:00
|
|
|
protoKey = JSCLASS_CACHED_PROTO_KEY(clasp);
|
|
|
|
if (protoKey != JSProto_Null) {
|
|
|
|
classDef |= (protoKey << 1);
|
|
|
|
} else {
|
|
|
|
atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
|
|
|
|
if (!atom)
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
2006-04-19 04:22:43 +04:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
} else {
|
1998-11-05 03:08:43 +03:00
|
|
|
clasp = NULL; /* quell GCC overwarning */
|
2006-04-19 04:22:43 +04:00
|
|
|
classDef = 0;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
|
2006-04-20 09:18:09 +04:00
|
|
|
/*
|
|
|
|
* XDR a flag word, which could be 0 for a class use, in which case no
|
|
|
|
* name follows, only the id in xdr's class registry; 1 for a class def,
|
|
|
|
* in which case the flag word is followed by the class name transferred
|
|
|
|
* from or to atom; or a value greater than 1, an odd number that when
|
|
|
|
* divided by two yields the JSProtoKey for class. In the last case, as
|
|
|
|
* in the 0 classDef case, no name is transferred via atom.
|
|
|
|
*/
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!JS_XDRUint32(xdr, &classDef))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
2006-04-20 09:18:09 +04:00
|
|
|
if (classDef == 1 && !js_XDRCStringAtom(xdr, &atom))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2006-02-14 12:33:45 +03:00
|
|
|
if (!JS_XDRUint32(xdr, &classId))
|
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2006-02-14 12:33:45 +03:00
|
|
|
if (xdr->mode == JSXDR_DECODE) {
|
2001-02-07 02:19:44 +03:00
|
|
|
if (classDef) {
|
2006-04-19 04:22:43 +04:00
|
|
|
/* NB: we know that JSProto_Null is 0 here, for backward compat. */
|
|
|
|
protoKey = classDef >> 1;
|
|
|
|
classKey = (protoKey != JSProto_Null)
|
|
|
|
? INT_TO_JSID(protoKey)
|
|
|
|
: ATOM_TO_JSID(atom);
|
|
|
|
if (!js_GetClassPrototype(cx, NULL, classKey, &proto))
|
2006-02-14 12:33:45 +03:00
|
|
|
return JS_FALSE;
|
2001-02-07 02:19:44 +03:00
|
|
|
clasp = OBJ_GET_CLASS(cx, proto);
|
2006-02-14 12:33:45 +03:00
|
|
|
if (!JS_XDRRegisterClass(xdr, clasp, &classId))
|
|
|
|
return JS_FALSE;
|
2001-02-07 02:19:44 +03:00
|
|
|
} else {
|
2001-03-06 04:56:30 +03:00
|
|
|
clasp = JS_XDRFindClassById(xdr, classId);
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!clasp) {
|
|
|
|
char numBuf[12];
|
|
|
|
JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId);
|
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_CANT_FIND_CLASS, numBuf);
|
2006-02-14 12:33:45 +03:00
|
|
|
return JS_FALSE;
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!clasp->xdrObject) {
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_CANT_XDR_CLASS, clasp->name);
|
2006-02-14 12:33:45 +03:00
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
2006-02-14 12:33:45 +03:00
|
|
|
return clasp->xdrObject(xdr, objp);
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* JS_HAS_XDR */
|
1998-07-31 04:07:22 +04:00
|
|
|
|
2002-02-23 06:49:27 +03:00
|
|
|
#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)]++;
|
|
|
|
}
|
|
|
|
|
2006-02-09 09:02:02 +03:00
|
|
|
#define DEBUG_scopemeters
|
|
|
|
#endif /* DEBUG_brendan */
|
|
|
|
|
|
|
|
#ifdef DEBUG_scopemeters
|
2002-02-23 06:49:27 +03:00
|
|
|
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);
|
|
|
|
}
|
2006-02-09 09:02:02 +03:00
|
|
|
#endif
|
2002-02-23 06:49:27 +03:00
|
|
|
|
2000-08-26 06:30:22 +04:00
|
|
|
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);
|
2002-02-23 06:49:27 +03:00
|
|
|
#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;
|
2007-01-05 00:15:09 +03:00
|
|
|
MARK_SCOPE_PROPERTY(cx, sprop);
|
2000-08-26 06:30:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* No one runs while the GC is running, so we can use LOCKED_... here. */
|
|
|
|
clasp = LOCKED_OBJ_GET_CLASS(obj);
|
|
|
|
if (clasp->mark)
|
2006-03-23 14:50:30 +03:00
|
|
|
(void) clasp->mark(cx, obj, NULL);
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
|
|
|
|
if (scope->object != obj) {
|
|
|
|
/*
|
|
|
|
* An unmutated object that shares a prototype's scope. We can't tell
|
2007-01-07 01:03:06 +03:00
|
|
|
* how many slots are in use in obj by looking at scope, so we use
|
|
|
|
* STOBJ_NSLOTS(obj).
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
*/
|
2006-12-09 18:02:37 +03:00
|
|
|
return STOBJ_NSLOTS(obj);
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
}
|
2007-01-07 01:03:06 +03:00
|
|
|
return LOCKED_OBJ_NSLOTS(obj);
|
2000-08-26 06:30:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
js_Clear(JSContext *cx, JSObject *obj)
|
|
|
|
{
|
|
|
|
JSScope *scope;
|
2002-02-23 06:49:27 +03:00
|
|
|
JSRuntime *rt;
|
|
|
|
JSScopeProperty *sprop;
|
2000-08-26 06:30:22 +04:00
|
|
|
uint32 i, n;
|
|
|
|
|
2001-01-12 02:55:30 +03:00
|
|
|
/*
|
2002-02-23 06:49:27 +03:00
|
|
|
* 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).
|
2001-01-12 02:55:30 +03:00
|
|
|
*/
|
2000-08-26 06:30:22 +04:00
|
|
|
JS_LOCK_OBJ(cx, obj);
|
|
|
|
scope = OBJ_SCOPE(obj);
|
2001-01-12 02:55:30 +03:00
|
|
|
if (scope->object == obj) {
|
2002-02-23 06:49:27 +03:00
|
|
|
/* 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);
|
2001-01-12 02:55:30 +03:00
|
|
|
|
|
|
|
/* Clear slot values and reset freeslot so we're consistent. */
|
2007-01-07 01:03:06 +03:00
|
|
|
i = STOBJ_NSLOTS(obj);
|
2001-01-12 02:55:30 +03:00
|
|
|
n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj));
|
|
|
|
while (--i >= n)
|
2006-12-09 18:02:37 +03:00
|
|
|
STOBJ_SET_SLOT(obj, i, JSVAL_VOID);
|
2001-01-12 02:55:30 +03:00
|
|
|
scope->map.freeslot = n;
|
|
|
|
}
|
2000-08-26 06:30:22 +04:00
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
jsval
|
|
|
|
js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot)
|
|
|
|
{
|
|
|
|
jsval v;
|
|
|
|
|
|
|
|
JS_LOCK_OBJ(cx, obj);
|
2006-12-09 18:02:37 +03:00
|
|
|
v = (slot < STOBJ_NSLOTS(obj)) ? STOBJ_GET_SLOT(obj, slot) : JSVAL_VOID;
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2004-06-12 05:50:09 +04:00
|
|
|
JSBool
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
|
|
|
|
{
|
2004-07-22 19:47:22 +04:00
|
|
|
JSScope *scope;
|
2005-01-05 09:15:03 +03:00
|
|
|
uint32 nslots;
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
JSClass *clasp;
|
|
|
|
|
|
|
|
JS_LOCK_OBJ(cx, obj);
|
2004-07-22 19:47:22 +04:00
|
|
|
scope = OBJ_SCOPE(obj);
|
2007-01-07 01:03:06 +03:00
|
|
|
if (slot >= JS_INITIAL_NSLOTS && !obj->dslots) {
|
2004-07-22 19:47:22 +04:00
|
|
|
/*
|
|
|
|
* At this point, obj may or may not own scope. If some path calls
|
|
|
|
* js_GetMutableScope but does not add a slot-owning property, then
|
2007-01-07 01:03:06 +03:00
|
|
|
* scope->object == obj but obj->dslots will be null. If obj shares a
|
2006-12-12 13:45:06 +03:00
|
|
|
* prototype's scope, then we cannot update scope->map here. Instead
|
2007-01-07 01:03:06 +03:00
|
|
|
* we rely on STOBJ_NSLOTS(obj) to get the number of available slots
|
|
|
|
* in obj after we allocate dynamic slots.
|
2004-07-22 19:47:22 +04:00
|
|
|
*
|
|
|
|
* See js_Mark, before the last return, where we make a special case
|
|
|
|
* for unmutated (scope->object != obj) objects.
|
|
|
|
*/
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
clasp = LOCKED_OBJ_GET_CLASS(obj);
|
2004-07-22 19:47:22 +04:00
|
|
|
nslots = JSSLOT_FREE(clasp);
|
2004-04-13 05:25:17 +04:00
|
|
|
if (clasp->reserveSlots)
|
2004-07-22 19:47:22 +04:00
|
|
|
nslots += clasp->reserveSlots(cx, obj);
|
|
|
|
JS_ASSERT(slot < nslots);
|
2007-01-07 01:03:06 +03:00
|
|
|
if (!ReallocSlots(cx, obj, nslots, JS_TRUE)) {
|
2004-07-22 19:47:22 +04:00
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
2004-06-12 05:50:09 +04:00
|
|
|
return JS_FALSE;
|
- Add JS_GetReservedSlot, JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS(n)
to the JS API, for per class extra slots beyond JSSLOT_PRIVATE (or starting
there for a class that lacks JSCLASS_HAS_PRIVATE). To avoid penalizing all
instances, these slots are allocated only upon first property-owned slot
allocation, or upon first JS_SetReservedSlot.
This entailed adding getRequiredSlot and setRequiredSlot hooks to the
JSObjectOps struct, and making obj->slots self-describing, a la BSTR. It
also afforded me a chance to clean up obj->slots locking so that non-native
JSObjectOps didn't risk unlocked accesses! Now there are thread-safe hooks
for all uses of obj.
First consumer is the new, DOM-glue-unifying XPConnect, which needs two
slots per wrapped function. Hence the change to js_FunctionClass.flags'
initializer.
- Commented the heck out of JSClass and JSObjectOps function typedefs in
jspubtd.h. I hope embedders see these comments!
- Fix JS_XDRValue's default case to handle int exclusively, there is no other
possible type (and therefore no JSMSG_BAD_JVAL_TYPE error).
- Clean up tabs in select old, tab-ridden files and sections.
- s/\<fh\>/file/g for stdio FILE * canonical variable names.
2001-04-05 05:53:24 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-07-22 19:47:22 +04:00
|
|
|
/* Whether or not we grew nslots, we may need to advance freeslot. */
|
|
|
|
if (scope->object == obj && slot >= scope->map.freeslot)
|
|
|
|
scope->map.freeslot = slot + 1;
|
|
|
|
|
2006-12-09 18:02:37 +03:00
|
|
|
STOBJ_SET_SLOT(obj, slot, v);
|
2004-07-22 19:47:22 +04:00
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
2004-06-12 05:50:09 +04:00
|
|
|
return JS_TRUE;
|
2000-08-26 06:30:22 +04:00
|
|
|
}
|
1998-11-05 03:08:43 +03:00
|
|
|
|
1998-07-31 04:07:22 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
|
|
|
|
/* Routines to print out values during debugging. */
|
|
|
|
|
|
|
|
void printChar(jschar *cp) {
|
2006-10-07 01:06:08 +04:00
|
|
|
fprintf(stderr, "jschar* (%p) \"", (void *)cp);
|
1998-07-31 04:07:22 +04:00
|
|
|
while (*cp)
|
2001-02-07 02:19:44 +03:00
|
|
|
fputc(*cp++, stderr);
|
1998-07-31 04:07:22 +04:00
|
|
|
fputc('"', stderr);
|
|
|
|
fputc('\n', stderr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void printString(JSString *str) {
|
2001-10-25 04:26:38 +04:00
|
|
|
size_t i, n;
|
|
|
|
jschar *s;
|
2006-10-07 01:06:08 +04:00
|
|
|
fprintf(stderr, "string (%p) \"", (void *)str);
|
2001-10-25 04:26:38 +04:00
|
|
|
s = JSSTRING_CHARS(str);
|
|
|
|
for (i=0, n=JSSTRING_LENGTH(str); i < n; i++)
|
|
|
|
fputc(s[i], stderr);
|
1998-07-31 04:07:22 +04:00
|
|
|
fputc('"', stderr);
|
|
|
|
fputc('\n', stderr);
|
|
|
|
}
|
|
|
|
|
1999-10-02 07:41:37 +04:00
|
|
|
void printVal(JSContext *cx, jsval val);
|
1998-11-05 03:08:43 +03:00
|
|
|
|
1999-10-02 07:41:37 +04:00
|
|
|
void printObj(JSContext *cx, JSObject *jsobj) {
|
1998-11-05 03:08:43 +03:00
|
|
|
jsuint i;
|
|
|
|
jsval val;
|
|
|
|
JSClass *clasp;
|
|
|
|
|
2006-10-07 01:06:08 +04:00
|
|
|
fprintf(stderr, "object %p\n", (void *)jsobj);
|
1999-10-02 07:41:37 +04:00
|
|
|
clasp = OBJ_GET_CLASS(cx, jsobj);
|
2006-10-07 01:06:08 +04:00
|
|
|
fprintf(stderr, "class %p %s\n", (void *)clasp, clasp->name);
|
2007-01-07 01:03:06 +03:00
|
|
|
for (i=0; i < STOBJ_NSLOTS(jsobj); i++) {
|
1998-11-05 03:08:43 +03:00
|
|
|
fprintf(stderr, "slot %3d ", i);
|
2006-12-09 18:02:37 +03:00
|
|
|
val = STOBJ_GET_SLOT(jsobj, i);
|
1998-11-05 03:08:43 +03:00
|
|
|
if (JSVAL_IS_OBJECT(val))
|
2006-10-07 01:06:08 +04:00
|
|
|
fprintf(stderr, "object %p\n", (void *)JSVAL_TO_OBJECT(val));
|
1998-11-05 03:08:43 +03:00
|
|
|
else
|
1999-10-02 07:41:37 +04:00
|
|
|
printVal(cx, val);
|
1998-11-05 03:08:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-10-02 07:41:37 +04:00
|
|
|
void printVal(JSContext *cx, jsval val) {
|
2006-10-07 01:06:08 +04:00
|
|
|
fprintf(stderr, "val %d (%p) = ", (int)val, (void *)val);
|
1998-07-31 04:07:22 +04:00
|
|
|
if (JSVAL_IS_NULL(val)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
fprintf(stderr, "null\n");
|
1998-07-31 04:07:22 +04:00
|
|
|
} else if (JSVAL_IS_VOID(val)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
fprintf(stderr, "undefined\n");
|
1998-07-31 04:07:22 +04:00
|
|
|
} else if (JSVAL_IS_OBJECT(val)) {
|
1999-10-02 07:41:37 +04:00
|
|
|
printObj(cx, JSVAL_TO_OBJECT(val));
|
1998-07-31 04:07:22 +04:00
|
|
|
} else if (JSVAL_IS_INT(val)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
fprintf(stderr, "(int) %d\n", JSVAL_TO_INT(val));
|
1998-07-31 04:07:22 +04:00
|
|
|
} else if (JSVAL_IS_STRING(val)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
printString(JSVAL_TO_STRING(val));
|
1998-07-31 04:07:22 +04:00
|
|
|
} else if (JSVAL_IS_DOUBLE(val)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
fprintf(stderr, "(double) %g\n", *JSVAL_TO_DOUBLE(val));
|
1998-07-31 04:07:22 +04:00
|
|
|
} else {
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_ASSERT(JSVAL_IS_BOOLEAN(val));
|
|
|
|
fprintf(stderr, "(boolean) %s\n",
|
|
|
|
JSVAL_TO_BOOLEAN(val) ? "true" : "false");
|
1998-07-31 04:07:22 +04:00
|
|
|
}
|
|
|
|
fflush(stderr);
|
|
|
|
}
|
|
|
|
|
1999-10-02 07:41:37 +04:00
|
|
|
void printId(JSContext *cx, jsid id) {
|
2006-10-07 01:06:08 +04:00
|
|
|
fprintf(stderr, "id %d (%p) is ", (int)id, (void *)id);
|
2002-02-23 06:49:27 +03:00
|
|
|
printVal(cx, ID_TO_VALUE(id));
|
1998-07-31 04:07:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void printAtom(JSAtom *atom) {
|
|
|
|
printString(ATOM_TO_STRING(atom));
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|