2015-02-20 07:10:52 +03:00
/ * *
2018-09-12 01:27:47 +03:00
* Copyright ( c ) Facebook , Inc . and its affiliates .
2015-03-23 23:35:08 +03:00
*
2018-02-17 05:24:55 +03:00
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree .
2015-02-20 07:10:52 +03:00
*
2020-07-22 19:43:45 +03:00
* @ flow strict
2017-09-28 23:11:08 +03:00
* @ format
2015-02-20 07:10:52 +03:00
* /
[ReactNative] Refactor BatchedBridge and MessageQueue
Summary:
@public
The current implementation of `MessageQueue` is huge, over-complicated and spread
across `MethodQueue`, `MethodQueueMixin`, `BatchedBridge` and `BatchedBridgeFactory`
Refactored in a simpler way, were it's just a `MessageQueue` class and `BatchedBridge`
is only an instance of it.
Test Plan:
I had to make some updates to the tests, but no real update to the native side.
There's also tests covering the `remoteAsync` methods, and more integration tests for UIExplorer.
Verified whats being used by Android, and it should be safe, also tests Android tests have been pretty reliable.
Manually testing: Create a big hierarchy, like `<ListView>` example. Use the `TimerMixin` example to generate multiple calls.
Test the failure callback on the `Geolocation` example.
All the calls go through this entry point, so it's hard to miss if it's broken.
2015-06-17 17:51:48 +03:00
'use strict' ;
2015-04-29 21:54:26 +03:00
2019-05-08 18:44:25 +03:00
const ErrorUtils = require ( '../vendor/core/ErrorUtils' ) ;
const Systrace = require ( '../Performance/Systrace' ) ;
2015-03-24 03:09:14 +03:00
2019-05-08 18:44:25 +03:00
const deepFreezeAndThrowOnMutationInDev = require ( '../Utilities/deepFreezeAndThrowOnMutationInDev' ) ;
2018-12-03 10:49:12 +03:00
const invariant = require ( 'invariant' ) ;
2020-03-03 04:25:24 +03:00
const stringifySafe = require ( '../Utilities/stringifySafe' ) . default ;
2019-09-23 20:12:45 +03:00
const warnOnce = require ( '../Utilities/warnOnce' ) ;
2015-03-24 03:09:14 +03:00
2016-09-23 21:12:51 +03:00
export type SpyData = {
type : number ,
module : ? string ,
2017-09-28 23:11:08 +03:00
method : string | number ,
2020-07-22 19:43:45 +03:00
args : mixed [ ] ,
2019-11-21 20:38:13 +03:00
...
2017-09-28 23:11:08 +03:00
} ;
2016-09-23 21:12:51 +03:00
const TO _JS = 0 ;
const TO _NATIVE = 1 ;
2016-06-15 17:50:20 +03:00
const MODULE _IDS = 0 ;
const METHOD _IDS = 1 ;
const PARAMS = 2 ;
const MIN _TIME _BETWEEN _FLUSHES _MS = 5 ;
2015-02-20 07:10:52 +03:00
2017-10-11 02:20:15 +03:00
// eslint-disable-next-line no-bitwise
2016-08-04 18:38:49 +03:00
const TRACE _TAG _REACT _APPS = 1 << 17 ;
2015-07-09 19:12:53 +03:00
2017-09-08 00:23:13 +03:00
const DEBUG _INFO _LIMIT = 32 ;
2016-08-27 04:44:07 +03:00
2016-05-06 14:13:30 +03:00
class MessageQueue {
2020-07-22 19:43:45 +03:00
_lazyCallableModules : { [ key : string ] : ( void ) => { ... } , ... } ;
_queue : [ number [ ] , number [ ] , mixed [ ] , number ] ;
_successCallbacks : Map < number , ? ( ... mixed [ ] ) => void > ;
_failureCallbacks : Map < number , ? ( ... mixed [ ] ) => void > ;
2016-09-23 21:12:51 +03:00
_callID : number ;
_lastFlush : number ;
_eventLoopStartTime : number ;
2018-06-16 03:00:53 +03:00
_immediatesCallback : ? ( ) => void ;
2016-09-23 21:12:51 +03:00
2019-11-21 20:38:13 +03:00
_debugInfo : { [ number ] : [ number , number ] , ... } ;
_remoteModuleTable : { [ number ] : string , ... } ;
_remoteMethodTable : { [ number ] : $ReadOnlyArray < string > , ... } ;
2016-09-23 21:12:51 +03:00
_ _spy : ? ( data : SpyData ) => void ;
2018-04-03 15:59:07 +03:00
constructor ( ) {
2017-06-06 17:03:10 +03:00
this . _lazyCallableModules = { } ;
2016-01-04 13:15:19 +03:00
this . _queue = [ [ ] , [ ] , [ ] , 0 ] ;
2019-07-18 00:05:43 +03:00
this . _successCallbacks = new Map ( ) ;
this . _failureCallbacks = new Map ( ) ;
2016-01-04 13:15:19 +03:00
this . _callID = 0 ;
2015-10-13 18:00:36 +03:00
this . _lastFlush = 0 ;
2018-09-21 20:59:19 +03:00
this . _eventLoopStartTime = Date . now ( ) ;
2018-06-16 03:00:53 +03:00
this . _immediatesCallback = null ;
[ReactNative] Refactor BatchedBridge and MessageQueue
Summary:
@public
The current implementation of `MessageQueue` is huge, over-complicated and spread
across `MethodQueue`, `MethodQueueMixin`, `BatchedBridge` and `BatchedBridgeFactory`
Refactored in a simpler way, were it's just a `MessageQueue` class and `BatchedBridge`
is only an instance of it.
Test Plan:
I had to make some updates to the tests, but no real update to the native side.
There's also tests covering the `remoteAsync` methods, and more integration tests for UIExplorer.
Verified whats being used by Android, and it should be safe, also tests Android tests have been pretty reliable.
Manually testing: Create a big hierarchy, like `<ListView>` example. Use the `TimerMixin` example to generate multiple calls.
Test the failure callback on the `Geolocation` example.
All the calls go through this entry point, so it's hard to miss if it's broken.
2015-06-17 17:51:48 +03:00
2016-06-15 17:50:20 +03:00
if ( _ _DEV _ _ ) {
this . _debugInfo = { } ;
this . _remoteModuleTable = { } ;
this . _remoteMethodTable = { } ;
}
2017-09-08 00:23:13 +03:00
2020-07-22 19:43:45 +03:00
// $FlowFixMe[cannot-write]
this . callFunctionReturnFlushedQueue = this . callFunctionReturnFlushedQueue . bind (
2017-09-28 23:11:08 +03:00
this ,
) ;
2020-07-22 19:43:45 +03:00
// $FlowFixMe[cannot-write]
this . flushedQueue = this . flushedQueue . bind ( this ) ;
// $FlowFixMe[cannot-write]
this . invokeCallbackAndReturnFlushedQueue = this . invokeCallbackAndReturnFlushedQueue . bind (
2017-09-28 23:11:08 +03:00
this ,
) ;
[ReactNative] Refactor BatchedBridge and MessageQueue
Summary:
@public
The current implementation of `MessageQueue` is huge, over-complicated and spread
across `MethodQueue`, `MethodQueueMixin`, `BatchedBridge` and `BatchedBridgeFactory`
Refactored in a simpler way, were it's just a `MessageQueue` class and `BatchedBridge`
is only an instance of it.
Test Plan:
I had to make some updates to the tests, but no real update to the native side.
There's also tests covering the `remoteAsync` methods, and more integration tests for UIExplorer.
Verified whats being used by Android, and it should be safe, also tests Android tests have been pretty reliable.
Manually testing: Create a big hierarchy, like `<ListView>` example. Use the `TimerMixin` example to generate multiple calls.
Test the failure callback on the `Geolocation` example.
All the calls go through this entry point, so it's hard to miss if it's broken.
2015-06-17 17:51:48 +03:00
}
2015-02-20 07:10:52 +03:00
/ * *
[ReactNative] Refactor BatchedBridge and MessageQueue
Summary:
@public
The current implementation of `MessageQueue` is huge, over-complicated and spread
across `MethodQueue`, `MethodQueueMixin`, `BatchedBridge` and `BatchedBridgeFactory`
Refactored in a simpler way, were it's just a `MessageQueue` class and `BatchedBridge`
is only an instance of it.
Test Plan:
I had to make some updates to the tests, but no real update to the native side.
There's also tests covering the `remoteAsync` methods, and more integration tests for UIExplorer.
Verified whats being used by Android, and it should be safe, also tests Android tests have been pretty reliable.
Manually testing: Create a big hierarchy, like `<ListView>` example. Use the `TimerMixin` example to generate multiple calls.
Test the failure callback on the `Geolocation` example.
All the calls go through this entry point, so it's hard to miss if it's broken.
2015-06-17 17:51:48 +03:00
* Public APIs
2015-02-20 07:10:52 +03:00
* /
2017-09-08 00:23:13 +03:00
2017-09-28 23:11:08 +03:00
static spy ( spyOrToggle : boolean | ( ( data : SpyData ) => void ) ) {
if ( spyOrToggle === true ) {
2020-03-25 07:35:58 +03:00
MessageQueue . prototype . _ _spy = info => {
2017-09-28 23:11:08 +03:00
console . log (
` ${ info . type === TO _JS ? 'N->JS' : 'JS->N' } : ` +
2020-07-22 19:43:45 +03:00
` ${ info . module != null ? info . module + '.' : '' } ${ info . method } ` +
2017-09-28 23:11:08 +03:00
` ( ${ JSON . stringify ( info . args ) } ) ` ,
) ;
2016-08-09 16:32:41 +03:00
} ;
2016-08-04 18:38:49 +03:00
} else if ( spyOrToggle === false ) {
MessageQueue . prototype . _ _spy = null ;
} else {
MessageQueue . prototype . _ _spy = spyOrToggle ;
}
}
2019-08-09 20:06:53 +03:00
callFunctionReturnFlushedQueue (
module : string ,
method : string ,
2020-07-22 19:43:45 +03:00
args : mixed [ ] ,
) : null | [ Array < number > , Array < number > , Array < mixed > , number ] {
2017-03-28 17:11:45 +03:00
this . _ _guard ( ( ) => {
2015-10-13 13:22:40 +03:00
this . _ _callFunction ( module , method , args ) ;
[ReactNative] Refactor BatchedBridge and MessageQueue
Summary:
@public
The current implementation of `MessageQueue` is huge, over-complicated and spread
across `MethodQueue`, `MethodQueueMixin`, `BatchedBridge` and `BatchedBridgeFactory`
Refactored in a simpler way, were it's just a `MessageQueue` class and `BatchedBridge`
is only an instance of it.
Test Plan:
I had to make some updates to the tests, but no real update to the native side.
There's also tests covering the `remoteAsync` methods, and more integration tests for UIExplorer.
Verified whats being used by Android, and it should be safe, also tests Android tests have been pretty reliable.
Manually testing: Create a big hierarchy, like `<ListView>` example. Use the `TimerMixin` example to generate multiple calls.
Test the failure callback on the `Geolocation` example.
All the calls go through this entry point, so it's hard to miss if it's broken.
2015-06-17 17:51:48 +03:00
} ) ;
2015-09-29 13:14:14 +03:00
[ReactNative] Refactor BatchedBridge and MessageQueue
Summary:
@public
The current implementation of `MessageQueue` is huge, over-complicated and spread
across `MethodQueue`, `MethodQueueMixin`, `BatchedBridge` and `BatchedBridgeFactory`
Refactored in a simpler way, were it's just a `MessageQueue` class and `BatchedBridge`
is only an instance of it.
Test Plan:
I had to make some updates to the tests, but no real update to the native side.
There's also tests covering the `remoteAsync` methods, and more integration tests for UIExplorer.
Verified whats being used by Android, and it should be safe, also tests Android tests have been pretty reliable.
Manually testing: Create a big hierarchy, like `<ListView>` example. Use the `TimerMixin` example to generate multiple calls.
Test the failure callback on the `Geolocation` example.
All the calls go through this entry point, so it's hard to miss if it's broken.
2015-06-17 17:51:48 +03:00
return this . flushedQueue ( ) ;
2017-09-08 00:23:13 +03:00
}
2015-02-20 07:10:52 +03:00
2019-08-09 20:06:53 +03:00
invokeCallbackAndReturnFlushedQueue (
cbID : number ,
2020-07-22 19:43:45 +03:00
args : mixed [ ] ,
) : null | [ Array < number > , Array < number > , Array < mixed > , number ] {
2017-03-28 17:11:45 +03:00
this . _ _guard ( ( ) => {
2015-10-13 13:22:40 +03:00
this . _ _invokeCallback ( cbID , args ) ;
} ) ;
[ReactNative] Refactor BatchedBridge and MessageQueue
Summary:
@public
The current implementation of `MessageQueue` is huge, over-complicated and spread
across `MethodQueue`, `MethodQueueMixin`, `BatchedBridge` and `BatchedBridgeFactory`
Refactored in a simpler way, were it's just a `MessageQueue` class and `BatchedBridge`
is only an instance of it.
Test Plan:
I had to make some updates to the tests, but no real update to the native side.
There's also tests covering the `remoteAsync` methods, and more integration tests for UIExplorer.
Verified whats being used by Android, and it should be safe, also tests Android tests have been pretty reliable.
Manually testing: Create a big hierarchy, like `<ListView>` example. Use the `TimerMixin` example to generate multiple calls.
Test the failure callback on the `Geolocation` example.
All the calls go through this entry point, so it's hard to miss if it's broken.
2015-06-17 17:51:48 +03:00
return this . flushedQueue ( ) ;
2017-09-08 00:23:13 +03:00
}
2015-02-20 07:10:52 +03:00
2020-07-22 19:43:45 +03:00
flushedQueue ( ) : null | [ Array < number > , Array < number > , Array < mixed > , number ] {
2017-06-02 18:16:04 +03:00
this . _ _guard ( ( ) => {
this . _ _callImmediates ( ) ;
} ) ;
2015-10-13 13:22:40 +03:00
2016-06-15 17:50:20 +03:00
const queue = this . _queue ;
2016-01-04 13:15:19 +03:00
this . _queue = [ [ ] , [ ] , [ ] , this . _callID ] ;
2015-10-13 13:22:40 +03:00
return queue [ 0 ] . length ? queue : null ;
2017-09-08 00:23:13 +03:00
}
2015-09-29 13:14:14 +03:00
2019-08-09 20:06:53 +03:00
getEventLoopRunningTime ( ) : number {
2018-09-21 20:59:19 +03:00
return Date . now ( ) - this . _eventLoopStartTime ;
Add yieldy, chained async task support to InteractionManager
Summary:
Default behavior should be unchanged.
If we queue up a bunch of expensive tasks during an interaction, the default
`InteractionManager` behavior would execute them all in one synchronous loop at
the end the JS event loop via one `setImmediate` call, blocking the JS thread
the entire time.
The `setDeadline` addition in this diff enables an option to only execute tasks
until the `eventLoopRunningTime` is hit (added to MessageQueue/BatchedBridge),
allowing the queue execution to be paused if an interaction starts in between
tasks, making the app more responsive.
Additionally, if a task ends up generating a bunch of additional tasks
asynchronously, the previous implementation would execute these new tasks after
already scheduled tasks. This is often fine, but I want it to fully resolve
async tasks and all their dependencies before making progress in the rest of the
queue, so I added support for `type PromiseTask = {gen: () => Promise}` to do
just this. It works by building a stack of queues each time a `PromiseTask` is
started, and pops them off the stack once they are resolved and the queues are
processed.
I also pulled all of the actual queue logic out of `InteractionManager` and into
a new `TaskQueue` class to isolate concerns a bit.
public
Reviewed By: josephsavona
Differential Revision: D2754311
fb-gh-sync-id: bfd6d0c54e6410cb261aa1d2c5024dd91a3959e6
2015-12-24 03:10:39 +03:00
}
2020-07-22 19:43:45 +03:00
registerCallableModule ( name : string , module : { ... } ) {
2017-06-06 17:03:10 +03:00
this . _lazyCallableModules [ name ] = ( ) => module ;
}
2021-03-23 20:22:57 +03:00
registerLazyCallableModule ( name : string , factory : void => interface { } ) {
let module : interface { } ;
let getValue : ? ( void ) => interface { } = factory ;
2017-06-06 17:03:10 +03:00
this . _lazyCallableModules [ name ] = ( ) => {
if ( getValue ) {
module = getValue ( ) ;
getValue = null ;
}
return module ;
} ;
}
2020-07-22 19:43:45 +03:00
getCallableModule ( name : string ) : { ... } | null {
2017-06-08 22:40:19 +03:00
const getValue = this . _lazyCallableModules [ name ] ;
return getValue ? getValue ( ) : null ;
2016-09-08 13:59:32 +03:00
}
2019-04-08 19:10:44 +03:00
callNativeSyncHook (
moduleID : number ,
methodID : number ,
2020-07-22 19:43:45 +03:00
params : mixed [ ] ,
onFail : ? ( ... mixed [ ] ) => void ,
onSucc : ? ( ... mixed [ ] ) => void ,
) : mixed {
2019-04-08 19:10:44 +03:00
if ( _ _DEV _ _ ) {
invariant (
global . nativeCallSyncHook ,
'Calling synchronous methods on native ' +
'modules is not supported in Chrome.\n\n Consider providing alternative ' +
'methods to expose this method in debug mode, e.g. by exposing constants ' +
'ahead-of-time.' ,
) ;
}
this . processCallbacks ( moduleID , methodID , params , onFail , onSucc ) ;
2019-10-09 15:46:34 +03:00
return global . nativeCallSyncHook ( moduleID , methodID , params ) ;
2019-04-08 19:10:44 +03:00
}
processCallbacks (
2017-09-28 23:11:08 +03:00
moduleID : number ,
methodID : number ,
2020-07-22 19:43:45 +03:00
params : mixed [ ] ,
onFail : ? ( ... mixed [ ] ) => void ,
onSucc : ? ( ... mixed [ ] ) => void ,
) : void {
[ReactNative] Refactor BatchedBridge and MessageQueue
Summary:
@public
The current implementation of `MessageQueue` is huge, over-complicated and spread
across `MethodQueue`, `MethodQueueMixin`, `BatchedBridge` and `BatchedBridgeFactory`
Refactored in a simpler way, were it's just a `MessageQueue` class and `BatchedBridge`
is only an instance of it.
Test Plan:
I had to make some updates to the tests, but no real update to the native side.
There's also tests covering the `remoteAsync` methods, and more integration tests for UIExplorer.
Verified whats being used by Android, and it should be safe, also tests Android tests have been pretty reliable.
Manually testing: Create a big hierarchy, like `<ListView>` example. Use the `TimerMixin` example to generate multiple calls.
Test the failure callback on the `Geolocation` example.
All the calls go through this entry point, so it's hard to miss if it's broken.
2015-06-17 17:51:48 +03:00
if ( onFail || onSucc ) {
2016-06-15 17:50:20 +03:00
if ( _ _DEV _ _ ) {
2017-04-28 13:47:19 +03:00
this . _debugInfo [ this . _callID ] = [ moduleID , methodID ] ;
if ( this . _callID > DEBUG _INFO _LIMIT ) {
delete this . _debugInfo [ this . _callID - DEBUG _INFO _LIMIT ] ;
2016-08-27 04:44:07 +03:00
}
2019-07-18 00:05:43 +03:00
if ( this . _successCallbacks . size > 500 ) {
const info = { } ;
this . _successCallbacks . forEach ( ( _ , callID ) => {
const debug = this . _debugInfo [ callID ] ;
const module = debug && this . _remoteModuleTable [ debug [ 0 ] ] ;
const method = debug && this . _remoteMethodTable [ debug [ 0 ] ] [ debug [ 1 ] ] ;
info [ callID ] = { module , method } ;
} ) ;
2019-09-23 20:12:45 +03:00
warnOnce (
'excessive-number-of-pending-callbacks' ,
2019-07-18 00:05:43 +03:00
` Please report: Excessive number of pending callbacks: ${
this . _successCallbacks . size
2019-09-23 20:12:45 +03:00
} . Some pending callbacks that might have leaked by never being called from native code : $ { stringifySafe (
info ,
) } ` ,
2019-07-18 00:05:43 +03:00
) ;
}
2016-06-15 17:50:20 +03:00
}
2017-04-28 13:47:19 +03:00
// Encode callIDs into pairs of callback identifiers by shifting left and using the rightmost bit
// to indicate fail (0) or success (1)
2017-10-11 02:20:15 +03:00
// eslint-disable-next-line no-bitwise
2017-04-28 13:47:19 +03:00
onFail && params . push ( this . _callID << 1 ) ;
2017-10-11 02:20:15 +03:00
// eslint-disable-next-line no-bitwise
2017-04-28 13:47:19 +03:00
onSucc && params . push ( ( this . _callID << 1 ) | 1 ) ;
2019-07-18 00:05:43 +03:00
this . _successCallbacks . set ( this . _callID , onSucc ) ;
this . _failureCallbacks . set ( this . _callID , onFail ) ;
2015-06-15 23:01:39 +03:00
}
2016-06-21 22:02:31 +03:00
if ( _ _DEV _ _ ) {
global . nativeTraceBeginAsyncFlow &&
2017-09-28 23:11:08 +03:00
global . nativeTraceBeginAsyncFlow (
TRACE _TAG _REACT _APPS ,
'native' ,
this . _callID ,
) ;
2016-06-21 22:02:31 +03:00
}
2016-01-04 13:15:19 +03:00
this . _callID ++ ;
2019-04-08 19:10:44 +03:00
}
enqueueNativeCall (
moduleID : number ,
methodID : number ,
2020-07-22 19:43:45 +03:00
params : mixed [ ] ,
onFail : ? ( ... mixed [ ] ) => void ,
onSucc : ? ( ... mixed [ ] ) => void ,
2019-04-08 19:10:44 +03:00
) {
this . processCallbacks ( moduleID , methodID , params , onFail , onSucc ) ;
2016-01-04 13:15:19 +03:00
2016-09-23 21:12:51 +03:00
this . _queue [ MODULE _IDS ] . push ( moduleID ) ;
this . _queue [ METHOD _IDS ] . push ( methodID ) ;
2016-09-08 13:59:32 +03:00
2016-09-19 14:43:09 +03:00
if ( _ _DEV _ _ ) {
2017-12-14 22:05:35 +03:00
// Validate that parameters passed over the bridge are
// folly-convertible. As a special case, if a prop value is a
// function it is permitted here, and special-cased in the
// conversion.
2020-03-25 07:35:58 +03:00
const isValidArgument = val => {
2020-07-22 19:43:45 +03:00
switch ( typeof val ) {
case 'undefined' :
case 'boolean' :
case 'string' :
return true ;
case 'number' :
return isFinite ( val ) ;
case 'object' :
if ( val == null ) {
return true ;
}
if ( Array . isArray ( val ) ) {
return val . every ( isValidArgument ) ;
}
for ( const k in val ) {
if ( typeof val [ k ] !== 'function' && ! isValidArgument ( val [ k ] ) ) {
return false ;
}
}
return true ;
case 'function' :
return false ;
default :
2017-12-14 22:05:35 +03:00
return false ;
}
} ;
2018-09-13 04:56:27 +03:00
// Replacement allows normally non-JSON-convertible values to be
// seen. There is ambiguity with string values, but in context,
// it should at least be a strong hint.
const replacer = ( key , val ) => {
const t = typeof val ;
if ( t === 'function' ) {
return '<<Function ' + val . name + '>>' ;
} else if ( t === 'number' && ! isFinite ( val ) ) {
return '<<' + val . toString ( ) + '>>' ;
} else {
return val ;
}
} ;
// Note that JSON.stringify
2017-12-14 22:05:35 +03:00
invariant (
isValidArgument ( params ) ,
'%s is not usable as a native method argument' ,
2018-09-13 04:56:27 +03:00
JSON . stringify ( params , replacer ) ,
2017-12-14 22:05:35 +03:00
) ;
2016-09-19 14:43:09 +03:00
// The params object should not be mutated after being queued
2020-07-22 19:43:45 +03:00
deepFreezeAndThrowOnMutationInDev ( params ) ;
2016-09-19 14:43:09 +03:00
}
this . _queue [ PARAMS ] . push ( params ) ;
2015-10-13 18:00:36 +03:00
2018-09-21 20:59:19 +03:00
const now = Date . now ( ) ;
2017-09-28 23:11:08 +03:00
if (
global . nativeFlushQueueImmediate &&
2018-05-10 20:45:36 +03:00
now - this . _lastFlush >= MIN _TIME _BETWEEN _FLUSHES _MS
2017-09-28 23:11:08 +03:00
) {
2018-05-11 05:06:39 +03:00
const queue = this . _queue ;
2016-01-04 13:15:19 +03:00
this . _queue = [ [ ] , [ ] , [ ] , this . _callID ] ;
2015-10-13 18:00:36 +03:00
this . _lastFlush = now ;
2017-03-31 10:46:14 +03:00
global . nativeFlushQueueImmediate ( queue ) ;
2015-10-13 18:00:36 +03:00
}
2015-12-30 10:36:34 +03:00
Systrace . counterEvent ( 'pending_js_to_native_queue' , this . _queue [ 0 ] . length ) ;
2017-09-08 00:23:13 +03:00
if ( _ _DEV _ _ && this . _ _spy && isFinite ( moduleID ) ) {
2017-09-28 23:11:08 +03:00
this . _ _spy ( {
type : TO _NATIVE ,
module : this . _remoteModuleTable [ moduleID ] ,
method : this . _remoteMethodTable [ moduleID ] [ methodID ] ,
args : params ,
} ) ;
2017-09-08 00:23:13 +03:00
} else if ( this . _ _spy ) {
2017-09-28 23:11:08 +03:00
this . _ _spy ( {
type : TO _NATIVE ,
module : moduleID + '' ,
method : methodID ,
args : params ,
} ) ;
2015-07-13 19:37:05 +03:00
}
[ReactNative] Refactor BatchedBridge and MessageQueue
Summary:
@public
The current implementation of `MessageQueue` is huge, over-complicated and spread
across `MethodQueue`, `MethodQueueMixin`, `BatchedBridge` and `BatchedBridgeFactory`
Refactored in a simpler way, were it's just a `MessageQueue` class and `BatchedBridge`
is only an instance of it.
Test Plan:
I had to make some updates to the tests, but no real update to the native side.
There's also tests covering the `remoteAsync` methods, and more integration tests for UIExplorer.
Verified whats being used by Android, and it should be safe, also tests Android tests have been pretty reliable.
Manually testing: Create a big hierarchy, like `<ListView>` example. Use the `TimerMixin` example to generate multiple calls.
Test the failure callback on the `Geolocation` example.
All the calls go through this entry point, so it's hard to miss if it's broken.
2015-06-17 17:51:48 +03:00
}
2015-02-20 07:10:52 +03:00
2019-06-19 19:09:57 +03:00
createDebugLookup (
moduleID : number ,
name : string ,
methods : ? $ReadOnlyArray < string > ,
) {
2016-09-23 21:12:54 +03:00
if ( _ _DEV _ _ ) {
this . _remoteModuleTable [ moduleID ] = name ;
2019-06-19 19:09:57 +03:00
this . _remoteMethodTable [ moduleID ] = methods || [ ] ;
2016-09-23 21:12:54 +03:00
}
}
2018-06-16 03:00:53 +03:00
// For JSTimers to register its callback. Otherwise a circular dependency
// between modules is introduced. Note that only one callback may be
// registered at a time.
setImmediatesCallback ( fn : ( ) => void ) {
this . _immediatesCallback = fn ;
}
2016-09-23 21:12:54 +03:00
/ * *
2017-03-28 17:11:45 +03:00
* Private methods
2016-09-23 21:12:54 +03:00
* /
2018-04-03 15:59:07 +03:00
_ _guard ( fn : ( ) => void ) {
if ( this . _ _shouldPauseOnThrow ( ) ) {
fn ( ) ;
} else {
try {
fn ( ) ;
} catch ( error ) {
ErrorUtils . reportFatalError ( error ) ;
}
}
2017-12-11 18:59:45 +03:00
}
2018-04-03 15:59:07 +03:00
// MessageQueue installs a global handler to catch all exceptions where JS users can register their own behavior
// This handler makes all exceptions to be propagated from inside MessageQueue rather than by the VM at their origin
// This makes stacktraces to be placed at MessageQueue rather than at where they were launched
// The parameter DebuggerInternal.shouldPauseOnThrow is used to check before catching all exceptions and
// can be configured by the VM or any Inspector
2019-08-09 20:06:53 +03:00
_ _shouldPauseOnThrow ( ) : boolean {
2018-04-03 15:59:07 +03:00
return (
2021-04-01 04:19:54 +03:00
// $FlowFixMe[cannot-resolve-name]
2018-05-21 21:08:25 +03:00
typeof DebuggerInternal !== 'undefined' &&
DebuggerInternal . shouldPauseOnThrow === true // eslint-disable-line no-undef
2018-04-03 15:59:07 +03:00
) ;
2017-03-28 17:11:45 +03:00
}
2016-09-23 21:12:54 +03:00
_ _callImmediates ( ) {
2017-06-22 19:46:28 +03:00
Systrace . beginEvent ( 'JSTimers.callImmediates()' ) ;
2018-06-16 03:00:53 +03:00
if ( this . _immediatesCallback != null ) {
this . _immediatesCallback ( ) ;
2017-06-22 19:46:28 +03:00
}
2016-09-23 21:12:54 +03:00
Systrace . endEvent ( ) ;
}
2020-07-22 19:43:45 +03:00
_ _callFunction ( module : string , method : string , args : mixed [ ] ) : void {
2018-09-21 20:59:19 +03:00
this . _lastFlush = Date . now ( ) ;
Add yieldy, chained async task support to InteractionManager
Summary:
Default behavior should be unchanged.
If we queue up a bunch of expensive tasks during an interaction, the default
`InteractionManager` behavior would execute them all in one synchronous loop at
the end the JS event loop via one `setImmediate` call, blocking the JS thread
the entire time.
The `setDeadline` addition in this diff enables an option to only execute tasks
until the `eventLoopRunningTime` is hit (added to MessageQueue/BatchedBridge),
allowing the queue execution to be paused if an interaction starts in between
tasks, making the app more responsive.
Additionally, if a task ends up generating a bunch of additional tasks
asynchronously, the previous implementation would execute these new tasks after
already scheduled tasks. This is often fine, but I want it to fully resolve
async tasks and all their dependencies before making progress in the rest of the
queue, so I added support for `type PromiseTask = {gen: () => Promise}` to do
just this. It works by building a stack of queues each time a `PromiseTask` is
started, and pops them off the stack once they are resolved and the queues are
processed.
I also pulled all of the actual queue logic out of `InteractionManager` and into
a new `TaskQueue` class to isolate concerns a bit.
public
Reviewed By: josephsavona
Differential Revision: D2754311
fb-gh-sync-id: bfd6d0c54e6410cb261aa1d2c5024dd91a3959e6
2015-12-24 03:10:39 +03:00
this . _eventLoopStartTime = this . _lastFlush ;
2018-04-30 19:52:34 +03:00
if ( _ _DEV _ _ || this . _ _spy ) {
Systrace . beginEvent ( ` ${ module } . ${ method } ( ${ stringifySafe ( args ) } ) ` ) ;
} else {
Systrace . beginEvent ( ` ${ module } . ${ method } (...) ` ) ;
}
2017-02-07 04:39:15 +03:00
if ( this . _ _spy ) {
2017-09-28 23:11:08 +03:00
this . _ _spy ( { type : TO _JS , module , method , args } ) ;
2015-07-09 19:12:53 +03:00
}
2017-07-03 17:44:38 +03:00
const moduleMethods = this . getCallableModule ( module ) ;
2015-12-11 10:15:38 +03:00
invariant (
! ! moduleMethods ,
2020-08-14 04:28:37 +03:00
` Module ${ module } is not a registered callable module (calling ${ method } ). A frequent cause of the error is that the application entry file path is incorrect.
This can also happen when the JS bundle is corrupt or there is an early initialization error when loading React Native . ` ,
2015-12-11 10:15:38 +03:00
) ;
2016-08-02 21:06:19 +03:00
invariant (
! ! moduleMethods [ method ] ,
2020-08-14 04:28:37 +03:00
` Method ${ method } does not exist on module ${ module } ` ,
2016-08-02 21:06:19 +03:00
) ;
2020-02-05 23:59:41 +03:00
moduleMethods [ method ] . apply ( moduleMethods , args ) ;
2015-12-11 14:49:15 +03:00
Systrace . endEvent ( ) ;
[ReactNative] Refactor BatchedBridge and MessageQueue
Summary:
@public
The current implementation of `MessageQueue` is huge, over-complicated and spread
across `MethodQueue`, `MethodQueueMixin`, `BatchedBridge` and `BatchedBridgeFactory`
Refactored in a simpler way, were it's just a `MessageQueue` class and `BatchedBridge`
is only an instance of it.
Test Plan:
I had to make some updates to the tests, but no real update to the native side.
There's also tests covering the `remoteAsync` methods, and more integration tests for UIExplorer.
Verified whats being used by Android, and it should be safe, also tests Android tests have been pretty reliable.
Manually testing: Create a big hierarchy, like `<ListView>` example. Use the `TimerMixin` example to generate multiple calls.
Test the failure callback on the `Geolocation` example.
All the calls go through this entry point, so it's hard to miss if it's broken.
2015-06-17 17:51:48 +03:00
}
2015-02-20 07:10:52 +03:00
2020-07-22 19:43:45 +03:00
_ _invokeCallback ( cbID : number , args : mixed [ ] ) {
2018-09-21 20:59:19 +03:00
this . _lastFlush = Date . now ( ) ;
Add yieldy, chained async task support to InteractionManager
Summary:
Default behavior should be unchanged.
If we queue up a bunch of expensive tasks during an interaction, the default
`InteractionManager` behavior would execute them all in one synchronous loop at
the end the JS event loop via one `setImmediate` call, blocking the JS thread
the entire time.
The `setDeadline` addition in this diff enables an option to only execute tasks
until the `eventLoopRunningTime` is hit (added to MessageQueue/BatchedBridge),
allowing the queue execution to be paused if an interaction starts in between
tasks, making the app more responsive.
Additionally, if a task ends up generating a bunch of additional tasks
asynchronously, the previous implementation would execute these new tasks after
already scheduled tasks. This is often fine, but I want it to fully resolve
async tasks and all their dependencies before making progress in the rest of the
queue, so I added support for `type PromiseTask = {gen: () => Promise}` to do
just this. It works by building a stack of queues each time a `PromiseTask` is
started, and pops them off the stack once they are resolved and the queues are
processed.
I also pulled all of the actual queue logic out of `InteractionManager` and into
a new `TaskQueue` class to isolate concerns a bit.
public
Reviewed By: josephsavona
Differential Revision: D2754311
fb-gh-sync-id: bfd6d0c54e6410cb261aa1d2c5024dd91a3959e6
2015-12-24 03:10:39 +03:00
this . _eventLoopStartTime = this . _lastFlush ;
2017-04-28 13:47:19 +03:00
// The rightmost bit of cbID indicates fail (0) or success (1), the other bits are the callID shifted left.
2017-10-11 02:20:15 +03:00
// eslint-disable-next-line no-bitwise
2017-04-28 13:47:19 +03:00
const callID = cbID >>> 1 ;
2017-10-11 02:20:15 +03:00
// eslint-disable-next-line no-bitwise
const isSuccess = cbID & 1 ;
const callback = isSuccess
2019-07-18 00:05:43 +03:00
? this . _successCallbacks . get ( callID )
: this . _failureCallbacks . get ( callID ) ;
2016-06-15 17:50:20 +03:00
if ( _ _DEV _ _ ) {
2017-04-28 13:47:19 +03:00
const debug = this . _debugInfo [ callID ] ;
2016-06-15 17:50:20 +03:00
const module = debug && this . _remoteModuleTable [ debug [ 0 ] ] ;
const method = debug && this . _remoteMethodTable [ debug [ 0 ] ] [ debug [ 1 ] ] ;
2019-04-08 19:10:44 +03:00
invariant (
callback ,
` No callback found with cbID ${ cbID } and callID ${ callID } for ` +
( method
? ` ${ module } . ${ method } - most likely the callback was already invoked `
: ` module ${ module || '<unknown>' } ` ) +
` . Args: ' ${ stringifySafe ( args ) } ' ` ,
) ;
2017-09-28 23:11:08 +03:00
const profileName = debug
? '<callback for ' + module + '.' + method + '>'
: cbID ;
2017-09-08 00:23:13 +03:00
if ( callback && this . _ _spy ) {
2017-09-28 23:11:08 +03:00
this . _ _spy ( { type : TO _JS , module : null , method : profileName , args } ) ;
2016-06-15 17:50:20 +03:00
}
Systrace . beginEvent (
2017-09-28 23:11:08 +03:00
` MessageQueue.invokeCallback( ${ profileName } , ${ stringifySafe ( args ) } ) ` ,
) ;
2015-02-20 07:10:52 +03:00
}
2016-06-15 17:50:20 +03:00
2017-04-28 13:47:19 +03:00
if ( ! callback ) {
return ;
}
2019-07-18 00:05:43 +03:00
this . _successCallbacks . delete ( callID ) ;
this . _failureCallbacks . delete ( callID ) ;
2017-10-11 02:20:15 +03:00
callback ( ... args ) ;
2016-06-15 17:50:20 +03:00
if ( _ _DEV _ _ ) {
Systrace . endEvent ( ) ;
}
[ReactNative] Refactor BatchedBridge and MessageQueue
Summary:
@public
The current implementation of `MessageQueue` is huge, over-complicated and spread
across `MethodQueue`, `MethodQueueMixin`, `BatchedBridge` and `BatchedBridgeFactory`
Refactored in a simpler way, were it's just a `MessageQueue` class and `BatchedBridge`
is only an instance of it.
Test Plan:
I had to make some updates to the tests, but no real update to the native side.
There's also tests covering the `remoteAsync` methods, and more integration tests for UIExplorer.
Verified whats being used by Android, and it should be safe, also tests Android tests have been pretty reliable.
Manually testing: Create a big hierarchy, like `<ListView>` example. Use the `TimerMixin` example to generate multiple calls.
Test the failure callback on the `Geolocation` example.
All the calls go through this entry point, so it's hard to miss if it's broken.
2015-06-17 17:51:48 +03:00
}
}
2015-02-20 07:10:52 +03:00
module . exports = MessageQueue ;