1// Copyright 2022-present 650 Industries. All rights reserved.
2
3#import <ExpoModulesCore/EXJSIUtils.h>
4#import <ExpoModulesCore/EXJavaScriptWeakObject.h>
5
6@implementation EXJavaScriptWeakObject {
7  /**
8   Pointer to the `EXJavaScriptRuntime` wrapper.
9
10   \note It must be weak because only then the original runtime can be safely deallocated
11   when the JS engine wants to without unsetting it on each created object.
12   */
13  __weak EXJavaScriptRuntime *_runtime;
14
15#if __has_include(<reacthermes/HermesExecutorFactory.h>)
16  /**
17   A weak reference to a JS object. Available only on Hermes engine.
18   */
19  std::shared_ptr<jsi::WeakObject> _weakObject;
20#else
21  /**
22   Shared pointer to the `WeakRef` JS object. Available only on JSC engine.
23   */
24  std::shared_ptr<jsi::Object> _weakObject;
25#endif
26}
27
28- (nonnull instancetype)initWith:(std::shared_ptr<jsi::Object>)jsObject
29                         runtime:(nonnull EXJavaScriptRuntime *)runtime
30{
31  if (self = [super init]) {
32    _runtime = runtime;
33
34#if __has_include(<reacthermes/HermesExecutorFactory.h>)
35    _weakObject = std::make_shared<jsi::WeakObject>(*[runtime get], *jsObject);
36#else
37    // Check whether the runtime supports `WeakRef` objects. If it does not,
38    // we consciously hold a strong reference to the object and cause memory leaks.
39    // This is the case on JSC prior to iOS 14.5.
40    if (expo::isWeakRefSupported(*[runtime get])) {
41      _weakObject = expo::createWeakRef(*[runtime get], jsObject);
42    } else {
43      _weakObject = jsObject;
44    }
45#endif
46  }
47  return self;
48}
49
50- (nullable EXJavaScriptObject *)lock
51{
52  jsi::Runtime *runtime = [_runtime get];
53
54#if __has_include(<reacthermes/HermesExecutorFactory.h>)
55  jsi::Value value = _weakObject->lock(*runtime);
56
57  // `lock` returns an undefined value if the underlying object no longer exists.
58  if (value.isUndefined()) {
59    return nil;
60  }
61  std::shared_ptr<jsi::Object> objectPtr = std::make_shared<jsi::Object>(value.asObject(*runtime));
62#else
63  std::shared_ptr<jsi::Object> objectPtr = expo::isWeakRefSupported(*runtime)
64    ? expo::derefWeakRef(*runtime, _weakObject)
65    : _weakObject;
66#endif
67
68  if (!objectPtr) {
69    return nil;
70  }
71  return [[EXJavaScriptObject alloc] initWith:objectPtr runtime:_runtime];
72}
73
74@end
75