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