[JSC] Drop VM and Context cache map in JavaScriptCore.framework
authorysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 Mar 2019 19:37:58 +0000 (19:37 +0000)
committerysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 Mar 2019 19:37:58 +0000 (19:37 +0000)
https://bugs.webkit.org/show_bug.cgi?id=196341

Reviewed by Saam Barati.

Previously, we created Objective-C weak map to maintain JSVirtualMachine and JSContext wrappers corresponding to VM and JSGlobalObject.
But Objective-C weak map is really memory costly. Even if the entry is only one, it consumes 2.5KB per weak map. Since we can modify
JSC intrusively for JavaScriptCore.framework (and we already did it, like, holding JSWrapperMap in JSGlobalObject), we can just hold
a pointer to a wrapper in VM and JSGlobalObject.

This patch adds void* members to VM and JSGlobalObject, which holds a non-strong reference to a wrapper. When a wrapper is gone, we
clear this pointer too. This removes unnecessary two Objective-C weak maps, and save 5KB.

* API/JSContext.mm:
(-[JSContext initWithVirtualMachine:]):
(-[JSContext dealloc]):
(-[JSContext initWithGlobalContextRef:]):
(-[JSContext wrapperMap]):
(+[JSContext contextWithJSGlobalContextRef:]):
* API/JSVirtualMachine.mm:
(-[JSVirtualMachine initWithContextGroupRef:]):
(-[JSVirtualMachine dealloc]):
(+[JSVirtualMachine virtualMachineWithContextGroupRef:]):
(scanExternalObjectGraph):
(scanExternalRememberedSet):
(initWrapperCache): Deleted.
(wrapperCache): Deleted.
(+[JSVMWrapperCache addWrapper:forJSContextGroupRef:]): Deleted.
(+[JSVMWrapperCache wrapperForJSContextGroupRef:]): Deleted.
(-[JSVirtualMachine contextForGlobalContextRef:]): Deleted.
(-[JSVirtualMachine addContext:forGlobalContextRef:]): Deleted.
* API/JSVirtualMachineInternal.h:
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::setAPIWrapper):
(JSC::JSGlobalObject::apiWrapper const):
* runtime/VM.h:

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

Source/JavaScriptCore/API/JSContext.mm
Source/JavaScriptCore/API/JSVirtualMachine.mm
Source/JavaScriptCore/API/JSVirtualMachineInternal.h
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/VM.h

index 41cae4f..094c937 100644 (file)
     };
 
     [self ensureWrapperMap];
-    [m_virtualMachine addContext:self forGlobalContextRef:m_context];
+
+    toJSGlobalObject(m_context)->setAPIWrapper((__bridge void*)self);
 
     return self;
 }
 
 - (void)dealloc
 {
+    toJSGlobalObject(m_context)->setAPIWrapper((__bridge void*)nil);
     m_exception.clear();
     JSGlobalContextRelease(m_context);
     [m_virtualMachine release];
         context.exception = exceptionValue;
     };
 
-    [m_virtualMachine addContext:self forGlobalContextRef:m_context];
+    toJSGlobalObject(m_context)->setAPIWrapper((__bridge void*)self);
 
     return self;
 }
 
 - (JSWrapperMap *)wrapperMap
 {
-    return toJS(m_context)->lexicalGlobalObject()->wrapperMap();
+    return toJSGlobalObject(m_context)->wrapperMap();
 }
 
 - (JSValue *)wrapperForJSObject:(JSValueRef)value
 
 + (JSContext *)contextWithJSGlobalContextRef:(JSGlobalContextRef)globalContext
 {
-    JSVirtualMachine *virtualMachine = [JSVirtualMachine virtualMachineWithContextGroupRef:toRef(&toJS(globalContext)->vm())];
-    JSContext *context = [virtualMachine contextForGlobalContextRef:globalContext];
+    JSContext *context = (__bridge JSContext *)toJSGlobalObject(globalContext)->apiWrapper();
     if (!context)
         context = [[[JSContext alloc] initWithGlobalContextRef:globalContext] autorelease];
     return context;
index d8506a2..2afc097 100644 (file)
 #import <wtf/BlockPtr.h>
 #import <wtf/Lock.h>
 
-static NSMapTable *globalWrapperCache = 0;
-
-static Lock wrapperCacheMutex;
-
-static void initWrapperCache()
-{
-    ASSERT(!globalWrapperCache);
-    NSPointerFunctionsOptions keyOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;
-    NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
-    globalWrapperCache = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];
-}
-
-static NSMapTable *wrapperCache()
-{
-    if (!globalWrapperCache)
-        initWrapperCache();
-    return globalWrapperCache;
-}
-
-@interface JSVMWrapperCache : NSObject
-+ (void)addWrapper:(JSVirtualMachine *)wrapper forJSContextGroupRef:(JSContextGroupRef)group;
-+ (JSVirtualMachine *)wrapperForJSContextGroupRef:(JSContextGroupRef)group;
-@end
-
-@implementation JSVMWrapperCache
-
-+ (void)addWrapper:(JSVirtualMachine *)wrapper forJSContextGroupRef:(JSContextGroupRef)group
-{
-    std::lock_guard<Lock> lock(wrapperCacheMutex);
-    NSMapInsert(wrapperCache(), group, (__bridge void*)wrapper);
-}
-
-+ (JSVirtualMachine *)wrapperForJSContextGroupRef:(JSContextGroupRef)group
-{
-    std::lock_guard<Lock> lock(wrapperCacheMutex);
-    return (__bridge JSVirtualMachine *)NSMapGet(wrapperCache(), group);
-}
-
-@end
-
 @implementation JSVirtualMachine {
     JSContextGroupRef m_group;
     Lock m_externalDataMutex;
-    NSMapTable *m_contextCache;
     NSMapTable *m_externalObjectGraph;
     NSMapTable *m_externalRememberedSet;
 }
