Title: [146682] trunk/Source/_javascript_Core
Revision
146682
Author
[email protected]
Date
2013-03-22 16:49:52 -0700 (Fri, 22 Mar 2013)

Log Message

opaqueJSClassData should be cached on JSGlobalObject, not the JSGlobalData
https://bugs.webkit.org/show_bug.cgi?id=113086

Reviewed by Geoffrey Garen.

opaqueJSClassData stores cached prototypes for JSClassRefs in the C API. It doesn't make sense to 
share these prototypes within a JSGlobalData across JSGlobalObjects, and in fact doing so will cause 
a leak of the original JSGlobalObject that these prototypes were created in. Therefore we should move 
this cache to JSGlobalObject where it belongs and where it won't cause memory leaks.

* API/JSBase.cpp: Needed to add an extern "C" so that testapi.c can use the super secret GC function.
* API/JSClassRef.cpp: We now grab the cached context data from the global object rather than the global data.
(OpaqueJSClass::contextData):
* API/JSClassRef.h: Remove this header because it's unnecessary and causes circular dependencies.
* API/tests/testapi.c: Added a new test that makes sure that using the same JSClassRef in two different contexts
doesn't cause leaks of the original global object.
(leakFinalize):
(nestedAllocateObject): This is a hack to bypass the conservative scan of the GC, which was unnecessarily marking
objects and keeping them alive, ruining the test result.
(testLeakingPrototypesAcrossContexts):
(main):
* API/tests/testapi.mm: extern "C" this so we can continue using it here.
* runtime/JSGlobalData.cpp: Remove JSClassRef related stuff.
(JSC::JSGlobalData::~JSGlobalData):
* runtime/JSGlobalData.h:
(JSGlobalData):
* runtime/JSGlobalObject.h: Add the stuff that JSGlobalData had. We add it to JSGlobalObjectRareData so that 
clients who don't use the C API don't have to pay the memory cost of this extra HashMap.
(JSGlobalObject):
(JSGlobalObjectRareData):
(JSC::JSGlobalObject::opaqueJSClassData):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/API/JSBase.cpp (146681 => 146682)


--- trunk/Source/_javascript_Core/API/JSBase.cpp	2013-03-22 23:48:40 UTC (rev 146681)
+++ trunk/Source/_javascript_Core/API/JSBase.cpp	2013-03-22 23:49:52 UTC (rev 146682)
@@ -111,7 +111,7 @@
     exec->globalData().heap.reportExtraMemoryCost(size);
 }
 
