1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo) 2 3 #include "JavaScriptRuntime.h" 4 #include "JavaScriptValue.h" 5 #include "JavaScriptObject.h" 6 #include "Exceptions.h" 7 8 #if FOR_HERMES 9 10 #include <hermes/hermes.h> 11 12 #include <utility> 13 14 #else 15 16 #include <jsi/JSCRuntime.h> 17 18 #endif 19 20 namespace jsi = facebook::jsi; 21 22 namespace expo { 23 24 void SyncCallInvoker::invokeAsync(std::function<void()> &&func) { 25 func(); 26 } 27 28 void SyncCallInvoker::invokeSync(std::function<void()> &&func) { 29 func(); 30 } 31 32 JavaScriptRuntime::JavaScriptRuntime() 33 : jsInvoker(std::make_shared<SyncCallInvoker>()), 34 nativeInvoker(std::make_shared<SyncCallInvoker>()) { 35 #if FOR_HERMES 36 auto config = ::hermes::vm::RuntimeConfig::Builder() 37 .withEnableSampleProfiling(false); 38 runtime = facebook::hermes::makeHermesRuntime(config.build()); 39 40 // This version of the Hermes uses a Promise implementation that is provided by the RN. 41 // The `setImmediate` function isn't defined, but is required by the Promise implementation. 42 // That's why we inject it here. 43 auto setImmediatePropName = jsi::PropNameID::forUtf8(*runtime, "setImmediate"); 44 runtime->global().setProperty( 45 *runtime, 46 setImmediatePropName, 47 jsi::Function::createFromHostFunction( 48 *runtime, 49 setImmediatePropName, 50 1, 51 [](jsi::Runtime &rt, 52 const jsi::Value &thisVal, 53 const jsi::Value *args, 54 size_t count) { 55 args[0].asObject(rt).asFunction(rt).call(rt); 56 return jsi::Value::undefined(); 57 } 58 ) 59 ); 60 #else 61 runtime = facebook::jsc::makeJSCRuntime(); 62 #endif 63 64 // By default "global" property isn't set. 65 runtime->global().setProperty( 66 *runtime, 67 jsi::PropNameID::forUtf8(*runtime, "global"), 68 runtime->global() 69 ); 70 71 // Mock the CodedError that in a typical scenario will be defined by the `expo-modules-core`. 72 // Note: we can't use `class` syntax here, because Hermes doesn't support it. 73 runtime->evaluateJavaScript( 74 std::make_shared<jsi::StringBuffer>( 75 "function CodedError(code, message) {\n" 76 " this.code = code;\n" 77 " this.message = message;\n" 78 " this.stack = (new Error).stack;\n" 79 "}\n" 80 "CodedError.prototype = new Error;\n" 81 "global.ExpoModulesCore_CodedError = CodedError" 82 ), 83 "<<evaluated>>" 84 ); 85 } 86 87 JavaScriptRuntime::JavaScriptRuntime( 88 jsi::Runtime *runtime, 89 std::shared_ptr<react::CallInvoker> jsInvoker, 90 std::shared_ptr<react::CallInvoker> nativeInvoker 91 ) : jsInvoker(std::move(jsInvoker)), nativeInvoker(std::move(nativeInvoker)) { 92 // Creating a shared pointer that points to the runtime but doesn't own it, thus doesn't release it. 93 // In this code flow, the runtime should be owned by something else like the CatalystInstance. 94 // See explanation for constructor (8): https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr 95 this->runtime = std::shared_ptr<jsi::Runtime>(std::shared_ptr<jsi::Runtime>(), runtime); 96 } 97 98 jsi::Runtime &JavaScriptRuntime::get() const { 99 return *runtime; 100 } 101 102 jni::local_ref<JavaScriptValue::javaobject> 103 JavaScriptRuntime::evaluateScript(const std::string &script) { 104 auto scriptBuffer = std::make_shared<jsi::StringBuffer>(script); 105 try { 106 return JavaScriptValue::newObjectCxxArgs( 107 weak_from_this(), 108 std::make_shared<jsi::Value>(runtime->evaluateJavaScript(scriptBuffer, "<<evaluated>>")) 109 ); 110 } catch (const jsi::JSError &error) { 111 jni::throwNewJavaException( 112 JavaScriptEvaluateException::create( 113 error.getMessage(), 114 error.getStack() 115 ).get() 116 ); 117 } catch (const jsi::JSIException &error) { 118 jni::throwNewJavaException( 119 JavaScriptEvaluateException::create( 120 error.what(), 121 "" 122 ).get() 123 ); 124 } 125 } 126 127 jni::local_ref<JavaScriptObject::javaobject> JavaScriptRuntime::global() { 128 auto global = std::make_shared<jsi::Object>(runtime->global()); 129 return JavaScriptObject::newObjectCxxArgs(weak_from_this(), global); 130 } 131 132 jni::local_ref<JavaScriptObject::javaobject> JavaScriptRuntime::createObject() { 133 auto newObject = std::make_shared<jsi::Object>(*runtime); 134 return JavaScriptObject::newObjectCxxArgs(weak_from_this(), newObject); 135 } 136 137 void JavaScriptRuntime::drainJSEventLoop() { 138 while (!runtime->drainMicrotasks()) {} 139 } 140 } // namespace expo 141