Bug 1145854 - Don't leak WrapperOwner::className's string. r=billm
authorBlake Kaplan <mrbkap@gmail.com>
Fri, 27 Mar 2015 13:12:37 -0700
changeset 266602 7264dc6085845a0018bbbdbb00b90cbcd538b2de
parent 266601 b336dc0af92c339da912b09f205fb9bffc9afdf3
child 266603 6802c30854387e14973d38114902396362fd2124
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs1145854
milestone39.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 1145854 - Don't leak WrapperOwner::className's string. r=billm
dom/base/test/chrome/cpows_parent.xul
js/ipc/JavaScriptBase.h
js/ipc/PJavaScript.ipdl
js/ipc/WrapperAnswer.cpp
js/ipc/WrapperAnswer.h
js/ipc/WrapperOwner.cpp
js/ipc/WrapperOwner.h
--- a/dom/base/test/chrome/cpows_parent.xul
+++ b/dom/base/test/chrome/cpows_parent.xul
@@ -227,16 +227,22 @@
       // See bug 1072980.
       if (test_state == "remote") {
         // This doesn't work because we intercept toString and QueryInterface specially
         // and don't cache the function pointer.
         // See bug 1140636.
         todo_is(savedElement.toString, savedElement.toString, "toString identity works");
         todo_is(savedElement.QueryInterface, savedElement.QueryInterface, "toString identity works");
 
+        is(Object.prototype.toString.call(savedElement), "[object HTMLDivElement]",
+           "prove that this works (and doesn't leak)");
+
+        is(Object.prototype.toString.call(savedElement), "[object HTMLDivElement]",
+           "prove that this works twice (since we cache it and doesn't leak)");
+
         // This does work because we create a CPOW for isEqualNode that stays
         // alive as long as we have a reference to the first CPOW (so as long
         // as it's detectable).
         is(savedElement.isEqualNode, savedElement.isEqualNode, "webidl function identity works");
 
         let walker = Components.classes["@mozilla.org/inspector/deep-tree-walker;1"]
                                .createInstance(Components.interfaces.inIDeepTreeWalker);
         const SHOW_ELEMENT = Components.interfaces.nsIDOMNodeFilter.SHOW_ELEMENT;
--- a/js/ipc/JavaScriptBase.h
+++ b/js/ipc/JavaScriptBase.h
@@ -87,17 +87,17 @@ class JavaScriptBase : public WrapperOwn
     }
     bool RecvHasInstance(const uint64_t &objId, const JSVariant &v, ReturnStatus *rs, bool *bp) {
         return Answer::RecvHasInstance(ObjectId::deserialize(objId), v, rs, bp);
     }
     bool RecvObjectClassIs(const uint64_t &objId, const uint32_t &classValue,
                              bool *result) {
         return Answer::RecvObjectClassIs(ObjectId::deserialize(objId), classValue, result);
     }
-    bool RecvClassName(const uint64_t &objId, nsString *result) {
+    bool RecvClassName(const uint64_t &objId, nsCString *result) {
         return Answer::RecvClassName(ObjectId::deserialize(objId), result);
     }
     bool RecvGetPrototype(const uint64_t &objId, ReturnStatus *rs, ObjectOrNullVariant *result) {
         return Answer::RecvGetPrototype(ObjectId::deserialize(objId), rs, result);
     }
     bool RecvRegExpToShared(const uint64_t &objId, ReturnStatus *rs, nsString *source, uint32_t *flags) {
         return Answer::RecvRegExpToShared(ObjectId::deserialize(objId), rs, source, flags);
     }
@@ -176,17 +176,17 @@ class JavaScriptBase : public WrapperOwn
     }
     bool SendHasInstance(const ObjectId &objId, const JSVariant &v, ReturnStatus *rs, bool *bp) {
         return Base::SendHasInstance(objId.serialize(), v, rs, bp);
     }
     bool SendObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
                            bool *result) {
         return Base::SendObjectClassIs(objId.serialize(), classValue, result);
     }
