1998-03-28 05:44:41 +03:00
|
|
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
|
|
*
|
1999-09-29 03:12:09 +04:00
|
|
|
* The contents of this file are subject to the Netscape 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/NPL/
|
1998-03-28 05:44:41 +03:00
|
|
|
*
|
1999-09-29 03:12:09 +04:00
|
|
|
* Software distributed under the License is distributed on an "AS
|
|
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
|
|
|
|
* implied. See the License for the specific language governing
|
|
|
|
* rights and limitations under the License.
|
1998-03-28 05:44:41 +03:00
|
|
|
*
|
1999-09-29 03:12:09 +04:00
|
|
|
* The Original Code is Mozilla Communicator client code, released
|
|
|
|
* March 31, 1998.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is Netscape
|
1998-03-28 05:44:41 +03:00
|
|
|
* Communications Corporation. Portions created by Netscape are
|
1999-09-29 03:12:09 +04:00
|
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
|
|
* Rights Reserved.
|
|
|
|
*
|
2000-06-27 06:37:25 +04:00
|
|
|
* Contributor(s):
|
1999-09-29 03:12:09 +04:00
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the
|
|
|
|
* terms of the GNU Public License (the "GPL"), in which case the
|
|
|
|
* provisions of the GPL are applicable instead of those above.
|
|
|
|
* If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of the GPL and not to allow others to use your
|
|
|
|
* version of this file under the NPL, indicate your decision by
|
|
|
|
* deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this
|
|
|
|
* file under either the NPL or the GPL.
|
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 */
|
|
|
|
#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"
|
|
|
|
#include "jsatom.h"
|
|
|
|
#include "jsbool.h"
|
|
|
|
#include "jscntxt.h"
|
|
|
|
#include "jsconfig.h"
|
|
|
|
#include "jsfun.h"
|
|
|
|
#include "jsgc.h"
|
|
|
|
#include "jsinterp.h"
|
|
|
|
#include "jslock.h"
|
|
|
|
#include "jsnum.h"
|
|
|
|
#include "jsobj.h"
|
|
|
|
#include "jsscope.h"
|
|
|
|
#include "jsscript.h"
|
|
|
|
#include "jsstr.h"
|
2000-02-02 04:10:31 +03:00
|
|
|
#include "jsopcode.h"
|
1998-03-28 05:44:41 +03:00
|
|
|
|
|
|
|
#if JS_HAS_OBJ_WATCHPOINT
|
|
|
|
#include "jsdbgapi.h"
|
1998-04-24 04:31:11 +04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#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-08-01 00:19:50 +04:00
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma export on
|
|
|
|
#endif
|
|
|
|
|
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,
|
1998-04-24 04:31:11 +04:00
|
|
|
#if defined JS_THREADSAFE && defined DEBUG
|
2000-06-27 06:37:25 +04:00
|
|
|
_js_LookupProperty, js_DefineProperty,
|
1998-04-24 04:31:11 +04:00
|
|
|
#else
|
2000-06-27 06:37:25 +04:00
|
|
|
js_LookupProperty, js_DefineProperty,
|
1998-03-28 05:44:41 +03:00
|
|
|
#endif
|
2000-06-27 06:37:25 +04:00
|
|
|
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
|
|
|
|
1998-08-01 00:19:50 +04:00
|
|
|
#ifdef XP_MAC
|
|
|
|
#pragma export off
|
|
|
|
#endif
|
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
JSClass js_ObjectClass = {
|
2000-08-19 23:17:32 +04:00
|
|
|
js_Object_str,
|
1998-03-28 05:44:41 +03:00
|
|
|
0,
|
|
|
|
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
1999-11-12 00:52:35 +03:00
|
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
|
|
|
|
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;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSAccessMode mode;
|
|
|
|
uintN attrs;
|
|
|
|
|
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;
|
1998-04-24 04:31:11 +04:00
|
|
|
if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
id = (jsid)cx->runtime->atomState.protoAtom;
|
|
|
|
mode = JSACC_PROTO;
|
1998-04-24 04:31:11 +04:00
|
|
|
} else {
|
2001-02-07 02:19:44 +03:00
|
|
|
id = (jsid)cx->runtime->atomState.parentAtom;
|
|
|
|
mode = JSACC_PARENT;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
if (!OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, &attrs))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
*vp = OBJ_GET_SLOT(cx, obj, slot);
|
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;
|
1999-07-28 10:48:44 +04:00
|
|
|
uint32 slot;
|
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);
|
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;
|
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,
|
|
|
|
JSMSG_CYCLIC_VALUE, object_props[slot].name);
|
|
|
|
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);
|
|
|
|
oldproto = JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO));
|
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. */
|
|
|
|
if (!pobj) {
|
|
|
|
/* With no proto and no scope of its own, obj is truly empty. */
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
} else if (OBJ_IS_NATIVE(pobj) && 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
|
|
|
}
|
|
|
|
LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj));
|
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
|
|
|
} else {
|
|
|
|
OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj));
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
{
|
1998-10-14 14:22:38 +04:00
|
|
|
return (JSHashNumber)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;
|
|
|
|
|
|
|
|
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;
|
|
|
|
he = JS_HashTableRawAdd(table, hep, hash, obj, (void *)sharpid);
|
|
|
|
if (!he) {
|
|
|
|
JS_ReportOutOfMemory(cx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ida = JS_Enumerate(cx, obj);
|
|
|
|
if (!ida)
|
|
|
|
return NULL;
|
|
|
|
ok = JS_TRUE;
|
|
|
|
for (i = 0, length = ida->length; i < length; i++) {
|
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;
|
2000-10-25 11:00:31 +04:00
|
|
|
if (prop) {
|
|
|
|
ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
|
|
|
|
if (ok) {
|
|
|
|
if (OBJ_IS_NATIVE(obj2) &&
|
|
|
|
(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
|
|
|
|
val = JSVAL_NULL;
|
|
|
|
if (attrs & JSPROP_GETTER) {
|
|
|
|
val = (jsval)
|
|
|
|
SPROP_GETTER((JSScopeProperty*)prop, obj2);
|
1999-09-21 04:13:48 +04:00
|
|
|
}
|
2000-10-25 11:00:31 +04:00
|
|
|
if (attrs & JSPROP_SETTER) {
|
|
|
|
if (val != JSVAL_NULL) {
|
|
|
|
/* Mark the getter, then set val to setter. */
|
|
|
|
ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
|
|
|
|
NULL)
|
|
|
|
!= NULL);
|
|
|
|
}
|
|
|
|
val = (jsval)
|
|
|
|
SPROP_SETTER((JSScopeProperty*)prop, obj2);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
|
1999-09-21 04:13:48 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
2000-10-25 11:00:31 +04:00
|
|
|
}
|
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 {
|
2001-02-07 02:19:44 +03:00
|
|
|
sharpid = (jsatomid) he->value;
|
|
|
|
if (sharpid == 0) {
|
|
|
|
sharpid = ++map->sharpgen << 1;
|
|
|
|
he->value = (void *) sharpid;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
|
2000-08-26 11:10:31 +04:00
|
|
|
*sp = NULL; /* Set to null in case we return an early error. */
|
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;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
JS_ASSERT((((jsatomid) he->value) & SHARP_BIT) == 0);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
*sp = NULL;
|
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
|
|
|
}
|
|
|
|
|
|
|
|
sharpid = (jsatomid) he->value;
|
|
|
|
if (sharpid == 0) {
|
2001-02-07 02:19:44 +03:00
|
|
|
*sp = NULL;
|
1998-03-28 05:44:41 +03:00
|
|
|
} else {
|
2001-02-07 02:19:44 +03:00
|
|
|
len = JS_snprintf(buf, sizeof buf, "#%u%c",
|
|
|
|
sharpid >> 1, (sharpid & SHARP_BIT) ? '#' : '=');
|
|
|
|
*sp = js_InflateString(cx, buf, len);
|
|
|
|
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) {
|
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) {
|
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
|
|
|
}
|
|
|
|
|
2001-04-14 11:34:58 +04:00
|
|
|
#define OBJ_TOSTRING_EXTRA 3 /* for 3 local GC roots */
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
#if JS_HAS_INITIALIZERS || 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;
|
|
|
|
const jschar *vchars;
|
1998-04-24 04:31:11 +04:00
|
|
|
size_t nchars, vlength, vsharplength;
|
|
|
|
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
|
1999-09-21 04:13:48 +04:00
|
|
|
jsval val[2];
|
|
|
|
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;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1999-11-17 07:32:37 +03:00
|
|
|
/*
|
|
|
|
* obj_toString for 1.2 calls toSource, and doesn't want the extra parens
|
|
|
|
* on the outside.
|
|
|
|
*/
|
|
|
|
outermost = (cx->version != JSVERSION_1_2 && cx->sharpObjectMap.depth == 0);
|
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
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
chars[nchars++] = '{';
|
|
|
|
|
|
|
|
comma = NULL;
|
|
|
|
|
|
|
|
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
|
2000-05-15 07:54:50 +04:00
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
|
|
|
|
if (!ok)
|
|
|
|
goto error;
|
2000-10-25 11:00:31 +04:00
|
|
|
if (prop) {
|
|
|
|
ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
|
|
|
|
if (!ok)
|
|
|
|
goto error;
|
|
|
|
if (OBJ_IS_NATIVE(obj2) &&
|
1999-09-21 04:13:48 +04:00
|
|
|
(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
|
|
|
|
valcnt = 0;
|
|
|
|
if (attrs & JSPROP_GETTER) {
|
1999-11-02 05:23:50 +03:00
|
|
|
val[valcnt] = (jsval)
|
|
|
|
SPROP_GETTER((JSScopeProperty *)prop, obj2);
|
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
|
|
|
|
gsop[valcnt] =
|
|
|
|
ATOM_TO_STRING(cx->runtime->atomState.getAtom);
|
|
|
|
#endif
|
1999-09-21 04:13:48 +04:00
|
|
|
valcnt++;
|
|
|
|
}
|
|
|
|
if (attrs & JSPROP_SETTER) {
|
1999-11-02 05:23:50 +03:00
|
|
|
val[valcnt] = (jsval)
|
|
|
|
SPROP_SETTER((JSScopeProperty *)prop, obj2);
|
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
|
|
|
|
gsop[valcnt] =
|
|
|
|
ATOM_TO_STRING(cx->runtime->atomState.setAtom);
|
|
|
|
#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
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
/* Convert id to a jsval and then to a string. */
|
2001-04-04 11:45:22 +04:00
|
|
|
atom = JSVAL_IS_INT(id) ? NULL : (JSAtom *)id;
|
2001-02-07 02:19:44 +03:00
|
|
|
id = js_IdToValue(id);
|
|
|
|
idstr = js_ValueToString(cx, id);
|
|
|
|
if (!idstr) {
|
|
|
|
ok = JS_FALSE;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
argv[0] = STRING_TO_JSVAL(idstr);
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2001-04-04 11:45:22 +04:00
|
|
|
/*
|
|
|
|
* If id is a string that's a reserved identifier, or else id is not
|
|
|
|
* an identifier at all, then it needs to be quoted.
|
|
|
|
*/
|
2001-08-24 07:32:31 +04:00
|
|
|
if (atom && (ATOM_KEYWORD(atom) || !js_IsIdentifier(idstr))) {
|
1999-09-21 04:13:48 +04:00
|
|
|
idstr = js_QuoteString(cx, idstr, (jschar)'\'');
|
|
|
|
if (!idstr) {
|
|
|
|
ok = JS_FALSE;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
argv[0] = STRING_TO_JSVAL(idstr);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j = 0; j < valcnt; j++) {
|
|
|
|
/* Convert val[j] to its canonical source form. */
|
|
|
|
valstr = js_ValueToSource(cx, val[j]);
|
|
|
|
if (!valstr) {
|
|
|
|
ok = JS_FALSE;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
argv[1+j] = STRING_TO_JSVAL(valstr);
|
|
|
|
vchars = valstr->chars;
|
|
|
|
vlength = valstr->length;
|
|
|
|
|
2000-03-03 02:21:03 +03:00
|
|
|
#ifndef OLD_GETTER_SETTER
|
2000-05-15 07:54:50 +04:00
|
|
|
/* Remove 'function ' from beginning of valstr. */
|
2000-03-03 02:21:03 +03:00
|
|
|
if (gsop[j]) {
|
|
|
|
int n = strlen(js_function_str) + 1;
|
|
|
|
vchars += n;
|
|
|
|
vlength -= n;
|
|
|
|
}
|
|
|
|
#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
|
|
|
|
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 *)
|
|
|
|
realloc((ochars = chars),
|
|
|
|
(nchars + (comma ? 2 : 0) +
|
|
|
|
idstr->length + 1 +
|
|
|
|
(gsop[j] ? 1 + gsop[j]->length : 0) +
|
|
|
|
vsharplength + vlength +
|
|
|
|
(outermost ? 2 : 1) + 1) * 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
|
1999-09-21 04:13:48 +04:00
|
|
|
js_strncpy(&chars[nchars], idstr->chars, idstr->length);
|
|
|
|
nchars += idstr->length;
|
|
|
|
if (gsop[j]) {
|
|
|
|
chars[nchars++] = ' ';
|
|
|
|
js_strncpy(&chars[nchars], gsop[j]->chars, gsop[j]->length);
|
|
|
|
nchars += gsop[j]->length;
|
|
|
|
}
|
|
|
|
chars[nchars++] = ':';
|
2000-03-03 02:21:03 +03:00
|
|
|
#else
|
|
|
|
if (gsop[j]) {
|
|
|
|
js_strncpy(&chars[nchars], gsop[j]->chars, gsop[j]->length);
|
|
|
|
nchars += gsop[j]->length;
|
|
|
|
chars[nchars++] = ' ';
|
|
|
|
}
|
|
|
|
js_strncpy(&chars[nchars], idstr->chars, idstr->length);
|
|
|
|
nchars += idstr->length;
|
|
|
|
if (!gsop[j])
|
|
|
|
chars[nchars++] = ':';
|
|
|
|
#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);
|
|
|
|
}
|
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;
|
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
#endif /* JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE */
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
#if JS_HAS_INITIALIZERS
|
|
|
|
if (cx->version == JSVERSION_1_2)
|
2001-02-07 02:19:44 +03:00
|
|
|
return js_obj_toSource(cx, obj, argc, argv, rval);
|
1998-04-24 04:31:11 +04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
static JSBool
|
|
|
|
obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
|
|
|
*rval = OBJECT_TO_JSVAL(obj);
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
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;
|
|
|
|
JSPrincipals *principals;
|
|
|
|
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;
|
|
|
|
JSBool setCallerScopeChain = JS_FALSE, setCallerVarObj = JS_FALSE;
|
1998-03-28 05:44:41 +03:00
|
|
|
#endif
|
|
|
|
|
2001-02-14 12:11:09 +03:00
|
|
|
fp = cx->fp;
|
|
|
|
caller = fp->down;
|
2000-03-11 09:32:42 +03:00
|
|
|
indirectCall = (!caller->pc || *caller->pc != JSOP_EVAL);
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2001-01-31 04:12:15 +03:00
|
|
|
if (JSVERSION_IS_ECMA(cx->version) &&
|
|
|
|
indirectCall &&
|
|
|
|
!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
|
|
|
}
|
|
|
|
|
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) {
|
2001-02-14 12:11:09 +03:00
|
|
|
callerScopeChain = caller->scopeChain;
|
|
|
|
if (obj != callerScopeChain) {
|
|
|
|
scopeobj = js_NewObject(cx, &js_WithClass, obj,
|
|
|
|
callerScopeChain);
|
|
|
|
if (!scopeobj)
|
|
|
|
return JS_FALSE;
|
|
|
|
|
|
|
|
/* Set fp->scopeChain too, for the compiler. */
|
|
|
|
caller->scopeChain = fp->scopeChain = scopeobj;
|
|
|
|
setCallerScopeChain = JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
callerVarObj = caller->varobj;
|
|
|
|
if (obj != callerVarObj) {
|
|
|
|
/* Set fp->varobj too, for the compiler. */
|
|
|
|
caller->varobj = fp->varobj = obj;
|
|
|
|
setCallerVarObj = JS_TRUE;
|
|
|
|
}
|
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
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
#if JS_BUG_EVAL_THIS_SCOPE
|
2001-02-07 02:19:44 +03:00
|
|
|
/* An old version used the object in which eval was found for scope. */
|
|
|
|
scopeobj = obj;
|
1998-04-24 04:31:11 +04:00
|
|
|
#else
|
2001-02-07 02:19:44 +03:00
|
|
|
/* Compile using caller's current scope object. */
|
|
|
|
scopeobj = caller->scopeChain;
|
1998-03-28 05:44:41 +03:00
|
|
|
#endif
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
|
|
|
str = JSVAL_TO_STRING(argv[0]);
|
|
|
|
if (caller->script) {
|
2001-02-07 02:19:44 +03:00
|
|
|
file = caller->script->filename;
|
|
|
|
line = js_PCToLineNumber(caller->script, caller->pc);
|
|
|
|
principals = caller->script->principals;
|
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
|
|
|
|
2001-02-14 12:11:09 +03:00
|
|
|
fp->special |= JSFRAME_EVAL;
|
1998-04-24 04:31:11 +04:00
|
|
|
script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,
|
2001-02-07 02:19:44 +03:00
|
|
|
str->chars, str->length,
|
|
|
|
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
|
|
|
}
|
|
|
|
|
1999-07-01 06:24:32 +04:00
|
|
|
#if !JS_BUG_EVAL_THIS_SCOPE
|
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). */
|
|
|
|
scopeobj = caller->scopeChain;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
#endif
|
2001-02-14 12:11:09 +03:00
|
|
|
ok = js_Execute(cx, scopeobj, script, caller, fp->special & JSFRAME_EVAL,
|
1999-12-01 07:30:04 +03:00
|
|
|
rval);
|
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. */
|
2001-02-14 12:11:09 +03:00
|
|
|
if (setCallerScopeChain)
|
|
|
|
caller->scopeChain = callerScopeChain;
|
|
|
|
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
|
|
|
{
|
|
|
|
JSObject *funobj;
|
|
|
|
jsval argv[3];
|
|
|
|
|
2000-02-04 05:01:49 +03:00
|
|
|
funobj = (JSObject *) closure;
|
1998-03-28 05:44:41 +03:00
|
|
|
argv[0] = id;
|
|
|
|
argv[1] = old;
|
|
|
|
argv[2] = *nvp;
|
1999-06-26 07:28:47 +04:00
|
|
|
return js_InternalCall(cx, obj, OBJECT_TO_JSVAL(funobj), 3, argv, nvp);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
|
|
|
JSFunction *fun;
|
1998-04-24 04:31:11 +04:00
|
|
|
jsval userid, value;
|
|
|
|
jsid symid;
|
|
|
|
uintN attrs;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
fun = js_ValueToFunction(cx, &argv[1], JS_FALSE);
|
1998-03-28 05:44:41 +03:00
|
|
|
if (!fun)
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
1998-03-28 05:44:41 +03:00
|
|
|
argv[1] = OBJECT_TO_JSVAL(fun->object);
|
|
|
|
|
|
|
|
/* Compute the unique int/atom symbol id needed by js_LookupProperty. */
|
|
|
|
userid = argv[0];
|
2000-06-01 02:10:53 +04:00
|
|
|
if (!JS_ValueToId(cx, userid, &symid))
|
|
|
|
return JS_FALSE;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!OBJ_CHECK_ACCESS(cx, obj, symid, 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;
|
1998-04-24 04:31:11 +04:00
|
|
|
return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, fun->object);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
1998-04-24 04:31:11 +04:00
|
|
|
JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL);
|
1998-03-28 05:44:41 +03:00
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* JS_HAS_OBJ_WATCHPOINT */
|
|
|
|
|
1998-11-05 03:08:43 +03:00
|
|
|
#if JS_HAS_NEW_OBJ_METHODS
|
|
|
|
/*
|
|
|
|
* Prototype and property query methods, to complement the 'in' and
|
|
|
|
* 'instanceof' operators.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Proposed ECMA 15.2.4.5. */
|
|
|
|
static JSBool
|
|
|
|
obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|
|
|
jsval *rval)
|
|
|
|
{
|
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;
|
|
|
|
JSBool sharedPermanent;
|
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;
|
|
|
|
if (!OBJ_LOOKUP_PROPERTY(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 if (OBJ_IS_NATIVE(obj2)) {
|
|
|
|
sprop = (JSScopeProperty *)prop;
|
|
|
|
sharedPermanent =
|
|
|
|
(~sprop->attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0;
|
|
|
|
*rval = BOOLEAN_TO_JSVAL(sharedPermanent);
|
|
|
|
} else {
|
|
|
|
*rval = JSVAL_FALSE;
|
|
|
|
}
|
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;
|
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;
|
2001-04-09 03:23:34 +04:00
|
|
|
/* XXX ECMA spec error compatible: return false unless hasOwnProperty. */
|
2000-11-29 05:05:10 +03:00
|
|
|
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
|
|
|
|
return JS_FALSE;
|
|
|
|
if (prop && obj2 != obj) {
|
|
|
|
*rval = JSVAL_FALSE;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
2000-06-01 02:10:53 +04:00
|
|
|
if (!OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs))
|
1999-09-22 09:54:44 +04:00
|
|
|
return JS_FALSE;
|
|
|
|
*rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);
|
|
|
|
return JS_TRUE;
|
1998-11-05 03:08:43 +03:00
|
|
|
}
|
|
|
|
#endif /* JS_HAS_NEW_OBJ_METHODS */
|
|
|
|
|
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-06-02 04:02:46 +04:00
|
|
|
JSBool found;
|
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;
|
2000-06-02 04:02:46 +04:00
|
|
|
if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, &found))
|
|
|
|
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,
|
|
|
|
JSPROP_GETTER, 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-06-02 04:02:46 +04:00
|
|
|
JSBool found;
|
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;
|
2000-06-02 04:02:46 +04:00
|
|
|
if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, &found))
|
|
|
|
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),
|
|
|
|
JSPROP_SETTER, 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;
|
|
|
|
JSScopeProperty *sprop;
|
|
|
|
|
|
|
|
if (!JS_ValueToId(cx, argv[0], &id))
|
|
|
|
return JS_FALSE;
|
|
|
|
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, (JSProperty **) &sprop))
|
|
|
|
return JS_FALSE;
|
|
|
|
if (sprop) {
|
|
|
|
if (sprop->attrs & JSPROP_GETTER)
|
|
|
|
*rval = OBJECT_TO_JSVAL(SPROP_GETTER(sprop, pobj));
|
|
|
|
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
|
|
|
|
}
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|
|
|
jsval *rval)
|
|
|
|
{
|
|
|
|
jsid id;
|
|
|
|
JSObject *pobj;
|
|
|
|
JSScopeProperty *sprop;
|
|
|
|
|
|
|
|
if (!JS_ValueToId(cx, argv[0], &id))
|
|
|
|
return JS_FALSE;
|
|
|
|
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, (JSProperty **) &sprop))
|
|
|
|
return JS_FALSE;
|
|
|
|
if (sprop) {
|
|
|
|
if (sprop->attrs & JSPROP_SETTER)
|
|
|
|
*rval = OBJECT_TO_JSVAL(SPROP_SETTER(sprop, pobj));
|
|
|
|
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
|
|
|
|
}
|
|
|
|
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
|
|
|
|
#if JS_HAS_NEW_OBJ_METHODS
|
|
|
|
const char js_hasOwnProperty_str[] = "hasOwnProperty";
|
|
|
|
const char js_isPrototypeOf_str[] = "isPrototypeOf";
|
|
|
|
const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
|
|
|
|
#endif
|
|
|
|
#if JS_HAS_GETTER_SETTER
|
|
|
|
const char js_defineGetter_str[] = "__defineGetter__";
|
|
|
|
const char js_defineSetter_str[] = "__defineSetter__";
|
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},
|
|
|
|
{js_toLocaleString_str, js_obj_toString, 0, 0, OBJ_TOSTRING_EXTRA},
|
|
|
|
{js_valueOf_str, obj_valueOf, 0,0,0},
|
|
|
|
{js_eval_str, obj_eval, 1,0,0},
|
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
|
|
|
|
#if JS_HAS_NEW_OBJ_METHODS
|
2000-08-19 12:37:07 +04:00
|
|
|
{js_hasOwnProperty_str, obj_hasOwnProperty, 1,0,0},
|
|
|
|
{js_isPrototypeOf_str, obj_isPrototypeOf, 1,0,0},
|
|
|
|
{js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0},
|
2000-03-03 02:21:03 +03:00
|
|
|
#endif
|
|
|
|
#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]));
|
|
|
|
if (cx->fp->constructing)
|
|
|
|
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,
|
2001-02-07 02:19:44 +03:00
|
|
|
JSProperty **propp
|
1998-04-24 04:31:11 +04:00
|
|
|
#if defined JS_THREADSAFE && defined DEBUG
|
2001-02-07 02:19:44 +03:00
|
|
|
, const char *file, uintN line
|
1998-04-24 04:31:11 +04:00
|
|
|
#endif
|
2001-02-07 02:19:44 +03:00
|
|
|
)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
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_LookupProperty(cx, obj, id, objp, propp);
|
1998-04-24 04:31:11 +04:00
|
|
|
return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
|
|
|
{
|
|
|
|
JSObject *proto = OBJ_GET_PROTO(cx, obj);
|
|
|
|
if (!proto)
|
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",
|
1998-04-24 04:31:11 +04:00
|
|
|
0,
|
|
|
|
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
|
|
|
};
|
|
|
|
|
|
|
|
#if JS_HAS_OBJ_PROTO_PROP
|
|
|
|
static JSBool
|
|
|
|
With(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
|
|
|
JSObject *parent, *proto;
|
|
|
|
jsval v;
|
|
|
|
|
2000-01-18 14:06:05 +03:00
|
|
|
if (JS_HAS_STRICT_OPTION(cx)) {
|
|
|
|
if (!JS_ReportErrorFlagsAndNumber(cx,
|
|
|
|
JSREPORT_WARNING | JSREPORT_STRICT,
|
|
|
|
js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_DEPRECATED_USAGE,
|
|
|
|
js_WithClass.name)) {
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!cx->fp->constructing) {
|
2001-02-07 02:19:44 +03:00
|
|
|
obj = js_NewObject(cx, &js_WithClass, NULL, NULL);
|
|
|
|
if (!obj)
|
|
|
|
return JS_FALSE;
|
|
|
|
*rval = OBJECT_TO_JSVAL(obj);
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
|
|
|
parent = cx->fp->scopeChain;
|
|
|
|
if (argc > 0) {
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!js_ValueToObject(cx, argv[0], &proto))
|
|
|
|
return JS_FALSE;
|
|
|
|
v = OBJECT_TO_JSVAL(proto);
|
|
|
|
if (!obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PROTO), &v))
|
|
|
|
return JS_FALSE;
|
|
|
|
if (argc > 1) {
|
|
|
|
if (!js_ValueToObject(cx, argv[1], &parent))
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
v = OBJECT_TO_JSVAL(parent);
|
|
|
|
return obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PARENT), &v);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
JSObject *
|
|
|
|
js_InitObjectClass(JSContext *cx, JSObject *obj)
|
|
|
|
{
|
|
|
|
JSObject *proto;
|
2000-08-19 12:37:07 +04:00
|
|
|
jsval eval;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
|
|
|
#if JS_HAS_SHARP_VARS
|
1998-10-14 14:22:38 +04:00
|
|
|
JS_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >= ATOM_INDEX_LIMIT_LOG2 + 1);
|
1998-03-28 05:44:41 +03:00
|
|
|
#endif
|
|
|
|
|
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);
|
1998-03-28 05:44:41 +03:00
|
|
|
#if JS_HAS_OBJ_PROTO_PROP
|
|
|
|
if (!JS_InitClass(cx, obj, NULL, &js_WithClass, With, 0,
|
2001-02-07 02:19:44 +03:00
|
|
|
NULL, NULL, NULL, NULL)) {
|
|
|
|
return NULL;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
#endif
|
2000-06-27 06:37:25 +04:00
|
|
|
|
2000-05-02 01:55:52 +04:00
|
|
|
/* ECMA (15.1.2.1) says 'eval' is also a property of the global object. */
|
|
|
|
if (!OBJ_GET_PROPERTY(cx, proto, (jsid)cx->runtime->atomState.evalAtom,
|
2000-08-19 12:37:07 +04:00
|
|
|
&eval)) {
|
2000-05-02 01:55:52 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (!OBJ_DEFINE_PROPERTY(cx, obj, (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;
|
- 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
|
|
|
map->nslots = JS_INITIAL_NSLOTS;
|
1998-04-24 04:31:11 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2001-04-14 11:34:58 +04:00
|
|
|
static JSBool
|
|
|
|
GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
|
|
|
|
JSObject **protop);
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
JSObject *
|
|
|
|
js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
|
|
|
|
{
|
|
|
|
JSObject *obj, *ctor;
|
|
|
|
JSObjectOps *ops;
|
|
|
|
JSObjectMap *map;
|
|
|
|
jsval cval;
|
- 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;
|
|
|
|
jsval *newslots;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
/* Allocate an object from the GC heap and zero it. */
|
2000-02-04 05:01:49 +03:00
|
|
|
obj = (JSObject *) js_AllocGCThing(cx, GCX_OBJECT);
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!obj)
|
2001-02-07 02:19:44 +03:00
|
|
|
return NULL;
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
/* Bootstrap the ur-object, and make it the default prototype object. */
|
|
|
|
if (!proto) {
|
2001-04-14 11:34:58 +04:00
|
|
|
if (!GetClassPrototype(cx, parent, clasp->name, &proto))
|
2001-02-07 02:19:44 +03:00
|
|
|
goto bad;
|
2001-04-14 11:34:58 +04:00
|
|
|
if (!proto && !GetClassPrototype(cx, parent, js_Object_str, &proto))
|
2001-02-07 02:19:44 +03:00
|
|
|
goto bad;
|
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
|
|
|
|
|
|
|
if (proto && (map = proto->map)->ops == ops) {
|
2001-02-07 02:19:44 +03:00
|
|
|
/* Default parent to the parent of the prototype's constructor. */
|
|
|
|
if (!parent) {
|
|
|
|
if (!OBJ_GET_PROPERTY(cx, proto,
|
|
|
|
(jsid)cx->runtime->atomState.constructorAtom,
|
|
|
|
&cval)) {
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
if (JSVAL_IS_OBJECT(cval) && (ctor = JSVAL_TO_OBJECT(cval)) != NULL)
|
|
|
|
parent = OBJ_GET_PARENT(cx, ctor);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Share the given prototype's map. */
|
|
|
|
obj->map = js_HoldObjectMap(cx, map);
|
- 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
|
|
|
|
|
|
|
/* Ensure that obj starts with the minimum slots for clasp. */
|
|
|
|
nslots = JS_INITIAL_NSLOTS;
|
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
|
|
|
|
|
|
|
/* Let ops->newObjectMap set nslots so as to reserve slots. */
|
|
|
|
nslots = map->nslots;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04: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
|
|
|
/* Allocate a slots vector, with a -1'st element telling its length. */
|
|
|
|
newslots = (jsval *) JS_malloc(cx, (nslots + 1) * sizeof(jsval));
|
|
|
|
if (!newslots)
|
2001-02-07 02:19:44 +03:00
|
|
|
goto bad;
|
- 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
|
|
|
newslots[0] = nslots;
|
|
|
|
newslots++;
|
|
|
|
|
|
|
|
/* Set the proto, parent, and class properties. */
|
|
|
|
newslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
|
|
|
|
newslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);
|
|
|
|
newslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp);
|
|
|
|
|
|
|
|
/* Clear above JSSLOT_CLASS so the GC doesn't load uninitialized memory. */
|
|
|
|
for (i = JSSLOT_CLASS + 1; i < nslots; i++)
|
|
|
|
newslots[i] = JSVAL_VOID;
|
1998-10-14 14:22:38 +04: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
|
|
|
/* Store newslots after initializing all of 'em, just in case. */
|
|
|
|
obj->slots = newslots;
|
|
|
|
|
|
|
|
if (cx->runtime->objectHook)
|
1998-10-14 14:22:38 +04:00
|
|
|
cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData);
|
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
return obj;
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
bad:
|
|
|
|
cx->newborn[GCX_OBJECT] = NULL;
|
|
|
|
return NULL;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
2001-04-14 11:34:58 +04:00
|
|
|
FindConstructor(JSContext *cx, JSObject *scope, const char *name, jsval *vp)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
|
|
|
JSAtom *atom;
|
2001-04-14 11:34:58 +04:00
|
|
|
JSObject *obj;
|
1998-06-10 03:04:48 +04:00
|
|
|
JSObject *pobj;
|
|
|
|
JSScopeProperty *sprop;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
atom = js_Atomize(cx, name, strlen(name), 0);
|
1998-03-28 05:44:41 +03:00
|
|
|
if (!atom)
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2001-04-14 11:34:58 +04:00
|
|
|
if (scope || (cx->fp && (scope = cx->fp->scopeChain) != NULL)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
/* Find the topmost object in the scope chain. */
|
|
|
|
do {
|
2001-04-14 11:34:58 +04:00
|
|
|
obj = scope;
|
|
|
|
scope = OBJ_GET_PARENT(cx, obj);
|
|
|
|
} while (scope);
|
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;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
1998-07-31 04:07:22 +04:00
|
|
|
if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, (JSProperty**)&sprop))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
1998-07-31 04:07:22 +04:00
|
|
|
if (!sprop) {
|
2001-02-07 02:19:44 +03:00
|
|
|
*vp = JSVAL_VOID;
|
|
|
|
return JS_TRUE;
|
1998-06-10 03:04:48 +04:00
|
|
|
}
|
|
|
|
|
1998-10-14 14:22:38 +04:00
|
|
|
JS_ASSERT(OBJ_IS_NATIVE(pobj));
|
2000-12-06 00:47:23 +03:00
|
|
|
JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop));
|
1998-06-10 03:04:48 +04:00
|
|
|
*vp = OBJ_GET_SLOT(cx, pobj, sprop->slot);
|
|
|
|
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
|
|
|
|
return JS_TRUE;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
JSObject *
|
|
|
|
js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
|
2001-02-07 02:19:44 +03:00
|
|
|
JSObject *parent)
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
2000-01-18 14:06:05 +03:00
|
|
|
jsval cval, rval;
|
1998-08-26 10:14:55 +04:00
|
|
|
JSObject *obj, *ctor;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2001-04-14 11:34:58 +04:00
|
|
|
if (!FindConstructor(cx, parent, clasp->name, &cval))
|
2001-02-07 02:19:44 +03:00
|
|
|
return NULL;
|
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,
|
|
|
|
(jsid)cx->runtime->atomState.classPrototypeAtom,
|
|
|
|
&rval)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
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)
|
2001-02-07 02:19:44 +03:00
|
|
|
return NULL;
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2000-01-18 14:06:05 +03:00
|
|
|
if (!js_InternalConstruct(cx, obj, cval, 0, NULL, &rval))
|
2001-02-07 02:19:44 +03:00
|
|
|
goto bad;
|
1998-03-28 05:44:41 +03:00
|
|
|
return JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : obj;
|
1998-04-24 04:31:11 +04:00
|
|
|
bad:
|
|
|
|
cx->newborn[GCX_OBJECT] = NULL;
|
|
|
|
return NULL;
|
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;
|
- 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(obj->slots);
|
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
|
|
|
#if JS_HAS_OBJ_WATCHPOINT
|
|
|
|
/* Remove all watchpoints with weak links to obj. */
|
|
|
|
JS_ClearWatchPointsForObject(cx, obj);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Finalize obj first, in case it needs map and slots. */
|
1998-04-24 04:31:11 +04:00
|
|
|
OBJ_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;
|
- 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_free(cx, obj->slots - 1);
|
1998-03-28 05:44:41 +03:00
|
|
|
obj->slots = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
|
|
|
js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)
|
|
|
|
{
|
|
|
|
JSObjectMap *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
|
|
|
uint32 nslots, i;
|
1998-03-28 05:44:41 +03:00
|
|
|
size_t nbytes;
|
|
|
|
jsval *newslots;
|
|
|
|
|
|
|
|
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);
|
1998-03-28 05:44:41 +03:00
|
|
|
nslots = map->nslots;
|
|
|
|
if (map->freeslot >= nslots) {
|
2001-06-05 04:47:56 +04:00
|
|
|
nslots = map->freeslot;
|
- 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(nslots >= JS_INITIAL_NSLOTS);
|
|
|
|
nslots += (nslots + 1) / 2;
|
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
|
|
|
nbytes = (nslots + 1) * sizeof(jsval);
|
1998-03-28 05:44:41 +03:00
|
|
|
#if defined(XP_PC) && defined _MSC_VER && _MSC_VER <= 800
|
2001-02-07 02:19:44 +03:00
|
|
|
if (nbytes > 60000U) {
|
|
|
|
JS_ReportOutOfMemory(cx);
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
#endif
|
|
|
|
|
- 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
|
|
|
newslots = (jsval *) JS_realloc(cx, obj->slots - 1, nbytes);
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!newslots)
|
|
|
|
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
|
|
|
for (i = 1 + newslots[0]; i <= nslots; i++)
|
|
|
|
newslots[i] = JSVAL_VOID;
|
|
|
|
newslots[0] = map->nslots = nslots;
|
|
|
|
obj->slots = newslots + 1;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef TOO_MUCH_GC
|
|
|
|
obj->slots[map->freeslot] = JSVAL_VOID;
|
|
|
|
#endif
|
|
|
|
*slotp = map->freeslot++;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
|
|
|
|
{
|
|
|
|
JSObjectMap *map;
|
|
|
|
uint32 nslots;
|
|
|
|
size_t nbytes;
|
|
|
|
jsval *newslots;
|
|
|
|
|
2000-12-06 00:47:23 +03:00
|
|
|
OBJ_CHECK_SLOT(obj, slot);
|
1998-04-24 04:31:11 +04:00
|
|
|
obj->slots[slot] = JSVAL_VOID;
|
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);
|
1998-03-28 05:44:41 +03:00
|
|
|
if (map->freeslot == slot + 1)
|
2001-02-07 02:19:44 +03:00
|
|
|
map->freeslot = slot;
|
1998-03-28 05:44:41 +03:00
|
|
|
nslots = map->nslots;
|
|
|
|
if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) {
|
2001-02-07 02:19:44 +03:00
|
|
|
nslots = map->freeslot;
|
|
|
|
nslots += nslots / 2;
|
2001-05-08 05:31:02 +04:00
|
|
|
if (nslots < JS_INITIAL_NSLOTS)
|
|
|
|
nslots = JS_INITIAL_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
|
|
|
nbytes = (nslots + 1) * sizeof(jsval);
|
|
|
|
newslots = (jsval *) JS_realloc(cx, obj->slots - 1, nbytes);
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!newslots)
|
|
|
|
return;
|
- 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
|
|
|
newslots[0] = map->nslots = nslots;
|
|
|
|
obj->slots = newslots + 1;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if JS_BUG_EMPTY_INDEX_ZERO
|
|
|
|
#define CHECK_FOR_EMPTY_INDEX(id) \
|
1998-10-14 14:22:38 +04:00
|
|
|
JS_BEGIN_MACRO \
|
2001-02-07 02:19:44 +03:00
|
|
|
if (_str->length == 0) \
|
|
|
|
id = JSVAL_ZERO; \
|
1998-10-14 14:22:38 +04:00
|
|
|
JS_END_MACRO
|
1998-03-28 05:44:41 +03:00
|
|
|
#else
|
|
|
|
#define CHECK_FOR_EMPTY_INDEX(id) /* nothing */
|
|
|
|
#endif
|
|
|
|
|
1998-07-31 04:07:22 +04:00
|
|
|
/* JSVAL_INT_MAX as a string */
|
|
|
|
#define JSVAL_INT_MAX_STRING "1073741823"
|
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
#define CHECK_FOR_FUNNY_INDEX(id) \
|
1998-10-14 14:22:38 +04:00
|
|
|
JS_BEGIN_MACRO \
|
2000-10-29 04:30:16 +03:00
|
|
|
if (!JSVAL_IS_INT(id)) { \
|
|
|
|
JSAtom *_atom = (JSAtom *)id; \
|
|
|
|
JSString *_str = ATOM_TO_STRING(_atom); \
|
|
|
|
const jschar *_cp = _str->chars; \
|
|
|
|
JSBool _negative = (*_cp == '-'); \
|
|
|
|
if (_negative) _cp++; \
|
|
|
|
if (JS7_ISDEC(*_cp) && \
|
|
|
|
_str->length - _negative <= sizeof(JSVAL_INT_MAX_STRING) - 1) \
|
|
|
|
{ \
|
|
|
|
jsuint _index = JS7_UNDEC(*_cp++); \
|
|
|
|
jsuint _oldIndex = 0; \
|
|
|
|
jsuint _c = 0; \
|
|
|
|
if (_index != 0) { \
|
|
|
|
while (JS7_ISDEC(*_cp)) { \
|
|
|
|
_oldIndex = _index; \
|
|
|
|
_c = JS7_UNDEC(*_cp); \
|
|
|
|
_index = 10 * _index + _c; \
|
|
|
|
_cp++; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
if (*_cp == 0 && \
|
|
|
|
(_oldIndex < (JSVAL_INT_MAX / 10) || \
|
|
|
|
(_oldIndex == (JSVAL_INT_MAX / 10) && \
|
|
|
|
_c <= (JSVAL_INT_MAX % 10)))) { \
|
2000-10-29 21:08:40 +03:00
|
|
|
if (_negative) _index = 0 - _index; \
|
2000-10-29 04:30:16 +03:00
|
|
|
id = INT_TO_JSVAL((jsint)_index); \
|
|
|
|
} \
|
|
|
|
} else { \
|
|
|
|
CHECK_FOR_EMPTY_INDEX(id); \
|
|
|
|
} \
|
|
|
|
} \
|
1998-10-14 14:22:38 +04:00
|
|
|
JS_END_MACRO
|
1998-03-28 05:44:41 +03:00
|
|
|
|
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)
|
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
|
|
|
*/
|
1998-03-28 05:44:41 +03:00
|
|
|
CHECK_FOR_FUNNY_INDEX(id);
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
/* Lock if object locking is required by this implementation. */
|
|
|
|
JS_LOCK_OBJ(cx, obj);
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (!js_LookupProperty(cx, obj, id, &pobj, (JSProperty **)&sprop))
|
|
|
|
goto bad;
|
|
|
|
if (sprop &&
|
|
|
|
pobj == obj &&
|
|
|
|
(sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
|
|
|
|
sprop->attrs |= attrs;
|
|
|
|
if (attrs & JSPROP_GETTER)
|
1999-11-02 05:23:50 +03:00
|
|
|
SPROP_GETTER(sprop, pobj) = getter;
|
1999-09-21 04:13:48 +04:00
|
|
|
else
|
1999-11-02 05:23:50 +03:00
|
|
|
SPROP_SETTER(sprop, pobj) = setter;
|
1999-09-21 04:13:48 +04:00
|
|
|
if (propp)
|
|
|
|
*propp = (JSProperty *) sprop;
|
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
else {
|
|
|
|
/* Release sprop and the lock acquired by js_LookupProperty. */
|
|
|
|
js_DropProperty(cx, obj, (JSProperty *)sprop);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Release our lock on obj, in which js_LookupProperty's nested. */
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
2000-04-01 08:23:04 +04:00
|
|
|
|
|
|
|
if (sprop) {
|
|
|
|
/* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */
|
|
|
|
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
|
|
|
|
}
|
1999-09-21 04:13:48 +04:00
|
|
|
}
|
|
|
|
#endif /* JS_HAS_GETTER_SETTER */
|
|
|
|
|
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
|
|
|
|
|
|
|
/* Find a sharable scope, or get a new one for obj. */
|
1998-04-24 04:31:11 +04:00
|
|
|
scope = js_MutateScope(cx, obj, id, getter, setter, attrs, &sprop);
|
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
|
|
|
|
|
|
|
/* Add the property only if MutateScope didn't find a shared scope. */
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!sprop) {
|
2000-12-06 00:47:23 +03:00
|
|
|
if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
|
|
|
|
attrs |= JSPROP_SHARED;
|
2001-02-07 02:19:44 +03:00
|
|
|
sprop = js_NewScopeProperty(cx, scope, id, getter, setter, attrs);
|
|
|
|
if (!sprop)
|
|
|
|
goto bad;
|
|
|
|
/* XXXbe called with lock held */
|
|
|
|
if (!clasp->addProperty(cx, obj, sprop->id, &value) ||
|
|
|
|
!scope->ops->add(cx, scope, id, sprop)) {
|
|
|
|
js_DestroyScopeProperty(cx, scope, sprop);
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
PROPERTY_CACHE_FILL(cx, &cx->runtime->propertyCache, obj, id,
|
|
|
|
(JSProperty *)sprop);
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
|
2000-12-06 00:47:23 +03:00
|
|
|
if (SPROP_HAS_VALID_SLOT(sprop))
|
|
|
|
LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value);
|
1998-04-24 04:31:11 +04:00
|
|
|
if (propp) {
|
|
|
|
#ifdef JS_THREADSAFE
|
2001-05-17 05:43:58 +04:00
|
|
|
HOLD_SCOPE_PROPERTY(scope, sprop);
|
1998-04-24 04:31:11 +04:00
|
|
|
#endif
|
2001-02-07 02:19:44 +03:00
|
|
|
*propp = (JSProperty *) sprop;
|
1998-04-24 04:31:11 +04:00
|
|
|
} else {
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2001-05-17 05:43:58 +04:00
|
|
|
JS_STATIC_DLL_CALLBACK(const void *)
|
|
|
|
resolving_GetKey(JSDHashTable *table, JSDHashEntryHdr *hdr)
|
|
|
|
{
|
|
|
|
JSResolvingEntry *entry = (JSResolvingEntry *)hdr;
|
|
|
|
|
|
|
|
return &entry->key;
|
|
|
|
}
|
|
|
|
|
|
|
|
JS_STATIC_DLL_CALLBACK(JSDHashNumber)
|
|
|
|
resolving_HashKey(JSDHashTable *table, const void *ptr)
|
|
|
|
{
|
|
|
|
const JSResolvingKey *key = (const JSResolvingKey *)ptr;
|
|
|
|
|
|
|
|
return ((JSDHashNumber)key->obj >> JSVAL_TAGBITS) ^ key->id;
|
|
|
|
}
|
|
|
|
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
|
|
resolving_MatchEntry(JSDHashTable *table,
|
|
|
|
const JSDHashEntryHdr *hdr,
|
|
|
|
const void *ptr)
|
|
|
|
{
|
|
|
|
const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
|
|
|
|
const JSResolvingKey *key = (const JSResolvingKey *)ptr;
|
|
|
|
|
|
|
|
return entry->key.obj == key->obj && entry->key.id == key->id;
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSDHashTableOps resolving_dhash_ops = {
|
|
|
|
JS_DHashAllocTable,
|
|
|
|
JS_DHashFreeTable,
|
|
|
|
resolving_GetKey,
|
|
|
|
resolving_HashKey,
|
|
|
|
resolving_MatchEntry,
|
|
|
|
JS_DHashMoveEntryStub,
|
|
|
|
JS_DHashClearEntryStub,
|
2001-05-23 03:25:18 +04:00
|
|
|
JS_DHashFinalizeStub,
|
|
|
|
NULL
|
2001-05-17 05:43:58 +04:00
|
|
|
};
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
#if defined JS_THREADSAFE && defined DEBUG
|
1998-03-28 05:44:41 +03:00
|
|
|
JS_FRIEND_API(JSBool)
|
1998-04-24 04:31:11 +04:00
|
|
|
_js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
|
2001-02-07 02:19:44 +03:00
|
|
|
JSProperty **propp, const char *file, uintN line)
|
1998-04-24 04:31:11 +04:00
|
|
|
#else
|
|
|
|
JS_FRIEND_API(JSBool)
|
|
|
|
js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
|
2001-02-07 02:19:44 +03:00
|
|
|
JSProperty **propp)
|
1998-04-24 04:31:11 +04:00
|
|
|
#endif
|
1998-03-28 05:44:41 +03:00
|
|
|
{
|
1998-10-14 14:22:38 +04:00
|
|
|
JSHashNumber hash;
|
2000-04-15 06:01:02 +04:00
|
|
|
JSScope *scope;
|
1998-03-28 05:44:41 +03:00
|
|
|
JSSymbol *sym;
|
|
|
|
JSClass *clasp;
|
|
|
|
JSResolveOp resolve;
|
2001-05-17 05:43:58 +04:00
|
|
|
JSResolvingKey key;
|
|
|
|
JSDHashTable *table;
|
|
|
|
JSDHashEntryHdr *entry;
|
2001-05-25 07:05:38 +04:00
|
|
|
uint32 generation;
|
1998-03-28 05:44:41 +03:00
|
|
|
JSNewResolveOp newresolve;
|
1998-04-24 04:31:11 +04:00
|
|
|
uintN flags;
|
|
|
|
uint32 format;
|
|
|
|
JSObject *obj2, *proto;
|
2001-05-17 05:43:58 +04:00
|
|
|
JSBool ok;
|
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
|
|
|
*/
|
1998-03-28 05:44:41 +03:00
|
|
|
CHECK_FOR_FUNNY_INDEX(id);
|
|
|
|
|
|
|
|
/* Search scopes starting with obj and following the prototype link. */
|
- [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
|
|
|
hash = js_HashId(id);
|
1998-04-24 04:31:11 +04:00
|
|
|
for (;;) {
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_LOCK_OBJ(cx, obj);
|
|
|
|
SET_OBJ_INFO(obj, file, line);
|
|
|
|
scope = OBJ_SCOPE(obj);
|
2000-04-15 06:01:02 +04:00
|
|
|
if (scope->object == obj) {
|
|
|
|
sym = scope->ops->lookup(cx, scope, id, hash);
|
|
|
|
} else {
|
|
|
|
/* Shared prototype scope: try resolve before lookup. */
|
|
|
|
sym = NULL;
|
|
|
|
}
|
2001-05-17 05:43:58 +04:00
|
|
|
|
|
|
|
/* Try obj's class resolve hook if id was not found in obj's scope. */
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!sym) {
|
|
|
|
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;
|
|
|
|
table = cx->resolving;
|
|
|
|
if (table) {
|
|
|
|
entry = JS_DHashTableOperate(table, &key, JS_DHASH_LOOKUP);
|
|
|
|
if (JS_DHASH_ENTRY_IS_BUSY(entry)) {
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
table = JS_NewDHashTable(&resolving_dhash_ops,
|
|
|
|
NULL,
|
|
|
|
sizeof(JSResolvingEntry),
|
|
|
|
JS_DHASH_MIN_SIZE);
|
|
|
|
if (!table)
|
|
|
|
goto outofmem;
|
|
|
|
cx->resolving = table;
|
|
|
|
}
|
|
|
|
entry = JS_DHashTableOperate(table, &key, JS_DHASH_ADD);
|
|
|
|
if (!entry) {
|
|
|
|
outofmem:
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
JS_ReportOutOfMemory(cx);
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
((JSResolvingEntry *)entry)->key = key;
|
2001-05-25 07:05:38 +04:00
|
|
|
generation = table->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;
|
|
|
|
flags = 0;
|
|
|
|
if (cx->fp && cx->fp->pc) {
|
|
|
|
format = js_CodeSpec[*cx->fp->pc].format;
|
|
|
|
if ((format & JOF_MODEMASK) != JOF_NAME)
|
|
|
|
flags |= JSRESOLVE_QUALIFIED;
|
|
|
|
if (format & JOF_SET)
|
|
|
|
flags |= JSRESOLVE_ASSIGNING;
|
|
|
|
}
|
|
|
|
obj2 = NULL;
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
2001-05-17 05:43:58 +04:00
|
|
|
ok = newresolve(cx, obj, js_IdToValue(id), flags, &obj2);
|
|
|
|
if (!ok)
|
|
|
|
goto cleanup;
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_LOCK_OBJ(cx, obj);
|
|
|
|
SET_OBJ_INFO(obj, file, line);
|
|
|
|
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)
|
|
|
|
sym = scope->ops->lookup(cx, scope, id, hash);
|
|
|
|
}
|
|
|
|
if (obj2 != obj && (!sym || !sym_property(sym))) {
|
|
|
|
JS_UNLOCK_OBJ(cx, obj2);
|
|
|
|
JS_LOCK_OBJ(cx, obj);
|
2001-03-11 05:35:10 +03:00
|
|
|
}
|
2001-05-17 05:43:58 +04:00
|
|
|
#ifdef DEBUG
|
|
|
|
/*
|
|
|
|
* Set obj so we can assert that its map points to
|
|
|
|
* scope, before we return scope->object in *objp.
|
|
|
|
*/
|
|
|
|
if (sym && sym_property(sym))
|
|
|
|
obj = obj2;
|
|
|
|
#endif
|
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);
|
2001-05-17 05:43:58 +04:00
|
|
|
ok = resolve(cx, obj, js_IdToValue(id));
|
|
|
|
if (!ok)
|
|
|
|
goto cleanup;
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_LOCK_OBJ(cx, obj);
|
|
|
|
SET_OBJ_INFO(obj, file, line);
|
|
|
|
scope = OBJ_SCOPE(obj);
|
2001-05-17 05:43:58 +04:00
|
|
|
JS_ASSERT(MAP_IS_NATIVE(&scope->map));
|
|
|
|
if (scope->object == obj)
|
2001-02-07 02:19:44 +03:00
|
|
|
sym = scope->ops->lookup(cx, scope, id, hash);
|
|
|
|
}
|
2001-05-17 05:43:58 +04:00
|
|
|
|
|
|
|
cleanup:
|
2001-05-25 07:05:38 +04:00
|
|
|
if (table->generation == generation)
|
|
|
|
JS_DHashTableRawRemove(table, entry);
|
|
|
|
else
|
|
|
|
JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE);
|
2001-05-17 05:43:58 +04:00
|
|
|
if (table->entryCount == 0) {
|
|
|
|
cx->resolving = NULL;
|
|
|
|
JS_DHashTableDestroy(table);
|
|
|
|
}
|
|
|
|
if (!ok || *propp)
|
|
|
|
return ok;
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
|
|
|
}
|
2001-05-17 05:43:58 +04:00
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
if (sym && (sprop = sym_property(sym)) != NULL) {
|
|
|
|
JS_ASSERT(OBJ_SCOPE(obj) == scope);
|
2001-04-14 11:34:58 +04:00
|
|
|
*objp = scope->object; /* XXXbe hide in jsscope.[ch] */
|
1998-04-24 04:31:11 +04:00
|
|
|
#ifdef JS_THREADSAFE
|
2001-05-17 05:43:58 +04:00
|
|
|
HOLD_SCOPE_PROPERTY(scope, sprop);
|
1998-04-24 04:31:11 +04:00
|
|
|
#endif
|
2001-02-07 02:19:44 +03:00
|
|
|
*propp = (JSProperty *) sprop;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
proto = LOCKED_OBJ_GET_PROTO(obj);
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
if (!proto)
|
|
|
|
break;
|
|
|
|
if (!OBJ_IS_NATIVE(proto))
|
|
|
|
return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
|
|
|
|
obj = proto;
|
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;
|
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. */
|
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
|
|
|
JS_LOCK_OBJ(cx, obj);
|
2001-02-07 02:19:44 +03:00
|
|
|
PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, prop);
|
|
|
|
if (prop) {
|
1998-04-24 04:31:11 +04:00
|
|
|
#ifdef JS_THREADSAFE
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
2001-05-17 05:43:58 +04:00
|
|
|
HOLD_SCOPE_PROPERTY(OBJ_SCOPE(obj), (JSScopeProperty *)prop);
|
1998-04-24 04:31:11 +04:00
|
|
|
#endif
|
2001-02-07 02:19:44 +03:00
|
|
|
*objp = obj;
|
|
|
|
*pobjp = obj;
|
|
|
|
*propp = prop;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
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
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
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) {
|
|
|
|
PROPERTY_CACHE_FILL(cx, &rt->propertyCache, pobj, id, prop);
|
|
|
|
*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;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
1998-04-24 04:31:11 +04:00
|
|
|
js_FindVariable(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
|
|
|
{
|
|
|
|
JSObject *obj;
|
|
|
|
JSProperty *prop;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First look for id's property along the "with" statement and the
|
|
|
|
* statically-linked scope chains.
|
|
|
|
*/
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!js_FindProperty(cx, id, objp, pobjp, propp))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
1998-03-28 05:44:41 +03:00
|
|
|
if (*propp)
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_TRUE;
|
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
|
|
|
*/
|
|
|
|
obj = *objp;
|
1998-10-14 14:22:38 +04:00
|
|
|
JS_ASSERT(obj);
|
1998-03-28 05:44:41 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Make a top-level variable.
|
|
|
|
*/
|
1999-11-12 09:03:40 +03:00
|
|
|
if (JS_HAS_STRICT_OPTION(cx)) {
|
|
|
|
JSString *str = JSVAL_TO_STRING(js_IdToValue(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))) {
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
1999-11-12 09:03:40 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
|
2001-02-07 02:19:44 +03:00
|
|
|
JSPROP_ENUMERATE, &prop)) {
|
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
*pobjp = obj;
|
1998-03-28 05:44:41 +03:00
|
|
|
*propp = prop;
|
|
|
|
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;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSScopeProperty *sprop;
|
2000-02-27 02:47:33 +03:00
|
|
|
JSScope *scope;
|
2000-12-06 00:47:23 +03:00
|
|
|
uint32 slot;
|
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.
|
|
|
|
*/
|
|
|
|
CHECK_FOR_FUNNY_INDEX(id);
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!js_LookupProperty(cx, obj, id, &obj2, (JSProperty **)&sprop))
|
2000-04-04 12:17:05 +04:00
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!sprop) {
|
2000-09-19 06:24:11 +04:00
|
|
|
jsval default_val;
|
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
#if JS_BUG_NULL_INDEX_PROPS
|
2000-04-04 12:17:05 +04:00
|
|
|
/* Indexed properties defaulted to null in old versions. */
|
2000-09-19 06:24:11 +04:00
|
|
|
default_val = (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0)
|
|
|
|
? JSVAL_NULL
|
|
|
|
: JSVAL_VOID;
|
1998-03-28 05:44:41 +03:00
|
|
|
#else
|
2000-09-19 06:24:11 +04:00
|
|
|
default_val = JSVAL_VOID;
|
1998-03-28 05:44:41 +03:00
|
|
|
#endif
|
2000-09-19 06:24:11 +04:00
|
|
|
*vp = default_val;
|
|
|
|
|
|
|
|
if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, js_IdToValue(id), vp))
|
|
|
|
return JS_FALSE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Give a strict warning if foo.bar is evaluated by a script for an
|
|
|
|
* object foo with no property named 'bar'.
|
|
|
|
*/
|
|
|
|
if (JS_HAS_STRICT_OPTION(cx) &&
|
|
|
|
*vp == default_val &&
|
|
|
|
cx->fp && cx->fp->pc &&
|
|
|
|
(*cx->fp->pc == JSOP_GETPROP || *cx->fp->pc == JSOP_GETELEM))
|
|
|
|
{
|
|
|
|
jsbytecode *pc, *endpc;
|
|
|
|
JSString *str;
|
|
|
|
|
|
|
|
/* Kludge to allow (typeof foo == "undefined") tests. */
|
|
|
|
JS_ASSERT(cx->fp->script);
|
|
|
|
pc = cx->fp->pc;
|
|
|
|
pc += js_CodeSpec[*pc].length;
|
|
|
|
endpc = cx->fp->script->code + cx->fp->script->length;
|
|
|
|
while (pc < endpc) {
|
|
|
|
if (*pc == JSOP_TYPEOF)
|
|
|
|
return JS_TRUE;
|
|
|
|
if (*pc != JSOP_GROUP)
|
|
|
|
break;
|
|
|
|
pc++;
|
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2000-09-19 06:24:11 +04:00
|
|
|
/* Ok, bad undefined property reference: whine about it. */
|
|
|
|
str = js_DecompileValueGenerator(cx, JS_FALSE, js_IdToValue(id),
|
|
|
|
NULL);
|
|
|
|
if (!str ||
|
|
|
|
!JS_ReportErrorFlagsAndNumber(cx,
|
|
|
|
JSREPORT_WARNING|JSREPORT_STRICT,
|
|
|
|
js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_UNDEFINED_PROP,
|
|
|
|
JS_GetStringBytes(str))) {
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return JS_TRUE;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
if (!OBJ_IS_NATIVE(obj2)) {
|
2000-04-04 12:17:05 +04:00
|
|
|
OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);
|
|
|
|
return OBJ_GET_PROPERTY(cx, obj2, id, vp);
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Unlock obj2 before calling getter, relock after to avoid deadlock. */
|
2000-02-27 02:47:33 +03:00
|
|
|
scope = OBJ_SCOPE(obj2);
|
1998-04-24 04:31:11 +04:00
|
|
|
slot = sprop->slot;
|
2000-12-06 00:47:23 +03:00
|
|
|
*vp = (slot != SPROP_INVALID_SLOT)
|
|
|
|
? LOCKED_OBJ_GET_SLOT(obj2, slot)
|
|
|
|
: JSVAL_VOID;
|
2000-02-27 02:47:33 +03:00
|
|
|
#ifndef JS_THREADSAFE
|
2001-05-17 05:43:58 +04:00
|
|
|
HOLD_SCOPE_PROPERTY(scope, sprop);
|
2000-02-27 02:47:33 +03:00
|
|
|
#endif
|
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!SPROP_GET(cx, sprop, obj, obj2, vp)) {
|
2000-02-27 02:47:33 +03:00
|
|
|
JS_LOCK_OBJ_VOID(cx, obj2, js_DropScopeProperty(cx, scope, sprop));
|
2000-04-04 12:17:05 +04:00
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
2000-02-27 02:47:33 +03:00
|
|
|
JS_LOCK_SCOPE(cx, scope);
|
|
|
|
sprop = js_DropScopeProperty(cx, scope, sprop);
|
|
|
|
if (sprop && SPROP_HAS_VALID_SLOT(sprop)) {
|
|
|
|
LOCKED_OBJ_SET_SLOT(obj2, slot, *vp);
|
|
|
|
PROPERTY_CACHE_FILL(cx, &cx->runtime->propertyCache, obj2, id,
|
|
|
|
(JSProperty *)sprop);
|
|
|
|
}
|
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
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;
|
|
|
|
JSScopeProperty *sprop;
|
1998-03-28 05:44:41 +03:00
|
|
|
JSRuntime *rt;
|
2000-06-27 06:37:25 +04:00
|
|
|
JSClass *clasp;
|
1998-10-14 14:22:38 +04:00
|
|
|
JSHashNumber hash;
|
2001-05-17 05:43:58 +04:00
|
|
|
JSScope *scope;
|
|
|
|
JSSymbol *sym;
|
|
|
|
uintN attrs;
|
2000-06-27 06:37:25 +04:00
|
|
|
jsval userid;
|
|
|
|
JSPropertyOp getter, setter;
|
|
|
|
JSBool ok;
|
1999-07-15 06:24:23 +04:00
|
|
|
jsval pval;
|
1999-07-28 10:48:44 +04:00
|
|
|
uint32 slot;
|
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
|
|
|
*/
|
1998-03-28 05:44:41 +03:00
|
|
|
CHECK_FOR_FUNNY_INDEX(id);
|
|
|
|
|
2001-05-17 05:43:58 +04:00
|
|
|
if (!js_LookupProperty(cx, obj, id, &pobj, (JSProperty **)&sprop))
|
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;
|
2000-06-27 06:37:25 +04:00
|
|
|
rt = cx->runtime;
|
2001-05-17 05:43:58 +04:00
|
|
|
clasp = OBJ_GET_CLASS(cx, 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
|
|
|
hash = js_HashId(id);
|
2000-06-27 06:37:25 +04:00
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
#if JS_HAS_OBJ_WATCHPOINT
|
2001-05-17 05:43:58 +04:00
|
|
|
if (!sprop) {
|
|
|
|
JS_LOCK_OBJ(cx, obj);
|
|
|
|
scope = OBJ_SCOPE(obj);
|
|
|
|
sym = scope->ops->lookup(cx, scope, id, hash);
|
|
|
|
if (sym) {
|
|
|
|
sprop = sym_property(sym);
|
|
|
|
if (!sprop && scope->object == obj) {
|
|
|
|
/*
|
|
|
|
* Deleted property place-holder, could have a watchpoint that
|
|
|
|
* holds the deleted-but-watched property. If so, obj->slots
|
|
|
|
* may have shrunk, or at least obj->map->freeslot may have
|
|
|
|
* shrunk due to a delete operation destroying the property.
|
|
|
|
*/
|
|
|
|
sprop = js_FindWatchPoint(rt, obj, js_IdToValue(id));
|
|
|
|
if (sprop) {
|
|
|
|
slot = sprop->slot;
|
|
|
|
if (slot != SPROP_INVALID_SLOT &&
|
|
|
|
slot >= scope->map.freeslot) {
|
|
|
|
if (slot >= scope->map.nslots) {
|
|
|
|
uint32 nslots;
|
|
|
|
jsval *slots;
|
|
|
|
|
|
|
|
nslots = slot + slot / 2;
|
|
|
|
slots = (jsval *)
|
|
|
|
JS_realloc(cx, obj->slots - 1,
|
|
|
|
(nslots + 1) * sizeof(jsval));
|
|
|
|
if (!slots) {
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
slots[0] = scope->map.nslots = nslots;
|
|
|
|
obj->slots = slots + 1;
|
|
|
|
}
|
|
|
|
scope->map.freeslot = slot + 1;
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
2001-05-17 05:43:58 +04:00
|
|
|
|
|
|
|
/* Reconnect sprop to its symbol as sym_property(sym). */
|
|
|
|
sym->entry.value = sprop;
|
|
|
|
HOLD_SCOPE_PROPERTY(scope, sprop);
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
2001-05-17 05:43:58 +04:00
|
|
|
}
|
|
|
|
if (sprop) {
|
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
HOLD_SCOPE_PROPERTY(scope, sprop);
|
|
|
|
#endif
|
|
|
|
pobj = obj;
|
2001-02-07 02:19:44 +03:00
|
|
|
}
|
|
|
|
}
|
2001-05-17 05:43:58 +04:00
|
|
|
if (!sprop)
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
#endif
|
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
|
|
|
|
* is held: it needs a js_DropScopeProperty followed by a JS_UNLOCK_OBJ (or
|
|
|
|
* what is equivalent to those two function calls, an OBJ_DROP_PROPERTY),
|
|
|
|
* before we return.
|
|
|
|
*/
|
|
|
|
if (!sprop) {
|
|
|
|
attrs = JSPROP_ENUMERATE;
|
|
|
|
userid = JSVAL_NULL;
|
|
|
|
getter = clasp->getProperty;
|
|
|
|
setter = clasp->setProperty;
|
1998-03-28 05:44:41 +03:00
|
|
|
} else {
|
2001-05-17 05:43:58 +04:00
|
|
|
attrs = sprop->attrs;
|
|
|
|
if (attrs & JSPROP_READONLY) {
|
|
|
|
/* XXXbe ECMA violation: readonly proto-property stops set cold. */
|
|
|
|
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
|
|
|
|
if (!JSVERSION_IS_ECMA(cx->version)) {
|
|
|
|
JSString *str = js_DecompileValueGenerator(cx,
|
|
|
|
JSDVG_IGNORE_STACK,
|
|
|
|
js_IdToValue(id),
|
|
|
|
NULL);
|
|
|
|
if (str) {
|
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_READ_ONLY,
|
|
|
|
JS_GetStringBytes(str));
|
|
|
|
}
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2001-05-17 05:43:58 +04: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);
|
2000-06-27 06:37:25 +04:00
|
|
|
|
2001-05-17 05:43:58 +04:00
|
|
|
if (pobj != obj) {
|
2000-06-27 06:37:25 +04:00
|
|
|
/* Don't clone a setter or shared prototype property. */
|
|
|
|
if (attrs & (JSPROP_SETTER | JSPROP_SHARED)) {
|
2001-05-17 05:43:58 +04:00
|
|
|
#ifndef JS_THREADSAFE
|
|
|
|
HOLD_SCOPE_PROPERTY(scope, sprop);
|
|
|
|
#endif
|
2000-06-27 06:37:25 +04:00
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
|
|
|
|
2001-05-17 05:43:58 +04:00
|
|
|
ok = SPROP_SET(cx, sprop, obj, pobj, vp);
|
|
|
|
JS_LOCK_OBJ_VOID(cx, pobj,
|
2000-06-27 06:37:25 +04:00
|
|
|
js_DropScopeProperty(cx, scope, sprop));
|
|
|
|
return ok;
|
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2001-05-17 05:43:58 +04:00
|
|
|
/* XXXbe ECMA violation: inherit attrs, getter, setter. */
|
2000-06-27 06:37:25 +04:00
|
|
|
userid = sprop->id;
|
|
|
|
getter = SPROP_GETTER_SCOPE(sprop, scope);
|
|
|
|
setter = SPROP_SETTER_SCOPE(sprop, scope);
|
2001-05-17 05:43:58 +04:00
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
2000-06-27 06:37:25 +04:00
|
|
|
sprop = NULL;
|
|
|
|
}
|
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) {
|
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);
|
2000-06-27 06:37:25 +04:00
|
|
|
scope = js_MutateScope(cx, obj, id, getter, setter, attrs, &sprop);
|
|
|
|
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
|
|
|
}
|
|
|
|
if (!sprop) {
|
2000-12-06 00:47:23 +03:00
|
|
|
if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
|
|
|
|
attrs |= JSPROP_SHARED;
|
2000-06-27 06:37:25 +04:00
|
|
|
sprop = js_NewScopeProperty(cx, scope, id, getter, setter, attrs);
|
|
|
|
if (!sprop) {
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
if (!JSVAL_IS_NULL(userid))
|
|
|
|
sprop->id = userid;
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
/* XXXbe called with obj locked */
|
|
|
|
if (!clasp->addProperty(cx, obj, sprop->id, vp)) {
|
|
|
|
js_DestroyScopeProperty(cx, scope, sprop);
|
2001-05-17 05:43:58 +04:00
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2001-05-17 05:43:58 +04:00
|
|
|
/* Initialize new property value (passed to setter) to undefined. */
|
2000-12-06 00:47:23 +03:00
|
|
|
if (SPROP_HAS_VALID_SLOT(sprop))
|
|
|
|
LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID);
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
/* Need a new symbol as well as a new property. */
|
|
|
|
sym = scope->ops->add(cx, scope, id, sprop);
|
|
|
|
if (!sym) {
|
|
|
|
js_DestroyScopeProperty(cx, scope, sprop);
|
2001-05-17 05:43:58 +04:00
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
#if JS_BUG_AUTO_INDEX_PROPS
|
2000-12-06 00:47:23 +03:00
|
|
|
if (SPROP_HAS_VALID_SLOT(sprop)) {
|
2001-05-17 05:43:58 +04:00
|
|
|
jsid index_id = (jsid) INT_TO_JSVAL(sprop->slot - JSSLOT_START);
|
|
|
|
if (!scope->ops->add(cx, scope, index_id, sprop)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
scope->ops->remove(cx, scope, id);
|
2001-05-17 05:43:58 +04:00
|
|
|
JS_UNLOCK_SCOPE(cx, scope);
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
|
|
|
}
|
2001-05-17 05:43:58 +04:00
|
|
|
PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, index_id,
|
2001-02-07 02:19:44 +03:00
|
|
|
(JSProperty *)sprop);
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
#endif
|
2001-02-07 02:19:44 +03:00
|
|
|
PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id,
|
|
|
|
(JSProperty *)sprop);
|
1998-03-28 05:44:41 +03:00
|
|
|
|
2001-05-17 05:43:58 +04:00
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
/* Hold sprop as if it were returned from js_LookupProperty. */
|
|
|
|
HOLD_SCOPE_PROPERTY(scope, sprop);
|
|
|
|
#endif
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
1999-07-01 06:24:32 +04:00
|
|
|
/* Get the current property value from its slot. */
|
|
|
|
slot = sprop->slot;
|
2000-12-06 00:47:23 +03:00
|
|
|
if (slot != SPROP_INVALID_SLOT) {
|
|
|
|
JS_ASSERT(slot < obj->map->freeslot);
|
|
|
|
pval = LOCKED_OBJ_GET_SLOT(obj, slot);
|
|
|
|
}
|
1999-07-01 06:24:32 +04:00
|
|
|
|
2001-05-17 05:43:58 +04:00
|
|
|
#ifndef JS_THREADSAFE
|
1998-04-24 04:31:11 +04:00
|
|
|
/* Hold sprop across setter callout, and drop after, in case of delete. */
|
2001-05-17 05:43:58 +04:00
|
|
|
HOLD_SCOPE_PROPERTY(scope, sprop);
|
|
|
|
#endif
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
/* Avoid deadlock by unlocking obj while calling sprop's setter. */
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
/* Let the setter modify vp before copying from it to obj->slots[slot]. */
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!SPROP_SET(cx, sprop, obj, obj, vp)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_LOCK_OBJ_VOID(cx, obj, js_DropScopeProperty(cx, scope, sprop));
|
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Relock obj until we are done with sprop. */
|
|
|
|
JS_LOCK_OBJ(cx, obj);
|
|
|
|
|
|
|
|
sprop = js_DropScopeProperty(cx, scope, sprop);
|
2000-12-06 00:47:23 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check whether sprop is still around (was not deleted), and whether it
|
|
|
|
* has a slot (it may never have had one, or we may have lost a race with
|
|
|
|
* someone who cleared scope).
|
|
|
|
*/
|
|
|
|
if (sprop && SPROP_HAS_VALID_SLOT(sprop)) {
|
|
|
|
GC_POKE(cx, pval);
|
|
|
|
LOCKED_OBJ_SET_SLOT(obj, slot, *vp);
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
#if JS_BUG_SET_ENUMERATE
|
2000-12-06 00:47:23 +03:00
|
|
|
/* Setting a property makes it enumerable. */
|
|
|
|
sprop->attrs |= JSPROP_ENUMERATE;
|
1998-04-24 04:31:11 +04:00
|
|
|
#endif
|
2000-12-06 00:47:23 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
return JS_TRUE;
|
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;
|
|
|
|
sprop->attrs = *attrsp;
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
{
|
|
|
|
#if JS_HAS_PROP_DELETE
|
1998-04-24 04:31:11 +04:00
|
|
|
JSObject *proto;
|
|
|
|
JSProperty *prop;
|
|
|
|
JSScopeProperty *sprop;
|
1998-03-28 05:44:41 +03:00
|
|
|
JSString *str;
|
|
|
|
JSScope *scope;
|
|
|
|
JSSymbol *sym;
|
|
|
|
|
|
|
|
*rval = JSVERSION_IS_ECMA(cx->version) ? JSVAL_TRUE : JSVAL_VOID;
|
|
|
|
|
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
|
|
|
*/
|
1998-04-24 04:31:11 +04:00
|
|
|
CHECK_FOR_FUNNY_INDEX(id);
|
|
|
|
|
|
|
|
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;
|
|
|
|
if ((~sprop->attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0)
|
|
|
|
*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
|
|
|
*/
|
|
|
|
return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, js_IdToValue(id),
|
|
|
|
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);
|
|
|
|
if (JSVERSION_IS_ECMA(cx->version)) {
|
|
|
|
*rval = JSVAL_FALSE;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
|
Fixes to make JS GC truly exact:
- All jsvals for which JSVAL_IS_GCTHING evaluates to true must contain tagged
pointers into the GC heap -- therefore jsapi.c's JS_DefineConstDoubles cannot
"cheat" by tagging addresses of static jsdoubles to avoid js_NewNumberValue.
- Finalization is now interleaved with the Sweep phase, to avoid allocating
memory for finalization records while sweeping. Instead, the JSRuntime holds a
preallocated JSGCThing vector (gcFinalVec) that the Sweep phase fills and
flushes via gc_finalize_phase, repeatedly.
This means that finalizers cannot allocate a new GC thing, an incompatible but
plausible change. js_AllocGCThing asserts and then checks whether it is called
while rt->gcLevel is non-zero, and fails the allocation attempt if so. But this
fixes bug 38942, where the old sweep-then-finalize with a sweep => malloc
dependency could lead to memory exhaustion.
- Instead of scanning whole stackPool arenas, which led to UMRs (bug 27924) and
sometimes to gross over-scanning that depended on the GC bounds-checking all
thing pointers against its heap, we scan exactly those stack slots in use:
- arguments reachable from fp->argv;
- variables reachable from fp->vars;
- operands now reachable from fp->spbase, bounded above by the lesser of
fp->sp or fp->spbase + fp->script->depth for an interpreted frame; if the
latter, fp->sp has advanced logically above the operand budget, in order to
call a native method, and all unused slots from fp->sp up to depth slots
above fp->spbase must be set to JSVAL_VOID;
- stack segments pushed when calling native methods, prefixed by JSStackHeader
structs and linked from cx->stackSegments through each header.
The stack segment headers help the GC avoid scanning unused portions of the
stack: the generating pc slots running depth slots below fp->spbase, and slots
at the end of an arena that aren't sufficient to satisfy a contiguous allocation
for more args, vars, or operands.
- Exact GC means the stack pointer must remain above live operands until the
interpreter is done with them, so jsinterp.c got heavily whacked. Instead of
POPs of various kinds followed by a PUSH for binary operators (e.g.), we use
FETCH and STORE macros that index by -1 and -2 from sp, and minimize adjustments
to sp. When sp is homed to fp->sp, this allows js_DecompileValueGenerator to
find the value reliably, and if possible its generating pc.
- Finally, the O(n**2) growth rate of gc_find_flags has been fixed, using the
scheme sketched in bug 49816 and documented in a new major comment in jsgc.c.
Briefly, by allocating flags and things from one arena, we can align things on
1024-byte "thing page" boundaries, and use JSGCPageInfo headers in each page to
find a given thing's flags in O(1) time.
/be
2000-09-14 10:14:45 +04:00
|
|
|
js_IdToValue(id), NULL);
|
2001-02-07 02:19:44 +03:00
|
|
|
if (str) {
|
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_PERMANENT, JS_GetStringBytes(str));
|
|
|
|
}
|
|
|
|
return JS_FALSE;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
/* XXXbe called with obj locked */
|
|
|
|
if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, sprop->id, 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
|
|
|
}
|
|
|
|
|
2000-12-06 00:47:23 +03:00
|
|
|
if (SPROP_HAS_VALID_SLOT(sprop))
|
|
|
|
GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot));
|
1999-11-02 05:23:50 +03:00
|
|
|
scope = OBJ_SCOPE(obj);
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Purge cache only if prop is not about to be destroyed (since
|
|
|
|
* js_DestroyScopeProperty purges for us).
|
|
|
|
*/
|
|
|
|
if (sprop->nrefs != 1) {
|
- 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
|
|
|
PROPERTY_CACHE_FILL(cx, &cx->runtime->propertyCache, obj, id, NULL);
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
|
1998-03-28 05:44:41 +03:00
|
|
|
#if JS_HAS_OBJ_WATCHPOINT
|
1999-11-02 05:23:50 +03:00
|
|
|
if (SPROP_SETTER_SCOPE(sprop, scope) == js_watch_set) {
|
2001-02-07 02:19:44 +03:00
|
|
|
/*
|
|
|
|
* Keep the symbol around with null value in case of re-set.
|
|
|
|
* The watchpoint will hold the "deleted" property until it
|
|
|
|
* is removed by obj_unwatch or a native JS_ClearWatchPoint.
|
|
|
|
* See js_SetProperty for the re-set logic.
|
|
|
|
*/
|
|
|
|
for (sym = sprop->symbols; sym; sym = sym->next) {
|
|
|
|
if (sym_id(sym) == id) {
|
|
|
|
sym->entry.value = NULL;
|
|
|
|
sprop = js_DropScopeProperty(cx, scope, sprop);
|
|
|
|
JS_ASSERT(sprop);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
#endif /* JS_HAS_OBJ_WATCHPOINT */
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
scope->ops->remove(cx, scope, id);
|
|
|
|
out:
|
|
|
|
OBJ_DROP_PROPERTY(cx, obj, prop);
|
1998-03-28 05:44:41 +03:00
|
|
|
return JS_TRUE;
|
1998-04-24 04:31:11 +04:00
|
|
|
#else /* !JS_HAS_PROP_DELETE */
|
1998-03-28 05:44:41 +03:00
|
|
|
jsval null = JSVAL_NULL;
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
*rval = JSVAL_VOID;
|
|
|
|
return js_SetProperty(cx, obj, id, &null);
|
|
|
|
#endif /* !JS_HAS_PROP_DELETE */
|
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
|
|
|
{
|
1998-04-24 04:31:11 +04:00
|
|
|
jsval v;
|
|
|
|
JSString *str;
|
1998-03-28 05:44:41 +03:00
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
v = OBJECT_TO_JSVAL(obj);
|
|
|
|
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,
|
|
|
|
&v))
|
|
|
|
return JS_FALSE;
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* JS1.2 never failed (except for malloc failure) to convert an
|
|
|
|
* object to a string. ECMA requires an error if both toString
|
|
|
|
* and valueOf fail to produce a primitive value.
|
|
|
|
*/
|
|
|
|
if (!JSVAL_IS_PRIMITIVE(v) && cx->version == JSVERSION_1_2) {
|
|
|
|
char *bytes = JS_smprintf("[object %s]",
|
|
|
|
OBJ_GET_CLASS(cx, obj)->name);
|
|
|
|
if (!bytes)
|
|
|
|
return JS_FALSE;
|
|
|
|
str = JS_NewString(cx, bytes, strlen(bytes));
|
|
|
|
if (!str) {
|
|
|
|
free(bytes);
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
v = STRING_TO_JSVAL(str);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
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;
|
|
|
|
}
|
|
|
|
/* Don't convert to string (source object literal) for JS1.2. */
|
|
|
|
if (cx->version == JSVERSION_1_2 && hint == JSTYPE_BOOLEAN)
|
|
|
|
goto out;
|
|
|
|
if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0,
|
1998-10-14 14:22:38 +04:00
|
|
|
NULL, &v))
|
|
|
|
return JS_FALSE;
|
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);
|
|
|
|
str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, str);
|
|
|
|
if (str) {
|
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_CANT_CONVERT_TO,
|
|
|
|
JS_GetStringBytes(str),
|
|
|
|
(hint == JSTYPE_VOID)
|
|
|
|
? "primitive type"
|
|
|
|
: js_type_str[hint]);
|
|
|
|
}
|
|
|
|
return JS_FALSE;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
out:
|
1998-04-24 04:31:11 +04:00
|
|
|
*vp = v;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern JSIdArray *
|
|
|
|
js_NewIdArray(JSContext *cx, jsint length)
|
|
|
|
{
|
|
|
|
JSIdArray *ida;
|
|
|
|
|
2000-02-04 05:01:49 +03:00
|
|
|
ida = (JSIdArray *)
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
1999-10-25 23:24:03 +04:00
|
|
|
extern JSIdArray *
|
|
|
|
js_GrowIdArray(JSContext *cx, JSIdArray *ida, jsint length)
|
|
|
|
{
|
2000-02-04 05:01:49 +03:00
|
|
|
ida = (JSIdArray *)
|
|
|
|
JS_realloc(cx, ida, sizeof(JSIdArray) + (length - 1) * sizeof(jsval));
|
1999-10-25 23:24:03 +04:00
|
|
|
if (ida)
|
2001-02-07 02:19:44 +03:00
|
|
|
ida->length = length;
|
1999-10-25 23:24:03 +04:00
|
|
|
return ida;
|
|
|
|
}
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
/* Private type used to iterate over all properties of a native JS object */
|
|
|
|
typedef struct JSNativeIteratorState {
|
|
|
|
jsint next_index; /* index into jsid array */
|
|
|
|
JSIdArray *ida; /* All property ids in enumeration */
|
|
|
|
} JSNativeIteratorState;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function is used to enumerate the properties of native JSObjects
|
|
|
|
* and those host objects that do not define a JSNewEnumerateOp-style iterator
|
|
|
|
* function.
|
|
|
|
*/
|
|
|
|
JSBool
|
|
|
|
js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
|
2001-02-07 02:19:44 +03:00
|
|
|
jsval *statep, jsid *idp)
|
1998-04-24 04:31:11 +04:00
|
|
|
{
|
1998-07-31 04:07:22 +04:00
|
|
|
JSObject *proto_obj;
|
1998-04-24 04:31:11 +04:00
|
|
|
JSClass *clasp;
|
|
|
|
JSEnumerateOp enumerate;
|
|
|
|
JSScopeProperty *sprop;
|
|
|
|
jsint i, length;
|
|
|
|
JSScope *scope;
|
|
|
|
JSIdArray *ida;
|
|
|
|
JSNativeIteratorState *state;
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
|
|
|
case JSENUMERATE_INIT:
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!enumerate(cx, obj))
|
|
|
|
goto init_error;
|
|
|
|
length = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The set of all property ids is pre-computed when the iterator
|
|
|
|
* is initialized so as to avoid problems with properties being
|
|
|
|
* deleted during the iteration.
|
|
|
|
*/
|
|
|
|
JS_LOCK_OBJ(cx, obj);
|
|
|
|
scope = OBJ_SCOPE(obj);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this object shares a scope with its prototype, don't enumerate
|
|
|
|
* its properties. Otherwise they will be enumerated a second time
|
|
|
|
* when the prototype object is enumerated.
|
|
|
|
*/
|
|
|
|
proto_obj = OBJ_GET_PROTO(cx, obj);
|
|
|
|
if (proto_obj && scope == OBJ_SCOPE(proto_obj)) {
|
|
|
|
ida = js_NewIdArray(cx, 0);
|
|
|
|
if (!ida) {
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
goto init_error;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Object has a private scope; Enumerate all props in scope. */
|
|
|
|
for (sprop = scope->props; sprop; sprop = sprop->next) {
|
|
|
|
if ((sprop->attrs & JSPROP_ENUMERATE) && sprop->symbols)
|
|
|
|
length++;
|
|
|
|
}
|
|
|
|
ida = js_NewIdArray(cx, length);
|
|
|
|
if (!ida) {
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
goto init_error;
|
|
|
|
}
|
|
|
|
i = 0;
|
|
|
|
for (sprop = scope->props; sprop; sprop = sprop->next) {
|
|
|
|
if ((sprop->attrs & JSPROP_ENUMERATE) && sprop->symbols) {
|
|
|
|
JS_ASSERT(i < length);
|
|
|
|
ida->vector[i++] = sym_id(sprop->symbols);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
goto init_error;
|
|
|
|
}
|
|
|
|
state->ida = ida;
|
|
|
|
state->next_index = 0;
|
|
|
|
*statep = PRIVATE_TO_JSVAL(state);
|
|
|
|
if (idp)
|
|
|
|
*idp = INT_TO_JSVAL(length);
|
|
|
|
return JS_TRUE;
|
1998-07-31 04:07:22 +04:00
|
|
|
|
1998-04-24 04:31:11 +04: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++];
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
|
2001-02-07 02:19:44 +03:00
|
|
|
/* Fall through ... */
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
case JSENUMERATE_DESTROY:
|
2001-02-07 02:19:44 +03:00
|
|
|
state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
|
|
|
|
JS_DestroyIdArray(cx, state->ida);
|
|
|
|
JS_free(cx, state);
|
|
|
|
*statep = JSVAL_NULL;
|
|
|
|
return JS_TRUE;
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
default:
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_ASSERT(0);
|
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
1998-06-10 03:04:48 +04:00
|
|
|
|
|
|
|
init_error:
|
|
|
|
*statep = JSVAL_NULL;
|
|
|
|
return JS_FALSE;
|
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
|
|
|
{
|
1998-04-24 04:31:11 +04:00
|
|
|
JSObject *pobj;
|
|
|
|
JSProperty *prop;
|
|
|
|
JSScopeProperty *sprop;
|
|
|
|
JSClass *clasp;
|
1998-03-28 05:44:41 +03:00
|
|
|
JSBool ok;
|
|
|
|
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!prop) {
|
2001-02-07 02:19:44 +03:00
|
|
|
*vp = JSVAL_VOID;
|
|
|
|
*attrsp = 0;
|
2000-04-14 07:14:53 +04:00
|
|
|
clasp = OBJ_GET_CLASS(cx, obj);
|
2001-02-07 02:19:44 +03:00
|
|
|
return !clasp->checkAccess ||
|
2001-07-04 13:44:57 +04:00
|
|
|
clasp->checkAccess(cx, obj, js_IdToValue(id), mode, vp);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
if (!OBJ_IS_NATIVE(pobj)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
|
|
|
return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp);
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
sprop = (JSScopeProperty *)prop;
|
2000-12-06 00:47:23 +03:00
|
|
|
*vp = (SPROP_HAS_VALID_SLOT(sprop))
|
|
|
|
? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
|
|
|
|
: JSVAL_VOID;
|
1998-04-24 04:31:11 +04:00
|
|
|
*attrsp = sprop->attrs;
|
|
|
|
clasp = LOCKED_OBJ_GET_CLASS(obj);
|
|
|
|
if (clasp->checkAccess) {
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_UNLOCK_OBJ(cx, pobj);
|
2001-07-04 13:44:57 +04:00
|
|
|
ok = clasp->checkAccess(cx, obj, js_IdToValue(id), mode, vp);
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_LOCK_OBJ(cx, pobj);
|
1998-04-24 04:31:11 +04:00
|
|
|
} else {
|
2001-02-07 02:19:44 +03:00
|
|
|
ok = JS_TRUE;
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
1998-03-28 05:44:41 +03:00
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
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) {
|
2000-06-27 06:37:25 +04:00
|
|
|
/*
|
2000-05-15 07:54:50 +04:00
|
|
|
* The decompiler may need to access the args of the function in
|
|
|
|
* progress, so we switch the function pointer in the frame to the
|
|
|
|
* function below us, rather than the one we had hoped to call.
|
|
|
|
* XXXbe doesn't this case arise for js_Construct too?
|
2000-05-02 01:55:52 +04:00
|
|
|
*/
|
|
|
|
JSStackFrame *fp = cx->fp;
|
|
|
|
JSFunction *fun = fp->fun;
|
|
|
|
if (fp->down) /* guaranteed ? */
|
2000-05-15 07:54:50 +04:00
|
|
|
fp->fun = fp->down->fun;
|
2001-02-07 02:19:44 +03:00
|
|
|
js_ReportIsNotFunction(cx, &argv[-2], JS_FALSE);
|
2000-05-02 01:55:52 +04:00
|
|
|
fp->fun = fun;
|
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) {
|
2001-02-07 02:19:44 +03:00
|
|
|
js_ReportIsNotFunction(cx, &argv[-2], JS_TRUE);
|
|
|
|
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;
|
|
|
|
|
|
|
|
clasp = OBJ_GET_CLASS(cx, obj);
|
|
|
|
if (clasp->hasInstance)
|
2001-02-07 02:19:44 +03:00
|
|
|
return clasp->hasInstance(cx, obj, v, bp);
|
1998-04-24 04:31:11 +04:00
|
|
|
*bp = JS_FALSE;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSBool
|
|
|
|
js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
|
|
|
|
{
|
|
|
|
JSObject *obj2;
|
|
|
|
|
|
|
|
*bp = JS_FALSE;
|
|
|
|
if (JSVAL_IS_PRIMITIVE(v))
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef JS_THREADSAFE
|
|
|
|
void
|
|
|
|
js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
|
|
|
|
{
|
1999-11-02 05:23:50 +03:00
|
|
|
js_DropScopeProperty(cx, OBJ_SCOPE(obj), (JSScopeProperty *)prop);
|
1998-04-24 04:31:11 +04:00
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
}
|
1998-03-28 05:44:41 +03:00
|
|
|
#endif
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
JSBool
|
|
|
|
js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop)
|
2001-04-14 11:34:58 +04:00
|
|
|
{
|
|
|
|
return GetClassPrototype(cx, NULL, name, protop);
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
|
|
|
|
JSObject **protop)
|
1998-04-24 04:31:11 +04:00
|
|
|
{
|
|
|
|
jsval v;
|
|
|
|
JSObject *ctor;
|
|
|
|
|
2001-04-14 11:34:58 +04:00
|
|
|
if (!FindConstructor(cx, scope, name, &v))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
if (JSVAL_IS_FUNCTION(cx, v)) {
|
2001-02-07 02:19:44 +03:00
|
|
|
ctor = JSVAL_TO_OBJECT(v);
|
|
|
|
if (!OBJ_GET_PROPERTY(cx, ctor,
|
|
|
|
(jsid)cx->runtime->atomState.classPrototypeAtom,
|
|
|
|
&v)) {
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
*protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2000-06-02 04:02:46 +04:00
|
|
|
* as user-defined constructors have a DontEnum | DontDelete prototype (it
|
|
|
|
* may be reset), while native or "system" constructors require DontEnum |
|
1998-04-24 04:31:11 +04:00
|
|
|
* ReadOnly | DontDelete.
|
|
|
|
*/
|
|
|
|
if (!OBJ_DEFINE_PROPERTY(cx, ctor,
|
2001-02-07 02:19:44 +03:00
|
|
|
(jsid)cx->runtime->atomState.classPrototypeAtom,
|
|
|
|
OBJECT_TO_JSVAL(proto), NULL, NULL,
|
|
|
|
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,
|
2001-02-07 02:19:44 +03:00
|
|
|
(jsid)cx->runtime->atomState.constructorAtom,
|
|
|
|
OBJECT_TO_JSVAL(ctor), NULL, NULL,
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
#if JS_HAS_VALUEOF_HINT
|
|
|
|
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
|
|
|
#else
|
1998-10-14 14:22:38 +04:00
|
|
|
return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 0, NULL,
|
|
|
|
rval);
|
1998-03-28 05:44:41 +03:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
jsval fval;
|
1998-10-14 14:22:38 +04:00
|
|
|
JSBool ok;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Report failure only if an appropriate method was found, and calling it
|
|
|
|
* returned failure. We propagate failure in this case to make exceptions
|
|
|
|
* behave properly.
|
|
|
|
*/
|
1998-03-28 05:44:41 +03:00
|
|
|
older = JS_SetErrorReporter(cx, NULL);
|
1998-04-24 04:31:11 +04:00
|
|
|
if (OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &fval) &&
|
2001-02-07 02:19:44 +03:00
|
|
|
!JSVAL_IS_PRIMITIVE(fval)) {
|
|
|
|
ok = js_InternalCall(cx, obj, fval, argc, argv, rval);
|
1998-10-14 14:22:38 +04:00
|
|
|
} else {
|
|
|
|
ok = JS_TRUE;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
|
|
|
JS_SetErrorReporter(cx, older);
|
1998-10-14 14:22:38 +04:00
|
|
|
return ok;
|
1998-03-28 05:44:41 +03:00
|
|
|
}
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
#if JS_HAS_XDR
|
|
|
|
|
|
|
|
#include "jsxdrapi.h"
|
|
|
|
|
|
|
|
JSBool
|
|
|
|
js_XDRObject(JSXDRState *xdr, JSObject **objp)
|
|
|
|
{
|
|
|
|
JSContext *cx;
|
|
|
|
JSClass *clasp;
|
2000-08-19 12:37:07 +04:00
|
|
|
const char *className;
|
1998-04-24 04:31:11 +04:00
|
|
|
uint32 classId, classDef;
|
|
|
|
JSBool ok;
|
|
|
|
JSObject *proto;
|
|
|
|
|
|
|
|
cx = xdr->cx;
|
|
|
|
if (xdr->mode == JSXDR_ENCODE) {
|
2001-02-07 02:19:44 +03:00
|
|
|
clasp = OBJ_GET_CLASS(cx, *objp);
|
|
|
|
className = clasp->name;
|
2001-03-06 04:56:30 +03:00
|
|
|
classId = JS_XDRFindClassIdByName(xdr, className);
|
2001-02-07 02:19:44 +03:00
|
|
|
classDef = !classId;
|
2001-03-06 04:56:30 +03:00
|
|
|
if (classDef && !JS_XDRRegisterClass(xdr, clasp, &classId))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
} else {
|
2001-02-07 02:19:44 +03:00
|
|
|
classDef = 0;
|
|
|
|
className = NULL;
|
1998-11-05 03:08:43 +03:00
|
|
|
clasp = NULL; /* quell GCC overwarning */
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* XDR a flag word followed (if true) by the class name. */
|
|
|
|
if (!JS_XDRUint32(xdr, &classDef))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
2000-08-19 12:37:07 +04:00
|
|
|
if (classDef && !JS_XDRCString(xdr, (char **) &className))
|
2001-02-07 02:19:44 +03:00
|
|
|
return JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
/* From here on, return through out: to free className if it was set. */
|
|
|
|
ok = JS_XDRUint32(xdr, &classId);
|
|
|
|
if (!ok)
|
2001-02-07 02:19:44 +03:00
|
|
|
goto out;
|
1998-04-24 04:31:11 +04:00
|
|
|
|
|
|
|
if (xdr->mode != JSXDR_ENCODE) {
|
2001-02-07 02:19:44 +03:00
|
|
|
if (classDef) {
|
|
|
|
ok = js_GetClassPrototype(cx, className, &proto);
|
|
|
|
if (!ok)
|
|
|
|
goto out;
|
|
|
|
clasp = OBJ_GET_CLASS(cx, proto);
|
2001-03-06 04:56:30 +03:00
|
|
|
ok = JS_XDRRegisterClass(xdr, clasp, &classId);
|
2001-02-07 02:19:44 +03:00
|
|
|
if (!ok)
|
|
|
|
goto out;
|
|
|
|
} 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);
|
|
|
|
ok = JS_FALSE;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
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);
|
|
|
|
ok = JS_FALSE;
|
1998-04-24 04:31:11 +04:00
|
|
|
} else {
|
2001-02-07 02:19:44 +03:00
|
|
|
ok = clasp->xdrObject(xdr, objp);
|
1998-04-24 04:31:11 +04:00
|
|
|
}
|
|
|
|
out:
|
|
|
|
if (xdr->mode != JSXDR_ENCODE && className)
|
2001-02-07 02:19:44 +03:00
|
|
|
JS_free(cx, (void *)className);
|
1998-04-24 04:31:11 +04:00
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* JS_HAS_XDR */
|
1998-07-31 04:07:22 +04:00
|
|
|
|
2000-08-26 06:30:22 +04:00
|
|
|
uint32
|
|
|
|
js_Mark(JSContext *cx, JSObject *obj, void *arg)
|
|
|
|
{
|
|
|
|
JSScope *scope;
|
|
|
|
JSScopeProperty *sprop;
|
|
|
|
JSSymbol *sym;
|
|
|
|
JSClass *clasp;
|
|
|
|
|
|
|
|
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
|
|
|
scope = OBJ_SCOPE(obj);
|
|
|
|
|
|
|
|
for (sprop = scope->props; sprop; sprop = sprop->next) {
|
|
|
|
for (sym = sprop->symbols; sym; sym = sym->next) {
|
|
|
|
if (JSVAL_IS_INT(sym_id(sym)))
|
|
|
|
continue;
|
2000-12-06 09:03:30 +03:00
|
|
|
GC_MARK_ATOM(cx, sym_atom(sym), arg);
|
2000-08-26 06:30:22 +04:00
|
|
|
}
|
|
|
|
#if JS_HAS_GETTER_SETTER
|
|
|
|
if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
|
|
|
|
#ifdef GC_MARK_DEBUG
|
|
|
|
char buf[64];
|
|
|
|
JSAtom *atom = sym_atom(sprop->symbols);
|
|
|
|
const char *id = (atom && ATOM_IS_STRING(atom))
|
|
|
|
? JS_GetStringBytes(ATOM_TO_STRING(atom))
|
|
|
|
: "unknown";
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (sprop->attrs & JSPROP_GETTER) {
|
|
|
|
#ifdef GC_MARK_DEBUG
|
|
|
|
JS_snprintf(buf, sizeof buf, "%s %s",
|
|
|
|
id, js_getter_str);
|
|
|
|
#endif
|
|
|
|
GC_MARK(cx,
|
|
|
|
JSVAL_TO_GCTHING((jsval)
|
|
|
|
SPROP_GETTER_SCOPE(sprop, scope)),
|
|
|
|
buf,
|
|
|
|
arg);
|
|
|
|
}
|
|
|
|
if (sprop->attrs & JSPROP_SETTER) {
|
|
|
|
#ifdef GC_MARK_DEBUG
|
|
|
|
JS_snprintf(buf, sizeof buf, "%s %s",
|
|
|
|
id, js_setter_str);
|
|
|
|
#endif
|
|
|
|
GC_MARK(cx,
|
|
|
|
JSVAL_TO_GCTHING((jsval)
|
|
|
|
SPROP_SETTER_SCOPE(sprop, scope)),
|
|
|
|
buf,
|
|
|
|
arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* JS_HAS_GETTER_SETTER */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No one runs while the GC is running, so we can use LOCKED_... here. */
|
|
|
|
clasp = LOCKED_OBJ_GET_CLASS(obj);
|
|
|
|
if (clasp->mark)
|
|
|
|
(void) clasp->mark(cx, obj, arg);
|
- 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
|
|
|
|
* how many slots are allocated and in use at obj->slots by looking at
|
|
|
|
* scope, so we get obj->slots' length from its -1'st element.
|
|
|
|
*/
|
|
|
|
return (uint32) obj->slots[-1];
|
|
|
|
}
|
2001-06-05 04:47:56 +04:00
|
|
|
return JS_MIN(obj->map->freeslot, obj->map->nslots);
|
2000-08-26 06:30:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
js_Clear(JSContext *cx, JSObject *obj)
|
|
|
|
{
|
|
|
|
JSScope *scope;
|
|
|
|
uint32 i, n;
|
|
|
|
|
2001-01-12 02:55:30 +03:00
|
|
|
/*
|
|
|
|
* Clear our scope of all symbols and properties, only if we own the scope
|
- 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
|
|
|
* (i.e., not if obj is unmutated and sharing its prototype's scope). We
|
|
|
|
* do not clear any reserved slots that lie 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) {
|
|
|
|
scope->ops->clear(cx, scope);
|
|
|
|
|
|
|
|
/* Clear slot values and reset freeslot so we're consistent. */
|
|
|
|
i = scope->map.nslots;
|
|
|
|
n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj));
|
|
|
|
while (--i >= n)
|
|
|
|
obj->slots[i] = JSVAL_VOID;
|
|
|
|
scope->map.freeslot = n;
|
|
|
|
}
|
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);
|
|
|
|
v = (slot < (uint32) obj->slots[-1]) ? obj->slots[slot] : JSVAL_VOID;
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
|
|
|
|
{
|
|
|
|
uint32 nslots, rlimit, i;
|
|
|
|
JSClass *clasp;
|
|
|
|
jsval *newslots;
|
|
|
|
|
|
|
|
JS_LOCK_OBJ(cx, obj);
|
|
|
|
nslots = (uint32) obj->slots[-1];
|
|
|
|
if (slot >= nslots) {
|
|
|
|
clasp = LOCKED_OBJ_GET_CLASS(obj);
|
|
|
|
rlimit = JSSLOT_START(clasp) + JSCLASS_RESERVED_SLOTS(clasp);
|
|
|
|
JS_ASSERT(slot < rlimit);
|
|
|
|
if (rlimit > nslots)
|
|
|
|
nslots = rlimit;
|
|
|
|
|
|
|
|
newslots = (jsval *)
|
|
|
|
JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval));
|
|
|
|
if (!newslots) {
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (i = 1 + newslots[0]; i <= rlimit; i++)
|
|
|
|
newslots[i] = JSVAL_VOID;
|
|
|
|
newslots[0] = nslots;
|
|
|
|
if (OBJ_SCOPE(obj)->object == obj)
|
|
|
|
obj->map->nslots = nslots;
|
|
|
|
obj->slots = newslots + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
obj->slots[slot] = v;
|
|
|
|
JS_UNLOCK_OBJ(cx, obj);
|
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) {
|
1998-11-05 03:08:43 +03:00
|
|
|
fprintf(stderr, "jschar* (0x%p) \"", 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) {
|
|
|
|
jsuint i;
|
1998-11-05 03:08:43 +03:00
|
|
|
fprintf(stderr, "string (0x%p) \"", str);
|
1998-07-31 04:07:22 +04:00
|
|
|
for (i=0; i < str->length; i++)
|
2001-02-07 02:19:44 +03:00
|
|
|
fputc(str->chars[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;
|
|
|
|
|
|
|
|
fprintf(stderr, "object 0x%p\n", jsobj);
|
1999-10-02 07:41:37 +04:00
|
|
|
clasp = OBJ_GET_CLASS(cx, jsobj);
|
1998-11-05 03:08:43 +03:00
|
|
|
fprintf(stderr, "class 0x%p %s\n", clasp, clasp->name);
|
|
|
|
for (i=0; i < jsobj->map->nslots; i++) {
|
|
|
|
fprintf(stderr, "slot %3d ", i);
|
|
|
|
val = jsobj->slots[i];
|
|
|
|
if (JSVAL_IS_OBJECT(val))
|
2001-02-07 02:19:44 +03:00
|
|
|
fprintf(stderr, "object 0x%p\n", 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) {
|
1999-07-28 10:48:44 +04:00
|
|
|
fprintf(stderr, "val %d (0x%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) {
|
1999-07-28 10:48:44 +04:00
|
|
|
fprintf(stderr, "id %d (0x%p) is ", (int)id, (void *)id);
|
1999-10-02 07:41:37 +04:00
|
|
|
printVal(cx, js_IdToValue(id));
|
1998-07-31 04:07:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void printAtom(JSAtom *atom) {
|
|
|
|
printString(ATOM_TO_STRING(atom));
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|