Stale entries in WeakGCMaps are keeping tons of WeakBlocks alive unnecessarily.
authorakling@apple.com <akling@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 10 Mar 2015 00:09:39 +0000 (00:09 +0000)
committerakling@apple.com <akling@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 10 Mar 2015 00:09:39 +0000 (00:09 +0000)
<https://webkit.org/b/142115>
<rdar://problem/19992268>

Reviewed by Geoffrey Garen.

Prune stale entries from WeakGCMaps as part of every full garbage collection.
This frees up tons of previously-stuck WeakBlocks that were only sitting around
with finalized handles waiting to die.

Note that WeakGCMaps register/unregister themselves with the GC heap in their
ctor/dtor, so creating one now requires passing the VM.

Average time spent in the PruningStaleEntriesFromWeakGCMaps GC phase appears
to be between 0.01ms and 0.3ms, though I've seen a few longer ones at ~1.2ms.
It seems somewhat excessive to do this on every Eden collection, so it's only
doing work in full collections for now.

Because the GC may now mutate WeakGCMap below object allocation, I've made it
so that the classic HashMap::add() optimization can't be used with WeakGCMap.
This caused intermittent test failures when originally landed due to having
an invalid iterator on the stack after add() inserted a new entry and we
proceeded to allocate the new object, triggering GC.

* API/JSWeakObjectMapRefInternal.h:
(OpaqueJSWeakObjectMap::create):
(OpaqueJSWeakObjectMap::OpaqueJSWeakObjectMap):
* API/JSWeakObjectMapRefPrivate.cpp:
* API/JSWrapperMap.mm:
(-[JSWrapperMap initWithContext:]):
(-[JSWrapperMap jsWrapperForObject:]): Pass VM to WeakGCMap constructor.

* JavaScriptCore.xcodeproj/project.pbxproj: Add WeakGCMapInlines.h and make
it project-private so WebCore clients can access it.

* heap/Heap.cpp:
(JSC::Heap::collect):
(JSC::Heap::pruneStaleEntriesFromWeakGCMaps): Added a new GC phase for pruning
stale entries from WeakGCMaps. This is only executed during full collections.

* heap/Heap.h:
* heap/HeapInlines.h:
(JSC::Heap::registerWeakGCMap):
(JSC::Heap::unregisterWeakGCMap): Added a mechanism for WeakGCMaps to register
themselves with the Heap and provide a pruning callback.

* runtime/PrototypeMap.h:
(JSC::PrototypeMap::PrototypeMap):
* runtime/Structure.cpp:
(JSC::StructureTransitionTable::add): Pass VM to WeakGCMap constructor.

* runtime/JSCInlines.h: Add "WeakGCMapInlines.h"

* runtime/JSGlobalObject.cpp: Include "WeakGCMapInlines.h" so this builds.

* runtime/JSString.cpp:
(JSC::jsStringWithCacheSlowCase):
* runtime/PrototypeMap.cpp:
(JSC::PrototypeMap::addPrototype):
(JSC::PrototypeMap::emptyObjectStructureForPrototype): Remove HashMap add()
optimization since it's not safe in the GC-managed WeakGCMap world.

* runtime/VM.cpp:
(JSC::VM::VM): Pass VM to WeakGCMap constructor.

* runtime/WeakGCMap.h:
(JSC::WeakGCMap::set):
(JSC::WeakGCMap::add):
(JSC::WeakGCMap::WeakGCMap): Deleted.
(JSC::WeakGCMap::gcMap): Deleted.
(JSC::WeakGCMap::gcMapIfNeeded): Deleted.
* runtime/WeakGCMapInlines.h: Added.
(JSC::WeakGCMap::WeakGCMap):
(JSC::WeakGCMap::~WeakGCMap):
(JSC::WeakGCMap::pruneStaleEntries): Moved ctor, dtor and pruning callback
to WeakGCMapInlines.h to fix interdependent header issues. Removed code that
prunes WeakGCMap at certain growth milestones and instead rely on the GC
callback for housekeeping.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@181297 268f45cc-cd09-0410-ab3c-d52691b4dbfc

