Compare commits

..
Author SHA1 Message Date
Nicola Corti 185443e299 Migrate RNTester to use {usesCleartextTraffic} Manifest Placeholder (#52620)
Summary:
This creates a `debugOptimized` build type for React Native Android, meaning that we can run C++ optimization on the debug build, while still having the debugger enabled. This is aimed at improving the developer experience for folks developing on low-end devices or emulators.

Users that intend to debug can still use the `debug` variant where the full debug symbols are shipped.

## Changelog:

[ANDROID] [ADDED] - Create a debugOptimized buildType for Android


Test Plan:
Tested locally with RNTester by doing:

```
./gradlew installDebugOptimized
```

This is the output of the 3 generated .aar. The size difference is a proof that we're correctly stripping out the C++ debug symbols:

<img width="193" height="54" alt="Screenshot 2025-07-15 at 17 49 50" src="https://github.com/user-attachments/assets/584a0e8d-2d17-40d4-ac29-da09049d6554" />
<img width="235" height="51" alt="Screenshot 2025-07-15 at 17 49 39" src="https://github.com/user-attachments/assets/eda8f9e7-3509-4334-8c16-990e55caa04d" />
<img width="184" height="52" alt="Screenshot 2025-07-15 at 17 49 32" src="https://github.com/user-attachments/assets/a5c94385-bc00-4484-b43e-088ee039827f" />

Rollback Plan:

Reviewed By: cipolleschi

Differential Revision: D78351347

Pulled By: cortinico
2025-07-18 07:43:44 -07:00
71 changed files with 184 additions and 493 deletions
+36
View File
@@ -0,0 +1,36 @@
{
"arrowParens": "avoid",
"bracketSameLine": true,
"bracketSpacing": false,
"requirePragma": true,
"singleQuote": true,
"trailingComma": "all",
"endOfLine": "lf",
"overrides": [
{
"files": ["*.code-workspace"],
"options": {
"parser": "json"
}
},
{
"files": [
"*.js",
"*.js.flow"
],
"options": {
"parser": "hermes"
}
},
{
"files": [
"**/__docs__/*.md"
],
"options": {
"parser": "markdown",
"proseWrap": "always",
"requirePragma": false
}
}
]
}
-48
View File
@@ -1,48 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
module.exports = {
arrowParens: 'avoid',
bracketSameLine: true,
bracketSpacing: false,
requirePragma: true,
singleQuote: true,
trailingComma: 'all',
endOfLine: 'lf',
plugins: [
// Using module.parent and createRequire hack to simulate prettier v2 plugin resolution behavior.
// The hack allows us to resolve the plugin from the install location of prettier.
(module.parent
? require('module').createRequire(module.parent.id)
: require
).resolve('prettier-plugin-hermes-parser'),
],
overrides: [
{
files: ['*.code-workspace'],
options: {
parser: 'json',
},
},
{
files: ['*.js', '*.js.flow'],
options: {
parser: 'hermes',
},
},
{
files: ['**/__docs__/*.md'],
options: {
parser: 'markdown',
proseWrap: 'always',
requirePragma: false,
},
},
],
};
+2 -2
View File
@@ -56,8 +56,8 @@
"@electron/packager": "^18.3.6",
"@jest/create-cache-key-function": "^29.7.0",
"@microsoft/api-extractor": "^7.52.2",
"@react-native/metro-babel-transformer": "0.82.0-main",
"@react-native/metro-config": "0.82.0-main",
"@react-native/metro-babel-transformer": "0.81.0-main",
"@react-native/metro-config": "0.81.0-main",
"@tsconfig/node22": "22.0.2",
"@types/react": "^19.1.0",
"@typescript-eslint/parser": "^8.36.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/assets-registry",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "Asset support code for React Native.",
"license": "MIT",
"repository": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/babel-plugin-codegen",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "Babel plugin to generate native module and view manager code for React Native.",
"license": "MIT",
"repository": {
@@ -26,7 +26,7 @@
],
"dependencies": {
"@babel/traverse": "^7.25.3",
"@react-native/codegen": "0.82.0-main"
"@react-native/codegen": "0.81.0-main"
},
"devDependencies": {
"@babel/core": "^7.25.2"
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/community-cli-plugin",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "Core CLI commands for React Native",
"keywords": [
"react-native",
@@ -22,7 +22,7 @@
"dist"
],
"dependencies": {
"@react-native/dev-middleware": "0.82.0-main",
"@react-native/dev-middleware": "0.81.0-main",
"debug": "^4.4.0",
"invariant": "^2.2.4",
"metro": "^0.83.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/core-cli-utils",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "React Native CLI library for Frameworks to build on",
"license": "MIT",
"main": "./src/index.flow.js",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/debugger-frontend",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "Debugger frontend for React Native based on Chrome DevTools",
"keywords": [
"react-native",
@@ -1,62 +0,0 @@
#!/usr/bin/env dotslash
// @generated SignedSource<<686df5695b32a90cd465412d979a1a3f>>
{
"name": "React Native DevTools",
"platforms": {
"linux-aarch64": {
"size": 113511487,
"hash": "sha256",
"digest": "c22f7d5e029357f05055ce7c56cc284a0d441d558e103a7b7ebae118c67d0b95",
"providers": [
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQPRilWDJV8xyKEv9lqYn7Hf2kyZg6nqW8KctGZCXsU95lS_MTFyAmEULrB3J6hGCjqBY2Zl-uq_FjERAIgzbZFWX2eceacTbutBSOKEj6QTZzkVN_L1zPh2lGh24x29MHpkwUzw4E-kAXV43f-11QxqD7orI1QicyOnsmUqeRNKE_QjM9rNRsw6iE8"
}
],
"format": "tar.gz",
"path": "React Native DevTools-linux-arm64/React Native DevTools"
},
"linux-x86_64": {
"size": 113244728,
"hash": "sha256",
"digest": "d749aee18d6c969f033511ed631b439c578068cc20786974bc0ee707c6c2f177",
"providers": [
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQP8NAAXjHJ-9NJThip1nnmVim4yiS1tdYAXmWl2Remiluq5f13nXA8YEadsmGRLWxXz2WJouHXSK47ea4a30fxewyKeTt0niFn3T-lr_91m3Ve5ZS-FOZ_9CRVCkv5zC-Z1FlXJGOnphfWetIU10Pw9tho3IzjdSNlXle0XTrkJ2AyKSl4cKPaX"
}
],
"format": "tar.gz",
"path": "React Native DevTools-linux-x64/React Native DevTools"
},
"macos-aarch64": {
"size": 108805847,
"hash": "sha256",
"digest": "dc4a6cfa3d2d8646db8793ef497e43ad72be54bb14ed96345d059880ff72ce1e",
"providers": [
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQPuZx9G4h30mUDM0hZ8y74LbzAi7-S6jnOqq8uyfDdIptLwIgpb8CKB8F2tduvaBFMGd7obwXd2NVy01r6XKVdgAK5-QTp8PYUPUE7VSCi6QAqobXcQ2uq_lY-jgyj8XkV4Ua4gA6KR609Bmh-beSTOrSqMymqVodaGqoGjB_9jVwfD99_PfFlex-Ta"
}
],
"format": "tar.gz",
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
},
"macos-x86_64": {
"size": 113766610,
"hash": "sha256",
"digest": "74178859ce6a1c26c32055e192b1b123822c76c827ea86a6dfdb5463b89b1ace",
"providers": [
{
"type": "http",
"url": "https://scontent.xx.fbcdn.net/mci_ab/uap/?ab_b=m&ab_page=react_native_devtools_binaries&ab_entry=AQN6DjGhr8_L7N_cwrh8pi3cfNcbn9dot_QQpgwe3Vv-780FDgi6JUeMvbo_wbFoT2rogxfHZ0-NbXbKdWYwhGUoGeQuikT57QnVMOYuPUTjlQUgYy0Ng9XPhq3iSEYwlMV1XsUJuPFRUg-hIHcgaaA55JoTLXZ1fLYydhHnmgRxGHc4pxaHvSpTtQ"
}
],
"format": "tar.gz",
"path": "React Native DevTools.app/Contents/MacOS/React Native DevTools"
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/debugger-shell",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "Experimental debugger shell for React Native for use with @react-native/debugger-frontend",
"keywords": [
"react-native",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/dev-middleware",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "Dev server middleware for React Native",
"keywords": [
"react-native",
@@ -23,7 +23,7 @@
],
"dependencies": {
"@isaacs/ttlcache": "^1.4.1",
"@react-native/debugger-frontend": "0.82.0-main",
"@react-native/debugger-frontend": "0.81.0-main",
"chrome-launcher": "^0.15.2",
"chromium-edge-launcher": "^0.2.0",
"connect": "^3.6.5",
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-config",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "ESLint config for React Native",
"license": "MIT",
"repository": {
@@ -22,7 +22,7 @@
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/eslint-parser": "^7.25.1",
"@react-native/eslint-plugin": "0.82.0-main",
"@react-native/eslint-plugin": "0.81.0-main",
"@typescript-eslint/eslint-plugin": "^8.36.0",
"@typescript-eslint/parser": "^8.36.0",
"eslint-config-prettier": "^8.5.0",
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-plugin",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "ESLint rules for @react-native/eslint-config",
"license": "MIT",
"repository": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-plugin-specs",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "ESLint rules to validate NativeModule and Component Specs",
"license": "MIT",
"repository": {
@@ -26,7 +26,7 @@
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/plugin-transform-flow-strip-types": "^7.25.2",
"@react-native/codegen": "0.82.0-main",
"@react-native/codegen": "0.81.0-main",
"make-dir": "^2.1.0",
"pirates": "^4.0.1",
"source-map-support": "0.5.0"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/gradle-plugin",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "Gradle Plugin for React Native",
"license": "MIT",
"repository": {
@@ -100,10 +100,10 @@ abstract class ReactExtension @Inject constructor(val project: Project) {
* Allows to specify the debuggable variants (by default just 'debug'). Variants in this list will
* not be bundled (the bundle file will not be created and won't be copied over).
*
* Default: ['debug', 'debugOptimized']
* Default: ['debug']
*/
val debuggableVariants: ListProperty<String> =
objects.listProperty(String::class.java).convention(listOf("debug", "debugOptimized"))
objects.listProperty(String::class.java).convention(listOf("debug"))
/** Hermes Config */
@@ -42,16 +42,6 @@ internal object AgpConfiguratorUtils {
getByName("release").apply {
manifestPlaceholders["usesCleartextTraffic"] = "false"
}
maybeCreate("debugOptimized").apply {
manifestPlaceholders["usesCleartextTraffic"] = "true"
initWith(debug)
externalNativeBuild {
cmake {
arguments("-DCMAKE_BUILD_TYPE=Release")
matchingFallbacks += listOf("release")
}
}
}
}
}
}
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/metro-config",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "Metro configuration for React Native.",
"license": "MIT",
"repository": {
@@ -26,8 +26,8 @@
"dist"
],
"dependencies": {
"@react-native/js-polyfills": "0.82.0-main",
"@react-native/metro-babel-transformer": "0.82.0-main",
"@react-native/js-polyfills": "0.81.0-main",
"@react-native/metro-babel-transformer": "0.81.0-main",
"metro-config": "^0.83.0",
"metro-runtime": "^0.83.0"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/new-app-screen",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "NewAppScreen component for React Native",
"keywords": [
"react-native"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/normalize-colors",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "Color normalization for React Native.",
"license": "MIT",
"repository": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/js-polyfills",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "Polyfills for React Native.",
"license": "MIT",
"repository": {
@@ -1,6 +1,6 @@
{
"name": "@react-native/babel-preset",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "Babel preset for React Native applications",
"repository": {
"type": "git",
@@ -66,7 +66,7 @@
"@babel/plugin-transform-typescript": "^7.25.2",
"@babel/plugin-transform-unicode-regex": "^7.24.7",
"@babel/template": "^7.25.0",
"@react-native/babel-plugin-codegen": "0.82.0-main",
"@react-native/babel-plugin-codegen": "0.81.0-main",
"babel-plugin-syntax-hermes-parser": "0.29.1",
"babel-plugin-transform-flow-enums": "^0.0.2",
"react-refresh": "^0.14.0"
@@ -1,6 +1,6 @@
{
"name": "@react-native/metro-babel-transformer",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "Babel transformer for React Native applications.",
"repository": {
"type": "git",
@@ -27,7 +27,7 @@
],
"dependencies": {
"@babel/core": "^7.25.2",
"@react-native/babel-preset": "0.82.0-main",
"@react-native/babel-preset": "0.81.0-main",
"hermes-parser": "0.29.1",
"nullthrows": "^1.1.1"
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/codegen",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "Code generation tools for React Native",
"license": "MIT",
"repository": {
@@ -1,6 +1,6 @@
{
"name": "@react-native/compatibility-check",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "Check a React Native app's boundary between JS and Native for incompatibilities",
"license": "MIT",
"repository": {
@@ -29,7 +29,7 @@
"dist"
],
"dependencies": {
"@react-native/codegen": "0.82.0-main"
"@react-native/codegen": "0.81.0-main"
},
"devDependencies": {
"flow-remove-types": "^2.237.2",
@@ -1,6 +1,6 @@
{
"name": "@react-native/popup-menu-android",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "PopupMenu for the Android platform",
"main": "index.js",
"files": [
@@ -21,7 +21,7 @@
},
"license": "MIT",
"devDependencies": {
"@react-native/codegen": "0.82.0-main"
"@react-native/codegen": "0.81.0-main"
},
"peerDependencies": {
"@types/react": "^19.1.0",
@@ -1,6 +1,6 @@
{
"name": "@react-native/oss-library-example",
"version": "0.82.0-main",
"version": "0.81.0-main",
"private": true,
"description": "Package that includes native module exapmle, native component example, targets both the old and the new architecture. It should serve as an example of a real-world OSS library.",
"license": "MIT",
@@ -26,7 +26,7 @@
],
"devDependencies": {
"@babel/core": "^7.25.2",
"@react-native/babel-preset": "0.82.0-main",
"@react-native/babel-preset": "0.81.0-main",
"react-native": "1000.0.0"
},
"peerDependencies": {
@@ -50,9 +50,8 @@ class LazyVector {
const_reference at(size_type pos) const {
#ifndef _LIBCPP_NO_EXCEPTIONS
if (!(pos < _size)) {
if (!(pos < _size))
throw std::out_of_range("out of range");
}
#else
assert(pos < _size || !"out of range");
#endif
@@ -25,13 +25,11 @@ static NSUInteger RCTDeviceFreeMemory(void)
kern_return_t kern;
kern = host_page_size(host_port, &page_size);
if (kern != KERN_SUCCESS) {
if (kern != KERN_SUCCESS)
return 0;
}
kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
if (kern != KERN_SUCCESS) {
if (kern != KERN_SUCCESS)
return 0;
}
return (vm_stat.free_count - vm_stat.speculative_count) * page_size;
}
@@ -280,9 +278,8 @@ static NSUInteger RCTDeviceFreeMemory(void)
- (void)calculateMaxBufferCount
{
NSUInteger bytes = CGImageGetBytesPerRow(self.currentFrame.CGImage) * CGImageGetHeight(self.currentFrame.CGImage);
if (bytes == 0) {
if (bytes == 0)
bytes = 1024;
}
NSUInteger max = 0;
if (self.maxBufferSize > 0) {
@@ -66,7 +66,7 @@
static NSString *const RCTJSThreadName = @"com.facebook.react.JavaScript";
using RCTPendingCall = void (^)();
typedef void (^RCTPendingCall)();
using namespace facebook::jsi;
using namespace facebook::react;
@@ -11,11 +11,11 @@
#import <jsinspector-modern/ReactCdp.h>
using RCTInspectorNetworkListener = facebook::react::jsinspector_modern::NetworkRequestListener;
typedef facebook::react::jsinspector_modern::NetworkRequestListener RCTInspectorNetworkListener;
using RCTInspectorNetworkExecutor = facebook::react::jsinspector_modern::ScopedExecutor<RCTInspectorNetworkListener>;
typedef facebook::react::jsinspector_modern::ScopedExecutor<RCTInspectorNetworkListener> RCTInspectorNetworkExecutor;
using RCTInspectorLoadNetworkResourceRequest = facebook::react::jsinspector_modern::LoadNetworkResourceRequest;
typedef facebook::react::jsinspector_modern::LoadNetworkResourceRequest RCTInspectorLoadNetworkResourceRequest;
/**
* A helper class that wraps around NSURLSession to make network requests.
@@ -8,7 +8,7 @@
#import "RCTInspectorNetworkHelper.h"
#import <React/RCTLog.h>
using ListenerBlock = void (^)(RCTInspectorNetworkListener *);
typedef void (^ListenerBlock)(RCTInspectorNetworkListener *);
@interface RCTInspectorNetworkHelper () <NSURLSessionDataDelegate>
@property (nonatomic, strong) NSURLSession *session;
+2 -2
View File
@@ -205,7 +205,7 @@ RCT_ENUM_CONVERTER(
UIFontWeightRegular,
doubleValue)
using RCTFontStyle = BOOL;
typedef BOOL RCTFontStyle;
RCT_ENUM_CONVERTER(
RCTFontStyle,
(@{
@@ -216,7 +216,7 @@ RCT_ENUM_CONVERTER(
NO,
boolValue)
using RCTFontVariantDescriptor = NSDictionary;
typedef NSDictionary RCTFontVariantDescriptor;
+ (RCTFontVariantDescriptor *)RCTFontVariantDescriptor:(id)json
{
static NSDictionary *mapping;
@@ -71,9 +71,8 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder)
- (void)beginRefreshingProgrammatically
{
if (!_hasMovedToWindow) {
if (!_hasMovedToWindow)
return;
}
UInt64 beginRefreshingTimestamp = _currentRefreshingStateTimestamp;
_refreshingProgrammatically = YES;
@@ -109,9 +108,8 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder)
- (void)endRefreshingProgrammatically
{
if (!_hasMovedToWindow) {
if (!_hasMovedToWindow)
return;
}
// The contentOffset of the scrollview MUST be greater than the contentInset before calling
// endRefreshing otherwise the next pull to refresh will not work properly.
UIScrollView *scrollView = self.scrollView;
@@ -599,7 +599,7 @@ android {
publishing {
multipleVariants {
withSourcesJar()
includeBuildTypeValues("debug", "release", "debugOptimized")
includeBuildTypeValues("debug", "release")
}
}
@@ -607,15 +607,6 @@ android {
unitTests { isIncludeAndroidResources = true }
targetSdk = libs.versions.targetSdk.get().toInt()
}
buildTypes {
create("debugOptimized") {
initWith(getByName("debug"))
externalNativeBuild {
cmake { arguments("-DCMAKE_BUILD_TYPE=Release", "-DREACT_NATIVE_DEBUG_OPTIMIZED=True") }
}
}
}
}
tasks.withType<KotlinCompile>().configureEach {
@@ -306,12 +306,6 @@ android {
}
}
}
buildTypes {
create("debugOptimized") {
initWith(getByName("debug"))
externalNativeBuild { cmake { arguments("-DCMAKE_BUILD_TYPE=Release") } }
}
}
}
sourceSets.getByName("main") {
@@ -675,18 +675,6 @@ public class FabricUIManager
: null);
}
@AnyThread
@ThreadConfined(ANY)
@UnstableReactNativeAPI
public PreparedLayout reusePreparedLayoutWithNewReactTags(
PreparedLayout preparedLayout, int[] reactTags) {
return new PreparedLayout(
preparedLayout.getLayout(),
preparedLayout.getMaximumNumberOfLines(),
preparedLayout.getVerticalOffset(),
reactTags);
}
@AnyThread
@ThreadConfined(ANY)
@UnstableReactNativeAPI
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<93aab733661b558c1701b728c18b3d00>>
* @generated SignedSource<<c8b19934d19d6b4514a0395edecb1330>>
*/
/**
@@ -23,5 +23,5 @@ public open class ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android :
// We could use JNI to get the defaults from C++,
// but that is more expensive than just duplicating the defaults here.
override fun preventShadowTreeCommitExhaustionWithLocking(): Boolean = true
}
@@ -407,9 +407,6 @@ public class ReactScrollView extends ScrollView
if (mMaintainVisibleContentPositionHelper != null) {
mMaintainVisibleContentPositionHelper.stop();
}
if (mVirtualViewContainerState != null) {
mVirtualViewContainerState.cleanup();
}
}
@Override
@@ -9,7 +9,6 @@ package com.facebook.react.views.scroll
import android.graphics.Rect
import android.view.ViewGroup
import android.view.ViewTreeObserver
import com.facebook.common.logging.FLog
import com.facebook.react.common.build.ReactBuildConfig
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
@@ -47,34 +46,13 @@ private fun rectsOverlap(rect1: Rect, rect2: Rect): Boolean {
return true
}
internal class VirtualViewContainerState {
internal class VirtualViewContainerState(private val scrollView: ViewGroup) {
private val prerenderRatio: Double = ReactNativeFeatureFlags.virtualViewPrerenderRatio()
private val detectWindowFocus = ReactNativeFeatureFlags.enableVirtualViewWindowFocusDetection()
private val virtualViews: MutableSet<VirtualView> = mutableSetOf()
private val emptyRect: Rect = Rect()
private val visibleRect: Rect = Rect()
private val prerenderRect: Rect = Rect()
private val onWindowFocusChangeListener =
ViewTreeObserver.OnWindowFocusChangeListener {
debugLog("onWindowFocusChanged")
updateModes()
}
private val scrollView: ViewGroup
constructor(scrollView: ViewGroup) {
this.scrollView = scrollView
if (detectWindowFocus) {
scrollView.viewTreeObserver.addOnWindowFocusChangeListener(onWindowFocusChangeListener)
}
}
public fun cleanup() {
if (detectWindowFocus) {
scrollView.viewTreeObserver.removeOnWindowFocusChangeListener(onWindowFocusChangeListener)
}
}
public fun onChange(virtualView: VirtualView) {
if (virtualViews.add(virtualView)) {
@@ -94,7 +72,7 @@ internal class VirtualViewContainerState {
// Called on ScrollView onLayout or onScroll
public fun updateState() {
debugLog("updateState")
debugLog("VirtualViewContainer.updateState")
updateModes()
}
@@ -114,16 +92,8 @@ internal class VirtualViewContainerState {
when {
rect.isEmpty -> {}
rectsOverlap(rect, visibleRect) -> {
mode = VirtualViewMode.Visible
thresholdRect = visibleRect
if (detectWindowFocus) {
if (scrollView.hasWindowFocus()) {
mode = VirtualViewMode.Visible
} else {
mode = VirtualViewMode.Prerender
}
} else {
mode = VirtualViewMode.Visible
}
}
rectsOverlap(rect, prerenderRect) -> {
mode = VirtualViewMode.Prerender
@@ -145,7 +115,7 @@ private val IS_DEBUG_BUILD =
ReactBuildConfig.DEBUG || ReactBuildConfig.IS_INTERNAL_BUILD || ReactBuildConfig.ENABLE_PERFETTO
internal inline fun debugLog(subtag: String, block: () -> String = { "" }) {
if (IS_DEBUG_BUILD && ReactNativeFeatureFlags.enableVirtualViewDebugFeatures()) {
if (IS_DEBUG_BUILD) {
FLog.d("$DEBUG_TAG:$subtag", block())
}
}
@@ -18,6 +18,5 @@ import com.facebook.proguard.annotations.DoNotStrip
internal class PreparedLayout(
val layout: Layout,
val maximumNumberOfLines: Int,
val verticalOffset: Float,
val reactTags: IntArray,
val verticalOffset: Float
)
@@ -27,7 +27,7 @@ import com.facebook.proguard.annotations.DoNotStrip
import com.facebook.react.uimanager.BackgroundStyleApplicator
import com.facebook.react.uimanager.ReactCompoundView
import com.facebook.react.uimanager.style.Overflow
import com.facebook.react.views.text.internal.span.ReactFragmentIndexSpan
import com.facebook.react.views.text.internal.span.ReactTagSpan
import kotlin.collections.ArrayList
import kotlin.math.roundToInt
@@ -310,9 +310,8 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re
override fun hasOverlappingRendering(): Boolean = false
override fun reactTagForTouch(touchX: Float, touchY: Float): Int =
getSpanInCoords(touchX.roundToInt(), touchY.roundToInt(), ReactFragmentIndexSpan::class.java)
?.fragmentIndex
?.let { preparedLayout?.reactTags[it] } ?: id
getSpanInCoords(touchX.roundToInt(), touchY.roundToInt(), ReactTagSpan::class.java)?.reactTag
?: id
@RequiresApi(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
private object Api34Utils {
@@ -42,8 +42,6 @@ import com.facebook.react.views.text.internal.span.ReactAbsoluteSizeSpan
import com.facebook.react.views.text.internal.span.ReactBackgroundColorSpan
import com.facebook.react.views.text.internal.span.ReactClickableSpan
import com.facebook.react.views.text.internal.span.ReactForegroundColorSpan
import com.facebook.react.views.text.internal.span.ReactFragmentIndexSpan
import com.facebook.react.views.text.internal.span.ReactLinkSpan
import com.facebook.react.views.text.internal.span.ReactOpacitySpan
import com.facebook.react.views.text.internal.span.ReactStrikethroughSpan
import com.facebook.react.views.text.internal.span.ReactTagSpan
@@ -220,8 +218,7 @@ internal object TextLayoutManager {
context: Context,
fragments: MapBuffer,
sb: SpannableStringBuilder,
ops: MutableList<SetSpanOperation>,
outputReactTags: IntArray?
ops: MutableList<SetSpanOperation>
) {
for (i in 0 until fragments.count) {
val fragment = fragments.getMapBuffer(i)
@@ -252,11 +249,7 @@ internal object TextLayoutManager {
(textAttributes.mAccessibilityRole ==
ReactAccessibilityDelegate.AccessibilityRole.LINK)
if (roleIsLink) {
if (ReactNativeFeatureFlags.enablePreparedTextLayout()) {
ops.add(SetSpanOperation(start, end, ReactLinkSpan(i)))
} else {
ops.add(SetSpanOperation(start, end, ReactClickableSpan(reactTag)))
}
ops.add(SetSpanOperation(start, end, ReactClickableSpan(reactTag)))
}
if (textAttributes.mIsColorSet) {
ops.add(SetSpanOperation(start, end, ReactForegroundColorSpan(textAttributes.mColor)))
@@ -314,14 +307,7 @@ internal object TextLayoutManager {
start, end, CustomLineHeightSpan(textAttributes.effectiveLineHeight)))
}
if (ReactNativeFeatureFlags.enablePreparedTextLayout()) {
ops.add(SetSpanOperation(start, end, ReactFragmentIndexSpan(i)))
if (outputReactTags != null) {
outputReactTags[i] = reactTag
}
} else {
ops.add(SetSpanOperation(start, end, ReactTagSpan(reactTag)))
}
ops.add(SetSpanOperation(start, end, ReactTagSpan(reactTag)))
}
}
}
@@ -337,8 +323,7 @@ internal object TextLayoutManager {
private fun buildSpannableFromFragmentsOptimized(
context: Context,
fragments: MapBuffer,
outputReactTags: IntArray?
fragments: MapBuffer
): Spannable {
val text = StringBuilder()
val parsedFragments = ArrayList<FragmentAttributes>(fragments.count)
@@ -378,7 +363,7 @@ internal object TextLayoutManager {
val spannable = SpannableString(text)
var start = 0
for ((i, fragment) in parsedFragments.withIndex()) {
for (fragment in parsedFragments) {
val end = start + fragment.length
val spanFlags =
if (start == 0) Spannable.SPAN_INCLUSIVE_INCLUSIVE else Spannable.SPAN_EXCLUSIVE_INCLUSIVE
@@ -401,11 +386,7 @@ internal object TextLayoutManager {
ReactAccessibilityDelegate.AccessibilityRole.LINK)
if (roleIsLink) {
if (ReactNativeFeatureFlags.enablePreparedTextLayout()) {
spannable.setSpan(ReactLinkSpan(i), start, end, spanFlags)
} else {
spannable.setSpan(ReactClickableSpan(fragment.reactTag), start, end, spanFlags)
}
spannable.setSpan(ReactClickableSpan(fragment.reactTag), start, end, spanFlags)
}
if (fragment.props.isColorSet) {
@@ -472,14 +453,7 @@ internal object TextLayoutManager {
CustomLineHeightSpan(fragment.props.effectiveLineHeight), start, end, spanFlags)
}
if (ReactNativeFeatureFlags.enablePreparedTextLayout()) {
spannable.setSpan(ReactFragmentIndexSpan(i), start, end, spanFlags)
if (outputReactTags != null) {
outputReactTags[i] = fragment.reactTag
}
} else {
spannable.setSpan(ReactTagSpan(fragment.reactTag), start, end, spanFlags)
}
spannable.setSpan(ReactTagSpan(fragment.reactTag), start, end, spanFlags)
}
start = end
@@ -500,10 +474,7 @@ internal object TextLayoutManager {
} else {
text =
createSpannableFromAttributedString(
context,
attributedString.getMapBuffer(AS_KEY_FRAGMENTS),
reactTextViewManagerCallback,
null)
context, attributedString, reactTextViewManagerCallback)
}
return text
@@ -511,12 +482,13 @@ internal object TextLayoutManager {
private fun createSpannableFromAttributedString(
context: Context,
fragments: MapBuffer,
reactTextViewManagerCallback: ReactTextViewManagerCallback?,
outputReactTags: IntArray?
attributedString: MapBuffer,
reactTextViewManagerCallback: ReactTextViewManagerCallback?
): Spannable {
if (ReactNativeFeatureFlags.enableAndroidTextMeasurementOptimizations()) {
val spannable = buildSpannableFromFragmentsOptimized(context, fragments, outputReactTags)
val spannable =
buildSpannableFromFragmentsOptimized(
context, attributedString.getMapBuffer(AS_KEY_FRAGMENTS))
reactTextViewManagerCallback?.onPostProcessSpannable(spannable)
return spannable
@@ -528,7 +500,7 @@ internal object TextLayoutManager {
// a new spannable will be wiped out
val ops: MutableList<SetSpanOperation> = ArrayList()
buildSpannableFromFragments(context, fragments, sb, ops, outputReactTags)
buildSpannableFromFragments(context, attributedString.getMapBuffer(AS_KEY_FRAGMENTS), sb, ops)
// TODO T31905686: add support for inline Images
// While setting the Spans on the final text, we also check whether any of them are images.
@@ -784,11 +756,7 @@ internal object TextLayoutManager {
heightYogaMeasureMode: YogaMeasureMode,
reactTextViewManagerCallback: ReactTextViewManagerCallback?
): PreparedLayout {
val fragments = attributedString.getMapBuffer(AS_KEY_FRAGMENTS)
val reactTags = IntArray(fragments.count)
val text =
createSpannableFromAttributedString(
context, fragments, reactTextViewManagerCallback, reactTags)
val text = getOrCreateSpannableForText(context, attributedString, reactTextViewManagerCallback)
val baseTextAttributes =
TextAttributeProps.fromMapBuffer(attributedString.getMapBuffer(AS_KEY_BASE_ATTRIBUTES))
val layout =
@@ -811,7 +779,7 @@ internal object TextLayoutManager {
getVerticalOffset(
layout, paragraphAttributes, height, heightYogaMeasureMode, maximumNumberOfLines)
return PreparedLayout(layout, maximumNumberOfLines, verticalOffset, reactTags)
return PreparedLayout(layout, maximumNumberOfLines, verticalOffset)
}
@JvmStatic
@@ -1,14 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.views.text.internal.span
/**
* Maps a section of the text to the index of the AttributedString fragment originally used to
* create it.
*/
internal class ReactFragmentIndexSpan(val fragmentIndex: Int) : ReactSpan
@@ -1,47 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.views.text.internal.span
import android.text.TextPaint
import android.text.style.ClickableSpan
import android.view.View
import com.facebook.react.bridge.ReactContext
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.views.text.PreparedLayoutTextView
import com.facebook.react.views.text.TextLayoutManager
import com.facebook.react.views.view.ViewGroupClickEvent
/**
* This class is used in [TextLayoutManager] to linkify and style a span of text with
* accessibilityRole="link". This is needed to make nested Text components accessible.
*
* For example, if your React component looks like this:
* ```js
* <Text>
* Some text with
* <Text onPress={onPress} accessible={true} accessibilityRole="link">a link</Text>
* in the middle.
* </Text>
* ```
*/
internal class ReactLinkSpan(val fragmentIndex: Int) : ClickableSpan(), ReactSpan {
override fun onClick(view: View) {
val context = view.context as ReactContext
val textView = view as? PreparedLayoutTextView ?: return
val preparedLayout = textView.preparedLayout ?: return
val reactTag = preparedLayout.reactTags[fragmentIndex]
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, reactTag)
eventDispatcher?.dispatchEvent(
ViewGroupClickEvent(UIManagerHelper.getSurfaceId(context), reactTag))
}
override fun updateDrawState(ds: TextPaint) {
// no super call so we don't change the link color or add an underline by default, as the
// superclass does.
}
}
@@ -15,7 +15,6 @@ import androidx.annotation.VisibleForTesting
import com.facebook.common.logging.FLog
import com.facebook.react.R
import com.facebook.react.common.build.ReactBuildConfig
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
import com.facebook.react.uimanager.ReactRoot
import com.facebook.react.views.scroll.VirtualView
import com.facebook.react.views.scroll.VirtualViewContainer
@@ -203,7 +202,7 @@ public class ReactVirtualViewExperimental(context: Context) :
}
internal inline fun debugLog(subtag: String, block: () -> String = { "" }) {
if (IS_DEBUG_BUILD && ReactNativeFeatureFlags.enableVirtualViewDebugFeatures()) {
if (IS_DEBUG_BUILD) {
FLog.d("$DEBUG_TAG:$subtag", "${block()} [$id][$nativeId]")
}
}
@@ -25,6 +25,4 @@ target_link_libraries(
reactnative
)
target_compile_reactnative_options(hermes_executor PRIVATE)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
target_compile_options(hermes_executor PRIVATE -DHERMES_ENABLE_DEBUGGER=1)
endif()
target_compile_options(hermes_executor PRIVATE $<$<CONFIG:Debug>:-DHERMES_ENABLE_DEBUGGER=1>)
@@ -27,6 +27,4 @@ target_link_libraries(hermesinstancejni
)
target_compile_reactnative_options(hermesinstancejni PRIVATE)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
target_compile_options(hermesinstancejni PRIVATE -DHERMES_ENABLE_DEBUGGER=1)
endif ()
target_compile_options(hermesinstancejni PRIVATE $<$<CONFIG:Debug>:-DHERMES_ENABLE_DEBUGGER=1>)
@@ -17,9 +17,7 @@ add_library(rninstance
)
target_compile_reactnative_options(rninstance PRIVATE)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
target_compile_options(rninstance PRIVATE -DHERMES_ENABLE_DEBUGGER=1)
endif ()
target_compile_options(rninstance PRIVATE $<$<CONFIG:Debug>:-DHERMES_ENABLE_DEBUGGER=1>)
target_merge_so(rninstance)
target_include_directories(rninstance PUBLIC .)
@@ -53,9 +53,9 @@ class CxxModule {
class SyncTagType {};
public:
using Provider = std::function<std::unique_ptr<CxxModule>()>;
typedef std::function<std::unique_ptr<CxxModule>()> Provider;
using Callback = std::function<void(std::vector<folly::dynamic>)>;
typedef std::function<void(std::vector<folly::dynamic>)> Callback;
constexpr static AsyncTagType AsyncTag = AsyncTagType();
constexpr static SyncTagType SyncTag = SyncTagType();
@@ -68,8 +68,8 @@ namespace detail {
// only for types compatible with folly::dynamic.
template <typename T>
struct is_dynamic {
using type = typename std::
enable_if<std::is_assignable<folly::dynamic, T>::value, T>::type;
typedef typename std::
enable_if<std::is_assignable<folly::dynamic, T>::value, T>::type type;
};
} // end namespace detail
@@ -16,9 +16,8 @@ using namespace facebook::react;
namespace {
int tempFileFromString(std::string contents) {
const char* tmpDir = getenv("TMPDIR");
if (tmpDir == nullptr) {
if (tmpDir == nullptr)
tmpDir = "/tmp";
}
std::string tmp{tmpDir};
tmp += "/temp.XXXXXX";
@@ -26,7 +26,7 @@ target_link_libraries(hermes_executor_common
)
target_compile_reactnative_options(hermes_executor_common PRIVATE)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
if(${CMAKE_BUILD_TYPE} MATCHES Debug)
target_compile_options(
hermes_executor_common
PRIVATE
@@ -17,7 +17,7 @@ add_library(hermes_inspector_modern
target_compile_reactnative_options(hermes_inspector_modern PRIVATE)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
if(${CMAKE_BUILD_TYPE} MATCHES Debug)
target_compile_options(
hermes_inspector_modern
PRIVATE
@@ -28,9 +28,7 @@ target_link_libraries(jsinspector
reactperflogger
)
target_compile_reactnative_options(jsinspector PRIVATE)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
target_compile_options(jsinspector PRIVATE
-DREACT_NATIVE_DEBUGGER_ENABLED=1
-DREACT_NATIVE_DEBUGGER_ENABLED_DEVONLY=1
)
endif ()
target_compile_options(jsinspector PRIVATE
$<$<CONFIG:Debug>:-DREACT_NATIVE_DEBUGGER_ENABLED=1>
$<$<CONFIG:Debug>:-DREACT_NATIVE_DEBUGGER_ENABLED_DEVONLY=1>
)
@@ -14,7 +14,7 @@ enum ReactNativeLogLevel {
ReactNativeLogLevelFatal = 4
};
using reactnativelogfunctype = void (*)(ReactNativeLogLevel, const char*);
typedef void (*reactnativelogfunctype)(ReactNativeLogLevel, const char*);
#ifdef __cplusplus
extern "C" {
@@ -21,6 +21,6 @@ endif()
target_compile_reactnative_options(react_debug PRIVATE)
target_compile_options(react_debug PRIVATE -Wpedantic)
if(NOT ${CMAKE_BUILD_TYPE} MATCHES Debug AND NOT REACT_NATIVE_DEBUG_OPTIMIZED)
if(NOT ${CMAKE_BUILD_TYPE} MATCHES Debug)
target_compile_options(react_debug PUBLIC -DNDEBUG)
endif()
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<16c5fdf431579bbfd454a28c06f28c41>>
* @generated SignedSource<<1de02178e1be302bb4b19501950b260a>>
*/
/**
@@ -27,9 +27,7 @@ class ReactNativeFeatureFlagsOverridesOSSExperimental : public ReactNativeFeatur
public:
ReactNativeFeatureFlagsOverridesOSSExperimental() = default;
bool preventShadowTreeCommitExhaustionWithLocking() override {
return true;
}
};
} // namespace facebook::react
@@ -190,8 +190,7 @@ TextMeasurement TextLayoutManager::measure(
};
auto measurement =
(ReactNativeFeatureFlags::disableTextLayoutManagerCacheAndroid() ||
ReactNativeFeatureFlags::enablePreparedTextLayout())
ReactNativeFeatureFlags::disableTextLayoutManagerCacheAndroid()
? measureText()
: textMeasureCache_.get(
{.attributedString = attributedString,
@@ -311,19 +310,14 @@ TextLayoutManager::PreparedLayout TextLayoutManager::prepareLayout(
jfloat,
jfloat)>("prepareTextLayout");
static auto reusePreparedLayoutWithNewReactTags =
jni::findClassStatic("com/facebook/react/fabric/FabricUIManager")
->getMethod<JPreparedLayout::javaobject(
JPreparedLayout::javaobject, jintArray)>(
"reusePreparedLayoutWithNewReactTags");
const auto [key, preparedText] = preparedTextCache_.getWithKey(
return preparedTextCache_.get(
{.attributedString = attributedString,
.paragraphAttributes = paragraphAttributes,
.layoutConstraints = layoutConstraints},
[&]() {
[&] {
const auto& fabricUIManager =
contextContainer_->at<jni::global_ref<jobject>>("FabricUIManager");
auto attributedStringMB = JReadableMapBuffer::createWithContents(
toMapBuffer(attributedString));
auto paragraphAttributesMB = JReadableMapBuffer::createWithContents(
@@ -342,39 +336,6 @@ TextLayoutManager::PreparedLayout TextLayoutManager::prepareLayout(
minimumSize.height,
maximumSize.height))};
});
// PreparedTextCacheKey allows equality of layouts which are the same
// display-wise, but ShadowView fragments (and thus react tags) may have
// changed.
const auto& fragments = attributedString.getFragments();
const auto& cacheKeyFragments = key->attributedString.getFragments();
bool needsNewReactTags = [&] {
for (size_t i = 0; i < fragments.size(); i++) {
if (fragments[i].parentShadowView.tag !=
cacheKeyFragments[i].parentShadowView.tag) {
return true;
}
}
return false;
}();
if (needsNewReactTags) {
std::vector<int> reactTags(fragments.size());
for (size_t i = 0; i < reactTags.size(); i++) {
reactTags[i] = fragments[i].parentShadowView.tag;
}
auto javaReactTags = jni::JArrayInt::newArray(fragments.size());
javaReactTags->setRegion(
0, static_cast<jsize>(reactTags.size()), reactTags.data());
const auto& fabricUIManager =
contextContainer_->at<jni::global_ref<jobject>>("FabricUIManager");
return PreparedLayout{jni::make_global(reusePreparedLayoutWithNewReactTags(
fabricUIManager, preparedText->get(), javaReactTags.get()))};
} else {
return PreparedLayout{*preparedText};
}
}
TextMeasurement TextLayoutManager::measurePreparedLayout(
@@ -16,9 +16,7 @@ add_library(bridgeless
${bridgeless_SRC}
)
target_compile_reactnative_options(bridgeless PRIVATE)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
target_compile_options(bridgeless PRIVATE -DHERMES_ENABLE_DEBUGGER=1)
endif ()
target_compile_options(bridgeless PRIVATE $<$<CONFIG:Debug>:-DHERMES_ENABLE_DEBUGGER=1>)
target_include_directories(bridgeless PUBLIC .)
react_native_android_selector(fabricjni fabricjni "")
@@ -29,7 +29,7 @@ target_link_libraries(bridgelesshermes
)
target_compile_reactnative_options(bridgelesshermes PRIVATE)
if(${CMAKE_BUILD_TYPE} MATCHES Debug OR REACT_NATIVE_DEBUG_OPTIMIZED)
if(${CMAKE_BUILD_TYPE} MATCHES Debug)
target_compile_options(
bridgelesshermes
PRIVATE
@@ -38,20 +38,24 @@ class SimpleThreadSafeCache {
*/
ValueT get(const KeyT& key, CacheGeneratorFunction<ValueT> auto generator)
const {
return getMapIterator(key, std::move(generator))->second->second;
}
std::lock_guard<std::mutex> lock(mutex_);
/*
* Returns pointers to both the key and value from the map with a given key.
* If the value wasn't found in the cache, constructs the value using given
* generator function, stores it inside a cache and returns it.
* Can be called from any thread.
*/
std::pair<const KeyT*, const ValueT*> getWithKey(
const KeyT& key,
CacheGeneratorFunction<ValueT> auto generator) const {
auto it = getMapIterator(key, std::move(generator));
return std::make_pair(&it->first, &it->second->second);
if (auto it = map_.find(key); it != map_.end()) {
// Move accessed item to front of list
list_.splice(list_.begin(), list_, it->second);
return it->second->second;
}
auto value = generator();
// Add new value to front of list and map
list_.emplace_front(key, value);
map_[key] = list_.begin();
if (list_.size() > maxSize_) {
// Evict least recently used item (back of list)
map_.erase(list_.back().first);
list_.pop_back();
}
return value;
}
/*
@@ -75,29 +79,6 @@ class SimpleThreadSafeCache {
using EntryT = std::pair<KeyT, ValueT>;
using iterator = typename std::list<EntryT>::iterator;
auto getMapIterator(
const KeyT& key,
CacheGeneratorFunction<ValueT> auto generator) const {
std::lock_guard<std::mutex> lock(mutex_);
if (auto it = map_.find(key); it != map_.end()) {
// Move accessed item to front of list
list_.splice(list_.begin(), list_, it->second);
return it;
}
auto value = generator();
// Add new value to front of list and map
list_.emplace_front(key, value);
auto [it, _] = map_.insert_or_assign(key, list_.begin());
if (list_.size() > maxSize_) {
// Evict least recently used item (back of list)
map_.erase(list_.back().first);
list_.pop_back();
}
return it;
}
size_t maxSize_;
mutable std::mutex mutex_;
mutable std::list<EntryT> list_;
+7 -7
View File
@@ -161,13 +161,13 @@
},
"dependencies": {
"@jest/create-cache-key-function": "^29.7.0",
"@react-native/assets-registry": "0.82.0-main",
"@react-native/codegen": "0.82.0-main",
"@react-native/community-cli-plugin": "0.82.0-main",
"@react-native/gradle-plugin": "0.82.0-main",
"@react-native/js-polyfills": "0.82.0-main",
"@react-native/normalize-colors": "0.82.0-main",
"@react-native/virtualized-lists": "0.82.0-main",
"@react-native/assets-registry": "0.81.0-main",
"@react-native/codegen": "0.81.0-main",
"@react-native/community-cli-plugin": "0.81.0-main",
"@react-native/gradle-plugin": "0.81.0-main",
"@react-native/js-polyfills": "0.81.0-main",
"@react-native/normalize-colors": "0.81.0-main",
"@react-native/virtualized-lists": "0.81.0-main",
"abort-controller": "^3.0.0",
"anser": "^1.4.9",
"ansi-regex": "^5.0.0",
@@ -545,7 +545,7 @@ const definitions: FeatureFlagDefinitions = {
expectedReleaseValue: true,
purpose: 'experimentation',
},
ossReleaseStage: 'experimental',
ossReleaseStage: 'none',
},
releaseImageDataWhenConsumed: {
defaultValue: false,
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/tester",
"version": "0.82.0-main",
"version": "0.81.0-main",
"private": true,
"description": "React Native tester app.",
"license": "MIT",
@@ -26,9 +26,9 @@
"e2e-test-ios": "./scripts/maestro-test-ios.sh"
},
"dependencies": {
"@react-native/oss-library-example": "0.82.0-main",
"@react-native/new-app-screen": "0.82.0-main",
"@react-native/popup-menu-android": "0.82.0-main",
"@react-native/oss-library-example": "0.81.0-main",
"@react-native/new-app-screen": "0.81.0-main",
"@react-native/popup-menu-android": "0.81.0-main",
"flow-enums-runtime": "^0.0.6",
"invariant": "^2.2.4",
"nullthrows": "^1.1.1"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/typescript-config",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "Default TypeScript configuration for React Native apps",
"license": "MIT",
"repository": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/virtualized-lists",
"version": "0.82.0-main",
"version": "0.81.0-main",
"description": "Virtualized lists for React Native.",
"license": "MIT",
"repository": {
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:usesCleartextTraffic="true"
tools:targetApi="28"
tools:ignore="GoogleAppIndexingWarning"/>
</manifest>
@@ -8,7 +8,6 @@
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:usesCleartextTraffic="${usesCleartextTraffic}"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
+5 -5
View File
@@ -19,11 +19,11 @@
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.3",
"@babel/runtime": "^7.25.0",
"@react-native/babel-preset": "0.82.0-main",
"@react-native/core-cli-utils": "0.82.0-main",
"@react-native/eslint-config": "0.82.0-main",
"@react-native/metro-config": "0.82.0-main",
"@react-native/typescript-config": "0.82.0-main",
"@react-native/babel-preset": "0.81.0-main",
"@react-native/core-cli-utils": "0.81.0-main",
"@react-native/eslint-config": "0.81.0-main",
"@react-native/metro-config": "0.81.0-main",
"@react-native/typescript-config": "0.81.0-main",
"@types/jest": "^29.5.14",
"commander": "^12.0.0",
"eslint": "^8.19.0",
@@ -13,7 +13,7 @@
"prepare": "yarn run build"
},
"dependencies": {
"@react-native/codegen": "0.82.0-main"
"@react-native/codegen": "0.81.0-main"
},
"devDependencies": {
"@babel/core": "^7.25.2",