178516026STomasz Sapeta// Copyright 2022-present 650 Industries. All rights reserved.
278516026STomasz Sapeta
378516026STomasz Sapeta#import <ExpoModulesCore/EXJSIUtils.h>
478516026STomasz Sapeta#import <ExpoModulesCore/EXJavaScriptWeakObject.h>
578516026STomasz Sapeta
678516026STomasz Sapeta@implementation EXJavaScriptWeakObject {
778516026STomasz Sapeta  /**
878516026STomasz Sapeta   Pointer to the `EXJavaScriptRuntime` wrapper.
978516026STomasz Sapeta
1078516026STomasz Sapeta   \note It must be weak because only then the original runtime can be safely deallocated
1178516026STomasz Sapeta   when the JS engine wants to without unsetting it on each created object.
1278516026STomasz Sapeta   */
1378516026STomasz Sapeta  __weak EXJavaScriptRuntime *_runtime;
1478516026STomasz Sapeta
15*2727a9f2STomasz Sapeta#if __has_include(<reacthermes/HermesExecutorFactory.h>)
1678516026STomasz Sapeta  /**
17*2727a9f2STomasz Sapeta   A weak reference to a JS object. Available only on Hermes engine.
1878516026STomasz Sapeta   */
19*2727a9f2STomasz Sapeta  std::shared_ptr<jsi::WeakObject> _weakObject;
20*2727a9f2STomasz Sapeta#else
21*2727a9f2STomasz Sapeta  /**
22*2727a9f2STomasz Sapeta   Shared pointer to the `WeakRef` JS object. Available only on JSC engine.
23*2727a9f2STomasz Sapeta   */
24*2727a9f2STomasz Sapeta  std::shared_ptr<jsi::Object> _weakObject;
25*2727a9f2STomasz Sapeta#endif
2678516026STomasz Sapeta}
2778516026STomasz Sapeta
2878516026STomasz Sapeta- (nonnull instancetype)initWith:(std::shared_ptr<jsi::Object>)jsObject
2978516026STomasz Sapeta                         runtime:(nonnull EXJavaScriptRuntime *)runtime
3078516026STomasz Sapeta{
3178516026STomasz Sapeta  if (self = [super init]) {
3278516026STomasz Sapeta    _runtime = runtime;
3378516026STomasz Sapeta
34*2727a9f2STomasz Sapeta#if __has_include(<reacthermes/HermesExecutorFactory.h>)
35*2727a9f2STomasz Sapeta    _weakObject = std::make_shared<jsi::WeakObject>(*[runtime get], *jsObject);
36*2727a9f2STomasz Sapeta#else
3778516026STomasz Sapeta    // Check whether the runtime supports `WeakRef` objects. If it does not,
3878516026STomasz Sapeta    // we consciously hold a strong reference to the object and cause memory leaks.
39*2727a9f2STomasz Sapeta    // This is the case on JSC prior to iOS 14.5.
4078516026STomasz Sapeta    if (expo::isWeakRefSupported(*[runtime get])) {
41*2727a9f2STomasz Sapeta      _weakObject = expo::createWeakRef(*[runtime get], jsObject);
4278516026STomasz Sapeta    } else {
43*2727a9f2STomasz Sapeta      _weakObject = jsObject;
4478516026STomasz Sapeta    }
45*2727a9f2STomasz Sapeta#endif
4678516026STomasz Sapeta  }
4778516026STomasz Sapeta  return self;
4878516026STomasz Sapeta}
4978516026STomasz Sapeta
5078516026STomasz Sapeta- (nullable EXJavaScriptObject *)lock
5178516026STomasz Sapeta{
5278516026STomasz Sapeta  jsi::Runtime *runtime = [_runtime get];
53*2727a9f2STomasz Sapeta
54*2727a9f2STomasz Sapeta#if __has_include(<reacthermes/HermesExecutorFactory.h>)
55*2727a9f2STomasz Sapeta  jsi::Value value = _weakObject->lock(*runtime);
56*2727a9f2STomasz Sapeta
57*2727a9f2STomasz Sapeta  // `lock` returns an undefined value if the underlying object no longer exists.
58*2727a9f2STomasz Sapeta  if (value.isUndefined()) {
59*2727a9f2STomasz Sapeta    return nil;
60*2727a9f2STomasz Sapeta  }
61*2727a9f2STomasz Sapeta  std::shared_ptr<jsi::Object> objectPtr = std::make_shared<jsi::Object>(value.asObject(*runtime));
62*2727a9f2STomasz Sapeta#else
6378516026STomasz Sapeta  std::shared_ptr<jsi::Object> objectPtr = expo::isWeakRefSupported(*runtime)
64*2727a9f2STomasz Sapeta    ? expo::derefWeakRef(*runtime, _weakObject)
65*2727a9f2STomasz Sapeta    : _weakObject;
66*2727a9f2STomasz Sapeta#endif
6778516026STomasz Sapeta
6878516026STomasz Sapeta  if (!objectPtr) {
6978516026STomasz Sapeta    return nil;
7078516026STomasz Sapeta  }
7178516026STomasz Sapeta  return [[EXJavaScriptObject alloc] initWith:objectPtr runtime:_runtime];
7278516026STomasz Sapeta}
7378516026STomasz Sapeta
7478516026STomasz Sapeta@end
75