19 files changed:
Source/JavaScriptCore/API/JSWeakObjectMapRefInternal.h
Source/JavaScriptCore/API/JSWeakObjectMapRefPrivate.cpp
Source/JavaScriptCore/API/JSWrapperMap.mm
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/heap/Heap.cpp
Source/JavaScriptCore/heap/Heap.h
Source/JavaScriptCore/heap/HeapInlines.h
Source/JavaScriptCore/runtime/JSCInlines.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSString.cpp
Source/JavaScriptCore/runtime/PrototypeMap.cpp
Source/JavaScriptCore/runtime/PrototypeMap.h
Source/JavaScriptCore/runtime/Structure.cpp
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/WeakGCMap.h
Source/JavaScriptCore/runtime/WeakGCMapInlines.h [new file with mode: 0644]
Source/WebCore/ForwardingHeaders/runtime/WeakGCMapInlines.h [new file with mode: 0644]
Source/WebCore/bindings/js/ScriptCachedFrameData.cpp

index f7b91da..9037947 100644 (file)
@@ -41,9 +41,9 @@ typedef JSC::WeakGCMap<void*, JSC::JSObject> WeakMapType;
 
 struct OpaqueJSWeakObjectMap : public RefCounted<OpaqueJSWeakObjectMap> {
 public:
-    static PassRefPtr<OpaqueJSWeakObjectMap> create(void* data, JSWeakMapDestroyedCallback callback)
+    static Ref<OpaqueJSWeakObjectMap> create(JSC::VM& vm, void* data, JSWeakMapDestroyedCallback callback)
     {
-        return adoptRef(new OpaqueJSWeakObjectMap(data, callback));
+        return adoptRef(*new OpaqueJSWeakObjectMap(vm, data, callback));
     }
 
     WeakMapType& map() { return m_map; }
@@ -54,8 +54,9 @@ public:
     }
 
 private:
-    OpaqueJSWeakObjectMap(void* data, JSWeakMapDestroyedCallback callback)
-        : m_data(data)
+    OpaqueJSWeakObjectMap(JSC::VM& vm, void* data, JSWeakMapDestroyedCallback callback)
+        : m_map(vm)
+        , m_data(data)
         , m_callback(callback)
     {
     }
index 446cf90..925c00f 100644 (file)
@@ -32,6 +32,7 @@
 #include "JSWeakObjectMapRefInternal.h"
 #include "JSCInlines.h"
 #include "Weak.h"
+#include "WeakGCMapInlines.h"
 #include <wtf/HashMap.h>
 #include <wtf/text/StringHash.h>
 
@@ -46,7 +47,7 @@ JSWeakObjectMapRef JSWeakObjectMapCreate(JSContextRef context, void* privateData
 {
     ExecState* exec = toJS(context);
     JSLockHolder locker(exec);
-    RefPtr<OpaqueJSWeakObjectMap> map = OpaqueJSWeakObjectMap::create(privateData, callback);
+    RefPtr<OpaqueJSWeakObjectMap> map = OpaqueJSWeakObjectMap::create(exec->vm(), privateData, callback);
     exec->lexicalGlobalObject()->registerWeakMap(map.get());
     return map.get();
 }
index 00627a3..2451715 100644 (file)
@@ -37,6 +37,7 @@
 #import "ObjCCallbackFunction.h"
 #import "ObjcRuntimeExtras.h"
 #import "WeakGCMap.h"
+#import "WeakGCMapInlines.h"
 #import <wtf/HashSet.h>
 #import <wtf/TCSpinLock.h>
 #import <wtf/Vector.h>
@@ -546,7 +547,7 @@ typedef std::pair<JSC::JSObject*, JSC::JSObject*> ConstructorPrototypePair;
 @implementation JSWrapperMap {
     JSContext *m_context;
     NSMutableDictionary *m_classMap;
-    JSC::WeakGCMap<id, JSC::JSObject> m_cachedJSWrappers;
+    std::unique_ptr<JSC::WeakGCMap<id, JSC::JSObject>> m_cachedJSWrappers;
     NSMapTable *m_cachedObjCWrappers;
 }
 
@@ -559,7 +560,9 @@ typedef std::pair<JSC::JSObject*, JSC::JSObject*> ConstructorPrototypePair;
     NSPointerFunctionsOptions keyOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;
     NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
     m_cachedObjCWrappers = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];
-    
+
+    m_cachedJSWrappers = std::make_unique<JSC::WeakGCMap<id, JSC::JSObject>>(toJS([context JSGlobalContextRef])->vm());
+
     m_context = context;
     m_classMap = [[NSMutableDictionary alloc] init];
     return self;