-JS_EXPORT void JSSynchronousGarbageCollectForDebugging(JSContextRef);
+extern "C" JS_EXPORT void JSSynchronousGarbageCollectForDebugging(JSContextRef);
 
 void JSSynchronousGarbageCollectForDebugging(JSContextRef ctx)
 {

Modified: trunk/Source/_javascript_Core/API/JSClassRef.cpp (146681 => 146682)


--- trunk/Source/_javascript_Core/API/JSClassRef.cpp	2013-03-22 23:48:40 UTC (rev 146681)
+++ trunk/Source/_javascript_Core/API/JSClassRef.cpp	2013-03-22 23:49:52 UTC (rev 146682)
@@ -151,7 +151,7 @@
 
 OpaqueJSClassContextData& OpaqueJSClass::contextData(ExecState* exec)
 {
-    OwnPtr<OpaqueJSClassContextData>& contextData = exec->globalData().opaqueJSClassData.add(this, nullptr).iterator->value;
+    OwnPtr<OpaqueJSClassContextData>& contextData = exec->lexicalGlobalObject()->opaqueJSClassData().add(this, nullptr).iterator->value;
     if (!contextData)
         contextData = adoptPtr(new OpaqueJSClassContextData(exec->globalData(), this));
     return *contextData;

Modified: trunk/Source/_javascript_Core/API/JSClassRef.h (146681 => 146682)


--- trunk/Source/_javascript_Core/API/JSClassRef.h	2013-03-22 23:48:40 UTC (rev 146681)
+++ trunk/Source/_javascript_Core/API/JSClassRef.h	2013-03-22 23:49:52 UTC (rev 146682)
@@ -26,10 +26,9 @@
 #ifndef JSClassRef_h
 #define JSClassRef_h
 
-#include "JSObjectRef.h"
+#include <_javascript_Core/JSObjectRef.h>
 
 #include "Weak.h"
-#include "JSObject.h"
 #include "Protect.h"
 #include <wtf/HashMap.h>
 #include <wtf/text/WTFString.h>
@@ -87,7 +86,7 @@
 struct OpaqueJSClass : public ThreadSafeRefCounted<OpaqueJSClass> {
     static PassRefPtr<OpaqueJSClass> create(const JSClassDefinition*);
     static PassRefPtr<OpaqueJSClass> createNoAutomaticPrototype(const JSClassDefinition*);
-    ~OpaqueJSClass();
+    JS_EXPORT_PRIVATE ~OpaqueJSClass();
     
     String className();
     OpaqueJSClassStaticValuesTable* staticValues(JSC::ExecState*);

Modified: trunk/Source/_javascript_Core/API/tests/testapi.c (146681 => 146682)


--- trunk/Source/_javascript_Core/API/tests/testapi.c	2013-03-22 23:48:40 UTC (rev 146681)
+++ trunk/Source/_javascript_Core/API/tests/testapi.c	2013-03-22 23:49:52 UTC (rev 146682)
@@ -55,6 +55,8 @@
 void testObjectiveCAPI(void);
 #endif
 
+extern void JSSynchronousGarbageCollectForDebugging(JSContextRef);
+
 static JSGlobalContextRef context;
 int failed;
 static void assertEqualsAsBoolean(JSValueRef value, bool expectedValue)
@@ -132,6 +134,61 @@
     JSStringRelease(valueAsString);
 }
 
+static int leakedObject = 1;
+
+static void leakFinalize(JSObjectRef object)
+{
+    (void)object;
+    leakedObject = 0;
+}
+
+// This is a hack to avoid the C++ stack keeping the original JSObject alive.
+static void nestedAllocateObject(JSContextRef context, JSClassRef class, unsigned n)
+{
+    if (!n) {
+        JSObjectRef object = JSObjectMake(context, class, 0);
+        JSObjectRef globalObject = JSContextGetGlobalObject(context);
+        JSStringRef propertyName = JSStringCreateWithUTF8CString("value");
+        JSObjectSetProperty(context, globalObject, propertyName, object, kJSPropertyAttributeNone, 0);
+        JSStringRelease(propertyName);
+        return;
+    }
+    nestedAllocateObject(context, class, n - 1);
+}
+
+static void testLeakingPrototypesAcrossContexts()
+{
+    JSClassDefinition leakDefinition = kJSClassDefinitionEmpty;
+    leakDefinition.finalize = leakFinalize;
+    JSClassRef leakClass = JSClassCreate(&leakDefinition);
+
+    JSContextGroupRef group = JSContextGroupCreate();
+
+    {
+        JSGlobalContextRef context1 = JSGlobalContextCreateInGroup(group, NULL);
+        nestedAllocateObject(context1, leakClass, 10);
+        JSGlobalContextRelease(context1);
+    }
+
+    {
+        JSGlobalContextRef context2 = JSGlobalContextCreateInGroup(group, NULL);
+        JSObjectRef object2 = JSObjectMake(context2, leakClass, 0);
+        JSValueProtect(context2, object2);
+        JSSynchronousGarbageCollectForDebugging(context2);
+        if (leakedObject) {
+            printf("FAIL: Failed to finalize the original object after the first GC.\n");
+            failed = 1;
+        } else
+            printf("PASS: Finalized the original object as expected.\n");
+        JSValueUnprotect(context2, object2);
+        JSGlobalContextRelease(context2);
+    }
+
+    JSContextGroupRelease(group);
+
+    JSClassRelease(leakClass);
+}
+
 static bool timeZoneIsPST()
 {
     char timeZoneName[70];
@@ -1685,6 +1742,8 @@
         free(scriptUTF8);
     }
 
+    testLeakingPrototypesAcrossContexts();
+
     // Clear out local variables pointing at JSObjectRefs to allow their values to be collected
     function = NULL;
     v = NULL;

Modified: trunk/Source/_javascript_Core/API/tests/testapi.mm (146681 => 146682)


--- trunk/Source/_javascript_Core/API/tests/testapi.mm	2013-03-22 23:48:40 UTC (rev 146681)
+++ trunk/Source/_javascript_Core/API/tests/testapi.mm	2013-03-22 23:49:52 UTC (rev 146682)
@@ -25,7 +25,7 @@
 
 #import <_javascript_Core/_javascript_Core.h>
 
-void JSSynchronousGarbageCollectForDebugging(JSContextRef);
+extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef);
 
 extern "C" bool _Block_has_signature(id);
 extern "C" const char * _Block_signature(id);

Modified: trunk/Source/_javascript_Core/ChangeLog (146681 => 146682)


--- trunk/Source/_javascript_Core/ChangeLog	2013-03-22 23:48:40 UTC (rev 146681)
+++ trunk/Source/_javascript_Core/ChangeLog	2013-03-22 23:49:52 UTC (rev 146682)
@@ -1,3 +1,37 @@
+2013-03-22  Mark Hahnenberg  <[email protected]>
+
+        opaqueJSClassData should be cached on JSGlobalObject, not the JSGlobalData
+        https://bugs.webkit.org/show_bug.cgi?id=113086
+
+        Reviewed by Geoffrey Garen.
+
+        opaqueJSClassData stores cached prototypes for JSClassRefs in the C API. It doesn't make sense to 
+        share these prototypes within a JSGlobalData across JSGlobalObjects, and in fact doing so will cause 
+        a leak of the original JSGlobalObject that these prototypes were created in. Therefore we should move 
+        this cache to JSGlobalObject where it belongs and where it won't cause memory leaks.
+
+        * API/JSBase.cpp: Needed to add an extern "C" so that testapi.c can use the super secret GC function.
+        * API/JSClassRef.cpp: We now grab the cached context data from the global object rather than the global data.
+        (OpaqueJSClass::contextData):
+        * API/JSClassRef.h: Remove this header because it's unnecessary and causes circular dependencies.
+        * API/tests/testapi.c: Added a new test that makes sure that using the same JSClassRef in two different contexts
+        doesn't cause leaks of the original global object.
+        (leakFinalize):
+        (nestedAllocateObject): This is a hack to bypass the conservative scan of the GC, which was unnecessarily marking
+        objects and keeping them alive, ruining the test result.
+        (testLeakingPrototypesAcrossContexts):
+        (main):
+        * API/tests/testapi.mm: extern "C" this so we can continue using it here.
+        * runtime/JSGlobalData.cpp: Remove JSClassRef related stuff.
+        (JSC::JSGlobalData::~JSGlobalData):
+        * runtime/JSGlobalData.h:
+        (JSGlobalData):
+        * runtime/JSGlobalObject.h: Add the stuff that JSGlobalData had. We add it to JSGlobalObjectRareData so that 
+        clients who don't use the C API don't have to pay the memory cost of this extra HashMap.
+        (JSGlobalObject):
+        (JSGlobalObjectRareData):
+        (JSC::JSGlobalObject::opaqueJSClassData):
+
 2013-03-19  Martin Robinson  <[email protected]>
 
         [GTK] Add support for building the WebCore bindings to the gyp build

Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (146681 => 146682)


--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2013-03-22 23:48:40 UTC (rev 146681)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2013-03-22 23:49:52 UTC (rev 146682)
@@ -742,7 +742,7 @@
 		BC18C41A0E16F5CD00B34460 /* JSCallbackFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 1440F88F0A508B100005F061 /* JSCallbackFunction.h */; };
 		BC18C41B0E16F5CD00B34460 /* JSCallbackObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 14ABDF5D0A437FEF00ECCA01 /* JSCallbackObject.h */; };
 		BC18C41C0E16F5CD00B34460 /* JSCallbackObjectFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = A8E894310CD0602400367179 /* JSCallbackObjectFunctions.h */; };
