]>
Commit | Line | Data |
---|---|---|
93a37866 A |
1 | /* |
2 | * Copyright (C) 2013 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" | |
81345200 | 33 | #import "JSManagedValueInternal.h" |
93a37866 A |
34 | #import "JSVirtualMachine.h" |
35 | #import "JSVirtualMachineInternal.h" | |
36 | #import "JSWrapperMap.h" | |
81345200 A |
37 | #import "SlotVisitorInlines.h" |
38 | #import <mutex> | |
39 | #import <wtf/NeverDestroyed.h> | |
ed1e77d3 | 40 | #import <wtf/spi/cocoa/NSMapTableSPI.h> |
93a37866 A |
41 | |
42 | static NSMapTable *globalWrapperCache = 0; | |
43 | ||
81345200 | 44 | static std::mutex& wrapperCacheMutex() |
93a37866 | 45 | { |
81345200 A |
46 | static NeverDestroyed<std::mutex> mutex; |
47 | ||
93a37866 A |
48 | return mutex; |
49 | } | |
50 | ||
51 | static void initWrapperCache() | |
52 | { | |
53 | ASSERT(!globalWrapperCache); | |
54 | NSPointerFunctionsOptions keyOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality; | |
55 | NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality; | |
56 | globalWrapperCache = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0]; | |
57 | } | |
58 | ||
59 | static NSMapTable *wrapperCache() | |
60 | { | |
61 | if (!globalWrapperCache) | |
62 | initWrapperCache(); | |
63 | return globalWrapperCache; | |
64 | } | |
65 | ||
66 | @interface JSVMWrapperCache : NSObject | |
67 | + (void)addWrapper:(JSVirtualMachine *)wrapper forJSContextGroupRef:(JSContextGroupRef)group; | |
68 | + (JSVirtualMachine *)wrapperForJSContextGroupRef:(JSContextGroupRef)group; | |
69 | @end | |
70 | ||
71 | @implementation JSVMWrapperCache | |
72 | ||
73 | + (void)addWrapper:(JSVirtualMachine *)wrapper forJSContextGroupRef:(JSContextGroupRef)group | |
74 | { | |
81345200 | 75 | std::lock_guard<std::mutex> lock(wrapperCacheMutex()); |
93a37866 A |
76 | NSMapInsert(wrapperCache(), group, wrapper); |
77 | } | |
78 | ||
79 | + (JSVirtualMachine *)wrapperForJSContextGroupRef:(JSContextGroupRef)group | |
80 | { | |
81345200 | 81 | std::lock_guard<std::mutex> lock(wrapperCacheMutex()); |
93a37866 A |
82 | return static_cast<JSVirtualMachine *>(NSMapGet(wrapperCache(), group)); |
83 | } | |
84 | ||
85 | @end | |
86 | ||
87 | @implementation JSVirtualMachine { | |
88 | JSContextGroupRef m_group; | |
89 | NSMapTable *m_contextCache; | |
90 | NSMapTable *m_externalObjectGraph; | |
81345200 | 91 | NSMapTable *m_externalRememberedSet; |
93a37866 A |
92 | } |
93 | ||
81345200 | 94 | - (instancetype)init |
93a37866 A |
95 | { |
96 | JSContextGroupRef group = JSContextGroupCreate(); | |
97 | self = [self initWithContextGroupRef:group]; | |
98 | // The extra JSContextGroupRetain is balanced here. | |
99 | JSContextGroupRelease(group); | |
100 | return self; | |
101 | } | |
102 | ||
81345200 | 103 | - (instancetype)initWithContextGroupRef:(JSContextGroupRef)group |
93a37866 A |
104 | { |
105 | self = [super init]; | |
106 | if (!self) | |
107 | return nil; | |
108 | ||
109 | m_group = JSContextGroupRetain(group); | |
110 | ||
111 | NSPointerFunctionsOptions keyOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality; | |
112 | NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality; | |
113 | m_contextCache = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0]; | |
114 | ||
115 | NSPointerFunctionsOptions weakIDOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality; | |
116 | NSPointerFunctionsOptions strongIDOptions = NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPersonality; | |
117 | m_externalObjectGraph = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:strongIDOptions capacity:0]; | |
81345200 A |
118 | |
119 | NSPointerFunctionsOptions integerOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsIntegerPersonality; | |
120 | m_externalRememberedSet = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:integerOptions capacity:0]; | |
93a37866 A |
121 | |
122 | [JSVMWrapperCache addWrapper:self forJSContextGroupRef:group]; | |
123 | ||
124 | return self; | |
125 | } | |
126 | ||
127 | - (void)dealloc | |
128 | { | |
129 | JSContextGroupRelease(m_group); | |
130 | [m_contextCache release]; | |
131 | [m_externalObjectGraph release]; | |
81345200 | 132 | [m_externalRememberedSet release]; |
93a37866 A |
133 | [super dealloc]; |
134 | } | |
135 | ||
136 | static id getInternalObjcObject(id object) | |
137 | { | |
138 | if ([object isKindOfClass:[JSManagedValue class]]) { | |
139 | JSValue* value = [static_cast<JSManagedValue *>(object) value]; | |
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 | ||
81345200 A |
154 | - (bool)isOldExternalObject:(id)object |
155 | { | |
156 | JSC::VM* vm = toJS(m_group); | |
157 | return vm->heap.slotVisitor().containsOpaqueRoot(object); | |
158 | } | |
159 | ||
160 | - (void)addExternalRememberedObject:(id)object | |
161 | { | |
162 | ASSERT([self isOldExternalObject:object]); | |
163 | [m_externalRememberedSet setObject:[NSNumber numberWithBool:true] forKey:object]; | |
164 | } | |
165 | ||
93a37866 A |
166 | - (void)addManagedReference:(id)object withOwner:(id)owner |
167 | { | |
81345200 A |
168 | if ([object isKindOfClass:[JSManagedValue class]]) |
169 | [object didAddOwner:owner]; | |
170 | ||
93a37866 A |
171 | object = getInternalObjcObject(object); |
172 | owner = getInternalObjcObject(owner); | |
173 | ||
174 | if (!object || !owner) | |
175 | return; | |
176 | ||
81345200 A |
177 | JSC::JSLockHolder locker(toJS(m_group)); |
178 | if ([self isOldExternalObject:owner] && ![self isOldExternalObject:object]) | |
179 | [self addExternalRememberedObject:owner]; | |
180 | ||
93a37866 A |
181 | NSMapTable *ownedObjects = [m_externalObjectGraph objectForKey:owner]; |
182 | if (!ownedObjects) { | |
183 | NSPointerFunctionsOptions weakIDOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality; | |
184 | NSPointerFunctionsOptions integerOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsIntegerPersonality; | |
185 | ownedObjects = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:integerOptions capacity:1]; | |
186 | ||
187 | [m_externalObjectGraph setObject:ownedObjects forKey:owner]; | |
188 | [ownedObjects release]; | |
189 | } | |
81345200 A |
190 | |
191 | size_t count = reinterpret_cast<size_t>(NSMapGet(ownedObjects, object)); | |
192 | NSMapInsert(ownedObjects, object, reinterpret_cast<void*>(count + 1)); | |
93a37866 A |
193 | } |
194 | ||
195 | - (void)removeManagedReference:(id)object withOwner:(id)owner | |
196 | { | |
81345200 A |
197 | if ([object isKindOfClass:[JSManagedValue class]]) |
198 | [object didRemoveOwner:owner]; | |
199 | ||
93a37866 A |
200 | object = getInternalObjcObject(object); |
201 | owner = getInternalObjcObject(owner); | |
202 | ||
203 | if (!object || !owner) | |
204 | return; | |
205 | ||
81345200 | 206 | JSC::JSLockHolder locker(toJS(m_group)); |
93a37866 A |
207 | |
208 | NSMapTable *ownedObjects = [m_externalObjectGraph objectForKey:owner]; | |
209 | if (!ownedObjects) | |
210 | return; | |
211 | ||
212 | size_t count = reinterpret_cast<size_t>(NSMapGet(ownedObjects, object)); | |
213 | if (count > 1) { | |
214 | NSMapInsert(ownedObjects, object, reinterpret_cast<void*>(count - 1)); | |
215 | return; | |
216 | } | |
217 | ||
218 | if (count == 1) | |
219 | NSMapRemove(ownedObjects, object); | |
220 | ||
81345200 | 221 | if (![ownedObjects count]) { |
93a37866 | 222 | [m_externalObjectGraph removeObjectForKey:owner]; |
81345200 A |
223 | [m_externalRememberedSet removeObjectForKey:owner]; |
224 | } | |
93a37866 A |
225 | } |
226 | ||
227 | @end | |
228 | ||
229 | @implementation JSVirtualMachine(Internal) | |
230 | ||
231 | JSContextGroupRef getGroupFromVirtualMachine(JSVirtualMachine *virtualMachine) | |
232 | { | |
233 | return virtualMachine->m_group; | |
234 | } | |
235 | ||
236 | + (JSVirtualMachine *)virtualMachineWithContextGroupRef:(JSContextGroupRef)group | |
237 | { | |
238 | JSVirtualMachine *virtualMachine = [JSVMWrapperCache wrapperForJSContextGroupRef:group]; | |
239 | if (!virtualMachine) | |
240 | virtualMachine = [[[JSVirtualMachine alloc] initWithContextGroupRef:group] autorelease]; | |
241 | return virtualMachine; | |
242 | } | |
243 | ||
244 | - (JSContext *)contextForGlobalContextRef:(JSGlobalContextRef)globalContext | |
245 | { | |
246 | return static_cast<JSContext *>(NSMapGet(m_contextCache, globalContext)); | |
247 | } | |
248 | ||
249 | - (void)addContext:(JSContext *)wrapper forGlobalContextRef:(JSGlobalContextRef)globalContext | |
250 | { | |
251 | NSMapInsert(m_contextCache, globalContext, wrapper); | |
252 | } | |
253 | ||
254 | - (NSMapTable *)externalObjectGraph | |
255 | { | |
256 | return m_externalObjectGraph; | |
257 | } | |
258 | ||
81345200 A |
259 | - (NSMapTable *)externalRememberedSet |
260 | { | |
261 | return m_externalRememberedSet; | |
262 | } | |
263 | ||
93a37866 A |
264 | @end |
265 | ||
266 | void scanExternalObjectGraph(JSC::VM& vm, JSC::SlotVisitor& visitor, void* root) | |
267 | { | |
268 | @autoreleasepool { | |
269 | JSVirtualMachine *virtualMachine = [JSVMWrapperCache wrapperForJSContextGroupRef:toRef(&vm)]; | |
270 | if (!virtualMachine) | |
271 | return; | |
272 | NSMapTable *externalObjectGraph = [virtualMachine externalObjectGraph]; | |
273 | Vector<void*> stack; | |
274 | stack.append(root); | |
275 | while (!stack.isEmpty()) { | |
276 | void* nextRoot = stack.last(); | |
277 | stack.removeLast(); | |
278 | if (visitor.containsOpaqueRootTriState(nextRoot) == TrueTriState) | |
279 | continue; | |
280 | visitor.addOpaqueRoot(nextRoot); | |
281 | ||
282 | NSMapTable *ownedObjects = [externalObjectGraph objectForKey:static_cast<id>(nextRoot)]; | |
81345200 | 283 | for (id ownedObject in ownedObjects) |
93a37866 | 284 | stack.append(static_cast<void*>(ownedObject)); |
93a37866 A |
285 | } |
286 | } | |
287 | } | |
288 | ||
81345200 A |
289 | void scanExternalRememberedSet(JSC::VM& vm, JSC::SlotVisitor& visitor) |
290 | { | |
291 | @autoreleasepool { | |
292 | JSVirtualMachine *virtualMachine = [JSVMWrapperCache wrapperForJSContextGroupRef:toRef(&vm)]; | |
293 | if (!virtualMachine) | |
294 | return; | |
295 | NSMapTable *externalObjectGraph = [virtualMachine externalObjectGraph]; | |
296 | NSMapTable *externalRememberedSet = [virtualMachine externalRememberedSet]; | |
297 | for (id key in externalRememberedSet) { | |
298 | NSMapTable *ownedObjects = [externalObjectGraph objectForKey:key]; | |
299 | for (id ownedObject in ownedObjects) | |
300 | scanExternalObjectGraph(vm, visitor, ownedObject); | |
301 | } | |
302 | [externalRememberedSet removeAllObjects]; | |
303 | } | |
304 | } | |
93a37866 | 305 | |
81345200 | 306 | #endif // JSC_OBJC_API_ENABLED |