Don't load native module support as part of the initial CS bundle

Reviewed By: javache

Differential Revision: D4720386

fbshipit-source-id: cd8b6137aaff2d907adf089060bf7d356cd2f437
This commit is contained in:
Marc Horowitz 2017-03-20 13:03:04 -07:00 committed by Facebook Github Bot
parent 9344f3a95b
commit 6dc3a83e88
3 changed files with 63 additions and 30 deletions

View File

@ -361,7 +361,6 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> scrip
evaluateSourceCode(m_context, bcSourceCode, jsSourceURL); evaluateSourceCode(m_context, bcSourceCode, jsSourceURL);
bindBridge();
flush(); flush();
ReactMarker::logMarker("CREATE_REACT_CONTEXT_END"); ReactMarker::logMarker("CREATE_REACT_CONTEXT_END");
@ -412,7 +411,6 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> scrip
evaluateScript(m_context, jsScript, jsSourceURL); evaluateScript(m_context, jsScript, jsSourceURL);
} }
bindBridge();
flush(); flush();
ReactMarker::logMarker("CREATE_REACT_CONTEXT_END"); ReactMarker::logMarker("CREATE_REACT_CONTEXT_END");
@ -428,24 +426,32 @@ void JSCExecutor::setJSModulesUnbundle(std::unique_ptr<JSModulesUnbundle> unbund
void JSCExecutor::bindBridge() throw(JSException) { void JSCExecutor::bindBridge() throw(JSException) {
SystraceSection s("JSCExecutor::bindBridge"); SystraceSection s("JSCExecutor::bindBridge");
if (!m_delegate || !m_delegate->getModuleRegistry()) { std::call_once(m_bindFlag, [this] {
return; auto global = Object::getGlobalObject(m_context);
} auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
auto global = Object::getGlobalObject(m_context); if (batchedBridgeValue.isUndefined()) {
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge"); auto requireBatchedBridge = global.getProperty("__fbRequireBatchedBridge");
if (batchedBridgeValue.isUndefined()) { if (!requireBatchedBridge.isUndefined()) {
throwJSExecutionException("Could not get BatchedBridge, make sure your bundle is packaged correctly"); batchedBridgeValue = requireBatchedBridge.asObject().callAsFunction({});
} }
if (batchedBridgeValue.isUndefined()) {
throwJSExecutionException("Could not get BatchedBridge, make sure your bundle is packaged correctly");
}
}
auto batchedBridge = batchedBridgeValue.asObject(); auto batchedBridge = batchedBridgeValue.asObject();
m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject(); m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject(); m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject();
m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject(); m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();
m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject(); m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject();
});
} }
void JSCExecutor::callNativeModules(Value&& value) { void JSCExecutor::callNativeModules(Value&& value) {
SystraceSection s("JSCExecutor::callNativeModules"); SystraceSection s("JSCExecutor::callNativeModules");
// If this fails, you need to pass a fully functional delegate with a
// module registry to the factory/ctor.
CHECK(m_delegate) << "Attempting to use native modules without a delegate";
try { try {
auto calls = value.toJSONString(); auto calls = value.toJSONString();
m_delegate->callNativeModules(*this, folly::parseJson(calls), true); m_delegate->callNativeModules(*this, folly::parseJson(calls), true);
@ -462,17 +468,31 @@ void JSCExecutor::callNativeModules(Value&& value) {
void JSCExecutor::flush() { void JSCExecutor::flush() {
SystraceSection s("JSCExecutor::flush"); SystraceSection s("JSCExecutor::flush");
if (!m_delegate) {
// do nothing if (m_flushedQueueJS) {
} else if (!m_delegate->getModuleRegistry()) {
callNativeModules(Value::makeNull(m_context));
} else {
// If this is failing, chances are you have provided a delegate with a
// module registry, but haven't loaded the JS which enables native function
// queueing. Add BatchedBridge.js to your bundle, pass a nullptr delegate,
// or make delegate->getModuleRegistry() return nullptr.
CHECK(m_flushedQueueJS) << "Attempting to use native methods without loading BatchedBridge.js";
callNativeModules(m_flushedQueueJS->callAsFunction({})); callNativeModules(m_flushedQueueJS->callAsFunction({}));
return;
}
// When a native module is called from JS, BatchedBridge.enqueueNativeCall()
// is invoked. For that to work, require('BatchedBridge') has to be called,
// and when that happens, __fbBatchedBridge is set as a side effect.
auto global = Object::getGlobalObject(m_context);
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
// So here, if __fbBatchedBridge doesn't exist, then we know no native calls
// have happened, and we were able to determine this without forcing
// BatchedBridge to be loaded as a side effect.
if (!batchedBridgeValue.isUndefined()) {
// If calls were made, we bind to the JS bridge methods, and use them to
// get the pending queue of native calls.
bindBridge();
callNativeModules(m_flushedQueueJS->callAsFunction({}));
} else if (m_delegate) {
// If we have a delegate, we need to call it; we pass a null list to
// callNativeModules, since we know there are no native calls, without
// calling into JS again. If no calls were made and there's no delegate,
// nothing happens, which is correct.
callNativeModules(Value::makeNull(m_context));
} }
} }
@ -483,9 +503,9 @@ void JSCExecutor::callFunction(const std::string& moduleId, const std::string& m
auto result = [&] { auto result = [&] {
try { try {
// See flush() if (!m_callFunctionReturnResultAndFlushedQueueJS) {
CHECK(m_callFunctionReturnFlushedQueueJS) bindBridge();
<< "Attempting to call native methods without loading BatchedBridge.js"; }
return m_callFunctionReturnFlushedQueueJS->callAsFunction({ return m_callFunctionReturnFlushedQueueJS->callAsFunction({
Value(m_context, String::createExpectingAscii(m_context, moduleId)), Value(m_context, String::createExpectingAscii(m_context, moduleId)),
Value(m_context, String::createExpectingAscii(m_context, methodId)), Value(m_context, String::createExpectingAscii(m_context, methodId)),
@ -504,6 +524,9 @@ void JSCExecutor::invokeCallback(const double callbackId, const folly::dynamic&
SystraceSection s("JSCExecutor::invokeCallback"); SystraceSection s("JSCExecutor::invokeCallback");
auto result = [&] { auto result = [&] {
try { try {
if (!m_invokeCallbackAndReturnFlushedQueueJS) {
bindBridge();
}
return m_invokeCallbackAndReturnFlushedQueueJS->callAsFunction({ return m_invokeCallbackAndReturnFlushedQueueJS->callAsFunction({
Value::makeNumber(m_context, callbackId), Value::makeNumber(m_context, callbackId),
Value::fromDynamic(m_context, std::move(arguments)) Value::fromDynamic(m_context, std::move(arguments))
@ -521,8 +544,9 @@ Value JSCExecutor::callFunctionSyncWithValue(
const std::string& module, const std::string& method, Value args) { const std::string& module, const std::string& method, Value args) {
SystraceSection s("JSCExecutor::callFunction"); SystraceSection s("JSCExecutor::callFunction");
// See flush() if (!m_callFunctionReturnResultAndFlushedQueueJS) {
CHECK(m_callFunctionReturnResultAndFlushedQueueJS); bindBridge();
}
Object result = m_callFunctionReturnResultAndFlushedQueueJS->callAsFunction({ Object result = m_callFunctionReturnResultAndFlushedQueueJS->callAsFunction({
Value(m_context, String::createExpectingAscii(m_context, module)), Value(m_context, String::createExpectingAscii(m_context, module)),
Value(m_context, String::createExpectingAscii(m_context, method)), Value(m_context, String::createExpectingAscii(m_context, method)),

View File

@ -4,6 +4,7 @@
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <mutex>
#include <unordered_map> #include <unordered_map>
#include <cxxreact/Executor.h> #include <cxxreact/Executor.h>
@ -112,6 +113,7 @@ private:
std::unique_ptr<JSModulesUnbundle> m_unbundle; std::unique_ptr<JSModulesUnbundle> m_unbundle;
JSCNativeModules m_nativeModules; JSCNativeModules m_nativeModules;
folly::dynamic m_jscConfig; folly::dynamic m_jscConfig;
std::once_flag m_bindFlag;
folly::Optional<Object> m_invokeCallbackAndReturnFlushedQueueJS; folly::Optional<Object> m_invokeCallbackAndReturnFlushedQueueJS;
folly::Optional<Object> m_callFunctionReturnFlushedQueueJS; folly::Optional<Object> m_callFunctionReturnFlushedQueueJS;

View File

@ -237,6 +237,13 @@ public:
Value(JSContextRef context, JSStringRef value); Value(JSContextRef context, JSStringRef value);
Value(Value&&); Value(Value&&);
Value& operator=(Value&& other) {
m_context = other.m_context;
m_value = other.m_value;
other.m_value = NULL;
return *this;
};
operator JSValueRef() const { operator JSValueRef() const {
return m_value; return m_value;
} }