JSManagedValue.mm
1 /* 2 * Copyright (C) 2013, 2016 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. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 27 #import "config.h" 28 #import "JSManagedValue.h" 29 30 #if JSC_OBJC_API_ENABLED 31 32 #import "APICast.h" 33 #import "Heap.h" 34 #import "JSContextInternal.h" 35 #import "JSValueInternal.h" 36 #import "JSWeakValue.h" 37 #import "WeakHandleOwner.h" 38 #import "ObjcRuntimeExtras.h" 39 #import "JSCInlines.h" 40 #import <wtf/NeverDestroyed.h> 41 42 class JSManagedValueHandleOwner final : public JSC::WeakHandleOwner { 43 public: 44 void finalize(JSC::Handle<JSC::Unknown>, void* context) final; 45 bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor&, const char**) final; 46 }; 47 48 static JSManagedValueHandleOwner& managedValueHandleOwner() 49 { 50 static NeverDestroyed<JSManagedValueHandleOwner> jsManagedValueHandleOwner; 51 return jsManagedValueHandleOwner; 52 } 53 54 #if defined(DARLING) && __i386__ 55 @implementation JSManagedValue 56 #else 57 @implementation JSManagedValue { 58 JSC::Weak<JSC::JSGlobalObject> m_globalObject; 59 RefPtr<JSC::JSLock> m_lock; 60 JSC::JSWeakValue m_weakValue; 61 NSMapTable *m_owners; 62 } 63 #endif 64 65 + (JSManagedValue *)managedValueWithValue:(JSValue *)value 66 { 67 return [[[self alloc] initWithValue:value] autorelease]; 68 } 69 70 + (JSManagedValue *)managedValueWithValue:(JSValue *)value andOwner:(id)owner 71 { 72 JSManagedValue *managedValue = [[self alloc] initWithValue:value]; 73 [value.context.virtualMachine addManagedReference:managedValue withOwner:owner]; 74 return [managedValue autorelease]; 75 } 76 77 - (instancetype)init 78 { 79 return [self initWithValue:nil]; 80 } 81 82 - (instancetype)initWithValue:(JSValue *)value 83 { 84 self = [super init]; 85 if (!self) 86 return nil; 87 88 if (!value) 89 return self; 90 91 JSC::JSGlobalObject* globalObject = toJS([value.context JSGlobalContextRef]); 92 auto& owner = managedValueHandleOwner(); 93 JSC::Weak<JSC::JSGlobalObject> weak(globalObject, &owner, (__bridge void*)self); 94 m_globalObject.swap(weak); 95 96 m_lock = &globalObject->vm().apiLock(); 97 98 NSPointerFunctionsOptions weakIDOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality; 99 NSPointerFunctionsOptions integerOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsIntegerPersonality; 100 m_owners = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:integerOptions capacity:1]; 101 102 JSC::JSValue jsValue = toJS(globalObject, [value JSValueRef]); 103 if (jsValue.isObject()) 104 m_weakValue.setObject(JSC::jsCast<JSC::JSObject*>(jsValue.asCell()), owner, (__bridge void*)self); 105 else if (jsValue.isString()) 106 m_weakValue.setString(JSC::jsCast<JSC::JSString*>(jsValue.asCell()), owner, (__bridge void*)self); 107 else 108 m_weakValue.setPrimitive(jsValue); 109 return self; 110 } 111 112 - (void)dealloc 113 { 114 JSVirtualMachine *virtualMachine = [[[self value] context] virtualMachine]; 115 if (virtualMachine) { 116 NSMapTable *copy = [m_owners copy]; 117 for (id owner in [copy keyEnumerator]) { 118 size_t count = reinterpret_cast<size_t>(NSMapGet(m_owners, (__bridge void*)owner)); 119 while (count--) 120 [virtualMachine removeManagedReference:self withOwner:owner]; 121 } 122 [copy release]; 123 } 124 125 [self disconnectValue]; 126 [m_owners release]; 127 [super dealloc]; 128 } 129 130 - (void)didAddOwner:(id)owner 131 { 132 size_t count = reinterpret_cast<size_t>(NSMapGet(m_owners, (__bridge void*)owner)); 133 NSMapInsert(m_owners, (__bridge void*)owner, reinterpret_cast<void*>(count + 1)); 134 } 135 136 - (void)didRemoveOwner:(id)owner 137 { 138 size_t count = reinterpret_cast<size_t>(NSMapGet(m_owners, (__bridge void*)owner)); 139 140 if (!count) 141 return; 142 143 if (count == 1) { 144 NSMapRemove(m_owners, (__bridge void*)owner); 145 return; 146 } 147 148 NSMapInsert(m_owners, (__bridge void*)owner, reinterpret_cast<void*>(count - 1)); 149 } 150 151 - (JSValue *)value 152 { 153 WTF::Locker<JSC::JSLock> locker(m_lock.get()); 154 JSC::VM* vm = m_lock->vm(); 155 if (!vm) 156 return nil; 157 158 JSC::JSLockHolder apiLocker(vm); 159 if (!m_globalObject) 160 return nil; 161 if (m_weakValue.isClear()) 162 return nil; 163 JSC::JSGlobalObject* globalObject = m_globalObject.get(); 164 JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(globalObject)]; 165 JSC::JSValue value; 166 if (m_weakValue.isPrimitive()) 167 value = m_weakValue.primitive(); 168 else if (m_weakValue.isString()) 169 value = m_weakValue.string(); 170 else 171 value = m_weakValue.object(); 172 return [JSValue valueWithJSValueRef:toRef(globalObject, value) inContext:context]; 173 } 174 175 - (void)disconnectValue 176 { 177 m_globalObject.clear(); 178 m_weakValue.clear(); 179 } 180 181 @end 182 183 @interface JSManagedValue (PrivateMethods) 184 - (void)disconnectValue; 185 @end 186 187 bool JSManagedValueHandleOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor& visitor, const char** reason) 188 { 189 if (UNLIKELY(reason)) 190 *reason = "JSManagedValue is opaque root"; 191 JSManagedValue *managedValue = (__bridge JSManagedValue *)context; 192 return visitor.containsOpaqueRoot((__bridge void*)managedValue); 193 } 194 195 void JSManagedValueHandleOwner::finalize(JSC::Handle<JSC::Unknown>, void* context) 196 { 197 JSManagedValue *managedValue = (__bridge JSManagedValue *)context; 198 [managedValue disconnectValue]; 199 } 200 201 #endif // JSC_OBJC_API_ENABLED