/ API / JSManagedValue.mm
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