5e795f016f05a381bdcf1040f335d0895395deaa
[WebKit-https.git] / Source / JavaScriptCore / API / JSVirtualMachine.mm
1 /*
2  * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27
28 #import "JavaScriptCore.h"
29
30 #if JSC_OBJC_API_ENABLED
31
32 #import "APICast.h"
33 #import "DFGWorklist.h"
34 #import "JSManagedValueInternal.h"
35 #import "JSVirtualMachineInternal.h"
36 #import "JSVirtualMachinePrivate.h"
37 #import "JSWrapperMap.h"
38 #import "SigillCrashAnalyzer.h"
39 #import "SlotVisitorInlines.h"
40 #import <mutex>
41 #import <wtf/BlockPtr.h>
42 #import <wtf/Lock.h>
43
44 static NSMapTable *globalWrapperCache = 0;
45
46 static Lock wrapperCacheMutex;
47
48 static void initWrapperCache()
49 {
50     ASSERT(!globalWrapperCache);
51     NSPointerFunctionsOptions keyOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;
52     NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
53     globalWrapperCache = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];
54 }
55
56 static NSMapTable *wrapperCache()
57 {
58     if (!globalWrapperCache)
59         initWrapperCache();
60     return globalWrapperCache;
61 }
62
63 @interface JSVMWrapperCache : NSObject
64 + (void)addWrapper:(JSVirtualMachine *)wrapper forJSContextGroupRef:(JSContextGroupRef)group;
65 + (JSVirtualMachine *)wrapperForJSContextGroupRef:(JSContextGroupRef)group;
66 @end
67
68 @implementation JSVMWrapperCache
69
70 + (void)addWrapper:(JSVirtualMachine *)wrapper forJSContextGroupRef:(JSContextGroupRef)group
71 {
72     std::lock_guard<Lock> lock(wrapperCacheMutex);
73     NSMapInsert(wrapperCache(), group, (__bridge void*)wrapper);
74 }
75
76 + (JSVirtualMachine *)wrapperForJSContextGroupRef:(JSContextGroupRef)group
77 {
78     std::lock_guard<Lock> lock(wrapperCacheMutex);
79     return (__bridge JSVirtualMachine *)NSMapGet(wrapperCache(), group);
80 }
81
82 @end
83
84 @implementation JSVirtualMachine {
85     JSContextGroupRef m_group;
86     Lock m_externalDataMutex;
87     NSMapTable *m_contextCache;
88     NSMapTable *m_externalObjectGraph;
89     NSMapTable *m_externalRememberedSet;
90 }
91
92 - (instancetype)init
93 {
94     JSContextGroupRef group = JSContextGroupCreate();
95     self = [self initWithContextGroupRef:group];
96     // The extra JSContextGroupRetain is balanced here.
97     JSContextGroupRelease(group);
98     return self;
99 }
100
101 - (instancetype)initWithContextGroupRef:(JSContextGroupRef)group
102 {
103     self = [super init];
104     if (!self)
105         return nil;
106     
107     m_group = JSContextGroupRetain(group);
108     
109     NSPointerFunctionsOptions keyOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;
110     NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
111     m_contextCache = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];
112     
113     NSPointerFunctionsOptions weakIDOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
114     NSPointerFunctionsOptions strongIDOptions = NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPersonality;
115     m_externalObjectGraph = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:strongIDOptions capacity:0];
116
117     NSPointerFunctionsOptions integerOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsIntegerPersonality;
118     m_externalRememberedSet = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:integerOptions capacity:0];
119    
120     [JSVMWrapperCache addWrapper:self forJSContextGroupRef:group];
121  
122     return self;
123 }
124
125 - (void)dealloc
126 {
127     JSContextGroupRelease(m_group);
128     [m_contextCache release];
129     [m_externalObjectGraph release];
130     [m_externalRememberedSet release];
131     [super dealloc];
132 }
133
134 static id getInternalObjcObject(id object)
135 {
136     if ([object isKindOfClass:[JSManagedValue class]]) {
137         JSValue* value = [static_cast<JSManagedValue *>(object) value];
138         if (!value)
139             return nil;
140         id temp = tryUnwrapObjcObject([value.context JSGlobalContextRef], [value JSValueRef]);
141         if (temp)
142             return temp;
143         return object;
144     }
145     
146     if ([object isKindOfClass:[JSValue class]]) {
147         JSValue *value = static_cast<JSValue *>(object);
148         object = tryUnwrapObjcObject([value.context JSGlobalContextRef], [value JSValueRef]);
149     }
150
151     return object;
152 }
153
154 - (bool)isOldExternalObject:(id)object
155 {
156     JSC::VM* vm = toJS(m_group);
157     return vm->heap.collectorSlotVisitor().containsOpaqueRoot((__bridge void*)object);
158 }
159
160 - (void)addExternalRememberedObject:(id)object
161 {
162     auto locker = holdLock(m_externalDataMutex);
163     ASSERT([self isOldExternalObject:object]);
164     [m_externalRememberedSet setObject:@YES forKey:object];
165 }
166
167 - (void)addManagedReference:(id)object withOwner:(id)owner
168 {    
169     if ([object isKindOfClass:[JSManagedValue class]])
170         [object didAddOwner:owner];
171         
172     object = getInternalObjcObject(object);
173     owner = getInternalObjcObject(owner);
174     
175     if (!object || !owner)
176         return;
177     
178     JSC::JSLockHolder locker(toJS(m_group));
179     if ([self isOldExternalObject:owner] && ![self isOldExternalObject:object])
180         [self addExternalRememberedObject:owner];
181  
182     auto externalDataMutexLocker = holdLock(m_externalDataMutex);
183     NSMapTable *ownedObjects = [m_externalObjectGraph objectForKey:owner];
184     if (!ownedObjects) {
185         NSPointerFunctionsOptions weakIDOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
186         NSPointerFunctionsOptions integerOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsIntegerPersonality;
187         ownedObjects = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:integerOptions capacity:1];
188
189         [m_externalObjectGraph setObject:ownedObjects forKey:owner];
190     }
191
192     size_t count = reinterpret_cast<size_t>(NSMapGet(ownedObjects, (__bridge void*)object));
193     NSMapInsert(ownedObjects, (__bridge void*)object, reinterpret_cast<void*>(count + 1));
194 }
195
196 - (void)removeManagedReference:(id)object withOwner:(id)owner
197 {
198     if ([object isKindOfClass:[JSManagedValue class]])
199         [object didRemoveOwner:owner];
200
201     object = getInternalObjcObject(object);
202     owner = getInternalObjcObject(owner);
203     
204     if (!object || !owner)
205         return;
206     
207     JSC::JSLockHolder locker(toJS(m_group));
208     
209     auto externalDataMutexLocker = holdLock(m_externalDataMutex);
210     NSMapTable *ownedObjects = [m_externalObjectGraph objectForKey:owner];
211     if (!ownedObjects)
212         return;
213    
214     size_t count = reinterpret_cast<size_t>(NSMapGet(ownedObjects, (__bridge void*)object));
215     if (count > 1) {
216         NSMapInsert(ownedObjects, (__bridge void*)object, reinterpret_cast<void*>(count - 1));
217         return;
218     }
219     
220     if (count == 1)
221         NSMapRemove(ownedObjects, (__bridge void*)object);
222
223     if (![ownedObjects count]) {
224         [m_externalObjectGraph removeObjectForKey:owner];
225         [m_externalRememberedSet removeObjectForKey:owner];
226     }
227 }
228
229 @end
230
231 @implementation JSVirtualMachine(Internal)
232
233 JSContextGroupRef getGroupFromVirtualMachine(JSVirtualMachine *virtualMachine)
234 {
235     return virtualMachine->m_group;
236 }
237
238 + (JSVirtualMachine *)virtualMachineWithContextGroupRef:(JSContextGroupRef)group
239 {
240     JSVirtualMachine *virtualMachine = [JSVMWrapperCache wrapperForJSContextGroupRef:group];
241     if (!virtualMachine)
242         virtualMachine = [[[JSVirtualMachine alloc] initWithContextGroupRef:group] autorelease];
243     return virtualMachine;
244 }
245
246 - (JSContext *)contextForGlobalContextRef:(JSGlobalContextRef)globalContext
247 {
248     return (__bridge JSContext *)NSMapGet(m_contextCache, globalContext);
249 }
250
251 - (void)addContext:(JSContext *)wrapper forGlobalContextRef:(JSGlobalContextRef)globalContext
252 {
253     NSMapInsert(m_contextCache, globalContext, (__bridge void*)wrapper);
254 }
255
256 - (Lock&)externalDataMutex
257 {
258     return m_externalDataMutex;
259 }
260
261 - (NSMapTable *)externalObjectGraph
262 {
263     return m_externalObjectGraph;
264 }
265
266 - (NSMapTable *)externalRememberedSet
267 {
268     return m_externalRememberedSet;
269 }
270
271 - (void)shrinkFootprintWhenIdle
272 {
273     JSC::VM* vm = toJS(m_group);
274     JSC::JSLockHolder locker(vm);
275     vm->shrinkFootprintWhenIdle();
276 }
277
278 #if ENABLE(DFG_JIT)
279
280 + (NSUInteger)setNumberOfDFGCompilerThreads:(NSUInteger)numberOfThreads
281 {
282     JSC::DFG::Worklist* worklist = JSC::DFG::existingGlobalDFGWorklistOrNull();
283     if (worklist)
284         return worklist->setNumberOfThreads(numberOfThreads, JSC::Options::priorityDeltaOfDFGCompilerThreads());
285     else
286         return JSC::DFG::setNumberOfDFGCompilerThreads(numberOfThreads);
287 }
288
289 + (NSUInteger)setNumberOfFTLCompilerThreads:(NSUInteger)numberOfThreads
290 {
291     JSC::DFG::Worklist* worklist = JSC::DFG::existingGlobalFTLWorklistOrNull();
292     if (worklist)
293         return worklist->setNumberOfThreads(numberOfThreads, JSC::Options::priorityDeltaOfFTLCompilerThreads());
294     else
295         return JSC::DFG::setNumberOfFTLCompilerThreads(numberOfThreads);
296 }
297
298 #endif // ENABLE(DFG_JIT)
299
300 @end
301
302 static void scanExternalObjectGraph(JSC::VM& vm, JSC::SlotVisitor& visitor, void* root, bool lockAcquired)
303 {
304     @autoreleasepool {
305         JSVirtualMachine *virtualMachine = [JSVMWrapperCache wrapperForJSContextGroupRef:toRef(&vm)];
306         if (!virtualMachine)
307             return;
308         NSMapTable *externalObjectGraph = [virtualMachine externalObjectGraph];
309         Lock& externalDataMutex = [virtualMachine externalDataMutex];
310         Vector<void*> stack;
311         stack.append(root);
312         while (!stack.isEmpty()) {
313             void* nextRoot = stack.last();
314             stack.removeLast();
315             if (!visitor.addOpaqueRoot(nextRoot))
316                 continue;
317
318             auto appendOwnedObjects = [&] {
319                 NSMapTable *ownedObjects = [externalObjectGraph objectForKey:(__bridge id)nextRoot];
320                 for (id ownedObject in ownedObjects)
321                     stack.append((__bridge void*)ownedObject);
322             };
323
324             if (lockAcquired)
325                 appendOwnedObjects();
326             else {
327                 auto locker = holdLock(externalDataMutex);
328                 appendOwnedObjects();
329             }
330         }
331     }
332 }
333
334 void scanExternalObjectGraph(JSC::VM& vm, JSC::SlotVisitor& visitor, void* root)
335 {
336     bool lockAcquired = false;
337     scanExternalObjectGraph(vm, visitor, root, lockAcquired);
338 }
339
340 void scanExternalRememberedSet(JSC::VM& vm, JSC::SlotVisitor& visitor)
341 {
342     @autoreleasepool {
343         JSVirtualMachine *virtualMachine = [JSVMWrapperCache wrapperForJSContextGroupRef:toRef(&vm)];
344         if (!virtualMachine)
345             return;
346         Lock& externalDataMutex = [virtualMachine externalDataMutex];
347         auto locker = holdLock(externalDataMutex);
348         NSMapTable *externalObjectGraph = [virtualMachine externalObjectGraph];
349         NSMapTable *externalRememberedSet = [virtualMachine externalRememberedSet];
350         for (id key in externalRememberedSet) {
351             NSMapTable *ownedObjects = [externalObjectGraph objectForKey:key];
352             bool lockAcquired = true;
353             for (id ownedObject in ownedObjects)
354                 scanExternalObjectGraph(vm, visitor, (__bridge void*)ownedObject, lockAcquired);
355         }
356         [externalRememberedSet removeAllObjects];
357     }
358 }
359
360 #endif // JSC_OBJC_API_ENABLED