Fix for bug 590612 (Speed up js-wrapping in classinfo when we already have a wrapper). r=bz, a=jst.
authorPeter Van der Beken <peterv@propagandism.org>
Sat, 28 Aug 2010 10:04:25 +0200
changeset 57854 7e42ccaa7269cd8cf0ed30684bc4a718673710c4
parent 57853 789f0f85f75a3fa55d36c5d69a5b3bbbf3596410
child 57855 2aceaa1070b43970bb058cb589a6a37ae96e94ac
push idunknown
push userunknown
push dateunknown
reviewersbz, jst
bugs590612
milestone2.0b8pre
Fix for bug 590612 (Speed up js-wrapping in classinfo when we already have a wrapper). r=bz, a=jst.
caps/src/nsScriptSecurityManager.cpp
content/base/src/nsContentUtils.cpp
dom/base/nsDOMClassInfo.h
js/src/jstl.h
js/src/jsvector.h
js/src/xpconnect/src/qsgen.py
js/src/xpconnect/src/xpcprivate.h
js/src/xpconnect/src/xpcpublic.h
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -101,22 +101,16 @@ static NS_DEFINE_CID(kZipReaderCID, NS_Z
 
 nsIIOService    *nsScriptSecurityManager::sIOService = nsnull;
 nsIXPConnect    *nsScriptSecurityManager::sXPConnect = nsnull;
 nsIThreadJSContextStack *nsScriptSecurityManager::sJSContextStack = nsnull;
 nsIStringBundle *nsScriptSecurityManager::sStrBundle = nsnull;
 JSRuntime       *nsScriptSecurityManager::sRuntime   = 0;
 PRBool nsScriptSecurityManager::sStrictFileOriginPolicy = PR_TRUE;
 
-// Info we need about the JSClasses used by XPConnects wrapped
-// natives, to avoid having to QI to nsIXPConnectWrappedNative all the
-// time when doing security checks.
-static JSEqualityOp sXPCWrappedNativeEqualityOps;
-
-
 ///////////////////////////
 // Convenience Functions //
 ///////////////////////////
 // Result of this function should not be freed.
 static inline const PRUnichar *
 IDToString(JSContext *cx, jsid id)
 {
     if (JSID_IS_STRING(id))
@@ -2409,21 +2403,18 @@ nsScriptSecurityManager::doGetObjectPrin
 
             jsClass = aObj->getClass();
         }
     }
 
     do {
         // Note: jsClass is set before this loop, and also at the
         // *end* of this loop.
-
-        // NOTE: These class and equality hook checks better match
-        // what IS_WRAPPER_CLASS() does in xpconnect!
         
-        if (jsClass->ext.equality == js::Valueify(sXPCWrappedNativeEqualityOps)) {
+        if (IS_WRAPPER_CLASS(jsClass)) {
             result = sXPConnect->GetPrincipal(aObj,
 #ifdef DEBUG
                                               aAllowShortCircuit
 #else
                                               PR_TRUE
 #endif
                                               );
             if (result) {
@@ -3418,17 +3409,16 @@ nsresult nsScriptSecurityManager::Init()
     };
 
 #ifdef DEBUG
     JSSecurityCallbacks *oldcallbacks =
 #endif
     JS_SetRuntimeSecurityCallbacks(sRuntime, &securityCallbacks);
     NS_ASSERTION(!oldcallbacks, "Someone else set security callbacks!");
 
-    sXPConnect->GetXPCWrappedNativeJSClassInfo(&sXPCWrappedNativeEqualityOps);
     return NS_OK;
 }
 
 static nsScriptSecurityManager *gScriptSecMan = nsnull;
 
 jsid nsScriptSecurityManager::sEnabledID   = JSID_VOID;
 
 nsScriptSecurityManager::~nsScriptSecurityManager(void)
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -5532,16 +5532,21 @@ nsContentUtils::WrapNative(JSContext *cx
   if (!native) {
     NS_ASSERTION(!aHolder || !*aHolder, "*aHolder should be null!");
 
     *vp = JSVAL_NULL;
 
     return NS_OK;
   }
 
+  JSObject *wrapper = xpc_GetCachedSlimWrapper(cache, scope, vp);
+  if (wrapper) {
+    return NS_OK;
+  }
+
   NS_ENSURE_TRUE(sXPConnect && sThreadJSContextStack, NS_ERROR_UNEXPECTED);
 
   // Keep sXPConnect and sThreadJSContextStack alive. If we're on the main
   // thread then this can be done simply and cheaply by adding a reference to
   // nsLayoutStatics. If we're not on the main thread then we need to add a
   // more expensive reference sXPConnect directly. We have to use manual
   // AddRef and Release calls so don't early-exit from this function after we've
   // added the reference!
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -43,16 +43,17 @@
 #include "nsIDOMClassInfo.h"
 #include "nsIXPCScriptable.h"
 #include "jsapi.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIScriptContext.h"
 #include "nsDOMJSUtils.h" // for GetScriptContextFromJSContext
 #include "nsIScriptGlobalObject.h"
 #include "nsContentUtils.h"
+#include "xpcpublic.h"
 
 class nsIDOMWindow;
 class nsIDOMNSHTMLOptionCollection;
 class nsIPluginInstance;
 class nsIForm;
 class nsIDOMNodeList;
 class nsIDOMDocument;
 class nsIHTMLDocument;
@@ -135,18 +136,18 @@ public:
 
   static nsresult WrapNative(JSContext *cx, JSObject *scope,
                              nsISupports *native, const nsIID* aIID,
                              PRBool aAllowWrapping, jsval *vp,
                              // If non-null aHolder will keep the jsval alive
                              // while there's a ref to it
                              nsIXPConnectJSObjectHolder** aHolder = nsnull)
   {
-    return nsContentUtils::WrapNative(cx, scope, native, aIID, vp, aHolder,
-                                      aAllowWrapping);
+    return WrapNative(cx, scope, native, nsnull, aIID, vp, aHolder,
+                      aAllowWrapping);
   }
 
   // Used for cases where PreCreate needs to wrap the native parent, and the
   // native parent is likely to have been wrapped already.  |native| must
   // implement nsWrapperCache, and nativeWrapperCache must be |native|'s
   // nsWrapperCache.
   static inline nsresult WrapNativeParent(JSContext *cx, JSObject *scope,
                                           nsISupports *native,
@@ -156,28 +157,28 @@ public:
   // Same as the WrapNative above, but use these if aIID is nsISupports' IID.
   static nsresult WrapNative(JSContext *cx, JSObject *scope,
                              nsISupports *native, PRBool aAllowWrapping,
                              jsval *vp,
                              // If non-null aHolder will keep the jsval alive
                              // while there's a ref to it
                              nsIXPConnectJSObjectHolder** aHolder = nsnull)
   {
-    return nsContentUtils::WrapNative(cx, scope, native, vp, aHolder,
-                                      aAllowWrapping);
+    return WrapNative(cx, scope, native, nsnull, nsnull, vp, aHolder,
+                      aAllowWrapping);
   }
   static nsresult WrapNative(JSContext *cx, JSObject *scope,
                              nsISupports *native, nsWrapperCache *cache,
                              PRBool aAllowWrapping, jsval *vp,
                              // If non-null aHolder will keep the jsval alive
                              // while there's a ref to it
                              nsIXPConnectJSObjectHolder** aHolder = nsnull)
   {
-    return nsContentUtils::WrapNative(cx, scope, native, cache, vp, aHolder,
-                                      aAllowWrapping);
+    return WrapNative(cx, scope, native, cache, nsnull, vp, aHolder,
+                      aAllowWrapping);
   }
 
   static nsresult ThrowJSException(JSContext *cx, nsresult aResult);
 
   static PRBool ObjectIsNativeWrapper(JSContext* cx, JSObject* obj);
 
   static nsISupports *GetNative(nsIXPConnectWrappedNative *wrapper, JSObject *obj);
 
@@ -242,16 +243,39 @@ protected:
             id == sOuterHeight_id  ||
             id == sOuterWidth_id   ||
             id == sScreenX_id      ||
             id == sScreenY_id      ||
             id == sStatus_id       ||
             id == sName_id);
   }
 
+  static nsresult WrapNative(JSContext *cx, JSObject *scope,
+                             nsISupports *native, nsWrapperCache *cache,
+                             const nsIID* aIID, jsval *vp,
+                             nsIXPConnectJSObjectHolder** aHolder,
+                             PRBool aAllowWrapping)
+  {
+    if (!native) {
+      NS_ASSERTION(!aHolder || !*aHolder, "*aHolder should be null!");
+
+      *vp = JSVAL_NULL;
+
+      return NS_OK;
+    }
+
+    JSObject *wrapper = xpc_GetCachedSlimWrapper(cache, scope, vp);
+    if (wrapper) {
+      return NS_OK;
+    }
+
+    return sXPConnect->WrapNativeToJSVal(cx, scope, native, cache, aIID,
+                                         aAllowWrapping, vp, aHolder);
+  }
+
   static nsIXPConnect *sXPConnect;
   static nsIScriptSecurityManager *sSecMan;
 
   // nsIXPCScriptable code
   static nsresult DefineStaticJSVals(JSContext *cx);
 
   static PRBool sIsInitialized;
   static PRBool sDisableDocumentAllSupport;
--- a/js/src/jstl.h
+++ b/js/src/jstl.h
@@ -35,29 +35,29 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef jstl_h_
 #define jstl_h_
 
-/* Gross special case for Gecko, which defines malloc/calloc/free. */
-#ifdef mozilla_mozalloc_macro_wrappers_h
-#  define JS_UNDEFD_MOZALLOC_WRAPPERS
-/* The "anti-header" */
-#  include "mozilla/mozalloc_undef_macro_wrappers.h"
-#endif
-
 #include "jsbit.h"
 #include "jsstaticcheck.h"
 
 #include <new>
 #include <string.h>
 
+/* Gross special case for Gecko, which defines malloc/calloc/free. */
+#ifdef mozilla_mozalloc_macro_wrappers_h
+#  define JSSTL_UNDEFD_MOZALLOC_WRAPPERS
+/* The "anti-header" */
+#  include "mozilla/mozalloc_undef_macro_wrappers.h"
+#endif
+
 namespace js {
 
 /* JavaScript Template Library. */
 namespace tl {
 
 /* Compute min/max/clamp. */
 template <size_t i, size_t j> struct Min {
     static const size_t result = i < j ? i : j;
@@ -462,9 +462,13 @@ template <class T>
 static T&
 InitConst(const T &t)
 {
     return const_cast<T &>(t);
 }
 
 } /* namespace js */
 
+#ifdef JSSTL_UNDEFD_MOZALLOC_WRAPPERS
+#  include "mozilla/mozalloc_macro_wrappers.h"
+#endif
+
 #endif /* jstl_h_ */
--- a/js/src/jsvector.h
+++ b/js/src/jsvector.h
@@ -45,16 +45,22 @@
 #include "jsprvtd.h"
 
 /* Silence dire "bugs in previous versions of MSVC have been fixed" warnings */
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4345)
 #endif
 
+/* Gross special case for Gecko, which defines malloc/calloc/free. */
+#ifdef mozilla_mozalloc_macro_wrappers_h
+#  define JSVECTOR_UNDEFD_MOZALLOC_WRAPPERS
+#  include "mozilla/mozalloc_undef_macro_wrappers.h"
+#endif
+
 namespace js {
 
 /*
  * This template class provides a default implementation for vector operations
  * when the element type is not known to be a POD, as judged by IsPodType.
  */
 template <class T, size_t N, class AP, bool IsPod>
 struct VectorImpl
@@ -738,9 +744,13 @@ Vector<T,N,AP>::replaceRawBuffer(T *p, s
 }
 
 }  /* namespace js */
 
 #ifdef _MSC_VER
 #pragma warning(pop)
 #endif
 
+#ifdef JSVECTOR_UNDEFD_MOZALLOC_WRAPPERS
+#  include "mozilla/mozalloc_macro_wrappers.h"
+#endif
+
 #endif /* jsvector_h_ */
--- a/js/src/xpconnect/src/qsgen.py
+++ b/js/src/xpconnect/src/qsgen.py
@@ -702,27 +702,18 @@ def writeResultConv(f, type, jsvalPtr, j
         # else fall through; this type isn't supported yet
     elif isInterfaceType(type):
         if isVariantType(type):
             f.write("    return xpc_qsVariantToJsval(lccx, result, %s);\n"
                     % jsvalPtr)
             return
         else:
             f.write("    nsWrapperCache* cache = xpc_qsGetWrapperCache(result);\n"
-                    "    if (cache) {\n"
-                    "      JSObject* wrapper = cache->GetWrapper();\n"
-                    "      NS_ASSERTION(cx->compartment == obj->compartment(),\n"
-                    "                   \"wrong compartment in object or context!\");\n"
-                    "      if (wrapper &&\n"
-                    # FIXME: Bug 585786, this check should go away
-                    "          IS_SLIM_WRAPPER_OBJECT(wrapper) &&\n"
-                    "          cx->compartment == wrapper->compartment()) {\n"
-                    "        *%s = OBJECT_TO_JSVAL(wrapper);\n"
-                    "        return JS_TRUE;\n"
-                    "      }\n"
+                    "    if (xpc_GetCachedSlimWrapper(cache, obj, %s)) {\n"
+                    "      return JS_TRUE;\n"
                     "    }\n"
                     "    // After this point do not use 'result'!\n"
                     "    qsObjectHelper helper(result, cache);\n"
                     "    return xpc_qsXPCOMObjectToJsval(lccx, "
                     "helper, &NS_GET_IID(%s), &interfaces[k_%s], %s);\n"
                     % (jsvalPtr, type.name, type.name, jsvalPtr))
             return
 
@@ -1254,27 +1245,20 @@ def writeTraceableResultConv(f, type):
             return
         # else fall through; this type isn't supported yet
     elif isInterfaceType(type):
         if isVariantType(type):
             f.write("    JSBool ok = xpc_qsVariantToJsval(lccx, result, "
                     "&vp.array[0]);\n")
         else:
             f.write("    nsWrapperCache* cache = xpc_qsGetWrapperCache(result);\n"
-                    "    if (cache) {\n"
-                    "      JSObject* wrapper = cache->GetWrapper();\n"
-                    "      NS_ASSERTION(cx->compartment == obj->compartment(),\n"
-                    "                   \"wrong compartment in object or context!\");\n"
-                    "      if (wrapper &&\n"
-                    # FIXME: Bug 585786, this check should go away
-                    "          IS_SLIM_WRAPPER_OBJECT(wrapper) &&\n"
-                    "          cx->compartment == wrapper->compartment()) {\n"
-                    "        vp.array[0] = OBJECT_TO_JSVAL(wrapper);\n"
-                    "        return wrapper;\n"
-                    "      }\n"
+                    "    JSObject* wrapper =\n"
+                    "      xpc_GetCachedSlimWrapper(cache, obj, &vp.array[0]);\n"
+                    "    if (wrapper) {\n"
+                    "      return wrapper;\n"
                     "    }\n"
                     "    // After this point do not use 'result'!\n"
                     "    qsObjectHelper helper(result, cache);\n"
                     "    JSBool ok = xpc_qsXPCOMObjectToJsval(lccx, "
                     "helper, &NS_GET_IID(%s), &interfaces[k_%s], "
                     "&vp.array[0]);\n"
                     % (type.name, type.name))
         f.write("    if (!ok) {\n");
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -45,16 +45,17 @@
 
 #ifndef xpcprivate_h___
 #define xpcprivate_h___
 
 #include <string.h>
 #include <stdlib.h>
 #include <stdarg.h>
 #include <math.h>
+#include "xpcpublic.h"
 #include "jsapi.h"
 #include "jsdhash.h"
 #include "jsprf.h"
 #include "prprf.h"
 #include "jsinterp.h"
 #include "jscntxt.h"
 #include "jsdbgapi.h"
 #include "jsgc.h"
@@ -1449,56 +1450,16 @@ XPC_WN_JSOp_ThisObject(JSContext *cx, JS
 // XPC_WN_Shared_Proto_Enumerate or something rather than checking for
 // 4 classes?
 #define IS_PROTO_CLASS(clazz)                                                 \
     ((clazz) == &XPC_WN_NoMods_WithCall_Proto_JSClass ||                      \
      (clazz) == &XPC_WN_NoMods_NoCall_Proto_JSClass ||                        \
      (clazz) == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass ||                 \
      (clazz) == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass)
 
-// NOTE!!!
-//
-// If this ever changes,
-// nsScriptSecurityManager::doGetObjectPrincipal() *must* be updated
-// also!
-//
-// NOTE!!!
-#define IS_WRAPPER_CLASS(clazz)                                               \
-    (clazz->ext.equality == js::Valueify(XPC_WN_Equality))
-
-inline JSBool
-DebugCheckWrapperClass(JSObject* obj)
-{
-    NS_ASSERTION(IS_WRAPPER_CLASS(obj->getClass()),
-                 "Forgot to check if this is a wrapper?");
-    return JS_TRUE;
-}
-
-// If IS_WRAPPER_CLASS for the JSClass of an object is true, the object can be
-// a slim wrapper, holding a native in its private slot, or a wrappednative
-// wrapper, holding the XPCWrappedNative in its private slot. A slim wrapper
-// also holds a pointer to its XPCWrappedNativeProto in a reserved slot, we can
-// check that slot for a non-void value to distinguish between the two.
-
-// Only use these macros if IS_WRAPPER_CLASS(obj->getClass()) is true.
-#define IS_WN_WRAPPER_OBJECT(obj)                                             \
-    (DebugCheckWrapperClass(obj) &&                                           \
-     obj->getSlot(0).isUndefined())
-#define IS_SLIM_WRAPPER_OBJECT(obj)                                           \
-    (DebugCheckWrapperClass(obj) &&                                           \
-     !obj->getSlot(0).isUndefined())
-
-// Use these macros if IS_WRAPPER_CLASS(obj->getClass()) might be false.
-// Avoid calling them if IS_WRAPPER_CLASS(obj->getClass()) can only be
-// true, as we'd do a redundant call to IS_WRAPPER_CLASS.
-#define IS_WN_WRAPPER(obj)                                                    \
-    (IS_WRAPPER_CLASS(obj->getClass()) && IS_WN_WRAPPER_OBJECT(obj))
-#define IS_SLIM_WRAPPER(obj)                                                  \
-    (IS_WRAPPER_CLASS(obj->getClass()) && IS_SLIM_WRAPPER_OBJECT(obj))
-
 // Comes from xpcwrappednativeops.cpp
 extern void
 xpc_TraceForValidWrapper(JSTracer *trc, XPCWrappedNative* wrapper);
 
 /***************************************************************************/
 
 namespace XPCWrapper {
 
@@ -2458,17 +2419,16 @@ private:
     ~XPCWrappedNativeTearOffChunk() {delete mNextChunk;}
 
 private:
     XPCWrappedNativeTearOff mTearOffs[XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK];
     XPCWrappedNativeTearOffChunk* mNextChunk;
 };
 
 void *xpc_GetJSPrivate(JSObject *obj);
-inline JSObject *xpc_GetGlobalForObject(JSObject *obj);
 
 /***************************************************************************/
 // XPCWrappedNative the wrapper around one instance of a native xpcom object
 // to be used from JavaScript.
 
 class XPCWrappedNative : public nsIXPConnectWrappedNative
 {
 public:
@@ -4452,23 +4412,16 @@ NS_DEFINE_STATIC_IID_ACCESSOR(PrincipalH
 /***************************************************************************/
 // Utilities
 
 inline void *
 xpc_GetJSPrivate(JSObject *obj)
 {
     return obj->getPrivate();
 }
-inline JSObject *
-xpc_GetGlobalForObject(JSObject *obj)
-{
-    while(JSObject *parent = obj->getParent())
-        obj = parent;
-    return obj;
-}
 
 
 #ifndef XPCONNECT_STANDALONE
 
 // Helper for creating a sandbox object to use for evaluating
 // untrusted code completely separated from all other code in the
 // system using xpc_EvalInSandbox(). Takes the JSContext on which to
 // do setup etc on, puts the sandbox object in *vp (which must be
--- a/js/src/xpconnect/src/xpcpublic.h
+++ b/js/src/xpconnect/src/xpcpublic.h
@@ -36,24 +36,88 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef xpcpublic_h
 #define xpcpublic_h
 
 #include "jsapi.h"
-#include "nsISupports.h"
+#include "jsobj.h"
+#include "nsAString.h"
+#include "nsIPrincipal.h"
+#include "nsWrapperCache.h"
 
 class nsIPrincipal;
 
 nsresult
 xpc_CreateGlobalObject(JSContext *cx, JSClass *clasp,
                        nsIPrincipal *principal, nsISupports *ptr,
                        bool wantXrays, JSObject **global,
                        JSCompartment **compartment);
 
 nsresult
 xpc_CreateMTGlobalObject(JSContext *cx, JSClass *clasp,
                          nsISupports *ptr, JSObject **global,
                          JSCompartment **compartment);
 
+extern JSBool
+XPC_WN_Equality(JSContext *cx, JSObject *obj, const jsval *v, JSBool *bp);
+
+#define IS_WRAPPER_CLASS(clazz)                                               \
+    (clazz->ext.equality == js::Valueify(XPC_WN_Equality))
+
+inline JSBool
+DebugCheckWrapperClass(JSObject* obj)
+{
+    NS_ASSERTION(IS_WRAPPER_CLASS(obj->getClass()),
+                 "Forgot to check if this is a wrapper?");
+    return JS_TRUE;
+}
+
+// If IS_WRAPPER_CLASS for the JSClass of an object is true, the object can be
+// a slim wrapper, holding a native in its private slot, or a wrappednative
+// wrapper, holding the XPCWrappedNative in its private slot. A slim wrapper
+// also holds a pointer to its XPCWrappedNativeProto in a reserved slot, we can
+// check that slot for a non-void value to distinguish between the two.
+
+// Only use these macros if IS_WRAPPER_CLASS(obj->getClass()) is true.
+#define IS_WN_WRAPPER_OBJECT(obj)                                             \
+    (DebugCheckWrapperClass(obj) && obj->getSlot(0).isUndefined())
+#define IS_SLIM_WRAPPER_OBJECT(obj)                                           \
+    (DebugCheckWrapperClass(obj) && !obj->getSlot(0).isUndefined())
+
+// Use these macros if IS_WRAPPER_CLASS(obj->getClass()) might be false.
+// Avoid calling them if IS_WRAPPER_CLASS(obj->getClass()) can only be
+// true, as we'd do a redundant call to IS_WRAPPER_CLASS.
+#define IS_WN_WRAPPER(obj)                                                    \
+    (IS_WRAPPER_CLASS(obj->getClass()) && IS_WN_WRAPPER_OBJECT(obj))
+#define IS_SLIM_WRAPPER(obj)                                                  \
+    (IS_WRAPPER_CLASS(obj->getClass()) && IS_SLIM_WRAPPER_OBJECT(obj))
+
+inline JSObject *
+xpc_GetGlobalForObject(JSObject *obj)
+{
+    while(JSObject *parent = obj->getParent())
+        obj = parent;
+    return obj;
+}
+
+inline JSObject*
+xpc_GetCachedSlimWrapper(nsWrapperCache *cache, JSObject *scope, jsval *vp)
+{
+    if (cache) {
+        JSObject* wrapper = cache->GetWrapper();
+        // FIXME: Bug 585786, the check for IS_SLIM_WRAPPER_OBJECT should go
+        //        away
+        if (wrapper &&
+            IS_SLIM_WRAPPER_OBJECT(wrapper) &&
+            wrapper->getCompartment() == scope->getCompartment()) {
+            *vp = OBJECT_TO_JSVAL(wrapper);
+
+            return wrapper;
+        }
+    }
+
+    return nsnull;
+}
+
 #endif