-    bool SendClassName(const ObjectId &objId, nsString *result) {
+    bool SendClassName(const ObjectId &objId, nsCString *result) {
         return Base::SendClassName(objId.serialize(), result);
     }
     bool SendGetPrototype(const ObjectId &objId, ReturnStatus *rs, ObjectOrNullVariant *result) {
         return Base::SendGetPrototype(objId.serialize(), rs, result);
     }
 
     bool SendRegExpToShared(const ObjectId &objId, ReturnStatus *rs,
                             nsString *source, uint32_t *flags) {
--- a/js/ipc/PJavaScript.ipdl
+++ b/js/ipc/PJavaScript.ipdl
@@ -34,17 +34,17 @@ both:
     prio(high) sync HasOwn(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool has);
     prio(high) sync Get(uint64_t objId, ObjectVariant receiver, JSIDVariant id) returns (ReturnStatus rs, JSVariant result);
     prio(high) sync Set(uint64_t objId, JSIDVariant id, JSVariant value, JSVariant receiver) returns (ReturnStatus rs);
 
     prio(high) sync IsExtensible(uint64_t objId) returns (ReturnStatus rs, bool result);
     prio(high) sync CallOrConstruct(uint64_t objId, JSParam[] argv, bool construct) returns (ReturnStatus rs, JSVariant result, JSParam[] outparams);
     prio(high) sync HasInstance(uint64_t objId, JSVariant v) returns (ReturnStatus rs, bool has);
     prio(high) sync ObjectClassIs(uint64_t objId, uint32_t classValue) returns (bool result);
-    prio(high) sync ClassName(uint64_t objId) returns (nsString name);
+    prio(high) sync ClassName(uint64_t objId) returns (nsCString name);
     prio(high) sync GetPrototype(uint64_t objId) returns (ReturnStatus rs, ObjectOrNullVariant result);
     prio(high) sync RegExpToShared(uint64_t objId) returns (ReturnStatus rs, nsString source, uint32_t flags);
 
     prio(high) sync GetPropertyKeys(uint64_t objId, uint32_t flags) returns (ReturnStatus rs, JSIDVariant[] ids);
     prio(high) sync InstanceOf(uint64_t objId, JSIID iid) returns (ReturnStatus rs, bool instanceof);
     prio(high) sync DOMInstanceOf(uint64_t objId, int prototypeID, int depth) returns (ReturnStatus rs, bool instanceof);
 
 parent:
--- a/js/ipc/WrapperAnswer.cpp
+++ b/js/ipc/WrapperAnswer.cpp
@@ -508,32 +508,32 @@ WrapperAnswer::RecvObjectClassIs(const O
 
     LOG("%s.objectClassIs()", ReceiverObj(objId));
 
     *result = js::ObjectClassIs(cx, obj, (js::ESClassValue)classValue);
     return true;
 }
 
 bool
-WrapperAnswer::RecvClassName(const ObjectId &objId, nsString *name)
+WrapperAnswer::RecvClassName(const ObjectId &objId, nsCString *name)
 {
     AutoJSAPI jsapi;
     if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
         return false;
     JSContext *cx = jsapi.cx();
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj) {
         // This is very unfortunate, but we have no choice.
         return "<dead CPOW>";
     }
 
     LOG("%s.className()", ReceiverObj(objId));
 
-    *name = NS_ConvertASCIItoUTF16(js::ObjectClassName(cx, obj));
+    *name = js::ObjectClassName(cx, obj);
     return true;
 }
 
 bool
 WrapperAnswer::RecvGetPrototype(const ObjectId &objId, ReturnStatus *rs, ObjectOrNullVariant *result)
 {
     *result = NullVariant();
 
--- a/js/ipc/WrapperAnswer.h
+++ b/js/ipc/WrapperAnswer.h
@@ -43,17 +43,17 @@ class WrapperAnswer : public virtual Jav
     bool RecvIsExtensible(const ObjectId &objId, ReturnStatus *rs,
                           bool *result);
     bool RecvCallOrConstruct(const ObjectId &objId, InfallibleTArray<JSParam> &&argv,
                              const bool &construct, ReturnStatus *rs, JSVariant *result,
                              nsTArray<JSParam> *outparams);
     bool RecvHasInstance(const ObjectId &objId, const JSVariant &v, ReturnStatus *rs, bool *bp);
     bool RecvObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
                            bool *result);
-    bool RecvClassName(const ObjectId &objId, nsString *result);
+    bool RecvClassName(const ObjectId &objId, nsCString *result);
     bool RecvGetPrototype(const ObjectId &objId, ReturnStatus *rs, ObjectOrNullVariant *result);
     bool RecvRegExpToShared(const ObjectId &objId, ReturnStatus *rs, nsString *source, uint32_t *flags);
 
     bool RecvGetPropertyKeys(const ObjectId &objId, const uint32_t &flags,
                              ReturnStatus *rs, nsTArray<JSIDVariant> *ids);
     bool RecvInstanceOf(const ObjectId &objId, const JSIID &iid,
                         ReturnStatus *rs, bool *instanceof);
     bool RecvDOMInstanceOf(const ObjectId &objId, const int &prototypeID, const int &depth,
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -28,16 +28,19 @@ struct AuxCPOWData
     bool isCallable;
     bool isConstructor;
     bool isDOMObject;
 
     // The object tag is just some auxilliary information that clients can use
     // however they see fit.
     nsCString objectTag;
 
+    // The class name for WrapperOwner::className, below.
+    nsCString className;
+
     AuxCPOWData(ObjectId id,
                 bool isCallable,
                 bool isConstructor,
                 bool isDOMObject,
                 const nsACString &objectTag)
       : id(id),
         isCallable(isCallable),
         isConstructor(isConstructor),
@@ -745,25 +748,27 @@ CPOWProxyHandler::className(JSContext *c
     if (!parent->active())
         return "<dead CPOW>";
     return parent->className(cx, proxy);
 }
 
 const char *
 WrapperOwner::className(JSContext *cx, HandleObject proxy)
 {
-    ObjectId objId = idOf(proxy);
+    AuxCPOWData *data = AuxCPOWDataOf(proxy);
+    if (data->className.IsEmpty()) {
+        ObjectId objId = idOf(proxy);
 
-    nsString name;
-    if (!SendClassName(objId, &name))
-        return "<error>";
+        if (!SendClassName(objId, &data->className))
+            return "<error>";
 
-    LOG_STACK();
+        LOG_STACK();
+    }
 
-    return ToNewCString(name);
+    return data->className.get();
 }
 
 bool
 CPOWProxyHandler::getPrototype(JSContext *cx, HandleObject proxy, MutableHandleObject objp) const
 {
     FORWARD(getPrototype, (cx, proxy, objp));
 }
 
--- a/js/ipc/WrapperOwner.h
+++ b/js/ipc/WrapperOwner.h
@@ -135,17 +135,17 @@ class WrapperOwner : public virtual Java
                                   bool *result) = 0;
     virtual bool SendCallOrConstruct(const ObjectId &objId, const nsTArray<JSParam> &argv,
                                      const bool &construct, ReturnStatus *rs, JSVariant *result,
                                      nsTArray<JSParam> *outparams) = 0;
     virtual bool SendHasInstance(const ObjectId &objId, const JSVariant &v,
                                  ReturnStatus *rs, bool *bp) = 0;
     virtual bool SendObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
                                    bool *result) = 0;
-    virtual bool SendClassName(const ObjectId &objId, nsString *result) = 0;
+    virtual bool SendClassName(const ObjectId &objId, nsCString *result) = 0;
     virtual bool SendGetPrototype(const ObjectId &objId, ReturnStatus *rs, ObjectOrNullVariant *result) = 0;
     virtual bool SendRegExpToShared(const ObjectId &objId, ReturnStatus *rs, nsString *source,
                                     uint32_t *flags) = 0;
 
     virtual bool SendGetPropertyKeys(const ObjectId &objId, const uint32_t &flags,
                                      ReturnStatus *rs, nsTArray<JSIDVariant> *ids) = 0;
     virtual bool SendInstanceOf(const ObjectId &objId, const JSIID &iid,
                                 ReturnStatus *rs, bool *instanceof) = 0;