Changeset 146682 in webkit


Ignore:
Timestamp:
Mar 22, 2013 4:49:52 PM (11 years ago)
Author:
mhahnenberg@apple.com
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):

Location:
trunk/Source/JavaScriptCore
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/API/JSBase.cpp

    r145119 r146682  
    112112}
    113113
    114 JS_EXPORT void JSSynchronousGarbageCollectForDebugging(JSContextRef);
     114extern "C" JS_EXPORT void JSSynchronousGarbageCollectForDebugging(JSContextRef);
    115115
    116116void JSSynchronousGarbageCollectForDebugging(JSContextRef ctx)
  • trunk/Source/JavaScriptCore/API/JSClassRef.cpp

    r139541 r146682  
    152152OpaqueJSClassContextData& OpaqueJSClass::contextData(ExecState* exec)
    153153{
    154     OwnPtr<OpaqueJSClassContextData>& contextData = exec->globalData().opaqueJSClassData.add(this, nullptr).iterator->value;
     154    OwnPtr<OpaqueJSClassContextData>& contextData = exec->lexicalGlobalObject()->opaqueJSClassData().add(this, nullptr).iterator->value;
    155155    if (!contextData)
    156156        contextData = adoptPtr(new OpaqueJSClassContextData(exec->globalData(), this));
  • trunk/Source/JavaScriptCore/API/JSClassRef.h

    r127191 r146682  
    2727#define JSClassRef_h
    2828
    29 #include "JSObjectRef.h"
     29#include <JavaScriptCore/JSObjectRef.h>
    3030
    3131#include "Weak.h"
    32 #include "JSObject.h"
    3332#include "Protect.h"
    3433#include <wtf/HashMap.h>
     
    8887    static PassRefPtr<OpaqueJSClass> create(const JSClassDefinition*);
    8988    static PassRefPtr<OpaqueJSClass> createNoAutomaticPrototype(const JSClassDefinition*);
    90     ~OpaqueJSClass();
     89    JS_EXPORT_PRIVATE ~OpaqueJSClass();
    9190   
    9291    String className();
  • trunk/Source/JavaScriptCore/API/tests/testapi.c

    r143637 r146682  
    5656#endif
    5757
     58extern void JSSynchronousGarbageCollectForDebugging(JSContextRef);
     59
    5860static JSGlobalContextRef context;
    5961int failed;
     
    131133    free(cfBuffer);
    132134    JSStringRelease(valueAsString);
     135}
     136
     137static int leakedObject = 1;
     138
     139static void leakFinalize(JSObjectRef object)
     140{
     141    (void)object;
     142    leakedObject = 0;
     143}
     144
     145// This is a hack to avoid the C++ stack keeping the original JSObject alive.
     146static void nestedAllocateObject(JSContextRef context, JSClassRef class, unsigned n)
     147{
     148    if (!n) {
     149        JSObjectRef object = JSObjectMake(context, class, 0);
     150        JSObjectRef globalObject = JSContextGetGlobalObject(context);
     151        JSStringRef propertyName = JSStringCreateWithUTF8CString("value");
     152        JSObjectSetProperty(context, globalObject, propertyName, object, kJSPropertyAttributeNone, 0);
     153        JSStringRelease(propertyName);
     154        return;
     155    }
     156    nestedAllocateObject(context, class, n - 1);
     157}
     158
     159static void testLeakingPrototypesAcrossContexts()
     160{
     161    JSClassDefinition leakDefinition = kJSClassDefinitionEmpty;
     162    leakDefinition.finalize = leakFinalize;
     163    JSClassRef leakClass = JSClassCreate(&leakDefinition);
     164
     165    JSContextGroupRef group = JSContextGroupCreate();
     166
     167    {
     168        JSGlobalContextRef context1 = JSGlobalContextCreateInGroup(group, NULL);
     169        nestedAllocateObject(context1, leakClass, 10);
     170        JSGlobalContextRelease(context1);
     171    }
     172
     173    {
     174        JSGlobalContextRef context2 = JSGlobalContextCreateInGroup(group, NULL);
     175        JSObjectRef object2 = JSObjectMake(context2, leakClass, 0);
     176        JSValueProtect(context2, object2);
     177        JSSynchronousGarbageCollectForDebugging(context2);
     178        if (leakedObject) {
     179            printf("FAIL: Failed to finalize the original object after the first GC.\n");
     180            failed = 1;
     181        } else
     182            printf("PASS: Finalized the original object as expected.\n");
     183        JSValueUnprotect(context2, object2);
     184        JSGlobalContextRelease(context2);
     185    }
     186
     187    JSContextGroupRelease(group);
     188
     189    JSClassRelease(leakClass);
    133190}
    134191
     
    16861743    }
    16871744
     1745    testLeakingPrototypesAcrossContexts();
     1746
    16881747    // Clear out local variables pointing at JSObjectRefs to allow their values to be collected
    16891748    function = NULL;
  • trunk/Source/JavaScriptCore/API/tests/testapi.mm

    r146603 r146682  
    2626#import <JavaScriptCore/JavaScriptCore.h>
    2727
    28 void JSSynchronousGarbageCollectForDebugging(JSContextRef);
     28extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef);
    2929
    3030extern "C" bool _Block_has_signature(id);
  • trunk/Source/JavaScriptCore/ChangeLog

    r146677 r146682  
     12013-03-22  Mark Hahnenberg  <mhahnenberg@apple.com>
     2
     3        opaqueJSClassData should be cached on JSGlobalObject, not the JSGlobalData
     4        https://bugs.webkit.org/show_bug.cgi?id=113086
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        opaqueJSClassData stores cached prototypes for JSClassRefs in the C API. It doesn't make sense to
     9        share these prototypes within a JSGlobalData across JSGlobalObjects, and in fact doing so will cause
     10        a leak of the original JSGlobalObject that these prototypes were created in. Therefore we should move
     11        this cache to JSGlobalObject where it belongs and where it won't cause memory leaks.
     12
     13        * API/JSBase.cpp: Needed to add an extern "C" so that testapi.c can use the super secret GC function.
     14        * API/JSClassRef.cpp: We now grab the cached context data from the global object rather than the global data.
     15        (OpaqueJSClass::contextData):
     16        * API/JSClassRef.h: Remove this header because it's unnecessary and causes circular dependencies.
     17        * API/tests/testapi.c: Added a new test that makes sure that using the same JSClassRef in two different contexts
     18        doesn't cause leaks of the original global object.
     19        (leakFinalize):
     20        (nestedAllocateObject): This is a hack to bypass the conservative scan of the GC, which was unnecessarily marking
     21        objects and keeping them alive, ruining the test result.
     22        (testLeakingPrototypesAcrossContexts):
     23        (main):
     24        * API/tests/testapi.mm: extern "C" this so we can continue using it here.
     25        * runtime/JSGlobalData.cpp: Remove JSClassRef related stuff.
     26        (JSC::JSGlobalData::~JSGlobalData):
     27        * runtime/JSGlobalData.h:
     28        (JSGlobalData):
     29        * runtime/JSGlobalObject.h: Add the stuff that JSGlobalData had. We add it to JSGlobalObjectRareData so that
     30        clients who don't use the C API don't have to pay the memory cost of this extra HashMap.
     31        (JSGlobalObject):
     32        (JSGlobalObjectRareData):
     33        (JSC::JSGlobalObject::opaqueJSClassData):
     34
    1352013-03-19  Martin Robinson  <mrobinson@igalia.com>
    236
  • trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj

    r146494 r146682  
    743743                BC18C41B0E16F5CD00B34460 /* JSCallbackObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 14ABDF5D0A437FEF00ECCA01 /* JSCallbackObject.h */; };
    744744                BC18C41C0E16F5CD00B34460 /* JSCallbackObjectFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = A8E894310CD0602400367179 /* JSCallbackObjectFunctions.h */; };
    745                 BC18C41D0E16F5CD00B34460 /* JSClassRef.h in Headers */ = {isa = PBXBuildFile; fileRef = 1440FCE10A51E46B0005F061 /* JSClassRef.h */; };
     745                BC18C41D0E16F5CD00B34460 /* JSClassRef.h in Headers */ = {isa = PBXBuildFile; fileRef = 1440FCE10A51E46B0005F061 /* JSClassRef.h */; settings = {ATTRIBUTES = (Private, ); }; };
    746746                BC18C41E0E16F5CD00B34460 /* JSContextRef.h in Headers */ = {isa = PBXBuildFile; fileRef = 14BD5A2A0A3E91F600BAF59C /* JSContextRef.h */; settings = {ATTRIBUTES = (Public, ); }; };
    747747                BC18C41F0E16F5CD00B34460 /* JSFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = F692A85F0255597D01FF60F7 /* JSFunction.h */; settings = {ATTRIBUTES = (Private, ); }; };
     
    29572957                                0F63945515D07057006A597C /* ArrayProfile.h in Headers */,
    29582958                                BC18C3E70E16F5CD00B34460 /* ArrayPrototype.h in Headers */,
     2959                                BC18C41D0E16F5CD00B34460 /* JSClassRef.h in Headers */,
    29592960                                86E3C61D167BABEE006D760A /* JSVirtualMachineInternal.h in Headers */,
    29602961                                BC18C5240E16FC8A00B34460 /* ArrayPrototype.lut.h in Headers */,
     
    31553156                                BC18C42B0E16F5CD00B34460 /* JSCJSValue.h in Headers */,
    31563157                                865A30F1135007E100CDB49E /* JSCJSValueInlines.h in Headers */,
    3157                                 BC18C41D0E16F5CD00B34460 /* JSClassRef.h in Headers */,
    31583158                                86E3C613167BABD7006D760A /* JSContext.h in Headers */,
    31593159                                BC18C41E0E16F5CD00B34460 /* JSContextRef.h in Headers */,
  • trunk/Source/JavaScriptCore/runtime/JSGlobalData.cpp

    r146383 r146682  
    4545#include "JSAPIValueWrapper.h"
    4646#include "JSArray.h"
    47 #include "JSClassRef.h"
    4847#include "JSFunction.h"
    4948#include "JSLock.h"
     
    307306    fastDelete(const_cast<HashTable*>(stringConstructorTable));
    308307
    309     opaqueJSClassData.clear();
    310 
    311308    delete emptyList;
    312309
  • trunk/Source/JavaScriptCore/runtime/JSGlobalData.h

    r146383 r146682  
    6363#endif
    6464
    65 struct OpaqueJSClass;
    66 struct OpaqueJSClassContextData;
    67 
    6865namespace JSC {
    6966
     
    370367#endif
    371368
    372         HashMap<OpaqueJSClass*, OwnPtr<OpaqueJSClassContextData> > opaqueJSClassData;
    373 
    374369        JSGlobalObject* dynamicGlobalObject;
    375370
  • trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h

    r146494 r146682  
    2525#include "ArrayAllocationProfile.h"
    2626#include "JSArray.h"
     27#include "JSClassRef.h"
    2728#include "JSGlobalData.h"
    2829#include "JSSegmentedVariableObject.h"
     
    3839#include <wtf/OwnPtr.h>
    3940#include <wtf/RandomNumber.h>
     41
     42struct OpaqueJSClass;
     43struct OpaqueJSClassContextData;
    4044
    4145namespace JSC {
     
    8791private:
    8892    typedef HashSet<RefPtr<OpaqueJSWeakObjectMap> > WeakMapSet;
     93    typedef HashMap<OpaqueJSClass*, OwnPtr<OpaqueJSClassContextData> > OpaqueJSClassDataMap;
    8994
    9095    struct JSGlobalObjectRareData {
     
    96101        WeakMapSet weakMaps;
    97102        unsigned profileGroup;
     103       
     104        OpaqueJSClassDataMap opaqueJSClassData;
    98105    };
    99106
     
    396403    }
    397404
     405    OpaqueJSClassDataMap& opaqueJSClassData()
     406    {
     407        createRareDataIfNeeded();
     408        return m_rareData->opaqueJSClassData;
     409    }
     410
    398411    double weakRandomNumber() { return m_weakRandom.get(); }
    399412    unsigned weakRandomInteger() { return m_weakRandom.getUint32(); }
Note: See TracChangeset for help on using the changeset viewer.