Skip to content

Commit 6dc3a83

Browse files
mhorowitzfacebook-github-bot
authored andcommitted
Don't load native module support as part of the initial CS bundle
Reviewed By: javache Differential Revision: D4720386 fbshipit-source-id: cd8b6137aaff2d907adf089060bf7d356cd2f437
1 parent 9344f3a commit 6dc3a83

File tree

3 files changed

+63
-30
lines changed

3 files changed

+63
-30
lines changed

ReactCommon/cxxreact/JSCExecutor.cpp

+54-30
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,6 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> scrip
361361

362362
evaluateSourceCode(m_context, bcSourceCode, jsSourceURL);
363363

364-
bindBridge();
365364
flush();
366365

367366
ReactMarker::logMarker("CREATE_REACT_CONTEXT_END");
@@ -412,7 +411,6 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> scrip
412411
evaluateScript(m_context, jsScript, jsSourceURL);
413412
}
414413

415-
bindBridge();
416414
flush();
417415

418416
ReactMarker::logMarker("CREATE_REACT_CONTEXT_END");
@@ -428,24 +426,32 @@ void JSCExecutor::setJSModulesUnbundle(std::unique_ptr<JSModulesUnbundle> unbund
428426

429427
void JSCExecutor::bindBridge() throw(JSException) {
430428
SystraceSection s("JSCExecutor::bindBridge");
431-
if (!m_delegate || !m_delegate->getModuleRegistry()) {
432-
return;
433-
}
434-
auto global = Object::getGlobalObject(m_context);
435-
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
436-
if (batchedBridgeValue.isUndefined()) {
437-
throwJSExecutionException("Could not get BatchedBridge, make sure your bundle is packaged correctly");
438-
}
429+
std::call_once(m_bindFlag, [this] {
430+
auto global = Object::getGlobalObject(m_context);
431+
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
432+
if (batchedBridgeValue.isUndefined()) {
433+
auto requireBatchedBridge = global.getProperty("__fbRequireBatchedBridge");
434+
if (!requireBatchedBridge.isUndefined()) {
435+
batchedBridgeValue = requireBatchedBridge.asObject().callAsFunction({});
436+
}
437+
if (batchedBridgeValue.isUndefined()) {
438+
throwJSExecutionException("Could not get BatchedBridge, make sure your bundle is packaged correctly");
439+
}
440+
}
439441

440-
auto batchedBridge = batchedBridgeValue.asObject();
441-
m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
442-
m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject();
443-
m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();
444-
m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject();
442+
auto batchedBridge = batchedBridgeValue.asObject();
443+
m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
444+
m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject();
445+
m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();
446+
m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject();
447+
});
445448
}
446449

447450
void JSCExecutor::callNativeModules(Value&& value) {
448451
SystraceSection s("JSCExecutor::callNativeModules");
452+
// If this fails, you need to pass a fully functional delegate with a
453+
// module registry to the factory/ctor.
454+
CHECK(m_delegate) << "Attempting to use native modules without a delegate";
449455
try {
450456
auto calls = value.toJSONString();
451457
m_delegate->callNativeModules(*this, folly::parseJson(calls), true);
@@ -462,17 +468,31 @@ void JSCExecutor::callNativeModules(Value&& value) {
462468

463469
void JSCExecutor::flush() {
464470
SystraceSection s("JSCExecutor::flush");
465-
if (!m_delegate) {
466-
// do nothing
467-
} else if (!m_delegate->getModuleRegistry()) {
468-
callNativeModules(Value::makeNull(m_context));
469-
} else {
470-
// If this is failing, chances are you have provided a delegate with a
471-
// module registry, but haven't loaded the JS which enables native function
472-
// queueing. Add BatchedBridge.js to your bundle, pass a nullptr delegate,
473-
// or make delegate->getModuleRegistry() return nullptr.
474-
CHECK(m_flushedQueueJS) << "Attempting to use native methods without loading BatchedBridge.js";
471+
472+
if (m_flushedQueueJS) {
475473
callNativeModules(m_flushedQueueJS->callAsFunction({}));
474+
return;
475+
}
476+
477+
// When a native module is called from JS, BatchedBridge.enqueueNativeCall()
478+
// is invoked. For that to work, require('BatchedBridge') has to be called,
479+
// and when that happens, __fbBatchedBridge is set as a side effect.
480+
auto global = Object::getGlobalObject(m_context);
481+
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
482+
// So here, if __fbBatchedBridge doesn't exist, then we know no native calls
483+
// have happened, and we were able to determine this without forcing
484+
// BatchedBridge to be loaded as a side effect.
485+
if (!batchedBridgeValue.isUndefined()) {
486+
// If calls were made, we bind to the JS bridge methods, and use them to
487+
// get the pending queue of native calls.
488+
bindBridge();
489+
callNativeModules(m_flushedQueueJS->callAsFunction({}));
490+
} else if (m_delegate) {
491+
// If we have a delegate, we need to call it; we pass a null list to
492+
// callNativeModules, since we know there are no native calls, without
493+
// calling into JS again. If no calls were made and there's no delegate,
494+
// nothing happens, which is correct.
495+
callNativeModules(Value::makeNull(m_context));
476496
}
477497
}
478498

@@ -483,9 +503,9 @@ void JSCExecutor::callFunction(const std::string& moduleId, const std::string& m
483503

484504
auto result = [&] {
485505
try {
486-
// See flush()
487-
CHECK(m_callFunctionReturnFlushedQueueJS)
488-
<< "Attempting to call native methods without loading BatchedBridge.js";
506+
if (!m_callFunctionReturnResultAndFlushedQueueJS) {
507+
bindBridge();
508+
}
489509
return m_callFunctionReturnFlushedQueueJS->callAsFunction({
490510
Value(m_context, String::createExpectingAscii(m_context, moduleId)),
491511
Value(m_context, String::createExpectingAscii(m_context, methodId)),
@@ -504,6 +524,9 @@ void JSCExecutor::invokeCallback(const double callbackId, const folly::dynamic&
504524
SystraceSection s("JSCExecutor::invokeCallback");
505525
auto result = [&] {
506526
try {
527+
if (!m_invokeCallbackAndReturnFlushedQueueJS) {
528+
bindBridge();
529+
}
507530
return m_invokeCallbackAndReturnFlushedQueueJS->callAsFunction({
508531
Value::makeNumber(m_context, callbackId),
509532
Value::fromDynamic(m_context, std::move(arguments))
@@ -521,8 +544,9 @@ Value JSCExecutor::callFunctionSyncWithValue(
521544
const std::string& module, const std::string& method, Value args) {
522545
SystraceSection s("JSCExecutor::callFunction");
523546

524-
// See flush()
525-
CHECK(m_callFunctionReturnResultAndFlushedQueueJS);
547+
if (!m_callFunctionReturnResultAndFlushedQueueJS) {
548+
bindBridge();
549+
}
526550
Object result = m_callFunctionReturnResultAndFlushedQueueJS->callAsFunction({
527551
Value(m_context, String::createExpectingAscii(m_context, module)),
528552
Value(m_context, String::createExpectingAscii(m_context, method)),

ReactCommon/cxxreact/JSCExecutor.h

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <cstdint>
66
#include <memory>
7+
#include <mutex>
78
#include <unordered_map>
89

910
#include <cxxreact/Executor.h>
@@ -112,6 +113,7 @@ class RN_EXPORT JSCExecutor : public JSExecutor {
112113
std::unique_ptr<JSModulesUnbundle> m_unbundle;
113114
JSCNativeModules m_nativeModules;
114115
folly::dynamic m_jscConfig;
116+
std::once_flag m_bindFlag;
115117

116118
folly::Optional<Object> m_invokeCallbackAndReturnFlushedQueueJS;
117119
folly::Optional<Object> m_callFunctionReturnFlushedQueueJS;

ReactCommon/jschelpers/Value.h

+7
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,13 @@ class Value : public noncopyable {
237237
Value(JSContextRef context, JSStringRef value);
238238
Value(Value&&);
239239

240+
Value& operator=(Value&& other) {
241+
m_context = other.m_context;
242+
m_value = other.m_value;
243+
other.m_value = NULL;
244+
return *this;
245+
};
246+
240247
operator JSValueRef() const {
241248
return m_value;
242249
}

0 commit comments

Comments
 (0)