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