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 72 JavaScriptRuntime::JavaScriptRuntime( 73 jsi::Runtime *runtime, 74 std::shared_ptr<react::CallInvoker> jsInvoker, 75 std::shared_ptr<react::CallInvoker> nativeInvoker 76 ) : jsInvoker(std::move(jsInvoker)), nativeInvoker(std::move(nativeInvoker)) { 77 // Creating a shared pointer that points to the runtime but doesn't own it, thus doesn't release it. 78 // In this code flow, the runtime should be owned by something else like the CatalystInstance. 79 // See explanation for constructor (8): https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr 80 this->runtime = std::shared_ptr<jsi::Runtime>(std::shared_ptr<jsi::Runtime>(), runtime); 81 } 82 83 jsi::Runtime &JavaScriptRuntime::get() const { 84 return *runtime; 85 } 86 87 jni::local_ref<JavaScriptValue::javaobject> 88 JavaScriptRuntime::evaluateScript(const std::string &script) { 89 auto scriptBuffer = std::make_shared<jsi::StringBuffer>(script); 90 try { 91 return JavaScriptValue::newObjectCxxArgs( 92 weak_from_this(), 93 std::make_shared<jsi::Value>(runtime->evaluateJavaScript(scriptBuffer, "<<evaluated>>")) 94 ); 95 } catch (const jsi::JSError &error) { 96 jni::throwNewJavaException( 97 JavaScriptEvaluateException::create( 98 error.getMessage(), 99 error.getStack() 100 ).get() 101 ); 102 } catch (const jsi::JSIException &error) { 103 jni::throwNewJavaException( 104 JavaScriptEvaluateException::create( 105 error.what(), 106 "" 107 ).get() 108 ); 109 } 110 } 111 112 jni::local_ref<JavaScriptObject::javaobject> JavaScriptRuntime::global() { 113 auto global = std::make_shared<jsi::Object>(runtime->global()); 114 return JavaScriptObject::newObjectCxxArgs(weak_from_this(), global); 115 } 116 117 jni::local_ref<JavaScriptObject::javaobject> JavaScriptRuntime::createObject() { 118 auto newObject = std::make_shared<jsi::Object>(*runtime); 119 return JavaScriptObject::newObjectCxxArgs(weak_from_this(), newObject); 120 } 121 122 void JavaScriptRuntime::drainJSEventLoop() { 123 while (!runtime->drainMicrotasks()) {} 124 } 125 } // namespace expo 126