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 // By default "global" property isn't set in the Hermes. 41 runtime->global().setProperty( 42 *runtime, 43 jsi::PropNameID::forUtf8(*runtime, "global"), 44 runtime->global() 45 ); 46 47 // This version of the Hermes uses a Promise implementation that is provided by the RN. 48 // The `setImmediate` function isn't defined, but is required by the Promise implementation. 49 // That's why we inject it here. 50 auto setImmediatePropName = jsi::PropNameID::forUtf8(*runtime, "setImmediate"); 51 runtime->global().setProperty( 52 *runtime, 53 setImmediatePropName, 54 jsi::Function::createFromHostFunction( 55 *runtime, 56 setImmediatePropName, 57 1, 58 [](jsi::Runtime &rt, 59 const jsi::Value &thisVal, 60 const jsi::Value *args, 61 size_t count) { 62 args[0].asObject(rt).asFunction(rt).call(rt); 63 return jsi::Value::undefined(); 64 } 65 ) 66 ); 67 #else 68 runtime = facebook::jsc::makeJSCRuntime(); 69 #endif 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() { 84 return runtime.get(); 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 std::shared_ptr<jsi::Value> result; 91 try { 92 result = std::make_shared<jsi::Value>( 93 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 return JavaScriptValue::newObjectCxxArgs(weak_from_this(), result); 112 } 113 114 jni::local_ref<JavaScriptObject::javaobject> JavaScriptRuntime::global() { 115 auto global = std::make_shared<jsi::Object>(runtime->global()); 116 return JavaScriptObject::newObjectCxxArgs(weak_from_this(), global); 117 } 118 119 jni::local_ref<JavaScriptObject::javaobject> JavaScriptRuntime::createObject() { 120 auto newObject = std::make_shared<jsi::Object>(*runtime); 121 return JavaScriptObject::newObjectCxxArgs(weak_from_this(), newObject); 122 } 123 124 void JavaScriptRuntime::drainJSEventLoop() { 125 while (!runtime->drainMicrotasks()) {} 126 } 127 } // namespace expo 128