Bug 834877 part 3. Add faster DOMString-to-JS conversion code. r=peterv
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 29 Jan 2013 09:42:14 -0500
changeset 120222 9ed387b675bb030f4521074fc7b46410c1a06271
parent 120221 c4209fe94d301cfe22d74f3e3b06d7161f94e469
child 120223 4f59efe7733355f97fb2a9fedf219a44bb306720
push id24243
push userryanvm@gmail.com
push dateWed, 30 Jan 2013 00:49:21 +0000
treeherdermozilla-central@5c248ef0fe62 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs834877
milestone21.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 834877 part 3. Add faster DOMString-to-JS conversion code. r=peterv
js/xpconnect/src/XPCString.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/src/xpcpublic.h
--- a/js/xpconnect/src/XPCString.cpp
+++ b/js/xpconnect/src/XPCString.cpp
@@ -21,37 +21,38 @@
 #include "xpcprivate.h"
 #include "nsStringBuffer.h"
 
 // One-slot cache, because it turns out it's common for web pages to
 // get the same string a few times in a row.  We get about a 40% cache
 // hit rate on this cache last it was measured.  We'd get about 70%
 // hit rate with a hashtable with removal on finalization, but that
 // would take a lot more machinery.
-static nsStringBuffer* sCachedBuffer = nullptr;
-static JSString* sCachedString = nullptr;
+nsStringBuffer* XPCStringConvert::sCachedBuffer = nullptr;
+JSString* XPCStringConvert::sCachedString = nullptr;
 
 // Called from GC finalize callback to make sure we don't hand out a pointer to
 // a JSString that's about to be finalized by incremental sweeping.
 // static
 void
 XPCStringConvert::ClearCache()
 {
     sCachedBuffer = nullptr;
     sCachedString = nullptr;
 }
 
-static void
-FinalizeDOMString(const JSStringFinalizer *fin, jschar *chars)
+void
+XPCStringConvert::FinalizeDOMString(const JSStringFinalizer *fin, jschar *chars)
 {
     nsStringBuffer* buf = nsStringBuffer::FromData(chars);
     buf->Release();
 }
 
-static const JSStringFinalizer sDOMStringFinalizer = { FinalizeDOMString };
+const JSStringFinalizer XPCStringConvert::sDOMStringFinalizer =
+    { XPCStringConvert::FinalizeDOMString };
 
 // convert a readable to a JSString, copying string data
 // static
 jsval
 XPCStringConvert::ReadableToJSVal(JSContext *cx,
                                   const nsAString &readable,
                                   nsStringBuffer** sharedBuffer)
 {
@@ -60,49 +61,45 @@ XPCStringConvert::ReadableToJSVal(JSCont
 
     uint32_t length = readable.Length();
 
     if (length == 0)
         return JS_GetEmptyStringValue(cx);
 
     nsStringBuffer *buf = nsStringBuffer::FromString(readable);
     if (buf) {
-        if (buf == sCachedBuffer &&
-            js::GetGCThingCompartment(sCachedString) == js::GetContextCompartment(cx)) {
-            // We're done.  Just return our existing string.
-            return JS::StringValue(sCachedString);
+        JS::Value val;
+        bool shared;
+        bool ok = StringBufferToJSVal(cx, buf, length, &val, &shared);
+        if (!ok) {
+            return JS::NullValue();
         }
 
-        // yay, we can share the string's buffer!
-
-        str = JS_NewExternalString(cx,
-                                   reinterpret_cast<jschar *>(buf->Data()),
-                                   length, &sDOMStringFinalizer);
+        if (shared) {
+            *sharedBuffer = buf;
+        }
+        return val;
+    }
 
-        if (str) {
-            *sharedBuffer = buf;
-            sCachedString = str;
-            sCachedBuffer = buf;
-        }
-    } else {
-        // blech, have to copy.
+    // blech, have to copy.
+
+    jschar *chars = reinterpret_cast<jschar *>
+                                    (JS_malloc(cx, (length + 1) *
+                                               sizeof(jschar)));
+    if (!chars)
+        return JS::NullValue();
 
-        jschar *chars = reinterpret_cast<jschar *>
-                                        (JS_malloc(cx, (length + 1) *
-                                                   sizeof(jschar)));
-        if (!chars)
-            return JSVAL_NULL;
+    if (length && !CopyUnicodeTo(readable, 0,
+                                 reinterpret_cast<PRUnichar *>(chars),
+                                 length)) {
+        JS_free(cx, chars);
+        return JS::NullValue();
+    }
 
-        if (length && !CopyUnicodeTo(readable, 0,
-                                     reinterpret_cast<PRUnichar *>(chars),
-                                     length)) {
-            JS_free(cx, chars);
-            return JSVAL_NULL;
-        }
+    chars[length] = 0;
 
-        chars[length] = 0;
+    str = JS_NewUCString(cx, chars, length);
+    if (!str) {
+        JS_free(cx, chars);
+    }
 
-        str = JS_NewUCString(cx, chars, length);
-        if (!str)
-            JS_free(cx, chars);
-    }
     return str ? STRING_TO_JSVAL(str) : JSVAL_NULL;
 }
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3418,35 +3418,16 @@ public:
                                        jsval *jsExceptionPtr);
 
 private:
     XPCConvert(); // not implemented
 
 };
 
 /***************************************************************************/