-		BC18C41D0E16F5CD00B34460 /* JSClassRef.h in Headers */ = {isa = PBXBuildFile; fileRef = 1440FCE10A51E46B0005F061 /* JSClassRef.h */; };
+		BC18C41D0E16F5CD00B34460 /* JSClassRef.h in Headers */ = {isa = PBXBuildFile; fileRef = 1440FCE10A51E46B0005F061 /* JSClassRef.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		BC18C41E0E16F5CD00B34460 /* JSContextRef.h in Headers */ = {isa = PBXBuildFile; fileRef = 14BD5A2A0A3E91F600BAF59C /* JSContextRef.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		BC18C41F0E16F5CD00B34460 /* JSFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = F692A85F0255597D01FF60F7 /* JSFunction.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		BC18C4200E16F5CD00B34460 /* JSGlobalData.h in Headers */ = {isa = PBXBuildFile; fileRef = E18E3A560DF9278C00D90B34 /* JSGlobalData.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -2956,6 +2956,7 @@
 				0FB7F39515ED8E4600F167B2 /* ArrayConventions.h in Headers */,
 				0F63945515D07057006A597C /* ArrayProfile.h in Headers */,
 				BC18C3E70E16F5CD00B34460 /* ArrayPrototype.h in Headers */,
+				BC18C41D0E16F5CD00B34460 /* JSClassRef.h in Headers */,
 				86E3C61D167BABEE006D760A /* JSVirtualMachineInternal.h in Headers */,
 				BC18C5240E16FC8A00B34460 /* ArrayPrototype.lut.h in Headers */,
 				86E3C617167BABEE006D760A /* JSContextInternal.h in Headers */,
@@ -3154,7 +3155,6 @@
 				0F9749711687ADE400A4FF6A /* JSCellInlines.h in Headers */,
 				BC18C42B0E16F5CD00B34460 /* JSCJSValue.h in Headers */,
 				865A30F1135007E100CDB49E /* JSCJSValueInlines.h in Headers */,
-				BC18C41D0E16F5CD00B34460 /* JSClassRef.h in Headers */,
 				86E3C613167BABD7006D760A /* JSContext.h in Headers */,
 				BC18C41E0E16F5CD00B34460 /* JSContextRef.h in Headers */,
 				148CD1D8108CF902008163C6 /* JSContextRefPrivate.h in Headers */,

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalData.cpp (146681 => 146682)


--- trunk/Source/_javascript_Core/runtime/JSGlobalData.cpp	2013-03-22 23:48:40 UTC (rev 146681)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalData.cpp	2013-03-22 23:49:52 UTC (rev 146682)
@@ -44,7 +44,6 @@
 #include "JSActivation.h"
 #include "JSAPIValueWrapper.h"
 #include "JSArray.h"
-#include "JSClassRef.h"
 #include "JSFunction.h"
 #include "JSLock.h"
 #include "JSNameScope.h"
@@ -306,8 +305,6 @@
     fastDelete(const_cast<HashTable*>(regExpPrototypeTable));
     fastDelete(const_cast<HashTable*>(stringConstructorTable));
 
-    opaqueJSClassData.clear();
-
     delete emptyList;
 
     delete propertyNames;

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalData.h (146681 => 146682)


--- trunk/Source/_javascript_Core/runtime/JSGlobalData.h	2013-03-22 23:48:40 UTC (rev 146681)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalData.h	2013-03-22 23:49:52 UTC (rev 146682)
@@ -62,9 +62,6 @@
 #include <wtf/ListHashSet.h>
 #endif
 
-struct OpaqueJSClass;
-struct OpaqueJSClassContextData;
-
 namespace JSC {
 
     class CodeBlock;
@@ -369,8 +366,6 @@
         void gatherConservativeRoots(ConservativeRoots&);
 #endif
 
-        HashMap<OpaqueJSClass*, OwnPtr<OpaqueJSClassContextData> > opaqueJSClassData;
-
         JSGlobalObject* dynamicGlobalObject;
 
         HashSet<JSObject*> stringRecursionCheckVisitedObjects;

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.h (146681 => 146682)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2013-03-22 23:48:40 UTC (rev 146681)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2013-03-22 23:49:52 UTC (rev 146682)
@@ -24,6 +24,7 @@
 
 #include "ArrayAllocationProfile.h"
 #include "JSArray.h"
+#include "JSClassRef.h"
 #include "JSGlobalData.h"
 #include "JSSegmentedVariableObject.h"
 #include "JSWeakObjectMapRefInternal.h"
@@ -38,6 +39,9 @@
 #include <wtf/OwnPtr.h>
 #include <wtf/RandomNumber.h>
 
+struct OpaqueJSClass;
+struct OpaqueJSClassContextData;
+
 namespace JSC {
 
 class ArrayPrototype;
@@ -86,6 +90,7 @@
 class JSGlobalObject : public JSSegmentedVariableObject {
 private:
     typedef HashSet<RefPtr<OpaqueJSWeakObjectMap> > WeakMapSet;
+    typedef HashMap<OpaqueJSClass*, OwnPtr<OpaqueJSClassContextData> > OpaqueJSClassDataMap;
 
     struct JSGlobalObjectRareData {
         JSGlobalObjectRareData()
@@ -95,6 +100,8 @@
 
         WeakMapSet weakMaps;
         unsigned profileGroup;
+        
+        OpaqueJSClassDataMap opaqueJSClassData;
     };
 
 protected:
@@ -395,6 +402,12 @@
             m_rareData->weakMaps.remove(map);
     }
 
+    OpaqueJSClassDataMap& opaqueJSClassData()
+    {
+        createRareDataIfNeeded();
+        return m_rareData->opaqueJSClassData;
+    }
+
     double weakRandomNumber() { return m_weakRandom.get(); }
     unsigned weakRandomInteger() { return m_weakRandom.getUint32(); }
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to