Summary:
TurboModule eager initialization is a bit dangerous if we get it wrong, which we did (twice): T69449176.
This diff gates TurboModule eager init behind a MC, so that we can control (i.e: turn off/on, and do gradually rollout of) TurobModule eager initialization in isolation from the larger TurboModules experiment.
Changelog: [Internal]
Reviewed By: PeteTheHeat
Differential Revision: D22460359
fbshipit-source-id: 3b8dce0529f1739bd68b8b16d6a28aa572d82c2c
Summary:
## Why?
1. RCTTurboModuleLookupDelegate sounds a bit nebulous.
2. In JS and Java, we use a `TurboModuleRegistry` interface to require TurboModules. So, this diff will make JS, Java, and ObjC consistent.
Changelog:
[Internal]
Reviewed By: PeteTheHeat
Differential Revision: D22405754
fbshipit-source-id: 30c85c246b39d198c5b8c6ca4432a3196ca0ebfd
Summary:
## Context
1. In FBReactModule jsExecutorForBridge, we asynchronously initialize a list of TurboModules on the main queue: https://fburl.com/diffusion/i56wi3px
2. After initializing the bridge, we start executing the JS bundle, here: e23e9328aa/React/CxxBridge/RCTCxxBridge.mm (L414-L417). Since bridge initialization knows nothing about TurboModule eager initialization, this happens concurrently with 1, and starts requiring NativeModules/TurboModules on the JS thread.
## The Race
1. Both the main thread and the JS thread race to create a TurboModule that requires main queue setup.
2. The JS thread wins, and starts creating the TurboModule. Meanwhile, the main thread blocks, waiting on a signal here, in RCTTurboModuleManager: e23e9328aa/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm (L430)
3. The JS thread tries to dispatch_sync to the main queue to setup the TurboModule because the TurboModule requires main queue setup, here: e23e9328aa/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm (L402)
4. We deadlock.
## The fix
Succinctly, NativeModule eager initialization finishes before execute the JS bundle, but TurboModule initialization doesn't. This diff corrects that mistake.
The changes in this diff:
1. The RN application via the TurboModuleManager delegate can now optionally provide the names of all eagerly initialized TurboModules by implementing two methods `getEagerInitModuleNames`, `getEagerInitMainQueueModuleNames`.
2. The TurboModuleManager grabs these two lists from the delegate, and exposes them to its owner via the `RCTTurboModuleRegistry` protocol.
3. The RCTCxxBridge, which already owns a `id<RCTTurboModuleRegistry>` object, uses it to eagerly initialize the TurboModules in these two lists with the correct timing requirements.
This is exactly how we implement eager initialization in Android.
**Note:** Right now, phase one and two of TurboModule eager initialization happen after phase one and two of NativeModule eager initialization. We could make the timing even more correct by initializing the TurboModules at the exact same time we initialize the NativeModules. However, that would require a bit more surgery on the bridge, and the bridge delegate. I think this is good enough for now.
Changelog:
[iOS][Fixed] - Fix TurboModule eager init race
Reviewed By: PeteTheHeat
Differential Revision: D22406171
fbshipit-source-id: 4715be0bceb478a8e4aa206180c0316eaaf287e8
Summary:
`RCTModuleData gatherConstants` is [used by `RCTModuleData exportedConstants` to compute and return the constants exported to JS](https://fburl.com/diffusion/ssg4jbeu). However, `RCTModuleData gatherConstants` is also [used by `RCTCxxBridge` to pre-compute NativeModule constants during bridge startup](https://fburl.com/diffusion/nfmjc1ke). Therefore, since `RCTModuleData gatherConstants` can be used outside the context of a JS require, we cannot start the JSRequireEnding marker inside `RCTModuleData gatherConstants` directly.
This diff moves the body of `RCTModuleData gatherConstants` into `gatherConstantsAndSignalJSRequireEnding:(BOOL)startMarkers`:
- `RCTModuleData gatherConstants` calls `RCTModuleData gatherConstantsAndSignalJSRequireEnding:NO`
- `RCTModuleData exportedConstants` calls `RCTModuleData gatherConstantsAndSignalJSRequireEnding:YES`. **Note:** This is okay, because `RCTModuleData exportedConstants` is only called inside `RCTNativeModule::getConstants()`.
This should make sure that we don't start the JSRequireEnding marker outside of a JS require.
Reviewed By: PeteTheHeat
Differential Revision: D22371889
fbshipit-source-id: de17b857259572fb0f840a22072a16b5e465cabd
Summary:
This diff inlines all mount-instruction functions into a single one - RCTPerformMountInstructions.
The main purpose is to reduce the number of calls to `RCTComponentViewRegistry` for some instructions. E.g., before the change, the `Insert` (or `Update`) instruction had seven identical calls to the registry. That's not a huge deal but there is no need to pay for it either. Maybe it can save us a couple of milliseconds during TTI.
The code of those functions is quite straight-forward, so this change probably even improves readability.
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: sammy-SC
Differential Revision: D22402987
fbshipit-source-id: 043a4114ba42622e9ed226f4d5e41ed45c1b066c
Summary:
Changelog:
Adding packager running check when RCTBundleURLProvider is returning JSLocation, this prevents an invalid address from being returned which might cause various issues.
Reviewed By: cpojer
Differential Revision: D22390156
fbshipit-source-id: a20dbf63103158a34cbf6dc0ae8349b2f9e5b0a8
Summary:
This refined algorithm now takes a zoom/scale into account and deals with `contentInset` properly.
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: sammy-SC
Differential Revision: D22382482
fbshipit-source-id: 7e9d34d3bababf68e624c045c308957e7e5c9d84
Summary:
Changelog: [Internal]
When JS Inspector is activated, ScrollView is unmounted and then mounted again with same state.
ScrollView's content offset was being set to 0 inside `[RCTScrollViewComponentView prepareForRecycle]` during unmount but when it is mounted again, we ignored value inside state.
Reviewed By: shergin
Differential Revision: D22333125
fbshipit-source-id: f232dc95b695605f4819f29d8e0bf14b2f3e9150
Summary:
Pet Peeve: Metro is a brand name. You don't say "the Metro server" just like you don't say "the iPhone phone". This is a leftover from when it used to be called "the packager server".
Note: It makes sense to refer to "the Metro server" when talking about it in the context of Metro's features, like if you are discussing "Metro's bundling" and "Metro's server". However, when talking about the tool itself, just Metro is enough.
Changelog: [Internal]
Reviewed By: motiz88
Differential Revision: D22330966
fbshipit-source-id: 667618363c641884df543d88cac65d1e44956ad3
Summary:
Changelog:
[iOS][Added] - This is the first step to build RCTParagraphComponentAccessibilityProvider. The main idea of RCTParagraphComponentAccessibilityProvider is to provide an array of accessible elements for the AttributedString in PCTParagraphComponentView.
Reviewed By: sammy-SC
Differential Revision: D22214855
fbshipit-source-id: 69d47555e86452620f89a4b2e21ed6065c8e669c
Summary:
The change contains a bunch of additional asserts that verify some assumptions on which mounting relies on. Working on the previous diffs I realized that it's very easy to broke those and then spend hours trying to understand what exactly went wrong.
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: sammy-SC
Differential Revision: D22324087
fbshipit-source-id: 1152c40248885d02bde62a493a574868c3732273
Summary:
This is the first implementation stage of on-demand child views mounting for <ScrollView> feature.
It works this way. When mounting layer requests mounting of a subview of <ScrollView>, the component does not mount it immediately. Instead, it stores it in an internal collection, and then after the transaction finishes use that to mount views that are in a visible area.
Then we re-evaluate which child views are should be mounted and unmounted on every significant onScroll event. We use some leeway to do it not so often to save CPU cycles on scrolling.
This feature already works fine but to make it shippable we need to integrate it with an `overflow inset` feature to make it 100% reliable in complex cases when some views have overflow content.
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: sammy-SC
Differential Revision: D22304012
fbshipit-source-id: be4ae21378d9d1c457cad2826da7d8c8d6395be5
Summary:
Changelog: [Internal]
Do not log warning for "Invalid view set to be the JS responder".
{F242140509}
Fabric doesn't store views in view registry, therefore this warning would be shown to the developer everytime `[RCTUIManager setJSResponder]` is called.
Reviewed By: shergin
Differential Revision: D22309447
fbshipit-source-id: cac8985cdc79ab2d03a246cc2d9377472a64a683
Summary:
The method does not do anything besides calling a super method. Even if this method does nothing special, overriding it can have negative performance implications.
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: sammy-SC
Differential Revision: D22309895
fbshipit-source-id: bd8237d15df20017629223f278b1b6ac628b0cc7
Summary:
Changelog: [Internal]
Introducing InputAccessoryView.
There is one big difference between Fabric's implementation and Paper's implementation.
Fabric searches for text input from InputAccessoryView, unlike Paper where it is the other way around.
Reviewed By: shergin
Differential Revision: D22160445
fbshipit-source-id: 55313fe50afeced7aead5b57137d711dd1cfd3ae
Summary:
Here is why:
* It was with us from the very beginning but we never use it.
* The main purpose of this - snap-to-pixel layout - was moved to Yoga, where it should be.
* The current implementation has a bug.
* It's not really correct conceptually because the value becomes incorrect when an immutable subtree is being reused as part of a new tree.
* It over-complicates a new feature I am working on.
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: sammy-SC
Differential Revision: D22284645
fbshipit-source-id: c4c2df8d24e8fe924725b465e04e8154d097d226
Summary:
Changelog: [Internal]
In D8552360 (48b9a6f887) an experimental integration between old and new UIManager has been introduced.
It isn't needed anymore.
I did some measurements and it is the slowest part of `[RCTComponentViewDescriptor dequeueComponentViewWithComponentHandle]`.
{F241943384}
Reviewed By: shergin
Differential Revision: D22274782
fbshipit-source-id: 799ba047f1c57f68f00b0b6fa7c58782874991bc
Summary:
Changelog: [Internal]
The methods were out of order, this diff reorders them.
Reviewed By: shergin
Differential Revision: D22233255
fbshipit-source-id: ea66bc701c4d021ec5721e191bc0d3413b3d36ae
Summary:
Changelog: [Internal]
# Why we crash?
`cStringUsingEncoding` returns `char *`, not `std::string`. Compiler uses implicit conversion to construct and copy `char *` into `std::string`. Maybe optimiser does something unexpected there? Maybe something weird happens there? I think it is worth trying to be more explicit about it and construct std::string there explicitly. Also if you do a google search, this seems to be a go to strategy when converting `NSString` to `std::string`.
This is all just an assumption, I can't repro the crash
# Why get rid of 2nd argument in RCTStringFromNSString
2nd argument is `NSStringEncoding`. It isn't being used, we always use default value.
Also, if you pass in `NSUTF16StringEncoding` or `NSUTF32StringEncoding`, you get undefined behaviour.
Check https://developer.apple.com/documentation/foundation/nsstring/1408489-cstringusingencoding?language=objc# section "Special Considerations"
Reviewed By: shergin
Differential Revision: D22089694
fbshipit-source-id: d449b383c61983c3822bc589c0a01fa97c0b6e64
Summary:
Changelog: [Internal]
Add support for dynamic font size.
New class `ThreadStorage` is introduced, which is used to pass LayoutContext to `YogaLayoutableShadowNode::yogaNodeMeasureCallbackConnector`.
## Shortcoming
This implementation doesn't cause re-render, if user changes font size and comes to the app without restarting it, it will show old font size. I believe this is fine for now as most people set their font size before they use the app and keep the same setting for a long time.
Reviewed By: shergin
Differential Revision: D22043728
fbshipit-source-id: 7453d165c280a2f4bcb73f4ee6daf9e64b637ded
Summary:
This function is unused. (Followup to D21941946)
Changelog: [iOS] Deprecate calculateChildFrames from RCTScrollView
Reviewed By: sammy-SC
Differential Revision: D22071415
fbshipit-source-id: 0c996ab02df1431ee9cfa082bc99681a2ec7118c
Summary:
Changelog: [Internal]
Paper views are not designed to be recycled, that's why a new view is created each time legacy interop layer is used.
However paper view was not deallocated immediately after it was used, it was still being strongly referenced by `self.contentView`.
This doesn't cause an immediate issue because eventually when legacy interop layer does get reused, it will create a new paper view and replace the old one inside `self.contentView`, triggering deallocation of the old one. But we were retaining the paper view beyond what was necessary.
Reviewed By: mdvacca
Differential Revision: D22066022
fbshipit-source-id: 17c3b81468f50ebcc05f1a7cdf4d4b9b00965fc3
Summary:
Changelog: [Internal]
A long time ago we experimented with JSC bytecode. We are not experimenting with JSC bytecode any more. This code can be removed.
Reviewed By: mhorowitz
Differential Revision: D22017374
fbshipit-source-id: 6fe3fb7ad7966f92a5cd103605ac5c0bd1f17a8e
Summary:
Fixes some comment typos, moves hit testing and accessibility code so it's beside each other.
No functionality changes.
Changelog:[Internal]
Reviewed By: RSNara
Differential Revision: D22003047
fbshipit-source-id: 0e785364d7e1a080034d24c9676a56acb45686bb
Summary:
Changelog: [Internal]
There is no need for custom dealloc, let's get rid of it.
We also prefer east const over west const.
Reviewed By: shergin
Differential Revision: D21997545
fbshipit-source-id: aa017f99aa26421fc59b353d0012687cb38fac08
Summary:
Changelog: [Internal]
Here is what I believe happens.
We have an instance of `RCTSliderComponentView` which has green thumb tint color.
It gets reused, in prepareForRecycle we call `setThumbTintImage:nil` on its UISlider which internally sets an ivar to `nil`.
Next time `RCTSliderComponentView` gets used without explicit thumb tint color, we assign nil to UISlider's thumb tint color.
Internally this nil gets compared to nil that it saved during `prepareForRecycle` and concludes that the value is already sets and exists early.
Since we don't have access to `UISlider` I can't prove this but here is a short video where I showcase this behavior
{F239923204}
The code in video is here P133083862.
Reviewed By: shergin
Differential Revision: D21997324
fbshipit-source-id: 28a11ed817cc863a313217c475042918ee726011
Summary:
Changelog: [internal]
We were not reseting value of slider when recycling, this would result in wrong value being displayed once slider gets reused.
Reviewed By: shergin
Differential Revision: D21996786
fbshipit-source-id: 3beac4936d0719c4ac5ac0499209300a912f6986
Summary:
Changelog:
[iOS][Removed] - Removed DEPRECATED_sendUpdatedChildFrames prop to ScrollView component because there are no callsites of it anymore
Reviewed By: shergin
Differential Revision: D21941946
fbshipit-source-id: 0b7d6d0986ddff4b250e70e0450a6f7e166b41f4
Summary:
Changelog: [Internal]
RCTScheduler was storing Scheduler as `shared_ptr`, but `RCTScheduler` is sole owner of it.
`unique_ptr` better expresses this ownership.
Reviewed By: JoshuaGross
Differential Revision: D21923573
fbshipit-source-id: e382f2d6e0a4875e1441b6063c1ad7056b338e29
Summary:
From the header of `RCTSurfaceHostingProxyRootView`:
This is a RCTRootView-compatible implementation of RCTSurfaceHostingView.
Use this class to replace all usages of RCTRootView in the app for easier migration
I need to do exactly this, but for a bridgeless mode callsite. This proxy class only uses the bridge for some perf logging, which we're fine with not having right now.
Reviewed By: shergin
Differential Revision: D21893522
fbshipit-source-id: 3547cff6143f44714e39e4104d03336010081e2e
Summary:
Changelog: [Internal]
# Problem
`MountingCoordinator` holds a pointer to instance of `MountingOverrideDelegate` which becomes invalid.
# Solution
Use `std::weak_ptr` instead of raw pointer so it is possible to tell whether the pointer is expired.
Reviewed By: JoshuaGross
Differential Revision: D21905351
fbshipit-source-id: c7bf9635742a6ec086a03ba83202e46e1f1f373f
Summary:
## Context
- If a NativeModule requires main queue setup, its `constantsToExport` method is executed on the main queue.
- In the TurboModule system, `constantsToExport` or `getConstants` is treated like a regular synchronous NativeModule method. Therefore, it's always executed on the JS thread.
This difference in behaviour is dangerous when we're A/B testing the TurboModule infra: One could write a NativeModule that requires main queue setup, and have it expose constants that access objects/state only accessible on the UI thread. This NativeModule would work fine in the legacy infra, which could be the case if the NativeModule author is testing locally. But once it ships to prod, it may run with the TurboModule system, and crash the application. To mitigate this risk, I'm removing this special main queue execution of `constantsToExport` from the legacy infrastructure.
## Consequences
- If a NativeModule's `constantsToExport` method accesses objects/state only accessible on the UI thread, it must do so by explicitly scheduling work on the main thread. I wrote up a codemod to fix this for our OSS modules: D21797048.
- Eagerly initialized NativeModules that required main queue setup had their constants calculated eagerly. After the changes in this diff, those NativeModules will have their constants calculated lazily. I don't think this is a big deal because only a handful of NativeModules are eagerly initialized, and eagerly initialized NativeModules are going away anyway.
Changelog:
[iOS][Removed] - Main queue execution of constantsToExport in NativeModules requiring main queue setup
Reviewed By: fkgozali
Differential Revision: D21829091
fbshipit-source-id: df21fd5fd2ef45a291c07400f360bba801ae290f
Summary:
If a NativeModule requires main queue setup, its `getConstants()` method must be executed on the main thead. The legacy NativeModule infra takes care of this for us. With TurboModules, however, all synchronous methods, including `getConstants()`, execute on the JS thread. Therefore, if a TurboModule requires main queue setup, and exports constants, we must execute its `getConstants()` method body on the main queue explicitly.
**Notes:**
- The changes in this diff should be a noop when TurboModules is off, because `RCTUnsafeExecuteOnMainQueueSync` synchronously execute its block on the current thread if the current thread is the main thread.
- If a NativeModule doens't have the `requiresMainQueueSetup` method, but has the `constantsToExport` method, both NativeModules and TurboModules assume that it requires main queue setup.
## Script
```
const exec = require("../lib/exec");
const abspath = require("../lib/abspath");
const relpath = require("../lib/relpath");
const readFile = (filename) => require("fs").readFileSync(filename, "utf8");
const writeFile = (filename, content) =>
require("fs").writeFileSync(filename, content);
function main() {
const tmFiles = exec("cd ~/fbsource && xbgs -n 10000 -l constantsToExport")
.split("\n")
.filter(Boolean);
const filesWithoutConstantsToExport = [];
const filesWithConstantsToExportButNotGetConstants = [];
const filesExplicitlyNotRequiringMainQueueSetup = [];
tmFiles
.filter((filename) => {
if (filename.includes("microsoft-fork-of-react-native")) {
return false;
}
return /\.mm?$/.test(filename);
})
.map(abspath)
.forEach((filename) => {
const code = readFile(filename);
const relFilename = relpath(filename);
if (!/constantsToExport\s*{/.test(code)) {
filesWithoutConstantsToExport.push(relFilename);
return;
}
if (!/getConstants\s*{/.test(code)) {
filesWithConstantsToExportButNotGetConstants.push(relFilename);
return;
}
if (/requiresMainQueueSetup\s*{/.test(code)) {
const requiresMainQueueSetupRegex = /requiresMainQueueSetup\s*{\s*return\s+(?<requiresMainQueueSetup>YES|NO)/;
const requiresMainQueueSetupRegexMatch = requiresMainQueueSetupRegex.exec(
code
);
if (!requiresMainQueueSetupRegexMatch) {
throw new Error(
"Detected requiresMainQueueSetup method in file " +
relFilename +
" but was unable to parse the method return value"
);
}
const {
requiresMainQueueSetup,
} = requiresMainQueueSetupRegexMatch.groups;
if (requiresMainQueueSetup == "NO") {
filesExplicitlyNotRequiringMainQueueSetup.push(relFilename);
return;
}
}
const getConstantsTypeRegex = () => /-\s*\((?<type>.*)\)getConstants\s*{/;
const getConstantsTypeRegexMatch = getConstantsTypeRegex().exec(code);
if (!getConstantsTypeRegexMatch) {
throw new Error(
`Failed to parse return type of getConstants method in file ${relFilename}`
);
}
const getConstantsType = getConstantsTypeRegexMatch.groups.type;
const getConstantsBody = code
.split(getConstantsTypeRegex())[2]
.split("\n}")[0];
const newGetConstantsBody = `
__block ${getConstantsType} constants;
RCTUnsafeExecuteOnMainQueueSync(^{${getConstantsBody
.replace(/\n/g, "\n ")
.replace(/_bridge/g, "self->_bridge")
.replace(/return /g, "constants = ")}
});
return constants;
`;
writeFile(
filename,
code
.replace(getConstantsBody, newGetConstantsBody)
.replace("#import", "#import <React/RCTUtils.h>\n#import")
);
});
console.log("Files without constantsToExport: ");
filesWithoutConstantsToExport.forEach((file) => console.log(file));
console.log();
console.log("Files with constantsToExport but no getConstants: ");
filesWithConstantsToExportButNotGetConstants.forEach((file) =>
console.log(file)
);
console.log();
console.log("Files with requiresMainQueueSetup = NO: ");
filesExplicitlyNotRequiringMainQueueSetup.forEach((file) =>
console.log(file)
);
}
if (!module.parent) {
main();
}
```
Changelog: [Internal]
Reviewed By: fkgozali
Differential Revision: D21797048
fbshipit-source-id: a822a858fecdbe976e6197f8339e509dc7cd917f
Summary:
This change is especially important for Fabric when a lot of objects (mostly ShadowNodes) have shared ownership. Without this change, JSVM could not know that bunch of natively allocated objects should be deallocated.
Changelog: [Internal] Fabric-specific internal change.
Reviewed By: mdvacca
Differential Revision: D21798527
fbshipit-source-id: f2051721b074b99660cdfd6c5b75679b9792403e
Summary:
This diff updates our RCTKeyCommands code to be more resilient by copying the [FLEX strategy for key commands](https://github.com/Flipboard/FLEX/blob/master/Classes/Utility/Keyboard/FLEXKeyboardShortcutManager.m).
This strategy swizzles UIApplication handleKeyUIEvent which is further upstream than our UIResponder. It also allows for single key hotkeys like pressing just `r` instead of `cmd+r`. It does this without interfering with typing input by checking the first responder first.
I've also updated our hotkey handling to support using just the keys like `r` in addition to `cmd+r`. In addition to brining these hotkeys more in line with other iOS tools, they're also easier to use and do not suffer the same issues hotkeys with modifiers like `cmd` have where keys are dropped.
Changelog: [iOS] [Added] Allow hotkeys to be used without command key
Reviewed By: shergin
Differential Revision: D21635129
fbshipit-source-id: 36e0210a62b1f310473e152e8305165024cd338b
Summary:
This was added in D3343907 June 1st 2016, disabled in D3428043 June 16, 2016 and never re-enabled.
Changelog: [Internal]
Reviewed By: shergin
Differential Revision: D21635227
fbshipit-source-id: db51dfb6271359bea7da34b4e2a71931fc7c2a63
Summary:
This diff adds a new swizzling method for replacing instance methods with blocks.
Changelog: [Internal]
Reviewed By: shergin
Differential Revision: D21635131
fbshipit-source-id: c8061817bed66dad160efffee5a13c8714134540