@@ -590,7 +593,7 @@ typedef std::pair<JSC::JSObject*, JSC::JSObject*> ConstructorPrototypePair;
 
 - (JSValue *)jsWrapperForObject:(id)object
 {
-    JSC::JSObject* jsWrapper = m_cachedJSWrappers.get(object);
+    JSC::JSObject* jsWrapper = m_cachedJSWrappers->get(object);
     if (jsWrapper)
         return [JSValue valueWithJSValueRef:toRef(jsWrapper) inContext:m_context];
 
@@ -606,7 +609,7 @@ typedef std::pair<JSC::JSObject*, JSC::JSObject*> ConstructorPrototypePair;
     // (1) For immortal objects JSValues will effectively leak and this results in error output being logged - we should avoid adding associated objects to immortal objects.
     // (2) A long lived object may rack up many JSValues. When the contexts are released these will unprotect the associated JavaScript objects,
     //     but still, would probably nicer if we made it so that only one associated object was required, broadcasting object dealloc.
-    m_cachedJSWrappers.set(object, jsWrapper);
+    m_cachedJSWrappers->set(object, jsWrapper);
     return [JSValue valueWithJSValueRef:toRef(jsWrapper) inContext:m_context];
 }
 
index 734934e..52686ca 100644 (file)
@@ -1,3 +1,84 @@
+2015-03-09  Andreas Kling  <akling@apple.com>
+
+        Stale entries in WeakGCMaps are keeping tons of WeakBlocks alive unnecessarily.
+        <https://webkit.org/b/142115>
+        <rdar://problem/19992268>
+
+        Reviewed by Geoffrey Garen.
+
+        Prune stale entries from WeakGCMaps as part of every full garbage collection.
+        This frees up tons of previously-stuck WeakBlocks that were only sitting around
+        with finalized handles waiting to die.
+
+        Note that WeakGCMaps register/unregister themselves with the GC heap in their
+        ctor/dtor, so creating one now requires passing the VM.
+
+        Average time spent in the PruningStaleEntriesFromWeakGCMaps GC phase appears
+        to be between 0.01ms and 0.3ms, though I've seen a few longer ones at ~1.2ms.
+        It seems somewhat excessive to do this on every Eden collection, so it's only
+        doing work in full collections for now.
+
+        Because the GC may now mutate WeakGCMap below object allocation, I've made it
+        so that the classic HashMap::add() optimization can't be used with WeakGCMap.
+        This caused intermittent test failures when originally landed due to having
+        an invalid iterator on the stack after add() inserted a new entry and we
+        proceeded to allocate the new object, triggering GC.
+
+        * API/JSWeakObjectMapRefInternal.h:
+        (OpaqueJSWeakObjectMap::create):
+        (OpaqueJSWeakObjectMap::OpaqueJSWeakObjectMap):
+        * API/JSWeakObjectMapRefPrivate.cpp:
+        * API/JSWrapperMap.mm:
+        (-[JSWrapperMap initWithContext:]):
+        (-[JSWrapperMap jsWrapperForObject:]): Pass VM to WeakGCMap constructor.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj: Add WeakGCMapInlines.h and make
+        it project-private so WebCore clients can access it.
+
+        * heap/Heap.cpp:
+        (JSC::Heap::collect):
+        (JSC::Heap::pruneStaleEntriesFromWeakGCMaps): Added a new GC phase for pruning
+        stale entries from WeakGCMaps. This is only executed during full collections.
+
+        * heap/Heap.h:
+        * heap/HeapInlines.h:
+        (JSC::Heap::registerWeakGCMap):
+        (JSC::Heap::unregisterWeakGCMap): Added a mechanism for WeakGCMaps to register
+        themselves with the Heap and provide a pruning callback.
+
+        * runtime/PrototypeMap.h:
+        (JSC::PrototypeMap::PrototypeMap):
+        * runtime/Structure.cpp:
+        (JSC::StructureTransitionTable::add): Pass VM to WeakGCMap constructor.
+
+        * runtime/JSCInlines.h: Add "WeakGCMapInlines.h"
+
+        * runtime/JSGlobalObject.cpp: Include "WeakGCMapInlines.h" so this builds.
+
+        * runtime/JSString.cpp:
+        (JSC::jsStringWithCacheSlowCase):
+        * runtime/PrototypeMap.cpp:
+        (JSC::PrototypeMap::addPrototype):
+        (JSC::PrototypeMap::emptyObjectStructureForPrototype): Remove HashMap add()
+        optimization since it's not safe in the GC-managed WeakGCMap world.
+
+        * runtime/VM.cpp:
+        (JSC::VM::VM): Pass VM to WeakGCMap constructor.
+
+        * runtime/WeakGCMap.h:
+        (JSC::WeakGCMap::set):
+        (JSC::WeakGCMap::add):
+        (JSC::WeakGCMap::WeakGCMap): Deleted.
+        (JSC::WeakGCMap::gcMap): Deleted.
+        (JSC::WeakGCMap::gcMapIfNeeded): Deleted.
+        * runtime/WeakGCMapInlines.h: Added.
+        (JSC::WeakGCMap::WeakGCMap):
+        (JSC::WeakGCMap::~WeakGCMap):
+        (JSC::WeakGCMap::pruneStaleEntries): Moved ctor, dtor and pruning callback
+        to WeakGCMapInlines.h to fix interdependent header issues. Removed code that
+        prunes WeakGCMap at certain growth milestones and instead rely on the GC
+        callback for housekeeping.
+
 2015-03-09  Ryosuke Niwa  <rniwa@webkit.org>
 
         Support extends and super keywords
