// Copyright 2018-present 650 Industries. All rights reserved. #import #if __has_include() #import #elif __has_include() #import #else #import #endif #import #import #import #import #import using namespace facebook; @implementation EXJavaScriptRuntime { std::shared_ptr _runtime; std::shared_ptr _jsCallInvoker; } - (nonnull instancetype)init { if (self = [super init]) { #if __has_include() || __has_include() _runtime = hermes::makeHermesRuntime(); #else _runtime = jsc::makeJSCRuntime(); #endif _jsCallInvoker = nil; } return self; } - (nonnull instancetype)initWithRuntime:(jsi::Runtime *)runtime callInvoker:(std::shared_ptr)callInvoker { if (self = [super init]) { // Creating a shared pointer that points to the runtime but doesn't own it, thus doesn't release it. // In this code flow, the runtime should be owned by something else like the RCTBridge. // See explanation for constructor (8): https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr _runtime = std::shared_ptr(std::shared_ptr(), runtime); _jsCallInvoker = callInvoker; } return self; } - (nonnull jsi::Runtime *)get { return _runtime.get(); } - (std::shared_ptr)callInvoker { return _jsCallInvoker; } - (nonnull EXJavaScriptObject *)createObject { auto jsObjectPtr = std::make_shared(*_runtime); return [[EXJavaScriptObject alloc] initWith:jsObjectPtr runtime:self]; } - (nonnull EXJavaScriptObject *)createHostObject:(std::shared_ptr)jsiHostObjectPtr { auto jsObjectPtr = std::make_shared(jsi::Object::createFromHostObject(*_runtime, jsiHostObjectPtr)); return [[EXJavaScriptObject alloc] initWith:jsObjectPtr runtime:self]; } - (nonnull EXJavaScriptObject *)global { auto jsGlobalPtr = std::make_shared(_runtime->global()); return [[EXJavaScriptObject alloc] initWith:jsGlobalPtr runtime:self]; } - (jsi::Function)createSyncFunction:(nonnull NSString *)name argsCount:(NSInteger)argsCount block:(nonnull JSSyncFunctionBlock)block { return [self createHostFunction:name argsCount:argsCount block:^jsi::Value(jsi::Runtime &runtime, std::shared_ptr callInvoker, NSArray * _Nonnull arguments) { return expo::convertObjCObjectToJSIValue(runtime, block(arguments)); }]; } - (jsi::Function)createAsyncFunction:(nonnull NSString *)name argsCount:(NSInteger)argsCount block:(nonnull JSAsyncFunctionBlock)block { return [self createHostFunction:name argsCount:argsCount block:^jsi::Value(jsi::Runtime &runtime, std::shared_ptr callInvoker, NSArray *arguments) { // The function that is invoked as a setup of the EXJavaScript `Promise`. auto promiseSetup = [callInvoker, block, arguments](jsi::Runtime &runtime, std::shared_ptr promise) { expo::callPromiseSetupWithBlock(runtime, callInvoker, promise, ^(RCTPromiseResolveBlock resolver, RCTPromiseRejectBlock rejecter) { block(arguments, resolver, rejecter); }); }; return createPromiseAsJSIValue(runtime, promiseSetup); }]; } #pragma mark - Script evaluation - (nullable id)evaluateScript:(nonnull NSString *)scriptSource { auto scriptBuffer = std::make_shared([[NSString stringWithFormat:@"(%@)", scriptSource] UTF8String]); auto result = _runtime->evaluateJavaScript(scriptBuffer, "<>"); return expo::convertJSIValueToObjCObject(*_runtime, result, _jsCallInvoker); } #pragma mark - Private typedef jsi::Value (^JSHostFunctionBlock)(jsi::Runtime &runtime, std::shared_ptr callInvoker, NSArray * _Nonnull arguments); - (jsi::Function)createHostFunction:(nonnull NSString *)name argsCount:(NSInteger)argsCount block:(nonnull JSHostFunctionBlock)block { jsi::PropNameID propNameId = jsi::PropNameID::forAscii(*_runtime, [name UTF8String], [name length]); std::weak_ptr weakCallInvoker = _jsCallInvoker; jsi::HostFunctionType function = [weakCallInvoker, block](jsi::Runtime &runtime, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value { if (auto callInvoker = weakCallInvoker.lock()) { NSArray *arguments = expo::convertJSIValuesToNSArray(runtime, args, count, callInvoker); return block(runtime, callInvoker, arguments); } // TODO: We should throw some kind of error. return jsi::Value::undefined(); }; return jsi::Function::createFromHostFunction(*_runtime, propNameId, (unsigned int)argsCount, function); } @end