1370fa39dSKudo Chien// Copyright 2022-present 650 Industries. All rights reserved.
2370fa39dSKudo Chien
3370fa39dSKudo Chien#import <ExpoModulesCore/EXJSIConversions.h>
478516026STomasz Sapeta#import <ExpoModulesCore/EXJavaScriptValue.h>
5370fa39dSKudo Chien#import <ExpoModulesCore/EXJavaScriptObject.h>
6370fa39dSKudo Chien#import <ExpoModulesCore/EXJavaScriptRuntime.h>
778516026STomasz Sapeta#import <ExpoModulesCore/EXJavaScriptWeakObject.h>
878516026STomasz Sapeta#import <ExpoModulesCore/EXJSIUtils.h>
9*e1f25825SŁukasz Kosmaty#import <ExpoModulesCore/JSIUtils.h>
10370fa39dSKudo Chien
11370fa39dSKudo Chien@implementation EXJavaScriptObject {
12370fa39dSKudo Chien  /**
13370fa39dSKudo Chien   Pointer to the `EXJavaScriptRuntime` wrapper.
14370fa39dSKudo Chien
15370fa39dSKudo Chien   \note It must be weak because only then the original runtime can be safely deallocated
16370fa39dSKudo Chien   when the JS engine wants to without unsetting it on each created object.
17370fa39dSKudo Chien   */
18370fa39dSKudo Chien  __weak EXJavaScriptRuntime *_runtime;
19370fa39dSKudo Chien
20370fa39dSKudo Chien  /**
21370fa39dSKudo Chien   Shared pointer to the original JSI object that is being wrapped by `EXJavaScriptObject` class.
22370fa39dSKudo Chien   */
23370fa39dSKudo Chien  std::shared_ptr<jsi::Object> _jsObjectPtr;
24370fa39dSKudo Chien}
25370fa39dSKudo Chien
26370fa39dSKudo Chien- (nonnull instancetype)initWith:(std::shared_ptr<jsi::Object>)jsObjectPtr
27370fa39dSKudo Chien                         runtime:(nonnull EXJavaScriptRuntime *)runtime
28370fa39dSKudo Chien{
29370fa39dSKudo Chien  if (self = [super init]) {
30370fa39dSKudo Chien    _runtime = runtime;
31370fa39dSKudo Chien    _jsObjectPtr = jsObjectPtr;
32370fa39dSKudo Chien  }
33370fa39dSKudo Chien  return self;
34370fa39dSKudo Chien}
35370fa39dSKudo Chien
36370fa39dSKudo Chien- (nonnull jsi::Object *)get
37370fa39dSKudo Chien{
38370fa39dSKudo Chien  return _jsObjectPtr.get();
39370fa39dSKudo Chien}
40370fa39dSKudo Chien
41fa88aeb8STomasz Sapeta- (std::shared_ptr<jsi::Object>)getShared
42fa88aeb8STomasz Sapeta{
43fa88aeb8STomasz Sapeta  return _jsObjectPtr;
44fa88aeb8STomasz Sapeta}
45fa88aeb8STomasz Sapeta
469bc9ec42STomasz Sapeta#pragma mark - Accessing object properties
47370fa39dSKudo Chien
489bc9ec42STomasz Sapeta- (BOOL)hasProperty:(nonnull NSString *)name
49370fa39dSKudo Chien{
509bc9ec42STomasz Sapeta  return _jsObjectPtr->hasProperty(*[_runtime get], [name UTF8String]);
51370fa39dSKudo Chien}
52370fa39dSKudo Chien
539bc9ec42STomasz Sapeta- (nonnull EXJavaScriptValue *)getProperty:(nonnull NSString *)name
54370fa39dSKudo Chien{
559bc9ec42STomasz Sapeta  std::shared_ptr<jsi::Value> value = std::make_shared<jsi::Value>(_jsObjectPtr->getProperty(*[_runtime get], [name UTF8String]));
569bc9ec42STomasz Sapeta  return [[EXJavaScriptValue alloc] initWithRuntime:_runtime value:value];
579bc9ec42STomasz Sapeta}
58370fa39dSKudo Chien
599bc9ec42STomasz Sapeta- (nonnull NSArray<NSString *> *)getPropertyNames
609bc9ec42STomasz Sapeta{
619bc9ec42STomasz Sapeta  jsi::Runtime *runtime = [_runtime get];
629bc9ec42STomasz Sapeta  jsi::Array propertyNamesArray = _jsObjectPtr->getPropertyNames(*[_runtime get]);
639bc9ec42STomasz Sapeta  return expo::convertJSIArrayToNSArray(*runtime, propertyNamesArray, nullptr);
64370fa39dSKudo Chien}
659bc9ec42STomasz Sapeta
669bc9ec42STomasz Sapeta#pragma mark - Modifying object properties
679bc9ec42STomasz Sapeta
689bc9ec42STomasz Sapeta- (void)setProperty:(nonnull NSString *)name value:(nullable id)value
699bc9ec42STomasz Sapeta{
709bc9ec42STomasz Sapeta  jsi::Value jsiValue = expo::convertObjCObjectToJSIValue(*[_runtime get], value);
719bc9ec42STomasz Sapeta  _jsObjectPtr->setProperty(*[_runtime get], [name UTF8String], jsiValue);
72370fa39dSKudo Chien}
739bc9ec42STomasz Sapeta
74ce45e284STomasz Sapeta- (void)defineProperty:(nonnull NSString *)name descriptor:(nonnull EXJavaScriptObject *)descriptor
75ce45e284STomasz Sapeta{
76ce45e284STomasz Sapeta  jsi::Runtime *runtime = [_runtime get];
77*e1f25825SŁukasz Kosmaty  jsi::Object *jsThis = _jsObjectPtr.get();
78ce45e284STomasz Sapeta
79*e1f25825SŁukasz Kosmaty  expo::common::definePropertyOnJSIObject(*runtime, jsThis, [name UTF8String], std::move(*[descriptor get]));
80ce45e284STomasz Sapeta}
81ce45e284STomasz Sapeta
829bc9ec42STomasz Sapeta- (void)defineProperty:(nonnull NSString *)name value:(nullable id)value options:(EXJavaScriptObjectPropertyDescriptor)options
839bc9ec42STomasz Sapeta{
849bc9ec42STomasz Sapeta  jsi::Runtime *runtime = [_runtime get];
85*e1f25825SŁukasz Kosmaty  jsi::Object *jsThis = _jsObjectPtr.get();
869bc9ec42STomasz Sapeta
87*e1f25825SŁukasz Kosmaty  jsi::Object descriptor = [self preparePropertyDescriptorWithOptions:options];
889bc9ec42STomasz Sapeta  descriptor.setProperty(*runtime, "value", expo::convertObjCObjectToJSIValue(*runtime, value));
899bc9ec42STomasz Sapeta
90*e1f25825SŁukasz Kosmaty  expo::common::definePropertyOnJSIObject(*runtime, jsThis, [name UTF8String], std::move(descriptor));
91370fa39dSKudo Chien}
92370fa39dSKudo Chien
9378516026STomasz Sapeta#pragma mark - WeakObject
9478516026STomasz Sapeta
9578516026STomasz Sapeta- (nonnull EXJavaScriptWeakObject *)createWeak
9678516026STomasz Sapeta{
9778516026STomasz Sapeta  return [[EXJavaScriptWeakObject alloc] initWith:_jsObjectPtr runtime:_runtime];
9878516026STomasz Sapeta}
9978516026STomasz Sapeta
10078516026STomasz Sapeta#pragma mark - Deallocator
10178516026STomasz Sapeta
10278516026STomasz Sapeta- (void)setObjectDeallocator:(void (^)(void))deallocatorBlock
10378516026STomasz Sapeta{
104*e1f25825SŁukasz Kosmaty  expo::common::setDeallocator(*[_runtime get], _jsObjectPtr, deallocatorBlock);
10578516026STomasz Sapeta}
10678516026STomasz Sapeta
10778516026STomasz Sapeta#pragma mark - Equality
10878516026STomasz Sapeta
10978516026STomasz Sapeta- (BOOL)isEqual:(id)object
11078516026STomasz Sapeta{
11178516026STomasz Sapeta  if ([object isKindOfClass:EXJavaScriptObject.class]) {
11278516026STomasz Sapeta    jsi::Runtime *runtime = [_runtime get];
11378516026STomasz Sapeta    jsi::Object *a = _jsObjectPtr.get();
11478516026STomasz Sapeta    jsi::Object *b = [object get];
11578516026STomasz Sapeta    return jsi::Object::strictEquals(*runtime, *a, *b);
11678516026STomasz Sapeta  }
11778516026STomasz Sapeta  return false;
11878516026STomasz Sapeta}
11978516026STomasz Sapeta
1209bc9ec42STomasz Sapeta#pragma mark - Private helpers
1219bc9ec42STomasz Sapeta
1229bc9ec42STomasz Sapeta- (jsi::Object)preparePropertyDescriptorWithOptions:(EXJavaScriptObjectPropertyDescriptor)options
1239bc9ec42STomasz Sapeta{
1249bc9ec42STomasz Sapeta  jsi::Runtime *runtime = [_runtime get];
1259bc9ec42STomasz Sapeta  jsi::Object descriptor(*runtime);
1269bc9ec42STomasz Sapeta  descriptor.setProperty(*runtime, "configurable", (bool)(options & EXJavaScriptObjectPropertyDescriptorConfigurable));
1279bc9ec42STomasz Sapeta  descriptor.setProperty(*runtime, "enumerable", (bool)(options & EXJavaScriptObjectPropertyDescriptorEnumerable));
1289bc9ec42STomasz Sapeta  descriptor.setProperty(*runtime, "writable", (bool)(options & EXJavaScriptObjectPropertyDescriptorWritable));
1299bc9ec42STomasz Sapeta  return descriptor;
1309bc9ec42STomasz Sapeta}
1319bc9ec42STomasz Sapeta
132370fa39dSKudo Chien@end
133