1// Copyright 2022-present 650 Industries. All rights reserved.
2
3#import <ExpoModulesCore/EXJSIConversions.h>
4#import <ExpoModulesCore/EXJavaScriptObject.h>
5#import <ExpoModulesCore/EXJavaScriptRuntime.h>
6#import <ExpoModulesCore/ExpoModulesProxySpec.h>
7
8@implementation EXJavaScriptObject {
9  /**
10   Pointer to the `EXJavaScriptRuntime` wrapper.
11
12   \note It must be weak because only then the original runtime can be safely deallocated
13   when the JS engine wants to without unsetting it on each created object.
14   */
15  __weak EXJavaScriptRuntime *_runtime;
16
17  /**
18   Shared pointer to the original JSI object that is being wrapped by `EXJavaScriptObject` class.
19   */
20  std::shared_ptr<jsi::Object> _jsObjectPtr;
21}
22
23- (nonnull instancetype)initWith:(std::shared_ptr<jsi::Object>)jsObjectPtr
24                         runtime:(nonnull EXJavaScriptRuntime *)runtime
25{
26  if (self = [super init]) {
27    _runtime = runtime;
28    _jsObjectPtr = jsObjectPtr;
29  }
30  return self;
31}
32
33- (nonnull jsi::Object *)get
34{
35  return _jsObjectPtr.get();
36}
37
38#pragma mark - Subscripting
39
40- (nullable id)objectForKeyedSubscript:(nonnull NSString *)key
41{
42  auto runtime = [_runtime get];
43  auto callInvoker = [_runtime callInvoker];
44
45  if (runtime && callInvoker) {
46    auto value = _jsObjectPtr->getProperty(*runtime, [key UTF8String]);
47    return expo::convertJSIValueToObjCObject(*runtime, value, callInvoker);
48  }
49  return nil;
50}
51
52- (void)setObject:(nullable id)obj forKeyedSubscript:(nonnull NSString *)key
53{
54  auto runtime = [_runtime get];
55
56  if (!runtime) {
57    NSLog(@"Cannot set '%@' property when the EXJavaScript runtime is no longer available.", key);
58    return;
59  }
60  if ([obj isKindOfClass:[EXJavaScriptObject class]]) {
61    _jsObjectPtr->setProperty(*runtime, [key UTF8String], *[obj get]);
62  } else {
63    _jsObjectPtr->setProperty(*runtime, [key UTF8String], expo::convertObjCObjectToJSIValue(*runtime, obj));
64  }
65}
66
67#pragma mark - Functions
68
69- (void)setAsyncFunction:(nonnull NSString *)name
70               argsCount:(NSInteger)argsCount
71                   block:(nonnull JSAsyncFunctionBlock)block
72{
73  if (!_runtime) {
74    NSLog(@"Cannot set '%@' async function when the EXJavaScript runtime is no longer available.", name);
75    return;
76  }
77  jsi::Function function = [_runtime createAsyncFunction:name argsCount:argsCount block:block];
78  _jsObjectPtr->setProperty(*[_runtime get], [name UTF8String], function);
79}
80
81- (void)setSyncFunction:(nonnull NSString *)name
82              argsCount:(NSInteger)argsCount
83                  block:(nonnull JSSyncFunctionBlock)block
84{
85  if (!_runtime) {
86    NSLog(@"Cannot set '%@' sync function when the EXJavaScript runtime is no longer available.", name);
87    return;
88  }
89  jsi::Function function = [_runtime createSyncFunction:name argsCount:argsCount block:block];
90  _jsObjectPtr->setProperty(*[_runtime get], [name UTF8String], function);
91}
92
93@end
94