index 60d7bc5..ab13f77 100644 (file)
                A7FB61001040C38B0017A286 /* PropertyDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = A7FB604B103F5EAB0017A286 /* PropertyDescriptor.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A7FCC26D17A0B6AA00786D1A /* FTLSwitchCase.h in Headers */ = {isa = PBXBuildFile; fileRef = A7FCC26C17A0B6AA00786D1A /* FTLSwitchCase.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A8A4748E151A8306004123FF /* libWTF.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A8A4748D151A8306004123FF /* libWTF.a */; };
+               AD86A93E1AA4D88D002FE77F /* WeakGCMapInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = AD86A93D1AA4D87C002FE77F /* WeakGCMapInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
                ADDB1F6318D77DBE009B58A8 /* OpaqueRootSet.h in Headers */ = {isa = PBXBuildFile; fileRef = ADDB1F6218D77DB7009B58A8 /* OpaqueRootSet.h */; settings = {ATTRIBUTES = (Private, ); }; };
                ADE39FFF16DD144B0003CD4A /* PropertyTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD1CF06816DCAB2D00B97123 /* PropertyTable.cpp */; };
                B59F89391891F29F00D5CCDC /* UnlinkedInstructionStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B59F89381891ADB500D5CCDC /* UnlinkedInstructionStream.cpp */; };
                A8E894310CD0602400367179 /* JSCallbackObjectFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCallbackObjectFunctions.h; sourceTree = "<group>"; };
                A8E894330CD0603F00367179 /* JSGlobalObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSGlobalObject.h; sourceTree = "<group>"; };
                AD1CF06816DCAB2D00B97123 /* PropertyTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PropertyTable.cpp; sourceTree = "<group>"; };
+               AD86A93D1AA4D87C002FE77F /* WeakGCMapInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WeakGCMapInlines.h; sourceTree = "<group>"; };
                ADDB1F6218D77DB7009B58A8 /* OpaqueRootSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpaqueRootSet.h; sourceTree = "<group>"; };
                B59F89371891AD3300D5CCDC /* UnlinkedInstructionStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UnlinkedInstructionStream.h; sourceTree = "<group>"; };
                B59F89381891ADB500D5CCDC /* UnlinkedInstructionStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UnlinkedInstructionStream.cpp; sourceTree = "<group>"; };
                                FED94F2C171E3E2300BE77A4 /* Watchdog.h */,
                                FED94F2D171E3E2300BE77A4 /* WatchdogMac.cpp */,
                                14BFCE6810CDB1FC00364CCE /* WeakGCMap.h */,
+                               AD86A93D1AA4D87C002FE77F /* WeakGCMapInlines.h */,
                                A7CA3ADD17DA41AE006538AF /* WeakMapConstructor.cpp */,
                                A7CA3ADE17DA41AE006538AF /* WeakMapConstructor.h */,
                                A7CA3AE917DA5168006538AF /* WeakMapData.cpp */,
                                0F885E111849A3BE00F1E3FA /* BytecodeUseDef.h in Headers */,
                                0F8023EA1613832B00A0BA45 /* ByValInfo.h in Headers */,
                                BC18C3ED0E16F5CD00B34460 /* CallData.h in Headers */,
