Summary:
Modules which call JS methods directly, or use `sendDeviceEventWithName:`, can trigger effects in JS without ever being referenced from the JS code. This breaks some assumptions in my earlier diff about when modules can be lazily loaded.
Pending a better solution, I've put explicit `init` methods in these modules to ensure they are eagerly initialized (the downside to this is that they'll still be initialized even if they are never used).
Reviewed By: javache
Differential Revision: D3258232
fb-gh-sync-id: f925bc2e5339c1fbfcc244d4613062c5ab848fc2
fbshipit-source-id: f925bc2e5339c1fbfcc244d4613062c5ab848fc2
Summary:
Now that we support initializing the bridge off the main thread, some of the assumptions in the bridge setup process are no longer safe.
In particular we were assuming that the JS executor and injected modules could always be synchronously initialized within bridge init, but that is only safe if those modules don't need to be set up on the main thread.
The setup for those modules was sync-dispatching to the main thread if bridge init happened on a background thread, and this lead to a deadlock under certain circumstances.
Reviewed By: javache
Differential Revision: D3224162
fb-gh-sync-id: 7319b70f541a46ef932cfe4f776e7e192f3ce1e8
fbshipit-source-id: 7319b70f541a46ef932cfe4f776e7e192f3ce1e8
Summary:When JSC throws an error on startup (e.g. a SyntaxError) or when invoking a method that is not caught by RCTExceptionsManager, we previously just reported is a native error, with a (useless) native stack trace in the redbox. This changes that behaviour to report a JS stacktrace.
The same issue was previously reported here: https://github.com/facebook/react-native/pull/5677
Reviewed By: majak
Differential Revision: D3037387
fb-gh-sync-id: 06f8333e0eb50dcef0b26284754262301b8a5f08
fbshipit-source-id: 06f8333e0eb50dcef0b26284754262301b8a5f08
Summary:The 200ms timeout was causing resource issues and causing a lot of overhead when you're not running the devtools, since it will basically create a new socket every 200ms.
Also clean up the way we do logging so it's completely compiled out in prod, and standardize all the names we use for threading to lowercase react.
Reviewed By: frantic
Differential Revision: D3115975
fb-gh-sync-id: e6e51c0621d8e9fc4eadb864acd678b8b5d322a1
fbshipit-source-id: e6e51c0621d8e9fc4eadb864acd678b8b5d322a1
Summary:Interface to `RCTBatchedBridge` was being declared in two different implementation files. This is suboptimal, since it makes it hard to mock that class in a test.
So I've merged and moved these two definitions in `RCTBridge+Private.h`, so it's still obvious it's a private class, but can be included if you really need it.
Reviewed By: javache
Differential Revision: D3126135
fb-gh-sync-id: 173e4c5c2925be387b92deb7f99952ca7bf28588
fbshipit-source-id: 173e4c5c2925be387b92deb7f99952ca7bf28588
Summary:`flowIDMap` lives on the bridge to map from the IDs used for the flow events in
JS and the ones generated by `RCTProfile` in the native side.
It was being accessed from multiple threads (the various modules' queues in the
bridge and the JS thread), so we lock before touching it.
Reviewed By: javache
Differential Revision: D3102745
fb-gh-sync-id: 93d012d124e8b5d1a390c10a98ef5e3a068ccf63
fbshipit-source-id: 93d012d124e8b5d1a390c10a98ef5e3a068ccf63
Summary: This diff adds support for initializing the bridge on an arbitrary thread. This is helpful if you want to defer bridge creation, or prevent it from delaying your app startup.
Reviewed By: javache
Differential Revision: D2965725
fb-gh-sync-id: 8065fa89e850031c72ee4427351300986985e9de
shipit-source-id: 8065fa89e850031c72ee4427351300986985e9de
Summary:Initializing native modules can block the main thread for tens of milliseconds when it starts up, making it difficult to instantiate the bridge on demand without causing a performance blip.
This diff splits up the initialization of modules so that - although they still happen on the main thread - they don't block the thread continuously.
Reviewed By: javache
Differential Revision: D2965438
fb-gh-sync-id: 38c9c9d281e4672b5874d68b57d4c60d1d268344
shipit-source-id: 38c9c9d281e4672b5874d68b57d4c60d1d268344
Summary: The module initialization process is complex and full of race conditions. This diff adds a set of unit tests that verify that modules setup happens in the correct order, and enforces all the various conditions for main/background init.
Reviewed By: javache
Differential Revision: D2994145
fb-gh-sync-id: 92ea84508cdeeb280ff0fb9e9b2dffa8dbc37e66
shipit-source-id: 92ea84508cdeeb280ff0fb9e9b2dffa8dbc37e66
Summary:
public
Lazy export of module constants required a sync dispatch to the main thread, which was deadlocking in some of our projects.
This moves the constants export to the initial bridge init, which may slightly increase initial startup time, but avoids the deadlock.
Reviewed By: javache
Differential Revision: D2911295
fb-gh-sync-id: 0d14a629ac4fc7ee21acd293c09595c18232659b
shipit-source-id: 0d14a629ac4fc7ee21acd293c09595c18232659b
Summary:
public
Expose JS hooks to create flow events in systrace (the nice arrows to show async work flow) +
add support to the showing all the work enqueued from the JS thread as added in D2743733
Depends on D2743733
Reviewed By: jspahrsummers
Differential Revision: D2815293
fb-gh-sync-id: 4278f61a67a6e78cf2704bacce34b1389328c6df
Summary:
This solves https://github.com/facebook/react-native/issues/5090. Since 5b4e873c68 we had better reporting for when calls from native to JS fail. When trying to load an invalid bundle, this would now cause a stackoverflow, since RCTFatal would schedule a JS call to log, which would RCTFatal, which would ...
By invalidating the jsExecutor immediately after loading fails, we prevent any more attempts to log. We can't invalidate the whole bridge at this point since we still need the redbox module to actually display the error.
public
Reviewed By: majak
Differential Revision: D2834251
fb-gh-sync-id: a3e2ad425e40560beae4d3eacb93f66ace5341bf
Summary:
public
Fixed a potential deadlock issue if code attempted to access a module via [bridge moduleForName/Class:] while it was being initialized.
Reviewed By: lry
Differential Revision: D2807827
fb-gh-sync-id: 58cafe9b92c094dde632d17245fb9b342a0fe9e0
Summary:
public
Android implement ViewManager methods via a dispatch method on UIManager, whereas iOS implements them by exposing the methods on the view manager modules directly.
This diff polyfills Android's implementation on top of the iOS implementation, allowing the same JS API to be used for both.
Reviewed By: javache
Differential Revision: D2803020
fb-gh-sync-id: 0da0544e593dc936467d16ce957a77f7ca41355b
Summary:
public
The logic inside RCTBatchedBridge contained some race conditions that would occasionally cause an error if modules were loaded in the wrong order. This improves that logic and makes it safer by adding a lock to prevent concurrency.
Reviewed By: jspahrsummers
Differential Revision: D2802930
fb-gh-sync-id: d1ad25fa578649363dcaac029cb24dc3a453ae67
Summary:
public
Expose JS hooks to create flow events in systrace (the nice arrows to show async work flow) +
add support to the showing all the work enqueued from the JS thread as added in D2743733
Depends on D2743733
Reviewed By: jspahrsummers
Differential Revision: D2773664
fb-gh-sync-id: 4a8854b17b4741b882f5f2cc425e4237a5e4b3eb
Summary:
public
The implementation of the `methodQueue` lazy initializer in `RCTModuleData` could result in the queue being set twice, because calling `methodQueue` for a module that hasn't been instantiated would call `RCTModuleData.instance` to create the module, which itself calls `methodQueue`.
It's not clear if this was causing a bug, but it may be related to an occasional bug where the `RCTViewManager.methodQueue` returns nil.
Reviewed By: majak
Differential Revision: D2783320
fb-gh-sync-id: 9194da0fd7392f63825da1f5c450363dd300b635
Summary:
public
Rename the executor to so it actually says something about the implementation.
Reviewed By: jspahrsummers, nicklockwood
Differential Revision: D2759688
fb-gh-sync-id: 5b1ac447e75109fbbc2ee71c804710d9926785aa
Summary:
public
Remove some of the manual markers from the bridge since they will already be added dinamically.
Reviewed By: jspahrsummers
Differential Revision: D2761748
fb-gh-sync-id: 0c726373f9105258feb8230d30453559ed1e6a65
Summary:
public
Thanks to the new lazy initialization system for modules, `RCTDidCreateNativeModules` no longer does what the name implies.
Previously, `RCTDidCreateNativeModules` was fired after all native modules had been initialized. Now, it simply fires each time the bridge is reloaded. Modules are created on demand when they are needed, so most of the assumptions about when `RCTDidCreateNativeModules` will fire are now incorrect.
This diff deprecates `RCTDidCreateNativeModules`, and adds a new notification, `RCTDidInitializeModuleNotification`, which fires each time a module a new module is instantiated.
If you need to access a module at any time you can just call `-[bridge moduleForClass:]` and the module will be instantiated on demand. If you want to access a module *only* after it has already been instantiated, you can use the `RCTDidInitializeModuleNotification` notification.
Reviewed By: tadeuzagallo
Differential Revision: D2755036
fb-gh-sync-id: 25bab6d5eb6fcd35d43125ac45908035eea01487
Summary:
public
A lot of the core modules have to use private methods in the bridge, specially
since the `RCTBatchedBridge` interface is never exposed. That was leading to a
lot of different private bridge categories spread across different modules,
which makes harder to identify which modules are affected by private API changes.
Replace all the categories with a single private header.
Reviewed By: nicklockwood
Differential Revision: D2757564
fb-gh-sync-id: 793158b9082d542b74a6094ed0db4d5dc3a88f78
Summary:
A component can be backed by native "node" that can change its internal state, which would result in a new UI after the next layout. Since js has no way of knowing that this has happened it wouldn't trigger a layout if nothing in js world has changed. Therefore we need a way how to trigger layout from native code.
This diff does it by adding methods `layoutIfNeeded` on the uimanager and `isBatchActive` on the bridge.
When `layoutIfNeeded` is called it checks whether a batch is in progress. If it is we do nothing, since at it's end layout happens. If a batch is not in progress we immidiately do layout.
I went with the easiest way how to implement this - `isBatchActive` is a public method on the bridge. It's not ideal, but consistent with other methods for modules.
public
Reviewed By: jspahrsummers, nicklockwood
Differential Revision: D2748896
fb-gh-sync-id: f3664c4af980d40a463b538e069b26c9ebad6300
Summary:
public
Use the actual timestamp provided through `CADisplayLink` instead of the time
the handler is called.
Reviewed By: jspahrsummers
Differential Revision: D2739121
fb-gh-sync-id: 1da28190bb25351dc3dd94efaff21d49279a570f
Summary:
The JavaScript ecosystem doesn't have the notion of a built-in native module loader. Even Node is decoupled from its module loader. The module loader system is just JS that runs on top of the global `process` object which has all the built-in goodies.
Additionally there is no such thing as a global require. That is something unique to our providesModule system. In other module systems such as node, every require is contextual. Even registered npm names are localized by version.
The only global namespace that is accessible to the host environment is the global object. Normally module systems attaches itself onto the hooks provided by the host environment on the global object.
Currently, we have two forms of dispatch that reaches directly into the module system. executeJSCall which reaches directly into require. Everything now calls through the BatchedBridge module (except one RCTLog edge case that I will fix). I propose that the executors calls directly onto `BatchedBridge` through an instance on the global so that everything is guaranteed to go through it. It becomes the main communication hub.
I also propose that we drop the dynamic requires inside of MessageQueue/BatchBridge and instead have the modules register themselves with the bridge.
executeJSCall was originally modeled after the XHP equivalent. The XHP equivalent was designed that way because the act of doing the call was the thing that defined a dependency on the module from the page. However, that is not how React Native works.
The JS side is driving the dependencies by virtue of requiring new modules and frameworks and the existence of dependencies is driven by the JS side, so this design doesn't make as much sense.
The main driver for this is to be able to introduce a new module system like Prepack's module system. However, it also unlocks the possibility to do dead module elimination even in our current module system. It is currently not possible because we don't know which module might be called from native.
Since the module system now becomes decoupled we could publish all our providesModule modules as npm/CommonJS modules using a rewrite script. That's what React Core does.
That way people could use any CommonJS bundler such as Webpack, Closure Compiler, Rollup or some new innovation to create a JS bundle.
This diff expands the executeJSCalls to the BatchedBridge's three individual pieces to make them first class instead of being dynamic. This removes one layer of abstraction. Hopefully we can also remove more of the things that register themselves with the BatchedBridge (various EventEmitters) and instead have everything go through the public protocol. ReactMethod/RCT_EXPORT_METHOD.
public
Reviewed By: vjeux
Differential Revision: D2717535
fb-gh-sync-id: 70114f05483124f5ac5c4570422bb91a60a727f6
Summary:
public
Looping through every `RCTModuleData` to check whether the module responds to `-batchDidComplete` or `-partialBatchDidFlush` is unnecessarily expensive. We can cache the answer at the time that the module instance is actually initialized.
Reviewed By: tadeuzagallo
Differential Revision: D2717594
fb-gh-sync-id: 274a59ec2d6014ce18c93404ce6b9940c1dc9c32
Summary:
public
Currently, we wait to invoke `-flushUIBlocks` until the JavaScript batch to native has completed. This means we may be waiting an unnecessarily long time to perform view hierarchy changes and prop changes.
By instead invoking this after each chunk of enqueued UI blocks, we can perform some updates more eagerly, increasing our utilization of the main thread while splitting up the amount of time we spend running upon it.
This shouldn't affect layout, which is still tied to `-batchDidComplete`, so any visual inconsistencies should be limited to prop changes, which seems acceptable for the dramatic improvement in performance.
Reviewed By: javache
Differential Revision: D2658552
fb-gh-sync-id: 6d4560e21d7da1b02d2f30d1860d60735f11c4b5
Summary: public
Fixes#3953
Bail out soon when the profiler is not running + move string formating into the macro so that it happens in a background queue.
Reviewed By: jspahrsummers
Differential Revision: D2696167
fb-gh-sync-id: a1b91ee4459078ab9a4c0be62bd23362ec05e208
Summary: public
The `bridge.modules` dictionary provides access to all native modules, but this API requires that every module is initialized in advance so that any module can be accessed.
This diff introduces a better API that will allow modules to be initialized lazily as they are needed, and deprecates `bridge.modules` (modules that use it will still work, but should be rewritten to use `bridge.moduleClasses` or `-[bridge moduleForName/Class:` instead.
The rules are now as follows:
* Any module that overrides `init` or `setBridge:` will be initialized on the main thread when the bridge is created
* Any module that implements `constantsToExport:` will be initialized later when the config is exported (the module itself will be initialized on a background queue, but `constantsToExport:` will still be called on the main thread.
* All other modules will be initialized lazily when a method is first called on them.
These rules may seem slightly arcane, but they have the advantage of not violating any assumptions that may have been made by existing code - any module written under the original assumption that it would be initialized synchronously on the main thread when the bridge is created should still function exactly the same, but modules that avoid overriding `init` or `setBridge:` will now be loaded lazily.
I've rewritten most of the standard modules to take advantage of this new lazy loading, with the following results:
Out of the 65 modules included in UIExplorer:
* 16 are initialized on the main thread when the bridge is created
* A further 8 are initialized when the config is exported to JS
* The remaining 41 will be initialized lazily on-demand
Reviewed By: jspahrsummers
Differential Revision: D2677695
fb-gh-sync-id: 507ae7e9fd6b563e89292c7371767c978e928f33
Summary: Exception message was the last part of the whole shown error. This is not optimal in case where there are deeply nested objects as parameters, which used to be displayed before the message.
This diff moves the exception message to the front.
public
Reviewed By: javache
Differential Revision: D2691426
fb-gh-sync-id: c6c9ad3ac4681a8102ea2c580f24382640b7246c