@@ -361,7 +361,6 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> scrip
361
361
362
362
evaluateSourceCode (m_context, bcSourceCode, jsSourceURL);
363
363
364
- bindBridge ();
365
364
flush ();
366
365
367
366
ReactMarker::logMarker (" CREATE_REACT_CONTEXT_END" );
@@ -412,7 +411,6 @@ void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> scrip
412
411
evaluateScript (m_context, jsScript, jsSourceURL);
413
412
}
414
413
415
- bindBridge ();
416
414
flush ();
417
415
418
416
ReactMarker::logMarker (" CREATE_REACT_CONTEXT_END" );
@@ -428,24 +426,32 @@ void JSCExecutor::setJSModulesUnbundle(std::unique_ptr<JSModulesUnbundle> unbund
428
426
429
427
void JSCExecutor::bindBridge () throw(JSException) {
430
428
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
+ }
439
441
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
+ });
445
448
}
446
449
447
450
void JSCExecutor::callNativeModules (Value&& value) {
448
451
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" ;
449
455
try {
450
456
auto calls = value.toJSONString ();
451
457
m_delegate->callNativeModules (*this , folly::parseJson (calls), true );
@@ -462,17 +468,31 @@ void JSCExecutor::callNativeModules(Value&& value) {
462
468
463
469
void JSCExecutor::flush () {
464
470
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) {
475
473
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));
476
496
}
477
497
}
478
498
@@ -483,9 +503,9 @@ void JSCExecutor::callFunction(const std::string& moduleId, const std::string& m
483
503
484
504
auto result = [&] {
485
505
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
+ }
489
509
return m_callFunctionReturnFlushedQueueJS->callAsFunction ({
490
510
Value (m_context, String::createExpectingAscii (m_context, moduleId)),
491
511
Value (m_context, String::createExpectingAscii (m_context, methodId)),
@@ -504,6 +524,9 @@ void JSCExecutor::invokeCallback(const double callbackId, const folly::dynamic&
504
524
SystraceSection s (" JSCExecutor::invokeCallback" );
505
525
auto result = [&] {
506
526
try {
527
+ if (!m_invokeCallbackAndReturnFlushedQueueJS) {
528
+ bindBridge ();
529
+ }
507
530
return m_invokeCallbackAndReturnFlushedQueueJS->callAsFunction ({
508
531
Value::makeNumber (m_context, callbackId),
509
532
Value::fromDynamic (m_context, std::move (arguments))
@@ -521,8 +544,9 @@ Value JSCExecutor::callFunctionSyncWithValue(
521
544
const std::string& module, const std::string& method, Value args) {
522
545
SystraceSection s (" JSCExecutor::callFunction" );
523
546
524
- // See flush()
525
- CHECK (m_callFunctionReturnResultAndFlushedQueueJS);
547
+ if (!m_callFunctionReturnResultAndFlushedQueueJS) {
548
+ bindBridge ();
549
+ }
526
550
Object result = m_callFunctionReturnResultAndFlushedQueueJS->callAsFunction ({
527
551
Value (m_context, String::createExpectingAscii (m_context, module)),
528
552
Value (m_context, String::createExpectingAscii (m_context, method)),
0 commit comments