Summary:
## Context
For now, assume TurboModules doesn't exist.
**What happens when we call an async NativeModule method?**
Everytime JS calls an async NativeModule method, we don't immediately execute it. The legacy infra pushes the call into some queue managed by `MessageQueue.js`. This queue is "flushed" or "emptied" by the following events:
- **Flushed:** A C++ -> JS call. NativeModule async methods can called with an `onSuccess` and/or `onFail` callback(s). Calling `NativeToJsBridge::invokeCallback` to invoke one of these callbacks is one way for ObjC++/C++/Java to call into JS. Another way is via JSModule method calls, which are initiated by `NativeToJsBridge::callFunction`.
- **Flushed:** When `JSIExecutor::flush` is called. Since TurboModules don't exist, this only happens when we call `JSIExecutor::loadApplicationScript`.
- **Emptied:** When more than 5 ms have passed, and the queue hasn't been flushed/emptied, on the next async NativeModule method call, we add to the queue. Afterwards, we empty it, and invoke all the NativeModule method calls.
**So, what's the difference between flushed and emptied?**
> Note: These are two terms I just made up, but the distinction is important.
If the queue was "flushed", and it contained at least one NativeModule method call, `JsToNativeBridge` dispatches the `onBatchComplete` event. On Android, the UIManager module is the only module that listens to this event. This `onBatchComplete` event doesn't fire if the queue was "emptied".
**Why does any of this matter?**
1. TurboModules exist.
2. We need the TurboModules infra to have `JsToNativeBridge` dispatch `onBatchComplete`, which depends on:
- **Problem 1:** The queue being flushed on calls into JS from Java/C++/ObjC++.
- **Problem 2:** There being queued up NativeModule async method calls when the queue is flushed.
In D14656466, fkgozali fixed Problem 1 by making every C++/Java/Obj -> JS call from TurboModules also execute `JSIExecutor::flush()`. This means that, with TurboModules, we flush the NativeModule async method call queue as often as we do without TurboModules. So far, so good. However, we still have one big problem: As we convert more NativeModules to TurboModules, the average size of the queue of NativeModule method calls will become smaller and smaller, because more NativeModule method calls will be TurboModule method calls. This queue will more often be empty than not. Therefore, we'll end up dispatching the `onBatchComplete` event less often with TurboModules enabled. So, somehow, when we're about to flush the NativeModule method call queue, we need `JsToNativeBridge` to understand that we've executed TurboModule method calls in the batch. These calls would have normally been queued, which would have led the queue size to be non-zero. So if, during a batch, some TurboModule async method calls were executed, `JsToNativeBridge` should dispatch `onBatchComplete`.
**So, what does this diff do?**
1. Make `Instance` responsible for creating the JS `CallInvoker`.
2. Make `NativeToJsBridge` responsible for creating the native `CallInvoker`. `Instance` calls into `NativeToJsBridge` to get the native `CallInvoker`.
3. Hook up `CatalystInstanceImpl`, the Android bridge, with the new JS `CallInvoker`, and the new native `CallInvoker`. This fixes `onBatchComplete` on Android. iOS work is pending.
Changelog:
[Android][Fixed] - Ensure `onBatchComplete` is dispatched correctly with TurboModules
Reviewed By: mdvacca
Differential Revision: D20717931
fbshipit-source-id: bc3ccbd6c135b7f084edbc6ddb4d1e3c0c7e0875
Summary:
Changelog: [Internal]
View with `ShadowColor` was getting flattened and therefore views didn't have shadow property set.
This is fixed by promoting ShadowColor so in case it is set, it forms stacking context.
Reviewed By: shergin
Differential Revision: D20792201
fbshipit-source-id: 1033ac00e32047ffbb14e61b7c26348c578d132d
Summary:
The differ was still producing correct, but not minimal, instruction sets in some cases due to an optimization that was buggy.
This affected cases where 2+ nodes were inserted at the beginning of a list. It would trigger the old behavior where all nodes after the first would be removed, deleted, then reinserted.
See the test case (which was failing and now passed) and P128190998 (the 3->4 transition) for samples.
Changelog: [Internal] Fabric differ
Reviewed By: mdvacca
Differential Revision: D20785729
fbshipit-source-id: 2fea6a816753066abb358ed7bb51003140cd5fc4
Summary:
This diff implements the instruction pointer save/restore trick Tzvetan came up with; allowing us to observe and modify the IP from outside the interpreter loop with negligible overhead.
From Tzvetan's internal post on the subject:
> [Today] the interpreter IP is just a local variable in the interpreter function, so there is no way to get to its value from outside the function. It lives in a register and we don't want to make it a Runtime field since the overhead [of accessing it via memory in the interpeter loop] would kill us.
> However, if you really think about it, it only lives in a register while the interpreter function is running. For the rest of the time, it is spilled by the C++ compiler onto the stack.
So, precisely when we need it, it is actually stored in memory. The only problem is, we don't know where! Admittedly, that is an annoying problem, but it feels like it should be solvable.
> What if, instead of relying on the compiler to spill the IP register, we manually spill it ourselves, to a known location? It works. Example: https://godbolt.org/z/ftSDnp
This diff implements this approach across the whole interpreter loop: whenever we call out of the loop we capture/publish the IP and restore it again immediately after the external call returns. This means we can now see the IP outside the interpret loop and even change it. This is effectively "for free" as the compiler now skips spilling/restoring the IP behind the scenes.
The immediate benefit of this is knowing the current IP allows us to have more accurate stack-traces during execution. In future this may enabled tricks like changing the IP before returning to the interpreter loop, allowing things outside the interpreter to affect program flow without adding logic to the interpreter loop.
Reviewed By: tmikov
Differential Revision: D20151091
fbshipit-source-id: 3814382639800208d8985a32ede31ba8f7ff7c80
Summary:
Changelog: [Internal]
`orderIndex_` was only being assigned for `ViewShadowNode`, not for other `ShadowNodes` that are later represented on the screen.
Reviewed By: shergin
Differential Revision: D20746477
fbshipit-source-id: c04c2cfea14b9141d22bc3d9e9bb4c0c59925754
Summary:
Changelog: [internal]
`ModalHostViewShadowNode` didn't have `RootNodeKit` trait, therefore `getRelativeLayoutMetrics` was including nodes in ancestors that it shouldn't have.
Reviewed By: shergin
Differential Revision: D20735801
fbshipit-source-id: 6b81e3b174c2f82e530abc2bca2da8bebc2270b0
Summary:
Here we implement a bunch of helper methods that allow customizing the behavior of a RuntimeExecutor "on-demand" on the caller side. We will use it in the next diff(s).
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: PeteTheHeat
Differential Revision: D20551411
fbshipit-source-id: 51d3cd02b69753110c0e1155347c6e52eb882c7d
Summary:
Changelog: [Internal]
Current implementation of `measure` doesn't take transform into account..
So if you had a view which has width and height 100 and had `Scale(0.5, 0.5, 1)` (this will shrink view by half). Calling `getRelativeLayoutMetrics` would report its size being `{100, 100}`.
This applies if view's parent has transformation as well, because transformation is applied to all subviews of the view as well.
Reviewed By: mdvacca
Differential Revision: D20621590
fbshipit-source-id: 2cf902a0494291c821ecada56f810c5e6620db5a
Summary:
Changelog: [Internal]
Until now `Rect operator*(Rect const &rect, Transform const &transform)` supported only scaling. Now it supports translation and rotation as well.
Reviewed By: shergin
Differential Revision: D20622876
fbshipit-source-id: 1b65393bd3fd6fd9a8941903e0f2681a10097e4a
Summary:
`LayoutInspectingPolicy` has two flags, `includeTransform` and `includeScrollViewContentOffset`.
`includeScrollViewContentOffset` seems to be redundant for two reasons.
# 1st
From looking at callers, they have always the same value.
I looked at all call sites, and they are either always both set to true or both set to false.
# 2nd
The way we include scroll view content offset, is through transformation, so setting `includeTransform` to true and `includeScrollViewContentOffset` to false will include content offset anyway. In order to make both flags work, we would need to introduce further changes to `getRelativeLayoutMetrics`. But since the flag isn't used anyway, I think it is better to get rid of it for now. If we need it in the future, we could re-introduce it.
Reviewed By: shergin
Differential Revision: D20622256
fbshipit-source-id: fb6156c66b752319ea928239fa723ff90688b0a0
Summary:
Two things:
1) I reimplement Valentin's idea in D19965405, so that TinyMaps can be iterated over, with a couple of bugfixes (calling front() or back() on an empty vector will crash).
2) I now use TinyMap instead of better::map in the "optimized" diffing algorithm.
3) `erase` now actually removes elements from the vector, but only when more than half of elements have been erased.
4) If you repeatedly erase elements at the beginning of the vector, they will no longer be iterated over. This is a specific optimization for our heaviest TinyMap use-cases.
These amount to some small but hopefully somewhat meaningful perf improvements.
Changelog: [Internal] Fabric perf
Reviewed By: shergin
Differential Revision: D20718719
fbshipit-source-id: 91f4b2e2e0f6387ae484e43d5b0095103087baa6
Summary:
This unit test is to verify that the new diffing algorithm generates a "minimal" instruction set, with regards to removes and inserts ("moves").
These unit tests are here to verify the expected behavior in this new algorithm, but these tests may be modified or deleted in the future if we decide we want to change this behavior.
Changelog: [Internal] fabric unit test
Reviewed By: mdvacca
Differential Revision: D20706592
fbshipit-source-id: 5f9991498e0d788ecbf88d938bfe6d3f0f27af40
Summary:
An evolution of D20633188 but more performant.
There are three optimized paths before the slow path.
The first optimized path tries to pair identical nodes from old/new tree, and generate Update mutations, until we hit nodes that are different (indicating either a remove or an insert). This already existed.
The next two optimizations, introduced by Tim in his JS pseudocode, were inspired by ReactJS's diffing algorithm. They work in cases where the rest of the nodes are (1) all removals/deletes or (2) all creates+inserts.
Finally, if those final two optimized paths can't run, it's because there is a mix of delete+remove, create+insert, and "move" operations, mixed at the beginning, middle, and/or end of the list.
This has slightly better average/best-case complexity as the previous implementation.
In particularly pathological cases where all nodes are arbitrarily reordered, or reversed, for instance (ABCDE->EDCBA) the algorithm has the same complexity as the previous algorithm (quadratic).
For now iOS is pinned to the older differ
Changelog: [Internal] Experiment to optimize diffing algorithm in Fabric
Reviewed By: shergin
Differential Revision: D20684094
fbshipit-source-id: d29fba95a0328156c023e1c87804f23770ee1d91
Summary:
`std::this_thread::sleep_for` is not really precise and will attempt to sleep for "at least" that much time, but may sleep much longer depending on what CPUs are doing and scheduling policies.
To get this to pass on my machine, I had to substantially increase the thresholds.
Changelog: [Internal]
Reviewed By: shergin
Differential Revision: D20689571
fbshipit-source-id: f159420d24a95da2b5d95d49ed7882e783291e98
Summary:
State Reconciliation has been running 50/50 for a while and all metrics look stable. This is necessary for providing a good experience so we should ship to everyone unconditionally.
Changelog: [Internal] Fabric diffing reconciliation process improvement
Reviewed By: mdvacca
Differential Revision: D20715694
fbshipit-source-id: 25b2635ecc29b67e2911679c9db66bc84d37dec1
Summary:
`fbsource//xplat` and `//xplat` are equivalent for FB BUCK targets. Removing extra prefix for consistency.
Changelog: [Internal]
Reviewed By: scottrice
Differential Revision: D20495655
fbshipit-source-id: a57b72f694c533e2e16dffe74eccb8fdec1f55f5
Summary:
While ViewConfig infra isn't perfect we need to check some value for correctness during prop-parsing.
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: JoshuaGross
Differential Revision: D20639055
fbshipit-source-id: 193dcd0769bc7777bc8d60c964ede72ebdaa83e4
Summary:
This gets us on the latest Prettier 2.x:
https://prettier.io/blog/2020/03/21/2.0.0.html
Notably, this adds support for TypeScript 3.8,
which introduces new syntax, such as `import type`.
Reviewed By: zertosh
Differential Revision: D20636268
fbshipit-source-id: fca5833d003804333a05ba16325bbbe0e06d6c8a
Summary:
Changelog: [Internal]
Hermes inspector includes pthreads, arpa and sys headers on all OSes that would break vanilla Windows builds. This diff adds a check for posix-compliance before inclusion
(Note: this ignores all push blocking failures!)
Reviewed By: dulinriley
Differential Revision: D20564449
fbshipit-source-id: 8e264bc3104065dc4315bb291e8560609fe65184
Summary:
This is pure syntactic change. Often we don't have a shared pointer to ShadowNodeFamily and only have just a reference. At the same time, `ComponentDescriptor::createState` does not have to accept a shared pointer. So, it's better to accept just a reference.
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: sammy-SC
Differential Revision: D20578787
fbshipit-source-id: 905277001e096d41e75007575b59ea2ea15fbf4b
Summary:
Changelog: [Internal]
# There are three changes in this diff
## _stateRevision is replaced with a BOOL
`_stateRevision` was protecting against setting attributed string that is already visible to the user. Previously this was ok because the change was only coming from native, any changes from JS were ignored.
Imagine following scenario:
1. User taps key.
2. Update state is called on component initiated by native.
3. New state is created with incremented revision by one.
4. `_stateRevision` gets set to new state's revision + 1.
5. Now JS wants to change something because it just learnt that user tapped the key.
6. New state is created again with incremented revision by one.
7. Update state is called on the component, but the change isn't applied to the text view because `_state->getRevision()` will equal `_stateRevision`.
By having a BOOL instead of number, we very explicitly mark the region in which we don't want state changes to be applied to text view.
## Calling [_backedTextInputView setAttributedText] move cursor to the end of text input
This is prevented by storing what the current selection is and applying it after `[_backedTextInputView setAttributedText]` is called.
This was previously invisible because JS wasn't changing contents of `_backedTextInputView`.
## Storing of previously applied JS attributed string in state
This is the mechanism used to detect when value of text input changes come from JavaScript. JavaScript sends text input value changes through props and as children of TextInput.
We compare what previously was set from JavaScript to what is currently being send from JavaScript and if they differ, this change is communicated to the component.
Previously only first attributed string send from JavaScript was send to the component.
# Problem
If children are used to set text input's value, then there is a case in which we can't tell what source of truth should be.
Let's take following example
We have a text field that allows only 4 characters, again this is only a problem if those 4 characters come as children, not as value.
This is a controller text input.
1. User types 1234.
2. User types 5th character.
3. JavaScript updates TextInput, saying that the content should stay 1234.
4. In `TextInputShadowNode` `hasJSUpdatedAttributedString` will be set to false, because previous JS value is the same as current JS value.
Reviewed By: shergin
Differential Revision: D20587681
fbshipit-source-id: 1b8a2efabbfa0fc87cba210570142d162efe61e6
Summary:
This fixes two things:
1) Currently it only respects Start and End padding, and if there's a Theme default, it will override Left/Right padding. Whoops.
2) Currently it doesn't respect when a TextInput starts with padding, but then is removed.
This resolves both.
It still does not account for RTL support.
Changelog: [Internal] Fix AndroidTextInput padding
Reviewed By: mdvacca
Differential Revision: D20573151
fbshipit-source-id: e89791641b6699e728cde9dbd661a8c21485fbc8
Summary:
Just includes the manual changes to get the perf logger moved to the ObjCTurboModule constructor.
Changelog: [Internal]
Reviewed By: fkgozali
Differential Revision: D20498443
fbshipit-source-id: e607588f47ccc8ae199cd97043b09dfaff7a72dd
Summary:
Previously, I had logic inside `RCTTurboModuleManager` to attach the `id<RCTTurboModulePerformanceLogger>` to the `ObjCTurboModule` object
```
/**
* By default, all TurboModules are long-lived.
* Additionally, if a TurboModule with the name `name` isn't found, then we
* trigger an assertion failure.
*/
auto turboModule = [strongSelf provideTurboModule:moduleName];
/**
* TODO(T63718299): Move this setter into the ObjCTurboModule constructor
*/
if (performanceLogger) {
if (auto objCTurboModule = std::dynamic_pointer_cast<facebook::react::ObjCTurboModule>(turboModule)) {
objCTurboModule->setRCTTurboModulePerformanceLogger(performanceLogger);
};
}
```
This diff removes that logic in `RCTTurboModuleManager`, and it also removes `ObjCTurboModule::setRCTTurboModulePerformanceLogger`. Henceforth, we'll instead pass the `id<RCTTurboModulePerformanceLogger>` into `ObjCTurboModule`'s constructor. I've made all the necessary changes to the codegen scripts in this diff as well.
This should also resolve T63903079 by simply eliminating the code that's crashing production FB apps.
Changelog: [Internal]
Reviewed By: fkgozali
Differential Revision: D20480971
fbshipit-source-id: c3899981f880aa5d1354b5c3f4018c8fd57c3bae
Summary:
Changelog: [internal]
layoutDirectionPass down layoutDirection from `RCTSurfacePresenter` down to `YogaLayutableShadowNode`.
In `ParagraphShadowNode`, we propagate layoutDirection from yoga node to `TextAttributes.layoutDirection`.
Reviewed By: shergin
Differential Revision: D20420041
fbshipit-source-id: 86e01d31ea9415acb8579c44c470cac870ec1b8f
Summary:
Changelog: [Internal]
# Why was it crashing?
Crash was caused by `data.getTrackImageRequest()` returning NULL. The crash happened inside `getCoordinator` method. If you look at the implementation of the method below
```
if (request) {
return &request->getObserverCoordinator();
} else {
return nullptr;
}
```
you might notice that we check for NULL, why is it crashing then? And why is it only crashing in production?
The reason is compiler's optimiser, which looks at `data.getTrackImageRequest()`, this method returns `ImageRequest const &` and sees that this method will always return a value, never NULL. Compiler then looks at our `getCoordinator` function, sees that we check there for nullity but since it can never be null, removes that branch.
# Solution
Create new SliderState with non null image observer.
Reviewed By: shergin
Differential Revision: D20491367
fbshipit-source-id: 17b7cf31feabbe6f8ece324a3d329902b2ef6a2d
Summary:
We don't need it anymore because the same logic is already implemented in `YogaLayoutableShadowNode::layoutTree()`.
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: sammy-SC
Differential Revision: D20464175
fbshipit-source-id: 3dd2293ee102a9b67a856104720d3a7dc4103d7f
Summary:
Fixes https://github.com/facebook/react-native/issues/28271
As explained by mmallet-youilabs , if the parameters passed to the `move` function are too expensive, this can have an impact on performance. Thus making these parameters captured by value mutable, the parameters are not movable.
## Changelog
[iOS] [Fixed] - Message
Pull Request resolved: https://github.com/facebook/react-native/pull/28297
Test Plan: Steps to reproduce (and expected results) are not applicable (unless running with a profiler).
Differential Revision: D20464278
Pulled By: shergin
fbshipit-source-id: 846a8bc6c61cb4aa21fbd96b419c3775190a2c84
Summary:
Now, having `orderIndex` feature in the core, we can use it for implementing `zIndex` feature. All we need to do is just to assign this `zIndex` value to `orderIndex`.
Then we will use this to remove some platform-specific code that implements `zIndex` on the mounting layer.
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: sammy-SC
Differential Revision: D20432155
fbshipit-source-id: b4d62b63006f45899de38e1f40b1dfbe69550ada
Summary:
This behavior matches the behavior of `dynamic_cast` (on which some callsites rely on).
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: sammy-SC
Differential Revision: D20456792
fbshipit-source-id: 9604da0f9f78cc7357e60ed11012756e753e4b45
Summary:
This was causing an exception cascade leading to production
errors. Added a test which repros the problem and passes with the
fix.
Changelog: [Internal]
Reviewed By: tmikov
Differential Revision: D20408858
fbshipit-source-id: 3fa9b8669bf3bf7617bfc05ef8f23d52bc969b4e
Summary:
This fixes a bug in Android TextInlineViews that was reproducible in ActivityLog screen, see T63438920
Since Text are virtual nodes it is not necessary for these kind of views to stack views during diffing.
changelog: [internal]
Reviewed By: shergin
Differential Revision: D20448085
fbshipit-source-id: 2d852975493bf6bcc7840c80c5de5cb5f7890303
Summary:
This method does not have implementation and we don't use it.
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: sammy-SC
Differential Revision: D20423392
fbshipit-source-id: cfb5e4a60dbeca26a968c29d20e74dd6af0bf660
Summary:
We need that to move all `TextShadowNode`s to the end of the list of nodes to make view indexes work for nodes that actually have views.
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: mdvacca
Differential Revision: D20396717
fbshipit-source-id: efbef47ef89ab37e83e020c029c39e1a62727fda
Summary:
The diff introduces a new field in `ShadowNode` which defines in which order ShadowViews created from the node and its siblings will appear ShadowView tree. The feature will be used to overcome some platform limitations (e.g. on Android) on the mounting layer (it will be able to move some nodes of some time to the end (or beginning) of the list) and build features like zIndex ordering in C++ core.
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: mdvacca
Differential Revision: D20396718
fbshipit-source-id: 16aef7c2b5511c874341ab7554e5585b2cdc356f