From 83c22ded1b54c9a8063fed168e9faf4597bc2293 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Tue, 24 May 2011 12:45:39 +0200 Subject: [PATCH] Bug 648801 (new DOM list bindings) - Cache prototypes and pick up function value from the proto slot instead of caching it locally. r=bz/jst/mrbkap. --HG-- extra : rebase_source : 56a04a93973f479998d40eb22b015e940419c5e5 --- content/base/src/nsContentList.cpp | 4 +- content/base/src/nsContentList.h | 2 +- content/base/src/nsGenericElement.cpp | 4 +- content/base/src/nsGenericElement.h | 2 +- content/xbl/src/nsXBLDocumentInfo.cpp | 5 +- .../document/src/nsXULPrototypeDocument.cpp | 5 +- dom/base/nsWrapperCache.h | 8 +- js/src/jsapi.h | 9 +- js/src/jsparse.cpp | 3 +- js/src/xpconnect/src/dombindings.cpp | 164 +++++++++--------- js/src/xpconnect/src/dombindings.h | 33 ++-- js/src/xpconnect/src/nsXPConnect.cpp | 45 +++++ js/src/xpconnect/src/xpccomponents.cpp | 4 +- js/src/xpconnect/src/xpcconvert.cpp | 2 +- js/src/xpconnect/src/xpcprivate.h | 22 +++ js/src/xpconnect/src/xpcpublic.h | 7 + js/src/xpconnect/src/xpcthreadcontext.cpp | 2 +- js/src/xpconnect/src/xpcwrappednative.cpp | 2 +- .../xpconnect/src/xpcwrappednativejsops.cpp | 2 +- .../xpconnect/src/xpcwrappednativescope.cpp | 29 ++++ 20 files changed, 234 insertions(+), 120 deletions(-) diff --git a/content/base/src/nsContentList.cpp b/content/base/src/nsContentList.cpp index 7aaf2f01875..0698a223c8d 100644 --- a/content/base/src/nsContentList.cpp +++ b/content/base/src/nsContentList.cpp @@ -497,9 +497,9 @@ nsContentList::~nsContentList() } JSObject* -nsContentList::WrapObject(JSContext *cx) +nsContentList::WrapObject(JSContext *cx, XPCWrappedNativeScope *scope) { - return xpc::dom::NodeListBase::create(cx, + return xpc::dom::NodeListBase::create(cx, scope, static_cast(this), this); } diff --git a/content/base/src/nsContentList.h b/content/base/src/nsContentList.h index 10ec148874c..9c197ec1552 100644 --- a/content/base/src/nsContentList.h +++ b/content/base/src/nsContentList.h @@ -277,7 +277,7 @@ public: virtual ~nsContentList(); // nsWrapperCache - virtual JSObject* WrapObject(JSContext *cx); + virtual JSObject* WrapObject(JSContext *cx, XPCWrappedNativeScope *scope); // nsIDOMHTMLCollection NS_DECL_NSIDOMHTMLCOLLECTION diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp index 8b66047c862..8193dd6ae19 100644 --- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -1531,9 +1531,9 @@ NS_INTERFACE_TABLE_HEAD(nsChildContentList) NS_INTERFACE_MAP_END JSObject* -nsChildContentList::WrapObject(JSContext *cx) +nsChildContentList::WrapObject(JSContext *cx, XPCWrappedNativeScope *scope) { - return xpc::dom::NodeListBase::create(cx, this); + return xpc::dom::NodeListBase::create(cx, scope, this); } NS_IMETHODIMP diff --git a/content/base/src/nsGenericElement.h b/content/base/src/nsGenericElement.h index 5e87afab51e..f9b2aae9794 100644 --- a/content/base/src/nsGenericElement.h +++ b/content/base/src/nsGenericElement.h @@ -106,7 +106,7 @@ public: NS_DECL_ISUPPORTS // nsWrapperCache - virtual JSObject* WrapObject(JSContext *cx); + virtual JSObject* WrapObject(JSContext *cx, XPCWrappedNativeScope *scope); // nsIDOMNodeList interface NS_DECL_NSIDOMNODELIST diff --git a/content/xbl/src/nsXBLDocumentInfo.cpp b/content/xbl/src/nsXBLDocumentInfo.cpp index 30efaefb9e7..c613c79d8ee 100644 --- a/content/xbl/src/nsXBLDocumentInfo.cpp +++ b/content/xbl/src/nsXBLDocumentInfo.cpp @@ -183,12 +183,13 @@ nsXBLDocGlobalObject_resolve(JSContext *cx, JSObject *obj, jsid id) JSClass nsXBLDocGlobalObject::gSharedGlobalClass = { "nsXBLPrototypeScript compilation scope", - JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_GLOBAL_FLAGS, + XPCONNECT_GLOBAL_FLAGS, JS_PropertyStub, JS_PropertyStub, nsXBLDocGlobalObject_getProperty, nsXBLDocGlobalObject_setProperty, JS_EnumerateStub, nsXBLDocGlobalObject_resolve, JS_ConvertStub, nsXBLDocGlobalObject_finalize, - NULL, nsXBLDocGlobalObject_checkAccess + NULL, nsXBLDocGlobalObject_checkAccess, NULL, NULL, NULL, NULL, + TraceXPCGlobal }; //---------------------------------------------------------------------- diff --git a/content/xul/document/src/nsXULPrototypeDocument.cpp b/content/xul/document/src/nsXULPrototypeDocument.cpp index ec7e9e82a4d..b176dd0bc01 100644 --- a/content/xul/document/src/nsXULPrototypeDocument.cpp +++ b/content/xul/document/src/nsXULPrototypeDocument.cpp @@ -143,10 +143,11 @@ nsXULPDGlobalObject_resolve(JSContext *cx, JSObject *obj, jsid id) JSClass nsXULPDGlobalObject::gSharedGlobalClass = { "nsXULPrototypeScript compilation scope", - JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_GLOBAL_FLAGS, + XPCONNECT_GLOBAL_FLAGS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, nsXULPDGlobalObject_resolve, JS_ConvertStub, - nsXULPDGlobalObject_finalize + nsXULPDGlobalObject_finalize, NULL, NULL, NULL, NULL, NULL, NULL, + TraceXPCGlobal }; diff --git a/dom/base/nsWrapperCache.h b/dom/base/nsWrapperCache.h index 070989510f6..3b755b4b10c 100644 --- a/dom/base/nsWrapperCache.h +++ b/dom/base/nsWrapperCache.h @@ -44,6 +44,7 @@ struct JSObject; struct JSContext; class nsContentUtils; +class XPCWrappedNativeScope; typedef PRUptrdiff PtrBits; @@ -126,11 +127,10 @@ public: } /** - * Wrap the object corresponding to this wrappe cache; this can return null - * if the object doesn't know how to wrap itself. If non-null is returned, - * the object has already been stored in the wrapper cache. + * Wrap the object corresponding to this wrapper cache. If non-null is + * returned, the object has already been stored in the wrapper cache. */ - virtual JSObject* WrapObject(JSContext *cx) { + virtual JSObject* WrapObject(JSContext *cx, XPCWrappedNativeScope *scope) { return nsnull; } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index e4cb83b266f..eda721acb7a 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -3046,6 +3046,8 @@ struct JSClass { #define JSCLASS_FREEZE_PROTO (1<<(JSCLASS_HIGH_FLAGS_SHIFT+5)) #define JSCLASS_FREEZE_CTOR (1<<(JSCLASS_HIGH_FLAGS_SHIFT+6)) +#define JSCLASS_XPCONNECT_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+7)) + /* Global flags. */ #define JSGLOBAL_FLAGS_CLEARED 0x1 @@ -3061,8 +3063,13 @@ struct JSClass { * prevously allowed, but is now an ES5 violation and thus unsupported. */ #define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 8) +#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \ + (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n))) #define JSCLASS_GLOBAL_FLAGS \ - (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT)) + JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0) +#define JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(clasp) \ + (((clasp)->flags & JSCLASS_IS_GLOBAL) \ + && JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT) /* Fast access to the original value of each standard class's prototype. */ #define JSCLASS_CACHED_PROTO_SHIFT (JSCLASS_HIGH_FLAGS_SHIFT + 8) diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index 5840056c280..afbfdfaaab9 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -933,8 +933,7 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerF : NULL; JS_ASSERT_IF(globalObj, globalObj->isNative()); - JS_ASSERT_IF(globalObj, (globalObj->getClass()->flags & JSCLASS_GLOBAL_FLAGS) == - JSCLASS_GLOBAL_FLAGS); + JS_ASSERT_IF(globalObj, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalObj->getClass())); /* Null script early in case of error, to reduce our code footprint. */ script = NULL; diff --git a/js/src/xpconnect/src/dombindings.cpp b/js/src/xpconnect/src/dombindings.cpp index ebda768e3ef..86b1416cbb9 100644 --- a/js/src/xpconnect/src/dombindings.cpp +++ b/js/src/xpconnect/src/dombindings.cpp @@ -44,6 +44,7 @@ #include "nsIDOMNode.h" #include "nsDOMClassInfo.h" +#include "nsGlobalWindow.h" extern XPCNativeInterface* interfaces[]; @@ -55,16 +56,18 @@ namespace dom { int NodeListBase::NodeListFamily; JSObject * -NodeListBase::create(JSContext *cx, nsINodeList *aNodeList) +NodeListBase::create(JSContext *cx, XPCWrappedNativeScope *scope, + nsINodeList *aNodeList) { - return NodeList::create(cx, aNodeList, aNodeList); + return NodeList::create(cx, scope, aNodeList, aNodeList); } JSObject * -NodeListBase::create(JSContext *cx, nsIHTMLCollection *aHTMLCollection, +NodeListBase::create(JSContext *cx, XPCWrappedNativeScope *scope, + nsIHTMLCollection *aHTMLCollection, nsWrapperCache *aWrapperCache) { - return NodeList::create(cx, aHTMLCollection, + return NodeList::create(cx, scope, aHTMLCollection, aWrapperCache); } @@ -77,7 +80,7 @@ template NodeList NodeList::instance; template<> -Class NodeList::NodeListProtoClass = { +Class NodeList::sProtoClass = { "NodeList", 0, JS_PropertyStub, /* addProperty */ @@ -90,7 +93,16 @@ Class NodeList::NodeListProtoClass = { }; template<> -Class NodeList::NodeListProtoClass = { +JSBool +NodeList::namedItem(JSContext *cx, uintN argc, jsval *vp); + +template<> +NodeList::Methods NodeList::sProtoMethods[] = { + { nsDOMClassInfo::sItem_id, &item, 1 } +}; + +template<> +Class NodeList::sProtoClass = { "HTMLCollection", 0, JS_PropertyStub, /* addProperty */ @@ -102,6 +114,16 @@ Class NodeList::NodeListProtoClass = { JS_ConvertStub }; +template<> +JSBool +NodeList::namedItem(JSContext *cx, uintN argc, jsval *vp); + +template<> +NodeList::Methods NodeList::sProtoMethods[] = { + { nsDOMClassInfo::sItem_id, &item, 1 }, + { nsDOMClassInfo::sNamedItem_id, &namedItem, 1 } +}; + template T* NodeList::getNodeList(JSObject *obj) @@ -126,22 +148,6 @@ NodeList::setProtoShape(JSObject *obj, uint32 shape) js::SetProxyExtra(obj, 0, PrivateUint32Value(shape)); } -template -JSObject * -NodeList::getItemFunction(JSObject *obj) -{ - JS_ASSERT(js::IsProxy(obj) && js::GetProxyHandler(obj) == &NodeList::instance); - return &js::GetProxyExtra(obj, 1).toObject(); -} - -template -void -NodeList::setItemFunction(JSObject *obj, JSObject *funobj) -{ - JS_ASSERT(js::IsProxy(obj) && js::GetProxyHandler(obj) == &NodeList::instance); - js::SetProxyExtra(obj, 1, ObjectValue(*funobj)); -} - template bool NodeList::instanceIsNodeListObject(JSContext *cx, JSObject *obj) @@ -220,63 +226,56 @@ NodeList::namedItem(JSContext *cx, uintN argc, jsval *vp) template JSObject * -NodeList::getPrototypeShared(JSContext *cx) +NodeList::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope) { - JSObject *proto = JS_NewObject(cx, Jsvalify(&NodeListProtoClass), NULL, NULL); + nsDataHashtable &cache = + scope->GetCachedDOMPrototypes(); + + JSObject *proto; + if (cache.IsInitialized()) { + if (cache.Get(NS_GET_TEMPLATE_IID(T), &proto)) + return proto; + } + else if (!cache.Init()) { + return NULL; + } + + proto = JS_NewObject(cx, Jsvalify(&sProtoClass), NULL, NULL); if (!proto) return NULL; JSAutoEnterCompartment ac; if (!ac.enter(cx, proto)) return NULL; + if (!JS_DefinePropertyById(cx, proto, nsDOMClassInfo::sLength_id, JSVAL_VOID, length_getter, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_SHARED)) return NULL; - JSFunction *fun = JS_NewFunctionById(cx, item, 1, 0, js::GetObjectParent(proto), - nsDOMClassInfo::sItem_id); - if (!fun) - return NULL; - JSObject *funobj = JS_GetFunctionObject(fun); - if (!JS_DefinePropertyById(cx, proto, nsDOMClassInfo::sItem_id, OBJECT_TO_JSVAL(funobj), - NULL, NULL, JSPROP_ENUMERATE)) - return NULL; - return proto; -} -template<> -JSObject * -NodeList::getPrototype(JSContext *cx) -{ - // FIXME: This should be cached, not recreated every time. - return getPrototypeShared(cx); -} + for (size_t n = 0; n < NS_ARRAY_LENGTH(sProtoMethods); ++n) { + jsid id = sProtoMethods[n].id; + JSFunction *fun = JS_NewFunctionById(cx, sProtoMethods[n].native, sProtoMethods[n].nargs, + 0, js::GetObjectParent(proto), id); + if (!fun) + return NULL; + JSObject *funobj = JS_GetFunctionObject(fun); + if (!JS_DefinePropertyById(cx, proto, id, OBJECT_TO_JSVAL(funobj), + NULL, NULL, JSPROP_ENUMERATE)) + return NULL; + } -template<> -JSObject * -NodeList::getPrototype(JSContext *cx) -{ - // FIXME: This should be cached, not recreated every time. - JSObject *proto = getPrototypeShared(cx); - if (!proto) + if (!cache.Put(NS_GET_TEMPLATE_IID(T), proto)) return NULL; - JSFunction *fun = JS_NewFunctionById(cx, item, 1, 0, js::GetObjectParent(proto), - nsDOMClassInfo::sNamedItem_id); - if (!fun) - return NULL; - JSObject *funobj = JS_GetFunctionObject(fun); - if (!JS_DefinePropertyById(cx, proto, nsDOMClassInfo::sNamedItem_id, - OBJECT_TO_JSVAL(funobj), - NULL, NULL, JSPROP_ENUMERATE)) - return NULL; return proto; } template JSObject * -NodeList::create(JSContext *cx, T *aNodeList, nsWrapperCache* aWrapperCache) +NodeList::create(JSContext *cx, XPCWrappedNativeScope *scope, T *aNodeList, + nsWrapperCache* aWrapperCache) { - JSObject *proto = getPrototype(cx); + JSObject *proto = getPrototype(cx, scope); if (!proto) return NULL; JSObject *obj = NewProxyObject(cx, &NodeList::instance, @@ -414,21 +413,26 @@ NodeList::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp) template bool -NodeList::cacheItemAndLength(JSContext *cx, JSObject *proxy, JSObject *proto) +NodeList::cacheProtoShape(JSContext *cx, JSObject *proxy, JSObject *proto) { JSPropertyDescriptor desc; if (!JS_GetPropertyDescriptorById(cx, proto, nsDOMClassInfo::sLength_id, JSRESOLVE_QUALIFIED, &desc)) return false; if (desc.obj != proto || desc.getter != length_getter) return true; // don't cache - if (!JS_GetPropertyDescriptorById(cx, proto, nsDOMClassInfo::sItem_id, JSRESOLVE_QUALIFIED, &desc)) - return false; - if (desc.obj != proto || desc.getter || JSVAL_IS_PRIMITIVE(desc.value) || - !JS_IsNativeFunction(JSVAL_TO_OBJECT(desc.value), item)) { - return true; // don't cache + + for (size_t n = 0; n < NS_ARRAY_LENGTH(sProtoMethods); ++n) { + jsid id = sProtoMethods[n].id; + if (!JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, &desc)) + return false; + if (desc.obj != proto || desc.getter || JSVAL_IS_PRIMITIVE(desc.value) || + n >= js::GetNumSlots(proto) || js::GetSlot(proto, n) != desc.value || + !JS_IsNativeFunction(JSVAL_TO_OBJECT(desc.value), sProtoMethods[n].native)) { + return true; // don't cache + } } + setProtoShape(proxy, js::GetObjectShape(proto)); - setItemFunction(proxy, JSVAL_TO_OBJECT(desc.value)); return true; } @@ -438,7 +442,7 @@ NodeList::checkForCacheHit(JSContext *cx, JSObject *proxy, JSObject *receiver jsid id, Value *vp, bool *hitp) { if (getProtoShape(proxy) != js::GetObjectShape(proto)) { - if (!cacheItemAndLength(cx, proxy, proto)) + if (!cacheProtoShape(cx, proxy, proto)) return false; if (getProtoShape(proxy) != js::GetObjectShape(proto)) { *hitp = false; @@ -463,25 +467,23 @@ NodeList::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Va } JSObject *proto = js::GetObjectProto(proxy); - if (id == nsDOMClassInfo::sLength_id) { - bool hit; - if (!checkForCacheHit(cx, proxy, receiver, proto, id, vp, &hit)) - return false; - if (hit) { + bool hit; + if (!checkForCacheHit(cx, proxy, receiver, proto, id, vp, &hit)) + return false; + if (hit) { + if (id == nsDOMClassInfo::sLength_id) { PRUint32 length; getNodeList(proxy)->GetLength(&length); JS_ASSERT(int32(length) >= 0); vp->setInt32(length); return true; } - } - else if (id == nsDOMClassInfo::sItem_id) { - bool hit; - if (!checkForCacheHit(cx, proxy, receiver, proto, id, vp, &hit)) - return false; - if (hit) { - vp->setObject(*getItemFunction(proxy)); - return true; + for (size_t n = 0; n < NS_ARRAY_LENGTH(sProtoMethods); ++n) { + if (id == sProtoMethods[n].id) { + *vp = js::GetSlot(proto, n); + JS_ASSERT(JS_IsNativeFunction(&vp->toObject(), sProtoMethods[n].native)); + return true; + } } } @@ -517,7 +519,7 @@ template bool NodeList::hasInstance(JSContext *cx, JSObject *proxy, const Value *vp, bool *bp) { - *bp = vp->isObject() && js::GetObjectClass(&vp->toObject()) == &NodeListProtoClass; + *bp = vp->isObject() && js::GetObjectClass(&vp->toObject()) == &sProtoClass; return true; } diff --git a/js/src/xpconnect/src/dombindings.h b/js/src/xpconnect/src/dombindings.h index f595bb606a2..28f8f7f8589 100644 --- a/js/src/xpconnect/src/dombindings.h +++ b/js/src/xpconnect/src/dombindings.h @@ -55,8 +55,10 @@ public: static void* ProxyFamily() { return &NodeListFamily; } - static JSObject *create(JSContext *cx, nsINodeList *aNodeList); - static JSObject *create(JSContext *cx, nsIHTMLCollection *aHTMLCollection, + static JSObject *create(JSContext *cx, XPCWrappedNativeScope *scope, + nsINodeList *aNodeList); + static JSObject *create(JSContext *cx, XPCWrappedNativeScope *scope, + nsIHTMLCollection *aHTMLCollection, nsWrapperCache *aWrapperCache); private: static int NodeListFamily; @@ -69,31 +71,29 @@ template class NodeList : public NodeListBase { static NodeList instance; - static js::Class NodeListProtoClass; + static js::Class sProtoClass; + + struct Methods { + jsid &id; + JSNative native; + uintN nargs; + }; + + static Methods sProtoMethods[]; static bool instanceIsNodeListObject(JSContext *cx, JSObject *obj); - - // Prototype-creation code that's the same (modulo templating) for - // all specializations. - static JSObject *getPrototypeShared(JSContext *cx); - - // Specialization-specific prototype setup. - static JSObject *getPrototype(JSContext *cx); + static JSObject *getPrototype(JSContext *cx, XPCWrappedNativeScope *scope); static T *getNodeList(JSObject *obj); static uint32 getProtoShape(JSObject *obj); static void setProtoShape(JSObject *obj, uint32 shape); - static JSObject *getItemFunction(JSObject *obj); - static void setItemFunction(JSObject *obj, JSObject *funobj); - static JSBool length_getter(JSContext *cx, JSObject *obj, jsid id, js::Value *vp); - static JSBool item(JSContext *cx, uintN argc, jsval *vp); static JSBool namedItem(JSContext *cx, uintN argc, jsval *vp); - static bool cacheItemAndLength(JSContext *cx, JSObject *proxy, JSObject *proto); + static bool cacheProtoShape(JSContext *cx, JSObject *proxy, JSObject *proto); static bool checkForCacheHit(JSContext *cx, JSObject *proxy, JSObject *receiver, JSObject *proto, jsid id, js::Value *vp, bool *hitp); public: @@ -123,7 +123,8 @@ class NodeList : public NodeListBase { JSString *obj_toString(JSContext *cx, JSObject *proxy); void finalize(JSContext *cx, JSObject *proxy); - static JSObject *create(JSContext *cx, T *, nsWrapperCache* aWrapperCache); + static JSObject *create(JSContext *cx, XPCWrappedNativeScope *scope, T *, + nsWrapperCache* aWrapperCache); static bool objIsNodeList(JSObject *obj) { return js::IsProxy(obj) && js::GetProxyHandler(obj) == &instance; diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index 934beddd87c..cc70d6a4b12 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -1070,6 +1070,40 @@ CreateNewCompartment(JSContext *cx, JSClass *clasp, nsIPrincipal *principal, return true; } +#ifdef DEBUG +struct VerifyTraceXPCGlobalCalledTracer +{ + JSTracer base; + bool ok; +}; + +static void +VerifyTraceXPCGlobalCalled(JSTracer *trc, void *thing, JSGCTraceKind kind) +{ + // We don't do anything here, we only want to verify that TraceXPCGlobal + // was called. +} +#endif + +void +TraceXPCGlobal(JSTracer *trc, JSObject *obj) +{ +#ifdef DEBUG + if(trc->callback == VerifyTraceXPCGlobalCalled) + { + // We don't do anything here, we only want to verify that TraceXPCGlobal + // was called. + reinterpret_cast(trc)->ok = JS_TRUE; + return; + } +#endif + + XPCWrappedNativeScope *scope = + XPCWrappedNativeScope::GetNativeScope(trc->context, obj); + if(scope) + scope->TraceDOMPrototypes(trc); +} + nsresult xpc_CreateGlobalObject(JSContext *cx, JSClass *clasp, nsIPrincipal *principal, nsISupports *ptr, @@ -1105,6 +1139,17 @@ xpc_CreateGlobalObject(JSContext *cx, JSClass *clasp, *global = tempGlobal; } +#ifdef DEBUG + if(clasp->flags & JSCLASS_XPCONNECT_GLOBAL) + { + VerifyTraceXPCGlobalCalledTracer trc; + JS_TRACER_INIT(&trc.base, cx, VerifyTraceXPCGlobalCalled); + trc.ok = JS_FALSE; + JS_TraceChildren(&trc.base, *global, JSTRACE_OBJECT); + NS_ABORT_IF_FALSE(trc.ok, "Trace hook needs to call TraceXPCGlobal if JSCLASS_XPCONNECT_GLOBAL is set."); + } +#endif + return NS_OK; } diff --git a/js/src/xpconnect/src/xpccomponents.cpp b/js/src/xpconnect/src/xpccomponents.cpp index 64a91bd35e4..2dc074eea2c 100644 --- a/js/src/xpconnect/src/xpccomponents.cpp +++ b/js/src/xpconnect/src/xpccomponents.cpp @@ -3103,10 +3103,10 @@ sandbox_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) static JSClass SandboxClass = { "Sandbox", - JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_GLOBAL_FLAGS, + XPCONNECT_GLOBAL_FLAGS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, sandbox_enumerate, sandbox_resolve, sandbox_convert, sandbox_finalize, - JSCLASS_NO_OPTIONAL_MEMBERS + NULL, NULL, NULL, NULL, NULL, NULL, TraceXPCGlobal }; static JSFunctionSpec SandboxFunctions[] = { diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index 44bca539abd..e1a5f249fa5 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -1169,7 +1169,7 @@ XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx, return JS_FALSE; if(!flat) { - flat = cache->WrapObject(lccx.GetJSContext()); + flat = cache->WrapObject(lccx.GetJSContext(), xpcscope); if (!flat) { flat = ConstructProxyObject(ccx, aHelper, xpcscope); } diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index 2ab190c89e4..ecf357e9c46 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -1600,6 +1600,23 @@ public: XPCContext *GetContext() { return mContext; } void SetContext(XPCContext *xpcc) { mContext = nsnull; } + nsDataHashtable& GetCachedDOMPrototypes() + { + return mCachedDOMPrototypes; + } + + static XPCWrappedNativeScope *GetNativeScope(JSContext *cx, JSObject *obj) + { + JS_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_XPCONNECT_GLOBAL); + + jsval vp; + JS_ALWAYS_TRUE(JS_GetReservedSlot(cx, obj, JSCLASS_GLOBAL_SLOT_COUNT, + &vp)); + return JSVAL_IS_VOID(vp) ? nsnull : + static_cast(JSVAL_TO_PRIVATE(vp)); + } + void TraceDOMPrototypes(JSTracer *trc); + protected: XPCWrappedNativeScope(XPCCallContext& ccx, JSObject* aGlobal); virtual ~XPCWrappedNativeScope(); @@ -1639,6 +1656,8 @@ private: // How do we deal? Do we need to? I suspect this isn't worth worrying // about, since all of our scope objects are verified as not doing that. nsIScriptObjectPrincipal* mScriptObjectPrincipal; + + nsDataHashtable mCachedDOMPrototypes; }; JSObject* xpc_CloneJSFunction(XPCCallContext &ccx, JSObject *funobj, @@ -2666,6 +2685,9 @@ public: JSObject* wrapper = GetWrapperPreserveColor(); if(wrapper) JS_CALL_OBJECT_TRACER(trc, wrapper, "XPCWrappedNative::mWrapper"); + if(mScriptableInfo && + (mScriptableInfo->GetJSClass()->flags & JSCLASS_XPCONNECT_GLOBAL)) + GetScope()->TraceDOMPrototypes(trc); } inline void AutoTrace(JSTracer* trc) diff --git a/js/src/xpconnect/src/xpcpublic.h b/js/src/xpconnect/src/xpcpublic.h index 400249669aa..5c6360587b6 100644 --- a/js/src/xpconnect/src/xpcpublic.h +++ b/js/src/xpconnect/src/xpcpublic.h @@ -68,6 +68,13 @@ xpc_CreateMTGlobalObject(JSContext *cx, JSClass *clasp, nsISupports *ptr, JSObject **global, JSCompartment **compartment); +#define XPCONNECT_GLOBAL_FLAGS \ + JSCLASS_XPCONNECT_GLOBAL | JSCLASS_HAS_PRIVATE | \ + JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(1) + +void +TraceXPCGlobal(JSTracer *trc, JSObject *obj); + // XXX where should this live? NS_EXPORT_(void) xpc_LocalizeContext(JSContext *cx); diff --git a/js/src/xpconnect/src/xpcthreadcontext.cpp b/js/src/xpconnect/src/xpcthreadcontext.cpp index 3756ada7e4b..f91c99878f2 100644 --- a/js/src/xpconnect/src/xpcthreadcontext.cpp +++ b/js/src/xpconnect/src/xpcthreadcontext.cpp @@ -212,7 +212,7 @@ SafeFinalize(JSContext* cx, JSObject* obj) static JSClass global_class = { "global_for_XPCJSContextStack_SafeJSContext", - JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_GLOBAL_FLAGS, + XPCONNECT_GLOBAL_FLAGS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, SafeGlobalResolve, JS_ConvertStub, SafeFinalize, JSCLASS_NO_OPTIONAL_MEMBERS diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index 2bf676d905f..49317565759 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -1146,7 +1146,7 @@ XPCWrappedNative::Init(XPCCallContext& ccx, // JS class without the proper global flags. Notice that here and fix // the problem. if(!(jsclazz->flags & JSCLASS_IS_GLOBAL)) - jsclazz->flags |= JSCLASS_GLOBAL_FLAGS; + jsclazz->flags |= XPCONNECT_GLOBAL_FLAGS; } else NS_ASSERTION(!(jsclazz->flags & JSCLASS_IS_GLOBAL), diff --git a/js/src/xpconnect/src/xpcwrappednativejsops.cpp b/js/src/xpconnect/src/xpcwrappednativejsops.cpp index 62e0949aecb..a582de04eb1 100644 --- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp +++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp @@ -1473,7 +1473,7 @@ XPCNativeScriptableShared::PopulateJSClass(JSBool isGlobal) JSCLASS_NEW_RESOLVE; if(isGlobal) - mJSClass.base.flags |= JSCLASS_GLOBAL_FLAGS; + mJSClass.base.flags |= XPCONNECT_GLOBAL_FLAGS; JSPropertyOp addProperty; if(mFlags.WantAddProperty()) diff --git a/js/src/xpconnect/src/xpcwrappednativescope.cpp b/js/src/xpconnect/src/xpcwrappednativescope.cpp index 2e10aea953d..a4c1b2da323 100644 --- a/js/src/xpconnect/src/xpcwrappednativescope.cpp +++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp @@ -129,6 +129,10 @@ XPCWrappedNativeScope::GetNewOrUsed(XPCCallContext& ccx, JSObject* aGlobal) // called by nsXPConnect::InitClasses. scope->SetGlobal(ccx, aGlobal); } + if(js::GetObjectClass(aGlobal)->flags & JSCLASS_XPCONNECT_GLOBAL) + JS_ALWAYS_TRUE(JS_SetReservedSlot(ccx, aGlobal, + JSCLASS_GLOBAL_SLOT_COUNT, + PRIVATE_TO_JSVAL(scope))); return scope; } @@ -449,6 +453,8 @@ XPCWrappedNativeScope::FinishedMarkPhaseOfGC(JSContext* cx, XPCJSRuntime* rt) { cur->mGlobalJSObject = nsnull; cur->mScriptObjectPrincipal = nsnull; + if(cur->GetCachedDOMPrototypes().IsInitialized()) + cur->GetCachedDOMPrototypes().Clear(); // Move this scope from the live list to the dying list. if(prev) prev->mNext = next; @@ -803,6 +809,13 @@ XPCWrappedNativeScope::FindInJSObjectScope(JSContext* cx, JSObject* obj, obj = JS_GetGlobalForObject(cx, obj); + if(js::GetObjectClass(obj)->flags & JSCLASS_XPCONNECT_GLOBAL) + { + scope = XPCWrappedNativeScope::GetNativeScope(cx, obj); + if(scope) + return scope; + } + if(!runtime) { runtime = nsXPConnect::GetRuntimeInstance(); @@ -907,6 +920,22 @@ XPCWrappedNativeScope::RemoveWrappedNativeProtos() GetRuntime()->GetDetachedWrappedNativeProtoMap()); } +static PLDHashOperator +TraceDOMPrototype(const nsID& aKey, JSObject* aData, void* aClosure) +{ + JSTracer *trc = static_cast(aClosure); + JS_CALL_OBJECT_TRACER(trc, aData, "DOM prototype"); + return PL_DHASH_NEXT; +} + +void +XPCWrappedNativeScope::TraceDOMPrototypes(JSTracer *trc) +{ + if(mCachedDOMPrototypes.IsInitialized()) { + mCachedDOMPrototypes.EnumerateRead(TraceDOMPrototype, trc); + } +} + /***************************************************************************/ // static