-
-// readable string conversions, static methods only
-class XPCStringConvert
-{
-public:
-
-    // If the string shares the readable's buffer, that buffer will
-    // get assigned to *sharedBuffer.  Otherwise null will be
-    // assigned.
-    static jsval ReadableToJSVal(JSContext *cx, const nsAString &readable,
-                                 nsStringBuffer** sharedBuffer);
-
-    static void ClearCache();
-
-private:
-    XPCStringConvert();         // not implemented
-};
-
-/***************************************************************************/
 // code for throwing exceptions into JS
 
 class XPCThrower
 {
 public:
     static void Throw(nsresult rv, JSContext* cx);
     static void Throw(nsresult rv, XPCCallContext& ccx);
     static void ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx);
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -19,16 +19,18 @@
 
 #include "nsISupports.h"
 #include "nsIPrincipal.h"
 #include "nsWrapperCache.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
 #include "mozilla/dom/DOMJSClass.h"
 #include "nsMathUtils.h"
+#include "nsStringBuffer.h"
+#include "mozilla/dom/BindingDeclarations.h"
 
 class nsIPrincipal;
 class nsIXPConnectWrappedJS;
 class nsScriptNameSpaceManager;
 
 #ifndef BAD_TLS_INDEX
 #define BAD_TLS_INDEX ((uint32_t) -1)
 #endif
@@ -201,16 +203,64 @@ xpc_UnmarkSkippableJSHolders();
 
 // No JS can be on the stack when this is called. Probably only useful from
 // xpcshell.
 NS_EXPORT_(void)
 xpc_ActivateDebugMode();
 
 class nsIMemoryMultiReporterCallback;
 
+// readable string conversions, static methods and members only
+class XPCStringConvert
+{
+public:
+
+    // If the string shares the readable's buffer, that buffer will
+    // get assigned to *sharedBuffer.  Otherwise null will be
+    // assigned.
+    static jsval ReadableToJSVal(JSContext *cx, const nsAString &readable,
+                                 nsStringBuffer** sharedBuffer);
+
+    // Convert the given stringbuffer/length pair to a jsval
+    static MOZ_ALWAYS_INLINE bool
+    StringBufferToJSVal(JSContext* cx, nsStringBuffer* buf, uint32_t length,
+                        JS::Value* rval, bool* sharedBuffer)
+    {
+        if (buf == sCachedBuffer &&
+            js::GetGCThingCompartment(sCachedString) == js::GetContextCompartment(cx)) {
+            *rval = JS::StringValue(sCachedString);
+            *sharedBuffer = false;
+            return true;
+        }
+
+        JSString *str = JS_NewExternalString(cx,
+                                             static_cast<jschar*>(buf->Data()),
+                                             length, &sDOMStringFinalizer);
+        if (!str) {
+            return false;
+        }
+        *rval = JS::StringValue(str);
+        sCachedString = str;
+        sCachedBuffer = buf;
+        *sharedBuffer = true;
+        return true;
+    }
+
+    static void ClearCache();
+
+private:
+    static nsStringBuffer* sCachedBuffer;
+    static JSString* sCachedString;
+    static const JSStringFinalizer sDOMStringFinalizer;
+
+    static void FinalizeDOMString(const JSStringFinalizer *fin, jschar *chars);
+
+    XPCStringConvert();         // not implemented
+};
+
 namespace xpc {
 
 bool DeferredRelease(nsISupports *obj);
 
 // If these functions return false, then an exception will be set on cx.
 NS_EXPORT_(bool) Base64Encode(JSContext *cx, JS::Value val, JS::Value *out);
 NS_EXPORT_(bool) Base64Decode(JSContext *cx, JS::Value val, JS::Value *out);
 
@@ -225,16 +275,58 @@ inline bool StringToJsval(JSContext *cx,
     // From the T_DOMSTRING case in XPCConvert::NativeData2JS.
     if (str.IsVoid()) {
         *rval = JSVAL_NULL;
         return true;
     }
     return NonVoidStringToJsval(cx, str, rval);
 }
 
+/**
+ * As above, but for mozilla::dom::DOMString.
+ */
+MOZ_ALWAYS_INLINE
+bool NonVoidStringToJsval(JSContext* cx, mozilla::dom::DOMString& str,
+                          JS::Value *rval)
+{
+    if (!str.HasStringBuffer()) {
+        // It's an actual XPCOM string
+        return NonVoidStringToJsval(cx, str.AsAString(), rval);
+    }
+
+    uint32_t length = str.StringBufferLength();
+    if (length == 0) {
+        *rval = JS_GetEmptyStringValue(cx);
+        return true;
+    }
+
+    nsStringBuffer* buf = str.StringBuffer();
+    bool shared;
+    if (!XPCStringConvert::StringBufferToJSVal(cx, buf, length, rval,
+                                               &shared)) {
+        return false;
+    }
+    if (shared) {
+        // JS now needs to hold a reference to the buffer
+        buf->AddRef();
+    }
+    return true;
+}
+
+MOZ_ALWAYS_INLINE
+bool StringToJsval(JSContext* cx, mozilla::dom::DOMString& str,
+                   JS::Value *rval)
+{
+    if (str.IsNull()) {
+        *rval = JS::NullValue();
+        return true;
+    }
+    return NonVoidStringToJsval(cx, str, rval);
+}
+
 nsIPrincipal *GetCompartmentPrincipal(JSCompartment *compartment);
 
 void DumpJSHeap(FILE* file);
 
 void SetLocationForGlobal(JSObject *global, const nsACString& location);
 void SetLocationForGlobal(JSObject *global, nsIURI *locationURI);
 
 /**