+                               AD86A93E1AA4D88D002FE77F /* WeakGCMapInlines.h in Headers */,
                                1429D8DE0ED2205B00B89619 /* CallFrame.h in Headers */,
                                A7C1EAEF17987AB600299DB2 /* CallFrameInlines.h in Headers */,
                                95E3BC050E1AE68200B2D1C1 /* CallIdentifier.h in Headers */,
index 283f47f..e405dd2 100644 (file)
@@ -1065,6 +1065,7 @@ NEVER_INLINE void Heap::collectImpl(HeapOperation collectionType, void* stackOri
         vm()->typeProfiler()->invalidateTypeSetCache();
 
     reapWeakHandles();
+    pruneStaleEntriesFromWeakGCMaps();
     sweepArrayBuffers();
     snapshotMarkedSpace();
 
@@ -1178,6 +1179,15 @@ void Heap::reapWeakHandles()
     m_objectSpace.reapWeakSets();
 }
 
+void Heap::pruneStaleEntriesFromWeakGCMaps()
+{
+    GCPHASE(PruningStaleEntriesFromWeakGCMaps);
+    if (m_operationInProgress != FullCollection)
+        return;
+    for (auto& pruneCallback : m_weakGCMaps.values())
+        pruneCallback();
+}
+
 void Heap::sweepArrayBuffers()
 {
     GCPHASE(SweepingArrayBuffers);
index ef7657e..e589ec0 100644 (file)
@@ -227,6 +227,9 @@ public:
 
     static bool isZombified(JSCell* cell) { return *(void**)cell == zombifiedBits; }
 
+    void registerWeakGCMap(void* weakGCMap, std::function<void()> pruningCallback);
+    void unregisterWeakGCMap(void* weakGCMap);
+
 private:
     friend class CodeBlock;
     friend class CopiedBlock;
@@ -301,6 +304,7 @@ private:
     void resetVisitors();
 
     void reapWeakHandles();
+    void pruneStaleEntriesFromWeakGCMaps();
     void sweepArrayBuffers();
     void snapshotMarkedSpace();
     void deleteSourceProviderCaches();
@@ -393,6 +397,8 @@ private:
     Vector<RetainPtr<CFTypeRef>> m_delayedReleaseObjects;
     unsigned m_delayedReleaseRecursionCount;
 #endif
+
+    HashMap<void*, std::function<void()>> m_weakGCMaps;
 };
 
 } // namespace JSC
index 77922b3..58e92d4 100644 (file)
@@ -314,6 +314,16 @@ inline HashSet<MarkedArgumentBuffer*>& Heap::markListSet()
         m_markListSet = std::make_unique<HashSet<MarkedArgumentBuffer*>>();
     return *m_markListSet;
 }
+
+inline void Heap::registerWeakGCMap(void* weakGCMap, std::function<void()> pruningCallback)
+{
+    m_weakGCMaps.add(weakGCMap, WTF::move(pruningCallback));
+}
+
+inline void Heap::unregisterWeakGCMap(void* weakGCMap)
+{
+    m_weakGCMaps.remove(weakGCMap);
+}
     
 } // namespace JSC
 
index 9cf7028..e9fabb5 100644 (file)
@@ -51,5 +51,6 @@
 #include "Operations.h"
 #include "SlotVisitorInlines.h"
 #include "StructureInlines.h"
+#include "WeakGCMapInlines.h"
 
 #endif // JSCInlines_h
index d8d3069..7a0b759 100644 (file)
 #include "SymbolConstructor.h"
 #include "SymbolPrototype.h"
 #include "VariableWatchpointSetInlines.h"
+#include "WeakGCMapInlines.h"
 #include "WeakMapConstructor.h"
 #include "WeakMapPrototype.h"
 
index 3ea2b7e..edd1cf5 100644 (file)
@@ -437,11 +437,12 @@ bool JSString::getStringPropertyDescriptor(ExecState* exec, PropertyName propert
 
 JSString* jsStringWithCacheSlowCase(VM& vm, StringImpl& stringImpl)
 {
-    auto addResult = vm.stringCache.add(&stringImpl, nullptr);
-    if (addResult.isNewEntry)
-        addResult.iterator->value = jsString(&vm, String(stringImpl));
-    vm.lastCachedString.set(vm, addResult.iterator->value.get());
-    return addResult.iterator->value.get();
+    if (JSString* string = vm.stringCache.get(&stringImpl))
+        return string;
+
+    JSString* string = jsString(&vm, String(stringImpl));
+    vm.lastCachedString.set(vm, string);
+    return string;
 }
 
 } // namespace JSC
