Permalink
Browse files

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

Reviewed By: javache

Differential Revision: D4720386

fbshipit-source-id: cd8b6137aaff2d907adf089060bf7d356cd2f437
  • Loading branch information...
mhorowitz authored and facebook-github-bot committed Mar 20, 2017
1 parent 9344f3a commit 6dc3a83e88ed120decbeaed8e4e62dc2bb7107a3
Showing with 63 additions and 30 deletions.
  1. +54 −30 ReactCommon/cxxreact/JSCExecutor.cpp
  2. +2 −0 ReactCommon/cxxreact/JSCExecutor.h
  3. +7 −0 ReactCommon/jschelpers/Value.h
@@ -361,7 +361,6 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> scrip
evaluateSourceCode(m_context, bcSourceCode, jsSourceURL);
bindBridge();
flush();
ReactMarker::logMarker("CREATE_REACT_CONTEXT_END");
@@ -412,7 +411,6 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> scrip
evaluateScript(m_context, jsScript, jsSourceURL);
}
bindBridge();
flush();
ReactMarker::logMarker("CREATE_REACT_CONTEXT_END");
@@ -428,24 +426,32 @@ void JSCExecutor::setJSModulesUnbundle(std::unique_ptr<JSModulesUnbundle> unbund
void JSCExecutor::bindBridge() throw(JSException) {
SystraceSection s("JSCExecutor::bindBridge");
if (!m_delegate || !m_delegate->getModuleRegistry()) {
return;
}
auto global = Object::getGlobalObject(m_context);
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
if (batchedBridgeValue.isUndefined()) {
throwJSExecutionException("Could not get BatchedBridge, make sure your bundle is packaged correctly");
}
std::call_once(m_bindFlag, [this] {
auto global = Object::getGlobalObject(m_context);
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
if (batchedBridgeValue.isUndefined()) {
auto requireBatchedBridge = global.getProperty("__fbRequireBatchedBridge");
if (!requireBatchedBridge.isUndefined()) {
batchedBridgeValue = requireBatchedBridge.asObject().callAsFunction({});
}
if (batchedBridgeValue.isUndefined()) {
throwJSExecutionException("Could not get BatchedBridge, make sure your bundle is packaged correctly");
}
}
auto batchedBridge = batchedBridgeValue.asObject();
m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject();
m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();
m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject();
auto batchedBridge = batchedBridgeValue.asObject();
m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject();
m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();
m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject();
});
}
void JSCExecutor::callNativeModules(Value&& value) {
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 {
auto calls = value.toJSONString();
m_delegate->callNativeModules(*this, folly::parseJson(calls), true);
@@ -462,17 +468,31 @@ void JSCExecutor::callNativeModules(Value&& value) {
void JSCExecutor::flush() {
SystraceSection s("JSCExecutor::flush");
if (!m_delegate) {
// do nothing
} 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";
if (m_flushedQueueJS) {
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 = [&] {
try {
// See flush()
CHECK(m_callFunctionReturnFlushedQueueJS)
<< "Attempting to call native methods without loading BatchedBridge.js";
if (!m_callFunctionReturnResultAndFlushedQueueJS) {
bindBridge();
}
return m_callFunctionReturnFlushedQueueJS->callAsFunction({
Value(m_context, String::createExpectingAscii(m_context, moduleId)),
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");
auto result = [&] {
try {
if (!m_invokeCallbackAndReturnFlushedQueueJS) {
bindBridge();
}
return m_invokeCallbackAndReturnFlushedQueueJS->callAsFunction({
Value::makeNumber(m_context, callbackId),
Value::fromDynamic(m_context, std::move(arguments))
@@ -521,8 +544,9 @@ Value JSCExecutor::callFunctionSyncWithValue(
const std::string& module, const std::string& method, Value args) {
SystraceSection s("JSCExecutor::callFunction");
// See flush()
CHECK(m_callFunctionReturnResultAndFlushedQueueJS);
if (!m_callFunctionReturnResultAndFlushedQueueJS) {
bindBridge();
}
Object result = m_callFunctionReturnResultAndFlushedQueueJS->callAsFunction({
Value(m_context, String::createExpectingAscii(m_context, module)),
Value(m_context, String::createExpectingAscii(m_context, method)),
@@ -4,6 +4,7 @@
#include <cstdint>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <cxxreact/Executor.h>
@@ -112,6 +113,7 @@ class RN_EXPORT JSCExecutor : public JSExecutor {
std::unique_ptr<JSModulesUnbundle> m_unbundle;
JSCNativeModules m_nativeModules;
folly::dynamic m_jscConfig;
std::once_flag m_bindFlag;
folly::Optional<Object> m_invokeCallbackAndReturnFlushedQueueJS;
folly::Optional<Object> m_callFunctionReturnFlushedQueueJS;
@@ -237,6 +237,13 @@ class Value : public noncopyable {
Value(JSContextRef context, JSStringRef 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 {
return m_value;
}

0 comments on commit 6dc3a83

Please sign in to comment.