1// Copyright 2018-present 650 Industries. All rights reserved. 2 3#import <jsi/jsi.h> 4 5#import <ExpoModulesCore/EXJavaScriptRuntime.h> 6 7#import <ExpoModulesCore/ExpoModulesHostObject.h> 8#import <ExpoModulesCore/ExpoModulesProxySpec.h> 9#import <ExpoModulesCore/EXJSIConversions.h> 10#import <ExpoModulesCore/Swift.h> 11 12using namespace facebook; 13 14@implementation EXJavaScriptRuntime { 15 jsi::Runtime *_runtime; 16 std::shared_ptr<react::CallInvoker> _jsCallInvoker; 17 18 EXJavaScriptObject *_global; 19} 20 21- (nonnull instancetype)initWithRuntime:(jsi::Runtime &)runtime callInvoker:(std::shared_ptr<react::CallInvoker>)callInvoker 22{ 23 if (self = [super init]) { 24 _runtime = &runtime; 25 _jsCallInvoker = callInvoker; 26 27 auto jsGlobalPtr = std::make_shared<jsi::Object>(_runtime->global()); 28 _global = [[EXJavaScriptObject alloc] initWith:jsGlobalPtr runtime:self]; 29 } 30 return self; 31} 32 33- (nonnull jsi::Runtime *)get 34{ 35 return _runtime; 36} 37 38- (std::shared_ptr<react::CallInvoker>)callInvoker 39{ 40 return _jsCallInvoker; 41} 42 43- (nonnull EXJavaScriptObject *)createObject 44{ 45 auto jsObjectPtr = std::make_shared<jsi::Object>(*_runtime); 46 return [[EXJavaScriptObject alloc] initWith:jsObjectPtr runtime:self]; 47} 48 49- (nonnull EXJavaScriptObject *)createHostObject:(std::shared_ptr<jsi::HostObject>)jsiHostObjectPtr 50{ 51 auto jsObjectPtr = std::make_shared<jsi::Object>(jsi::Object::createFromHostObject(*_runtime, jsiHostObjectPtr)); 52 return [[EXJavaScriptObject alloc] initWith:jsObjectPtr runtime:self]; 53} 54 55- (nonnull EXJavaScriptObject *)global 56{ 57 return _global; 58} 59 60- (jsi::Function)createSyncFunction:(nonnull NSString *)name 61 argsCount:(NSInteger)argsCount 62 block:(nonnull JSSyncFunctionBlock)block 63{ 64 return [self createHostFunction:name argsCount:argsCount block:^jsi::Value(jsi::Runtime &runtime, std::shared_ptr<react::CallInvoker> callInvoker, NSArray * _Nonnull arguments) { 65 return expo::convertObjCObjectToJSIValue(runtime, block(arguments)); 66 }]; 67} 68 69- (jsi::Function)createAsyncFunction:(nonnull NSString *)name 70 argsCount:(NSInteger)argsCount 71 block:(nonnull JSAsyncFunctionBlock)block 72{ 73 return [self createHostFunction:name argsCount:argsCount block:^jsi::Value(jsi::Runtime &runtime, std::shared_ptr<react::CallInvoker> callInvoker, NSArray *arguments) { 74 // The function that is invoked as a setup of the EXJavaScript `Promise`. 75 auto promiseSetup = [callInvoker, block, arguments](jsi::Runtime &runtime, std::shared_ptr<Promise> promise) { 76 expo::callPromiseSetupWithBlock(runtime, callInvoker, promise, ^(RCTPromiseResolveBlock resolver, RCTPromiseRejectBlock rejecter) { 77 block(arguments, resolver, rejecter); 78 }); 79 }; 80 return createPromiseAsJSIValue(runtime, promiseSetup); 81 }]; 82} 83 84#pragma mark - Private 85 86typedef jsi::Value (^JSHostFunctionBlock)(jsi::Runtime &runtime, std::shared_ptr<react::CallInvoker> callInvoker, NSArray * _Nonnull arguments); 87 88- (jsi::Function)createHostFunction:(nonnull NSString *)name 89 argsCount:(NSInteger)argsCount 90 block:(nonnull JSHostFunctionBlock)block 91{ 92 jsi::PropNameID propNameId = jsi::PropNameID::forAscii(*_runtime, [name UTF8String], [name length]); 93 std::weak_ptr<react::CallInvoker> weakCallInvoker = _jsCallInvoker; 94 jsi::HostFunctionType function = [weakCallInvoker, block](jsi::Runtime &runtime, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { 95 if (auto callInvoker = weakCallInvoker.lock()) { 96 NSArray *arguments = expo::convertJSIValuesToNSArray(runtime, args, count, callInvoker); 97 return block(runtime, callInvoker, arguments); 98 } 99 // TODO: We should throw some kind of error. 100 return jsi::Value::undefined(); 101 }; 102 return jsi::Function::createFromHostFunction(*_runtime, propNameId, (unsigned int)argsCount, function); 103} 104 105@end 106