@@ -106,26 +65,22 @@ static NSMapTable *wrapperCache()
     
     m_group = JSContextGroupRetain(group);
     
-    NSPointerFunctionsOptions keyOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;
-    NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
-    m_contextCache = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];
-    
     NSPointerFunctionsOptions weakIDOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
     NSPointerFunctionsOptions strongIDOptions = NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPersonality;
     m_externalObjectGraph = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:strongIDOptions capacity:0];
 
     NSPointerFunctionsOptions integerOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsIntegerPersonality;
     m_externalRememberedSet = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:integerOptions capacity:0];
-   
-    [JSVMWrapperCache addWrapper:self forJSContextGroupRef:group];
+
+    toJS(group)->m_apiWrapper = (__bridge void*)self;
  
     return self;
 }
 
 - (void)dealloc
 {
+    toJS(m_group)->m_apiWrapper = (__bridge void*)nil;
     JSContextGroupRelease(m_group);
-    [m_contextCache release];
     [m_externalObjectGraph release];
     [m_externalRememberedSet release];
     [super dealloc];
@@ -237,22 +192,13 @@ JSContextGroupRef getGroupFromVirtualMachine(JSVirtualMachine *virtualMachine)
 
 + (JSVirtualMachine *)virtualMachineWithContextGroupRef:(JSContextGroupRef)group
 {
-    JSVirtualMachine *virtualMachine = [JSVMWrapperCache wrapperForJSContextGroupRef:group];
+    auto* vm = toJS(group);
+    JSVirtualMachine *virtualMachine = (__bridge JSVirtualMachine *)vm->m_apiWrapper;
     if (!virtualMachine)
         virtualMachine = [[[JSVirtualMachine alloc] initWithContextGroupRef:group] autorelease];
     return virtualMachine;
 }
 
-- (JSContext *)contextForGlobalContextRef:(JSGlobalContextRef)globalContext
-{
-    return (__bridge JSContext *)NSMapGet(m_contextCache, globalContext);
-}
-
-- (void)addContext:(JSContext *)wrapper forGlobalContextRef:(JSGlobalContextRef)globalContext
-{
-    NSMapInsert(m_contextCache, globalContext, (__bridge void*)wrapper);
-}
-
 - (Lock&)externalDataMutex
 {
     return m_externalDataMutex;
@@ -312,7 +258,7 @@ JSContextGroupRef getGroupFromVirtualMachine(JSVirtualMachine *virtualMachine)
 static void scanExternalObjectGraph(JSC::VM& vm, JSC::SlotVisitor& visitor, void* root, bool lockAcquired)
 {
     @autoreleasepool {
-        JSVirtualMachine *virtualMachine = [JSVMWrapperCache wrapperForJSContextGroupRef:toRef(&vm)];
+        JSVirtualMachine *virtualMachine = (__bridge JSVirtualMachine *)vm.m_apiWrapper;
         if (!virtualMachine)
             return;
         NSMapTable *externalObjectGraph = [virtualMachine externalObjectGraph];
@@ -350,7 +296,7 @@ void scanExternalObjectGraph(JSC::VM& vm, JSC::SlotVisitor& visitor, void* root)
 void scanExternalRememberedSet(JSC::VM& vm, JSC::SlotVisitor& visitor)
 {
     @autoreleasepool {
-        JSVirtualMachine *virtualMachine = [JSVMWrapperCache wrapperForJSContextGroupRef:toRef(&vm)];
+        JSVirtualMachine *virtualMachine = (__bridge JSVirtualMachine *)vm.m_apiWrapper;
         if (!virtualMachine)
             return;
         Lock& externalDataMutex = [virtualMachine externalDataMutex];
index 9b0bf6b..f64ee1a 100644 (file)
@@ -44,8 +44,6 @@ JSContextGroupRef getGroupFromVirtualMachine(JSVirtualMachine *);
 
 + (JSVirtualMachine *)virtualMachineWithContextGroupRef:(JSContextGroupRef)group;
 
-- (JSContext *)contextForGlobalContextRef:(JSGlobalContextRef)globalContext;
-- (void)addContext:(JSContext *)wrapper forGlobalContextRef:(JSGlobalContextRef)globalContext;
 - (JSC::VM&)vm;
 
 @end
index 2b399c3..7ff5daa 100644 (file)
@@ -1,3 +1,42 @@
+2019-03-27  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] Drop VM and Context cache map in JavaScriptCore.framework
+        https://bugs.webkit.org/show_bug.cgi?id=196341
+
+        Reviewed by Saam Barati.
+
+        Previously, we created Objective-C weak map to maintain JSVirtualMachine and JSContext wrappers corresponding to VM and JSGlobalObject.
+        But Objective-C weak map is really memory costly. Even if the entry is only one, it consumes 2.5KB per weak map. Since we can modify
+        JSC intrusively for JavaScriptCore.framework (and we already did it, like, holding JSWrapperMap in JSGlobalObject), we can just hold
+        a pointer to a wrapper in VM and JSGlobalObject.
+
+        This patch adds void* members to VM and JSGlobalObject, which holds a non-strong reference to a wrapper. When a wrapper is gone, we
+        clear this pointer too. This removes unnecessary two Objective-C weak maps, and save 5KB.
+
+        * API/JSContext.mm:
+        (-[JSContext initWithVirtualMachine:]):
+        (-[JSContext dealloc]):
+        (-[JSContext initWithGlobalContextRef:]):
+        (-[JSContext wrapperMap]):
+        (+[JSContext contextWithJSGlobalContextRef:]):
+        * API/JSVirtualMachine.mm:
+        (-[JSVirtualMachine initWithContextGroupRef:]):
+        (-[JSVirtualMachine dealloc]):
+        (+[JSVirtualMachine virtualMachineWithContextGroupRef:]):
+        (scanExternalObjectGraph):
+        (scanExternalRememberedSet):
+        (initWrapperCache): Deleted.
+        (wrapperCache): Deleted.
+        (+[JSVMWrapperCache addWrapper:forJSContextGroupRef:]): Deleted.
+        (+[JSVMWrapperCache wrapperForJSContextGroupRef:]): Deleted.
+        (-[JSVirtualMachine contextForGlobalContextRef:]): Deleted.
+        (-[JSVirtualMachine addContext:forGlobalContextRef:]): Deleted.
+        * API/JSVirtualMachineInternal.h:
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::setAPIWrapper):
+        (JSC::JSGlobalObject::apiWrapper const):
+        * runtime/VM.h:
+
 2019-03-28  Tadeu Zagallo  <tzagallo@apple.com>
 
         In-memory code cache should not share bytecode across domains
index c59f2b6..46f56c5 100644 (file)
@@ -1005,6 +1005,8 @@ public:
 #if JSC_OBJC_API_ENABLED
     JSWrapperMap* wrapperMap() const { return m_wrapperMap.get(); }
     void setWrapperMap(JSWrapperMap* map) { m_wrapperMap = map; }
+    void setAPIWrapper(void* apiWrapper) { m_apiWrapper = apiWrapper; }
+    void* apiWrapper() const { return m_apiWrapper; }
 #endif
 #ifdef JSC_GLIB_API_ENABLED
     WrapperMap* wrapperMap() const { return m_wrapperMap.get(); }
@@ -1047,6 +1049,7 @@ private:
     bool m_needsSiteSpecificQuirks { false };
 #if JSC_OBJC_API_ENABLED
     RetainPtr<JSWrapperMap> m_wrapperMap;
+    void* m_apiWrapper { nullptr };
 #endif
 #ifdef JSC_GLIB_API_ENABLED
     std::unique_ptr<WrapperMap> m_wrapperMap;
index d9ff42b..06dec07 100644 (file)
@@ -804,6 +804,10 @@ public:
     RTTraceList* m_rtTraceList;
 #endif
 
+#if JSC_OBJC_API_ENABLED
+    void* m_apiWrapper { nullptr };
+#endif
+
     JS_EXPORT_PRIVATE void resetDateCache();
 
     RegExpCache* regExpCache() { return m_regExpCache; }