index a7a417f..43f0344 100644 (file)
@@ -33,7 +33,7 @@ namespace JSC {
 
 void PrototypeMap::addPrototype(JSObject* object)
 {
-    m_prototypes.add(object, object);
+    m_prototypes.set(object, object);
 
     // Note that this method makes the somewhat odd decision to not check if this
     // object currently has indexed accessors. We could do that check here, and if
@@ -54,16 +54,16 @@ void PrototypeMap::addPrototype(JSObject* object)
 
 Structure* PrototypeMap::emptyObjectStructureForPrototype(JSObject* prototype, unsigned inlineCapacity)
 {
-    StructureMap::AddResult addResult = m_structures.add(std::make_pair(prototype, inlineCapacity), nullptr);
-    if (!addResult.isNewEntry) {
+    auto key = std::make_pair(prototype, inlineCapacity);
+    if (Structure* structure = m_structures.get(key)) {
         ASSERT(isPrototype(prototype));
-        return addResult.iterator->value.get();
+        return structure;
     }
 
     addPrototype(prototype);
     Structure* structure = JSFinalObject::createStructure(
         prototype->globalObject()->vm(), prototype->globalObject(), prototype, inlineCapacity);
-    addResult.iterator->value = Weak<Structure>(structure);
+    m_structures.set(key, Weak<Structure>(structure));
     return structure;
 }
 
index 9752ca7..505e0b8 100644 (file)
@@ -33,10 +33,17 @@ namespace JSC {
 
 class JSObject;
 class Structure;
+class VM;
 
 // Tracks the canonical structure an object should be allocated with when inheriting from a given prototype.
 class PrototypeMap {
 public:
+    explicit PrototypeMap(VM& vm)
+        : m_prototypes(vm)
+        , m_structures(vm)
+    {
+    }
+
     JS_EXPORT_PRIVATE Structure* emptyObjectStructureForPrototype(JSObject*, unsigned inlineCapacity);
     void clearEmptyObjectStructureForPrototype(JSObject*, unsigned inlineCapacity);
     void addPrototype(JSObject*);
index c769051..869cbc5 100644 (file)
@@ -36,6 +36,7 @@
 #include "PropertyNameArray.h"
 #include "StructureChain.h"
 #include "StructureRareDataInlines.h"
+#include "WeakGCMapInlines.h"
 #include <wtf/CommaPrinter.h>
 #include <wtf/ProcessID.h>
 #include <wtf/RefCountedLeakCounter.h>
@@ -90,7 +91,7 @@ inline void StructureTransitionTable::add(VM& vm, Structure* structure)
 
         // This handles the second transition being added
         // (or the first transition being despecified!)
-        setMap(new TransitionMap());
+        setMap(new TransitionMap(vm));
         add(vm, existingTransition);
     }
 
index 4e72599..a7324b4 100644 (file)
@@ -83,6 +83,7 @@
 #include "TypeProfiler.h"
 #include "TypeProfilerLog.h"
 #include "UnlinkedCodeBlock.h"
+#include "WeakGCMapInlines.h"
 #include "WeakMapData.h"
 #include <wtf/ProcessID.h>
 #include <wtf/RetainPtr.h>
@@ -153,6 +154,8 @@ VM::VM(VMType vmType, HeapType heapType)
     , m_atomicStringTable(vmType == Default ? wtfThreadData().atomicStringTable() : new AtomicStringTable)
     , propertyNames(nullptr)
     , emptyList(new MarkedArgumentBuffer)
+    , stringCache(*this)
+    , prototypeMap(*this)
     , keywords(std::make_unique<Keywords>(*this))
     , interpreter(0)
     , jsArrayClassInfo(JSArray::info())
index cc0fb8f..fd1fa6e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -47,10 +47,8 @@ public:
     typedef typename HashMapType::iterator iterator;
     typedef typename HashMapType::const_iterator const_iterator;
 
-    WeakGCMap()
-        : m_gcThreshold(minGCThreshold)
-    {
-    }
+    explicit WeakGCMap(VM&);
+    ~WeakGCMap();
 
     ValueArg* get(const KeyType& key) const
     {
@@ -59,21 +57,9 @@ public:
 
     AddResult set(const KeyType& key, ValueType value)
     {
-        gcMapIfNeeded();
         return m_map.set(key, WTF::move(value));
     }
 
-    ALWAYS_INLINE AddResult add(const KeyType& key, ValueType value)
-    {
-        gcMapIfNeeded();
-        AddResult addResult = m_map.fastAdd(key, nullptr);
-        if (!addResult.iterator->value) { // New value or found a zombie value.
-            addResult.isNewEntry = true;
-            addResult.iterator->value = WTF::move(value);
-        }
-        return addResult;
-    }
-
     bool remove(const KeyType& key)
     {
         return m_map.remove(key);
@@ -103,38 +89,13 @@ public:
         return find(key) != m_map.end();
     }
 
-private:
-    static const int minGCThreshold = 3;
-
-    NEVER_INLINE void gcMap()
-    {
-        Vector<KeyType, 4> zombies;
-
-        for (iterator it = m_map.begin(), end = m_map.end(); it != end; ++it) {
-            if (!it->value)
-                zombies.append(it->key);
-        }
-
-        for (size_t i = 0; i < zombies.size(); ++i)
-            m_map.remove(zombies[i]);
-    }
-
-    void gcMapIfNeeded()
-    {
-        if (m_map.size() < m_gcThreshold)
-            return;
-
-        gcMap();
-        m_gcThreshold = std::max(minGCThreshold, m_map.size() * 2 - 1);
-    }
+    void pruneStaleEntries();
 
+private:
     HashMapType m_map;
-    int m_gcThreshold;
+    VM& m_vm;
 };
 
-template<typename KeyArg, typename RawMappedArg, typename HashArg, typename KeyTraitsArg>
-const int WeakGCMap<KeyArg, RawMappedArg, HashArg, KeyTraitsArg>::minGCThreshold;
-
 } // namespace JSC
 
 #endif // WeakGCMap_h
diff --git a/Source/JavaScriptCore/runtime/WeakGCMapInlines.h b/Source/JavaScriptCore/runtime/WeakGCMapInlines.h
new file mode 100644 (file)
index 0000000..b90acc0
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WeakGCMapInlines_h
+#define WeakGCMapInlines_h
+
+#include "HeapInlines.h"
+#include "WeakGCMap.h"
+
+namespace JSC {
+
+template<typename KeyArg, typename ValueArg, typename HashArg, typename KeyTraitsArg>
+inline WeakGCMap<KeyArg, ValueArg, HashArg, KeyTraitsArg>::WeakGCMap(VM& vm)
+    : m_vm(vm)
+{
+    vm.heap.registerWeakGCMap(this, [this]() {
+        pruneStaleEntries();
+    });
+}
+
+template<typename KeyArg, typename ValueArg, typename HashArg, typename KeyTraitsArg>
+inline WeakGCMap<KeyArg, ValueArg, HashArg, KeyTraitsArg>::~WeakGCMap()
+{
+    m_vm.heap.unregisterWeakGCMap(this);
+}
+
+template<typename KeyArg, typename ValueArg, typename HashArg, typename KeyTraitsArg>
+NEVER_INLINE void WeakGCMap<KeyArg, ValueArg, HashArg, KeyTraitsArg>::pruneStaleEntries()
+{
+    m_map.removeIf([](typename HashMapType::KeyValuePairType& entry) {
+        return !entry.value;
+    });
+}
+
+} // namespace JSC
+
+#endif // WeakGCMapInlines_h
diff --git a/Source/WebCore/ForwardingHeaders/runtime/WeakGCMapInlines.h b/Source/WebCore/ForwardingHeaders/runtime/WeakGCMapInlines.h
new file mode 100644 (file)
index 0000000..694598b
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef WebCore_FWD_WeakGCMapInlines_h
+#define WebCore_FWD_WeakGCMapInlines_h
+#include <JavaScriptCore/WeakGCMapInlines.h>
+#endif
index 1ce89dd..3b1e89e 100644 (file)
 #include "Page.h"
 #include "PageConsoleClient.h"
 #include "PageGroup.h"
+#include "ScriptController.h"
 #include <heap/StrongInlines.h>
 #include <profiler/Profile.h>
 #include <runtime/JSLock.h>
-#include "ScriptController.h"
+#include <runtime/WeakGCMapInlines.h>
 
 using namespace JSC;