2  * Copyright (C) 2013 Apple Inc. All rights reserved.
 
   4  * Redistribution and use in source and binary forms, with or without
 
   5  * modification, are permitted provided that the following conditions
 
   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.
 
  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. 
 
  28 #import "JavaScriptCore.h"
 
  30 #if JSC_OBJC_API_ENABLED
 
  34 #import "JSVirtualMachine.h"
 
  35 #import "JSVirtualMachineInternal.h"
 
  36 #import "JSWrapperMap.h"
 
  38 static NSMapTable *globalWrapperCache = 0;
 
  40 static Mutex& wrapperCacheLock()
 
  42     DEFINE_STATIC_LOCAL(Mutex, mutex, ());
 
  46 static void initWrapperCache()
 
  48     ASSERT(!globalWrapperCache);
 
  49     NSPointerFunctionsOptions keyOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;
 
  50     NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
 
  51     globalWrapperCache = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];
 
  54 static NSMapTable *wrapperCache()
 
  56     if (!globalWrapperCache)
 
  58     return globalWrapperCache;
 
  61 @interface JSVMWrapperCache : NSObject
 
  62 + (void)addWrapper:(JSVirtualMachine *)wrapper forJSContextGroupRef:(JSContextGroupRef)group;
 
  63 + (JSVirtualMachine *)wrapperForJSContextGroupRef:(JSContextGroupRef)group;
 
  66 @implementation JSVMWrapperCache
 
  68 + (void)addWrapper:(JSVirtualMachine *)wrapper forJSContextGroupRef:(JSContextGroupRef)group
 
  70     MutexLocker locker(wrapperCacheLock());
 
  71     NSMapInsert(wrapperCache(), group, wrapper);
 
  74 + (JSVirtualMachine *)wrapperForJSContextGroupRef:(JSContextGroupRef)group
 
  76     MutexLocker locker(wrapperCacheLock());
 
  77     return static_cast<JSVirtualMachine *>(NSMapGet(wrapperCache(), group));
 
  82 @implementation JSVirtualMachine {
 
  83     JSContextGroupRef m_group;
 
  84     NSMapTable *m_contextCache;
 
  85     NSMapTable *m_externalObjectGraph;
 
  90     JSContextGroupRef group = JSContextGroupCreate();
 
  91     self = [self initWithContextGroupRef:group];
 
  92     // The extra JSContextGroupRetain is balanced here.
 
  93     JSContextGroupRelease(group);
 
  97 - (id)initWithContextGroupRef:(JSContextGroupRef)group
 
 103     m_group = JSContextGroupRetain(group);
 
 105     NSPointerFunctionsOptions keyOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;
 
 106     NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
 
 107     m_contextCache = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];
 
 109     NSPointerFunctionsOptions weakIDOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
 
 110     NSPointerFunctionsOptions strongIDOptions = NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPersonality;
 
 111     m_externalObjectGraph = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:strongIDOptions capacity:0];
 
 113     [JSVMWrapperCache addWrapper:self forJSContextGroupRef:group];
 
 120     JSContextGroupRelease(m_group);
 
 121     [m_contextCache release];
 
 122     [m_externalObjectGraph release];
 
 126 static id getInternalObjcObject(id object)
 
 128     if ([object isKindOfClass:[JSManagedValue class]]) {
 
 129         JSValue* value = [static_cast<JSManagedValue *>(object) value];
 
 130         id temp = tryUnwrapObjcObject([value.context JSGlobalContextRef], [value JSValueRef]);
 
 136     if ([object isKindOfClass:[JSValue class]]) {
 
 137         JSValue *value = static_cast<JSValue *>(object);
 
 138         object = tryUnwrapObjcObject([value.context JSGlobalContextRef], [value JSValueRef]);
 
 144 - (void)addManagedReference:(id)object withOwner:(id)owner
 
 146     object = getInternalObjcObject(object);
 
 147     owner = getInternalObjcObject(owner);
 
 149     if (!object || !owner)
 
 152     JSC::APIEntryShim shim(toJS(m_group));
 
 154     NSMapTable *ownedObjects = [m_externalObjectGraph objectForKey:owner];
 
 156         NSPointerFunctionsOptions weakIDOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
 
 157         NSPointerFunctionsOptions integerOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsIntegerPersonality;
 
 158         ownedObjects = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:integerOptions capacity:1];
 
 160         [m_externalObjectGraph setObject:ownedObjects forKey:owner];
 
 161         [ownedObjects release];
 
 163     NSMapInsert(ownedObjects, object, reinterpret_cast<void*>(reinterpret_cast<size_t>(NSMapGet(ownedObjects, object)) + 1));
 
 166 - (void)removeManagedReference:(id)object withOwner:(id)owner
 
 168     object = getInternalObjcObject(object);
 
 169     owner = getInternalObjcObject(owner);
 
 171     if (!object || !owner)
 
 174     JSC::APIEntryShim shim(toJS(m_group));
 
 176     NSMapTable *ownedObjects = [m_externalObjectGraph objectForKey:owner];
 
 180     size_t count = reinterpret_cast<size_t>(NSMapGet(ownedObjects, object));
 
 182         NSMapInsert(ownedObjects, object, reinterpret_cast<void*>(count - 1));
 
 187         NSMapRemove(ownedObjects, object);
 
 189     if (![ownedObjects count])
 
 190         [m_externalObjectGraph removeObjectForKey:owner];
 
 195 @implementation JSVirtualMachine(Internal)
 
 197 JSContextGroupRef getGroupFromVirtualMachine(JSVirtualMachine *virtualMachine)
 
 199     return virtualMachine->m_group;
 
 202 + (JSVirtualMachine *)virtualMachineWithContextGroupRef:(JSContextGroupRef)group
 
 204     JSVirtualMachine *virtualMachine = [JSVMWrapperCache wrapperForJSContextGroupRef:group];
 
 206         virtualMachine = [[[JSVirtualMachine alloc] initWithContextGroupRef:group] autorelease];
 
 207     return virtualMachine;
 
 210 - (JSContext *)contextForGlobalContextRef:(JSGlobalContextRef)globalContext
 
 212     return static_cast<JSContext *>(NSMapGet(m_contextCache, globalContext));
 
 215 - (void)addContext:(JSContext *)wrapper forGlobalContextRef:(JSGlobalContextRef)globalContext
 
 217     NSMapInsert(m_contextCache, globalContext, wrapper);
 
 220 - (NSMapTable *)externalObjectGraph
 
 222     return m_externalObjectGraph;
 
 227 void scanExternalObjectGraph(JSC::VM& vm, JSC::SlotVisitor& visitor, void* root)
 
 230         JSVirtualMachine *virtualMachine = [JSVMWrapperCache wrapperForJSContextGroupRef:toRef(&vm)];
 
 233         NSMapTable *externalObjectGraph = [virtualMachine externalObjectGraph];
 
 236         while (!stack.isEmpty()) {
 
 237             void* nextRoot = stack.last();
 
 239             if (visitor.containsOpaqueRootTriState(nextRoot) == TrueTriState)
 
 241             visitor.addOpaqueRoot(nextRoot);
 
 243             NSMapTable *ownedObjects = [externalObjectGraph objectForKey:static_cast<id>(nextRoot)];
 
 245             NSEnumerator *enumerator = [ownedObjects keyEnumerator];
 
 246             while ((ownedObject = [enumerator nextObject])) {
 
 247                 ASSERT(reinterpret_cast<size_t>(NSMapGet(ownedObjects, ownedObject)) == 1);
 
 248                 stack.append(static_cast<void*>(ownedObject));