#import "APICast.h"
#import "Heap.h"
-#import "JSCJSValueInlines.h"
#import "JSContextInternal.h"
#import "JSValueInternal.h"
#import "Weak.h"
#import "WeakHandleOwner.h"
#import "ObjcRuntimeExtras.h"
+#import "JSCInlines.h"
+#import <wtf/spi/cocoa/NSMapTableSPI.h>
class JSManagedValueHandleOwner : public JSC::WeakHandleOwner {
public:
- virtual void finalize(JSC::Handle<JSC::Unknown>, void* context);
- virtual bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor&);
+ virtual void finalize(JSC::Handle<JSC::Unknown>, void* context) override;
+ virtual bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor&) override;
};
static JSManagedValueHandleOwner* managedValueHandleOwner()
{
- DEFINE_STATIC_LOCAL(JSManagedValueHandleOwner, jsManagedValueHandleOwner, ());
+ DEPRECATED_DEFINE_STATIC_LOCAL(JSManagedValueHandleOwner, jsManagedValueHandleOwner, ());
return &jsManagedValueHandleOwner;
}
+class WeakValueRef {
+public:
+ WeakValueRef()
+ : m_tag(NotSet)
+ {
+ }
+
+ ~WeakValueRef()
+ {
+ clear();
+ }
+
+ void clear()
+ {
+ switch (m_tag) {
+ case NotSet:
+ return;
+ case Primitive:
+ u.m_primitive = JSC::JSValue();
+ return;
+ case Object:
+ u.m_object.clear();
+ return;
+ case String:
+ u.m_string.clear();
+ return;
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+
+ bool isClear() const
+ {
+ switch (m_tag) {
+ case NotSet:
+ return true;
+ case Primitive:
+ return !u.m_primitive;
+ case Object:
+ return !u.m_object;
+ case String:
+ return !u.m_string;
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+
+ bool isSet() const { return m_tag != NotSet; }
+ bool isPrimitive() const { return m_tag == Primitive; }
+ bool isObject() const { return m_tag == Object; }
+ bool isString() const { return m_tag == String; }
+
+ void setPrimitive(JSC::JSValue primitive)
+ {
+ ASSERT(!isSet());
+ ASSERT(!u.m_primitive);
+ ASSERT(primitive.isPrimitive());
+ m_tag = Primitive;
+ u.m_primitive = primitive;
+ }
+
+ void setObject(JSC::JSObject* object, void* context)
+ {
+ ASSERT(!isSet());
+ ASSERT(!u.m_object);
+ m_tag = Object;
+ JSC::Weak<JSC::JSObject> weak(object, managedValueHandleOwner(), context);
+ u.m_object.swap(weak);
+ }
+
+ void setString(JSC::JSString* string, void* context)
+ {
+ ASSERT(!isSet());
+ ASSERT(!u.m_object);
+ m_tag = String;
+ JSC::Weak<JSC::JSString> weak(string, managedValueHandleOwner(), context);
+ u.m_string.swap(weak);
+ }
+
+ JSC::JSObject* object()
+ {
+ ASSERT(isObject());
+ return u.m_object.get();
+ }
+
+ JSC::JSValue primitive()
+ {
+ ASSERT(isPrimitive());
+ return u.m_primitive;
+ }
+
+ JSC::JSString* string()
+ {
+ ASSERT(isString());
+ return u.m_string.get();
+ }
+
+private:
+ enum WeakTypeTag { NotSet, Primitive, Object, String };
+ WeakTypeTag m_tag;
+ union WeakValueUnion {
+ public:
+ WeakValueUnion ()
+ : m_primitive(JSC::JSValue())
+ {
+ }
+
+ ~WeakValueUnion()
+ {
+ ASSERT(!m_primitive);
+ }
+
+ JSC::JSValue m_primitive;
+ JSC::Weak<JSC::JSObject> m_object;
+ JSC::Weak<JSC::JSString> m_string;
+ } u;
+};
+
@implementation JSManagedValue {
- JSC::Weak<JSC::JSObject> m_value;
+ JSC::Weak<JSC::JSGlobalObject> m_globalObject;
+ RefPtr<JSC::JSLock> m_lock;
+ WeakValueRef m_weakValue;
+ NSMapTable *m_owners;
}
+ (JSManagedValue *)managedValueWithValue:(JSValue *)value
return [[[self alloc] initWithValue:value] autorelease];
}
-- (id)init
++ (JSManagedValue *)managedValueWithValue:(JSValue *)value andOwner:(id)owner
+{
+ JSManagedValue *managedValue = [[self alloc] initWithValue:value];
+ [value.context.virtualMachine addManagedReference:managedValue withOwner:owner];
+ return [managedValue autorelease];
+}
+
+- (instancetype)init
{
return [self initWithValue:nil];
}
-- (id)initWithValue:(JSValue *)value
+- (instancetype)initWithValue:(JSValue *)value
{
self = [super init];
if (!self)
return nil;
- if (!value || !JSValueIsObject([value.context JSGlobalContextRef], [value JSValueRef])) {
- JSC::Weak<JSC::JSObject> weak;
- m_value.swap(weak);
- } else {
- JSC::JSObject* object = toJS(const_cast<OpaqueJSValue*>([value JSValueRef]));
- JSC::Weak<JSC::JSObject> weak(object, managedValueHandleOwner(), self);
- m_value.swap(weak);
- }
+ if (!value)
+ return self;
+
+ JSC::ExecState* exec = toJS([value.context JSGlobalContextRef]);
+ JSC::JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+ JSC::Weak<JSC::JSGlobalObject> weak(globalObject, managedValueHandleOwner(), self);
+ m_globalObject.swap(weak);
+
+ m_lock = &exec->vm().apiLock();
+
+ NSPointerFunctionsOptions weakIDOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
+ NSPointerFunctionsOptions integerOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsIntegerPersonality;
+ m_owners = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:integerOptions capacity:1];
+ JSC::JSValue jsValue = toJS(exec, [value JSValueRef]);
+ if (jsValue.isObject())
+ m_weakValue.setObject(JSC::jsCast<JSC::JSObject*>(jsValue.asCell()), self);
+ else if (jsValue.isString())
+ m_weakValue.setString(JSC::jsCast<JSC::JSString*>(jsValue.asCell()), self);
+ else
+ m_weakValue.setPrimitive(jsValue);
return self;
}
+- (void)dealloc
+{
+ JSVirtualMachine *virtualMachine = [[[self value] context] virtualMachine];
+ if (virtualMachine) {
+ NSMapTable *copy = [m_owners copy];
+ for (id owner in [copy keyEnumerator]) {
+ size_t count = reinterpret_cast<size_t>(NSMapGet(m_owners, owner));
+ while (count--)
+ [virtualMachine removeManagedReference:self withOwner:owner];
+ }
+ [copy release];
+ }
+
+ [self disconnectValue];
+ [m_owners release];
+ [super dealloc];
+}
+
+- (void)didAddOwner:(id)owner
+{
+ size_t count = reinterpret_cast<size_t>(NSMapGet(m_owners, owner));
+ NSMapInsert(m_owners, owner, reinterpret_cast<void*>(count + 1));
+}
+
+- (void)didRemoveOwner:(id)owner
+{
+ size_t count = reinterpret_cast<size_t>(NSMapGet(m_owners, owner));
+
+ if (!count)
+ return;
+
+ if (count == 1) {
+ NSMapRemove(m_owners, owner);
+ return;
+ }
+
+ NSMapInsert(m_owners, owner, reinterpret_cast<void*>(count - 1));
+}
+
- (JSValue *)value
{
- if (!m_value)
+ WTF::Locker<JSC::JSLock> locker(m_lock.get());
+ if (!m_lock->vm())
+ return nil;
+
+ JSC::JSLockHolder apiLocker(m_lock->vm());
+ if (!m_globalObject)
+ return nil;
+ if (m_weakValue.isClear())
return nil;
- JSC::JSObject* object = m_value.get();
- JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(object->structure()->globalObject()->globalExec())];
- return [JSValue valueWithJSValueRef:toRef(object) inContext:context];
+ JSC::ExecState* exec = m_globalObject->globalExec();
+ JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(exec)];
+ JSC::JSValue value;
+ if (m_weakValue.isPrimitive())
+ value = m_weakValue.primitive();
+ else if (m_weakValue.isString())
+ value = m_weakValue.string();
+ else
+ value = m_weakValue.object();
+ return [JSValue valueWithJSValueRef:toRef(exec, value) inContext:context];
}
- (void)disconnectValue
{
- m_value.clear();
+ m_globalObject.clear();
+ m_weakValue.clear();
}
@end