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 - Accessing object properties 38 39- (BOOL)hasProperty:(nonnull NSString *)name 40{ 41 return _jsObjectPtr->hasProperty(*[_runtime get], [name UTF8String]); 42} 43 44- (nonnull EXJavaScriptValue *)getProperty:(nonnull NSString *)name 45{ 46 std::shared_ptr<jsi::Value> value = std::make_shared<jsi::Value>(_jsObjectPtr->getProperty(*[_runtime get], [name UTF8String])); 47 return [[EXJavaScriptValue alloc] initWithRuntime:_runtime value:value]; 48} 49 50- (nonnull NSArray<NSString *> *)getPropertyNames 51{ 52 jsi::Runtime *runtime = [_runtime get]; 53 jsi::Array propertyNamesArray = _jsObjectPtr->getPropertyNames(*[_runtime get]); 54 return expo::convertJSIArrayToNSArray(*runtime, propertyNamesArray, nullptr); 55} 56 57#pragma mark - Modifying object properties 58 59- (void)setProperty:(nonnull NSString *)name value:(nullable id)value 60{ 61 jsi::Value jsiValue = expo::convertObjCObjectToJSIValue(*[_runtime get], value); 62 _jsObjectPtr->setProperty(*[_runtime get], [name UTF8String], jsiValue); 63} 64 65- (void)defineProperty:(nonnull NSString *)name value:(nullable id)value options:(EXJavaScriptObjectPropertyDescriptor)options 66{ 67 jsi::Runtime *runtime = [_runtime get]; 68 jsi::Object global = runtime->global(); 69 jsi::Object objectClass = global.getPropertyAsObject(*runtime, "Object"); 70 jsi::Function definePropertyFunction = objectClass.getPropertyAsFunction(*runtime, "defineProperty"); 71 jsi::Object descriptor = [self preparePropertyDescriptorWithOptions:options]; 72 73 descriptor.setProperty(*runtime, "value", expo::convertObjCObjectToJSIValue(*runtime, value)); 74 75 // This call is basically the same as `Object.defineProperty(object, name, descriptor)` in JS 76 definePropertyFunction.callWithThis(*runtime, objectClass, { 77 jsi::Value(*runtime, *_jsObjectPtr.get()), 78 jsi::String::createFromUtf8(*runtime, [name UTF8String]), 79 std::move(descriptor), 80 }); 81} 82 83#pragma mark - Private helpers 84 85- (jsi::Object)preparePropertyDescriptorWithOptions:(EXJavaScriptObjectPropertyDescriptor)options 86{ 87 jsi::Runtime *runtime = [_runtime get]; 88 jsi::Object descriptor(*runtime); 89 descriptor.setProperty(*runtime, "configurable", (bool)(options & EXJavaScriptObjectPropertyDescriptorConfigurable)); 90 descriptor.setProperty(*runtime, "enumerable", (bool)(options & EXJavaScriptObjectPropertyDescriptorEnumerable)); 91 descriptor.setProperty(*runtime, "writable", (bool)(options & EXJavaScriptObjectPropertyDescriptorWritable)); 92 return descriptor; 93} 94 95@end 96