Merge commit '7b82df287d36e39073d29e3ae3adbe82cf424055' into amgleitman/0.64-merge-head

This commit is contained in:
Adam Gleitman 2021-09-21 18:28:23 -07:00
Родитель b8d5b85590 7b82df287d
Коммит 3e0ced8068
91 изменённых файлов: 717 добавлений и 676 удалений

Просмотреть файл

@ -224,7 +224,8 @@ function createAnimatedComponent<Props: {+[string]: mixed, ...}, Instance>(
style={mergedStyle}
ref={this._setComponentRef}
nativeID={
this._isFabric() ? 'animatedComponent' : undefined
props.nativeID ??
(this._isFabric() ? 'animatedComponent' : undefined)
} /* TODO: T68258846. */
// The native driver updates views directly through the UI thread so we
// have to make sure the view doesn't get optimized away because it cannot

Просмотреть файл

@ -20,6 +20,6 @@ fb_apple_library(
deps = [
"//xplat/js/react-native-github:RCTTypeSafety",
"//xplat/js/react-native-github/Libraries/RCTRequired:RCTRequired",
react_native_xplat_target_apple("turbomodule/core:core"),
react_native_xplat_target_apple("react/nativemodule/core:core"),
],
)

Просмотреть файл

@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
*/
'use strict';
@ -14,8 +15,6 @@ import createPerformanceLogger from '../createPerformanceLogger';
import type {IPerformanceLogger} from '../createPerformanceLogger';
const TIMESPAN_1 = '<timespan_1>';
const TIMESPAN_2 = '<timespan_2>';
const TIMESPAN_2_DURATION = 123;
const EXTRA_KEY = '<extra_key>';
const EXTRA_VALUE = '<extra_value>';
const EXTRA_VALUE_2 = '<extra_value_2>';
@ -28,16 +27,16 @@ describe('PerformanceLogger', () => {
GlobalPerformanceLogger.clear();
});
it('starts & stops and adds a timespan', () => {
it('starts & stops a timespan', () => {
let perfLogger = createPerformanceLogger();
perfLogger.startTimespan(TIMESPAN_1);
perfLogger.stopTimespan(TIMESPAN_1);
perfLogger.addTimeAnnotation(TIMESPAN_2, TIMESPAN_2_DURATION);
expect(perfLogger.hasTimespan(TIMESPAN_1)).toBe(true);
expect(perfLogger.hasTimespan(TIMESPAN_2)).toBe(true);
expect(perfLogger.getTimespans()[TIMESPAN_2].totalTime).toBe(
TIMESPAN_2_DURATION,
);
expect(perfLogger.getTimespans()[TIMESPAN_1]).toEqual({
startTime: expect.any(Number),
endTime: expect.any(Number),
totalTime: expect.any(Number),
});
});
it('does not override a timespan', () => {
@ -46,18 +45,14 @@ describe('PerformanceLogger', () => {
let old = perfLogger.getTimespans()[TIMESPAN_1];
perfLogger.startTimespan(TIMESPAN_1);
expect(perfLogger.getTimespans()[TIMESPAN_1]).toBe(old);
perfLogger.addTimeAnnotation(TIMESPAN_1, 1);
expect(perfLogger.getTimespans()[TIMESPAN_1]).toBe(old);
});
it('adds a timespan with start and end timestamps', () => {
let perfLogger = createPerformanceLogger();
const startTime = 0;
const endTime = 100;
const description = 'description';
perfLogger.addTimespan(TIMESPAN_1, startTime, endTime, description);
perfLogger.addTimespan(TIMESPAN_1, startTime, endTime);
expect(perfLogger.getTimespans()[TIMESPAN_1]).toEqual({
description,
startTime,
endTime,
totalTime: endTime - startTime,
@ -69,7 +64,7 @@ describe('PerformanceLogger', () => {
perfLogger.startTimespan(TIMESPAN_1);
perfLogger.stopTimespan(TIMESPAN_1);
const existing = perfLogger.getTimespans()[TIMESPAN_1];
perfLogger.addTimespan(TIMESPAN_1, 0, 100, 'overriding');
perfLogger.addTimespan(TIMESPAN_1, 0, 100);
expect(perfLogger.getTimespans()[TIMESPAN_1]).toEqual(existing);
});

Просмотреть файл

@ -13,39 +13,30 @@
const Systrace = require('../Performance/Systrace');
const infoLog = require('./infoLog');
const performanceNow =
const performanceNow: () => number =
global.nativeQPLTimestamp ?? global.performance.now.bind(global.performance);
type Timespan = {
description?: string,
totalTime?: number,
startTime?: number,
startTime: number,
endTime?: number,
...
totalTime?: number,
};
// Extra values should be serializable primitives
type ExtraValue = number | string | boolean;
export interface IPerformanceLogger {
addTimeAnnotation(
key: string,
durationInMs: number,
description?: string,
): void;
addTimespan(
key: string,
startTime: number,
endTime: number,
description?: string,
): void;
startTimespan(key: string, description?: string): void;
stopTimespan(key: string, options?: {update?: boolean}): void;
addTimespan(key: string, startTime: number, endTime: number): void;
startTimespan(key: string): void;
stopTimespan(key: string): void;
clear(): void;
clearCompleted(): void;
currentTimestamp(): number;
getTimespans(): {[key: string]: Timespan, ...};
hasTimespan(key: string): boolean;
setExtra(key: string, value: mixed): void;
getExtras(): {[key: string]: mixed, ...};
removeExtra(key: string): ?mixed;
setExtra(key: string, value: ExtraValue): void;
getExtras(): {[key: string]: ExtraValue, ...};
removeExtra(key: string): ExtraValue | void;
markPoint(key: string, timestamp?: number): void;
getPoints(): {[key: string]: number, ...};
logEverything(): void;
@ -55,211 +46,183 @@ const _cookies: {[key: string]: number, ...} = {};
const PRINT_TO_CONSOLE: false = false; // Type as false to prevent accidentally committing `true`;
class PerformanceLogger implements IPerformanceLogger {
_timespans: {[key: string]: Timespan} = {};
_extras: {[key: string]: ExtraValue} = {};
_points: {[key: string]: number} = {};
addTimespan(key: string, startTime: number, endTime: number) {
if (this._timespans[key]) {
if (PRINT_TO_CONSOLE && __DEV__) {
infoLog(
'PerformanceLogger: Attempting to add a timespan that already exists ',
key,
);
}
return;
}
this._timespans[key] = {
startTime,
endTime,
totalTime: endTime - (startTime || 0),
};
}
startTimespan(key: string) {
if (this._timespans[key]) {
if (PRINT_TO_CONSOLE && __DEV__) {
infoLog(
'PerformanceLogger: Attempting to start a timespan that already exists ',
key,
);
}
return;
}
this._timespans[key] = {
startTime: performanceNow(),
};
_cookies[key] = Systrace.beginAsyncEvent(key);
if (PRINT_TO_CONSOLE) {
infoLog('PerformanceLogger.js', 'start: ' + key);
}
}
stopTimespan(key: string) {
const timespan = this._timespans[key];
if (!timespan || timespan.startTime == null) {
if (PRINT_TO_CONSOLE && __DEV__) {
infoLog(
'PerformanceLogger: Attempting to end a timespan that has not started ',
key,
);
}
return;
}
if (timespan.endTime != null) {
if (PRINT_TO_CONSOLE && __DEV__) {
infoLog(
'PerformanceLogger: Attempting to end a timespan that has already ended ',
key,
);
}
return;
}
timespan.endTime = performanceNow();
timespan.totalTime = timespan.endTime - (timespan.startTime || 0);
if (PRINT_TO_CONSOLE) {
infoLog('PerformanceLogger.js', 'end: ' + key);
}
if (_cookies[key] != null) {
Systrace.endAsyncEvent(key, _cookies[key]);
delete _cookies[key];
}
}
clear() {
this._timespans = {};
this._extras = {};
this._points = {};
if (PRINT_TO_CONSOLE) {
infoLog('PerformanceLogger.js', 'clear');
}
}
clearCompleted() {
for (const key in this._timespans) {
if (this._timespans[key].totalTime != null) {
delete this._timespans[key];
}
}
this._extras = {};
this._points = {};
if (PRINT_TO_CONSOLE) {
infoLog('PerformanceLogger.js', 'clearCompleted');
}
}
currentTimestamp() {
return performanceNow();
}
getTimespans() {
return this._timespans;
}
hasTimespan(key: string) {
return !!this._timespans[key];
}
setExtra(key: string, value: ExtraValue) {
if (this._extras.hasOwnProperty(key)) {
if (PRINT_TO_CONSOLE && __DEV__) {
infoLog(
'PerformanceLogger: Attempting to set an extra that already exists ',
{key, currentValue: this._extras[key], attemptedValue: value},
);
}
return;
}
this._extras[key] = value;
}
getExtras() {
return this._extras;
}
removeExtra(key: string): ExtraValue | void {
const value = this._extras[key];
delete this._extras[key];
return value;
}
markPoint(key: string, timestamp?: number) {
if (this._points[key]) {
if (PRINT_TO_CONSOLE && __DEV__) {
infoLog(
'PerformanceLogger: Attempting to mark a point that has been already logged ',
key,
);
}
return;
}
this._points[key] = timestamp ?? performanceNow();
}
getPoints() {
return this._points;
}
logEverything() {
if (PRINT_TO_CONSOLE) {
// log timespans
for (const key in this._timespans) {
if (this._timespans[key].totalTime != null) {
infoLog(key + ': ' + this._timespans[key].totalTime + 'ms');
}
}
// log extras
infoLog(this._extras);
// log points
for (const key in this._points) {
infoLog(key + ': ' + this._points[key] + 'ms');
}
}
}
}
/**
* This function creates performance loggers that can be used to collect and log
* various performance data such as timespans, points and extras.
* The loggers need to have minimal overhead since they're used in production.
*/
function createPerformanceLogger(): IPerformanceLogger {
const result: IPerformanceLogger & {
_timespans: {[key: string]: Timespan, ...},
_extras: {[key: string]: mixed, ...},
_points: {[key: string]: number, ...},
...
} = {
_timespans: {},
_extras: {},
_points: {},
addTimeAnnotation(key: string, durationInMs: number, description?: string) {
if (this._timespans[key]) {
if (PRINT_TO_CONSOLE && __DEV__) {
infoLog(
'PerformanceLogger: Attempting to add a timespan that already exists ',
key,
);
}
return;
}
this._timespans[key] = {
description: description,
totalTime: durationInMs,
};
},
addTimespan(
key: string,
startTime: number,
endTime: number,
description?: string,
) {
if (this._timespans[key]) {
if (PRINT_TO_CONSOLE && __DEV__) {
infoLog(
'PerformanceLogger: Attempting to add a timespan that already exists ',
key,
);
}
return;
}
this._timespans[key] = {
description,
startTime,
endTime,
totalTime: endTime - (startTime || 0),
};
},
startTimespan(key: string, description?: string) {
if (this._timespans[key]) {
if (PRINT_TO_CONSOLE && __DEV__) {
infoLog(
'PerformanceLogger: Attempting to start a timespan that already exists ',
key,
);
}
return;
}
this._timespans[key] = {
description: description,
startTime: performanceNow(),
};
_cookies[key] = Systrace.beginAsyncEvent(key);
if (PRINT_TO_CONSOLE) {
infoLog('PerformanceLogger.js', 'start: ' + key);
}
},
stopTimespan(key: string, options?: {update?: boolean}) {
const timespan = this._timespans[key];
if (!timespan || !timespan.startTime) {
if (PRINT_TO_CONSOLE && __DEV__) {
infoLog(
'PerformanceLogger: Attempting to end a timespan that has not started ',
key,
);
}
return;
}
if (timespan.endTime && !options?.update) {
if (PRINT_TO_CONSOLE && __DEV__) {
infoLog(
'PerformanceLogger: Attempting to end a timespan that has already ended ',
key,
);
}
return;
}
timespan.endTime = performanceNow();
timespan.totalTime = timespan.endTime - (timespan.startTime || 0);
if (PRINT_TO_CONSOLE) {
infoLog('PerformanceLogger.js', 'end: ' + key);
}
if (_cookies[key] != null) {
Systrace.endAsyncEvent(key, _cookies[key]);
delete _cookies[key];
}
},
clear() {
this._timespans = {};
this._extras = {};
this._points = {};
if (PRINT_TO_CONSOLE) {
infoLog('PerformanceLogger.js', 'clear');
}
},
clearCompleted() {
for (const key in this._timespans) {
if (this._timespans[key].totalTime) {
delete this._timespans[key];
}
}
this._extras = {};
this._points = {};
if (PRINT_TO_CONSOLE) {
infoLog('PerformanceLogger.js', 'clearCompleted');
}
},
currentTimestamp() {
return performanceNow();
},
getTimespans() {
return this._timespans;
},
hasTimespan(key: string) {
return !!this._timespans[key];
},
setExtra(key: string, value: mixed) {
if (this._extras[key]) {
if (PRINT_TO_CONSOLE && __DEV__) {
infoLog(
'PerformanceLogger: Attempting to set an extra that already exists ',
{key, currentValue: this._extras[key], attemptedValue: value},
);
}
return;
}
this._extras[key] = value;
},
getExtras() {
return this._extras;
},
removeExtra(key: string): ?mixed {
const value = this._extras[key];
delete this._extras[key];
return value;
},
markPoint(key: string, timestamp?: number) {
if (this._points[key]) {
if (PRINT_TO_CONSOLE && __DEV__) {
infoLog(
'PerformanceLogger: Attempting to mark a point that has been already logged ',
key,
);
}
return;
}
this._points[key] = timestamp ?? performanceNow();
},
getPoints() {
return this._points;
},
logEverything() {
if (PRINT_TO_CONSOLE) {
// log timespans
for (const key in this._timespans) {
if (this._timespans[key].totalTime) {
infoLog(key + ': ' + this._timespans[key].totalTime + 'ms');
}
}
// log extras
infoLog(this._extras);
// log points
for (const key in this._points) {
infoLog(key + ': ' + this._points[key] + 'ms');
}
}
},
};
return result;
return new PerformanceLogger();
}
module.exports = createPerformanceLogger;

Просмотреть файл

@ -7,6 +7,8 @@
#import "RCTActivityIndicatorViewComponentView.h"
#import <React/RCTConversions.h>
#import <react/renderer/components/rncore/ComponentDescriptors.h>
#import <react/renderer/components/rncore/EventEmitters.h>
#import <react/renderer/components/rncore/Props.h>
@ -50,7 +52,7 @@ static UIActivityIndicatorViewStyle convertActivityIndicatorViewStyle(const Acti
} else {
[_activityIndicatorView stopAnimating];
}
_activityIndicatorView.color = [UIColor colorWithCGColor:defaultProps->color.get()];
_activityIndicatorView.color = RCTUIColorFromSharedColor(defaultProps->color);
_activityIndicatorView.hidesWhenStopped = defaultProps->hidesWhenStopped;
_activityIndicatorView.activityIndicatorViewStyle = convertActivityIndicatorViewStyle(defaultProps->size);
@ -73,8 +75,8 @@ static UIActivityIndicatorViewStyle convertActivityIndicatorViewStyle(const Acti
}
}
if (oldViewProps.color.get() != newViewProps.color.get()) {
_activityIndicatorView.color = [UIColor colorWithCGColor:newViewProps.color.get()];
if (oldViewProps.color != newViewProps.color) {
_activityIndicatorView.color = RCTUIColorFromSharedColor(newViewProps.color);
}
// TODO: This prop should be deprecated.

Просмотреть файл

@ -74,7 +74,7 @@ using namespace facebook::react;
// `tintColor`
if (oldImageProps.tintColor != newImageProps.tintColor) {
_imageView.tintColor = [UIColor colorWithCGColor:newImageProps.tintColor.get()];
_imageView.tintColor = RCTUIColorFromSharedColor(newImageProps.tintColor);
}
[super updateProps:props oldProps:oldProps];

Просмотреть файл

@ -7,8 +7,10 @@
#import "RCTSliderComponentView.h"
#import <React/RCTConversions.h>
#import <React/RCTImageResponseDelegate.h>
#import <React/RCTImageResponseObserverProxy.h>
#import <react/renderer/components/rncore/EventEmitters.h>
#import <react/renderer/components/rncore/Props.h>
#import <react/renderer/components/slider/SliderComponentDescriptor.h>
@ -131,17 +133,17 @@ using namespace facebook::react;
// `thumbTintColor`
if (oldSliderProps.thumbTintColor != newSliderProps.thumbTintColor) {
_sliderView.thumbTintColor = [UIColor colorWithCGColor:newSliderProps.thumbTintColor.get()];
_sliderView.thumbTintColor = RCTUIColorFromSharedColor(newSliderProps.thumbTintColor);
}
// `minimumTrackTintColor`
if (oldSliderProps.minimumTrackTintColor != newSliderProps.minimumTrackTintColor) {
_sliderView.minimumTrackTintColor = [UIColor colorWithCGColor:newSliderProps.minimumTrackTintColor.get()];
_sliderView.minimumTrackTintColor = RCTUIColorFromSharedColor(newSliderProps.minimumTrackTintColor);
}
// `maximumTrackTintColor`
if (oldSliderProps.maximumTrackTintColor != newSliderProps.maximumTrackTintColor) {
_sliderView.maximumTrackTintColor = [UIColor colorWithCGColor:newSliderProps.maximumTrackTintColor.get()];
_sliderView.maximumTrackTintColor = RCTUIColorFromSharedColor(newSliderProps.maximumTrackTintColor);
}
[super updateProps:props oldProps:oldProps];

Просмотреть файл

@ -7,6 +7,8 @@
#import "RCTSwitchComponentView.h"
#import <React/RCTConversions.h>
#import <react/renderer/components/rncore/ComponentDescriptors.h>
#import <react/renderer/components/rncore/EventEmitters.h>
#import <react/renderer/components/rncore/Props.h>
@ -75,17 +77,17 @@ using namespace facebook::react;
// `tintColor`
if (oldSwitchProps.tintColor != newSwitchProps.tintColor) {
_switchView.tintColor = [UIColor colorWithCGColor:newSwitchProps.tintColor.get()];
_switchView.tintColor = RCTUIColorFromSharedColor(newSwitchProps.tintColor);
}
// `onTintColor
if (oldSwitchProps.onTintColor != newSwitchProps.onTintColor) {
_switchView.onTintColor = [UIColor colorWithCGColor:newSwitchProps.onTintColor.get()];
_switchView.onTintColor = RCTUIColorFromSharedColor(newSwitchProps.onTintColor);
}
// `thumbTintColor`
if (oldSwitchProps.thumbTintColor != newSwitchProps.thumbTintColor) {
_switchView.thumbTintColor = [UIColor colorWithCGColor:newSwitchProps.thumbTintColor.get()];
_switchView.thumbTintColor = RCTUIColorFromSharedColor(newSwitchProps.thumbTintColor);
}
[super updateProps:props oldProps:oldProps];

Просмотреть файл

@ -7,10 +7,12 @@
#import "RCTViewComponentView.h"
#import <CoreGraphics/CoreGraphics.h>
#import <objc/runtime.h>
#import <React/RCTAssert.h>
#import <React/RCTBorderDrawing.h>
#import <React/RCTConversions.h>
#import <objc/runtime.h>
#import <react/renderer/components/view/ViewComponentDescriptor.h>
#import <react/renderer/components/view/ViewEventEmitter.h>
#import <react/renderer/components/view/ViewProps.h>
@ -118,7 +120,7 @@ using namespace facebook::react;
// `shadowColor`
if (oldViewProps.shadowColor != newViewProps.shadowColor) {
CGColorRef shadowColor = RCTCGColorRefFromSharedColor(newViewProps.shadowColor);
CGColorRef shadowColor = RCTCreateCGColorRefFromSharedColor(newViewProps.shadowColor);
self.layer.shadowColor = shadowColor;
CGColorRelease(shadowColor);
needsInvalidateLayer = YES;
@ -344,12 +346,20 @@ static RCTCornerRadii RCTCornerRadiiFromBorderRadii(BorderRadii borderRadii)
.bottomRight = (CGFloat)borderRadii.bottomRight};
}
static RCTBorderColors RCTBorderColorsFromBorderColors(BorderColors borderColors)
static RCTBorderColors RCTCreateRCTBorderColorsFromBorderColors(BorderColors borderColors)
{
return RCTBorderColors{.top = RCTCGColorRefUnretainedFromSharedColor(borderColors.top),
.left = RCTCGColorRefUnretainedFromSharedColor(borderColors.left),
.bottom = RCTCGColorRefUnretainedFromSharedColor(borderColors.bottom),
.right = RCTCGColorRefUnretainedFromSharedColor(borderColors.right)};
return RCTBorderColors{.top = RCTCreateCGColorRefFromSharedColor(borderColors.top),
.left = RCTCreateCGColorRefFromSharedColor(borderColors.left),
.bottom = RCTCreateCGColorRefFromSharedColor(borderColors.bottom),
.right = RCTCreateCGColorRefFromSharedColor(borderColors.right)};
}
static void RCTReleaseRCTBorderColors(RCTBorderColors borderColors)
{
CGColorRelease(borderColors.top);
CGColorRelease(borderColors.left);
CGColorRelease(borderColors.bottom);
CGColorRelease(borderColors.right);
}
static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
@ -412,7 +422,7 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
}
layer.borderWidth = (CGFloat)borderMetrics.borderWidths.left;
CGColorRef borderColor = RCTCGColorRefFromSharedColor(borderMetrics.borderColors.left);
CGColorRef borderColor = RCTCreateCGColorRefFromSharedColor(borderMetrics.borderColors.left);
layer.borderColor = borderColor;
CGColorRelease(borderColor);
layer.cornerRadius = (CGFloat)borderMetrics.borderRadii.topLeft;
@ -431,15 +441,19 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
layer.borderColor = nil;
layer.cornerRadius = 0;
RCTBorderColors borderColors = RCTCreateRCTBorderColorsFromBorderColors(borderMetrics.borderColors);
UIImage *image = RCTGetBorderImage(
RCTBorderStyleFromBorderStyle(borderMetrics.borderStyles.left),
layer.bounds.size,
RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii),
RCTUIEdgeInsetsFromEdgeInsets(borderMetrics.borderWidths),
RCTBorderColorsFromBorderColors(borderMetrics.borderColors),
borderColors,
_backgroundColor.CGColor,
self.clipsToBounds);
RCTReleaseRCTBorderColors(borderColors);
if (image == nil) {
_borderLayer.contents = nil;
} else {

Просмотреть файл

@ -35,20 +35,32 @@ inline std::string RCTStringFromNSString(NSString *string)
return std::string{string.UTF8String ?: ""};
}
inline UIColor *_Nullable RCTUIColorFromSharedColor(const facebook::react::SharedColor &sharedColor)
inline UIColor *_Nullable RCTUIColorFromSharedColor(facebook::react::SharedColor const &sharedColor)
{
return sharedColor ? [UIColor colorWithCGColor:sharedColor.get()] : nil;
if (!sharedColor) {
return nil;
}
if (*facebook::react::clearColor() == *sharedColor) {
return [UIColor clearColor];
}
if (*facebook::react::blackColor() == *sharedColor) {
return [UIColor blackColor];
}
if (*facebook::react::whiteColor() == *sharedColor) {
return [UIColor whiteColor];
}
auto components = facebook::react::colorComponentsFromColor(sharedColor);
return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha];
}
inline CF_RETURNS_NOT_RETAINED CGColorRef
RCTCGColorRefUnretainedFromSharedColor(const facebook::react::SharedColor &sharedColor)
inline CF_RETURNS_RETAINED CGColorRef
RCTCreateCGColorRefFromSharedColor(const facebook::react::SharedColor &sharedColor)
{
return sharedColor ? sharedColor.get() : nil;
}
inline CF_RETURNS_RETAINED CGColorRef RCTCGColorRefFromSharedColor(const facebook::react::SharedColor &sharedColor)
{
return sharedColor ? CGColorCreateCopy(sharedColor.get()) : nil;
return CGColorRetain(RCTUIColorFromSharedColor(sharedColor).CGColor);
}
inline CGPoint RCTCGPointFromPoint(const facebook::react::Point &point)

Просмотреть файл

@ -48,7 +48,7 @@ static RuntimeExecutor RCTRuntimeExecutorFromBridge(RCTBridge *bridge)
auto bridgeWeakWrapper = wrapManagedObjectWeakly([bridge batchedBridge] ?: bridge);
RuntimeExecutor runtimeExecutor = [bridgeWeakWrapper](
std::function<void(facebook::jsi::Runtime & runtime)> &&callback) {
std::function<void(facebook::jsi::Runtime &runtime)> &&callback) {
RCTBridge *bridge = unwrapManagedObjectWeakly(bridgeWeakWrapper);
RCTAssert(bridge, @"RCTRuntimeExecutorFromBridge: Bridge must not be nil at the moment of scheduling a call.");

Просмотреть файл

@ -51,7 +51,7 @@ rn_xplat_cxx_library(
react_native_xplat_target("cxxreact:bridge"),
],
exported_deps = ([
react_native_xplat_target("turbomodule/core:core"),
react_native_xplat_target("react/nativemodule/core:core"),
]) + ([
"//xplat/jsi:jsi",
]) if not IS_OSS_BUILD else [],

Просмотреть файл

@ -428,11 +428,12 @@ local_ref<JMountItem::javaobject> createUpdatePaddingMountItem(
auto newChildShadowView = mutation.newChildShadowView;
if (oldChildShadowView.layoutMetrics.contentInsets ==
newChildShadowView.layoutMetrics.contentInsets) {
newChildShadowView.layoutMetrics.contentInsets &&
mutation.type != ShadowViewMutation::Type::Insert) {
return nullptr;
}
static auto updateLayoutInstruction =
static auto updatePaddingInstruction =
jni::findClassStatic(Binding::UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(jint, jint, jint, jint, jint)>(
"updatePaddingMountItem");
@ -446,7 +447,7 @@ local_ref<JMountItem::javaobject> createUpdatePaddingMountItem(
int right = round(contentInsets.right * pointScaleFactor);
int bottom = round(contentInsets.bottom * pointScaleFactor);
return updateLayoutInstruction(
return updatePaddingInstruction(
javaUIManager, newChildShadowView.tag, left, top, right, bottom);
}

Просмотреть файл

@ -399,7 +399,7 @@ public class MountingManager {
}
throw new IllegalStateException(
"Tried to delete view ["
"Tried to remove view ["
+ tag
+ "] of parent ["
+ parentTag
@ -577,12 +577,20 @@ public class MountingManager {
return;
}
View view = viewState.mView;
// To delete we simply remove the tag from the registry.
// In the past we called dropView here, but we want to rely on either
// (1) the correct set of MountInstructions being sent to the platform
// and/or (2) dropView being called by stopSurface.
// If Views are orphaned at this stage and leaked, it's a problem in
// the differ or LayoutAnimations, not MountingManager.
// Additionally, as documented in `dropView`, we cannot always trust a
// view's children to be up-to-date.
mTagToViewState.remove(reactTag);
if (view != null) {
dropView(view, false);
} else {
mTagToViewState.remove(reactTag);
// For non-root views we notify viewmanager with {@link ViewManager#onDropInstance}
ViewManager viewManager = viewState.mViewManager;
if (!viewState.mIsRoot && viewManager != null) {
viewManager.onDropViewInstance(viewState.mView);
}
}

Просмотреть файл

@ -8,6 +8,7 @@
package com.facebook.react.fabric.mounting.mountitems;
import androidx.annotation.NonNull;
import com.facebook.common.logging.FLog;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.ReactMarker;
import com.facebook.react.bridge.ReactMarkerConstants;
@ -70,8 +71,20 @@ public class BatchMountItem implements MountItem {
public void execute(@NonNull MountingManager mountingManager) {
beginMarkers("mountViews");
for (int mountItemIndex = 0; mountItemIndex < mSize; mountItemIndex++) {
mMountItems[mountItemIndex].execute(mountingManager);
int mountItemIndex = 0;
try {
for (; mountItemIndex < mSize; mountItemIndex++) {
mMountItems[mountItemIndex].execute(mountingManager);
}
} catch (RuntimeException e) {
FLog.e(
TAG,
"Caught exception executing mountItem @"
+ mountItemIndex
+ ": "
+ mMountItems[mountItemIndex].toString(),
e);
throw e;
}
endMarkers();

Просмотреть файл

@ -34,7 +34,7 @@ rn_xplat_cxx_library(
exported_deps = [
":callinvokerholder",
"//xplat/jsi:jsi",
react_native_xplat_target("turbomodule/core:core"),
react_native_xplat_target("react/nativemodule/core:core"),
react_native_target("java/com/facebook/react/reactperflogger/jni:jni"),
],
)

Просмотреть файл

@ -92,12 +92,6 @@ CatalystInstanceImpl::initHybrid(jni::alias_ref<jclass>) {
CatalystInstanceImpl::CatalystInstanceImpl()
: instance_(std::make_unique<Instance>()) {}
CatalystInstanceImpl::~CatalystInstanceImpl() {
if (moduleMessageQueue_ != NULL) {
moduleMessageQueue_->quitSynchronous();
}
}
void CatalystInstanceImpl::registerNatives() {
registerHybrid({
makeNativeMethod("initHybrid", CatalystInstanceImpl::initHybrid),

Просмотреть файл

@ -37,7 +37,6 @@ class CatalystInstanceImpl : public jni::HybridClass<CatalystInstanceImpl> {
"Lcom/facebook/react/bridge/CatalystInstanceImpl;";
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jclass>);
~CatalystInstanceImpl() override;
static void registerNatives();

Просмотреть файл

@ -36,6 +36,8 @@ Pod::Spec.new do |s|
"USE_HEADERMAP" => "YES",
"CLANG_CXX_LANGUAGE_STANDARD" => "c++14" }
# TODO (T48588859): Restructure this target to align with dir structure: "react/nativemodule/..."
# Note: Update this only when ready to minimize breaking changes.
s.subspec "turbomodule" do |ss|
ss.dependency "React-callinvoker", version
ss.dependency "React-perflogger", version
@ -47,13 +49,13 @@ Pod::Spec.new do |s|
ss.dependency "glog"
ss.subspec "core" do |sss|
sss.source_files = "turbomodule/core/*.{cpp,h}",
"turbomodule/core/platform/ios/*.{mm,cpp,h}"
sss.source_files = "react/nativemodule/core/*.{cpp,h}",
"react/nativemodule/core/platform/ios/*.{mm,cpp,h}"
end
ss.subspec "samples" do |sss|
sss.source_files = "turbomodule/samples/*.{cpp,h}",
"turbomodule/samples/platform/ios/*.{mm,cpp,h}"
sss.source_files = "react/nativemodule/samples/*.{cpp,h}",
"react/nativemodule/samples/platform/ios/*.{mm,cpp,h}"
sss.dependency "ReactCommon/turbomodule/core", version
end
end

Просмотреть файл

@ -331,8 +331,8 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation {
return plain().instrumentation().getHeapInfo(includeExpensive);
}
void collectGarbage() override {
plain().instrumentation().collectGarbage();
void collectGarbage(std::string cause) override {
plain().instrumentation().collectGarbage(std::move(cause));
}
void startTrackingHeapObjectStackTraces() override {

Просмотреть файл

@ -49,8 +49,10 @@ class JSI_EXPORT Instrumentation {
virtual std::unordered_map<std::string, int64_t> getHeapInfo(
bool includeExpensive) = 0;
/// perform a full garbage collection
virtual void collectGarbage() = 0;
/// Perform a full garbage collection.
/// \param cause The cause of this collection, as it should be reported in
/// logs.
virtual void collectGarbage(std::string cause) = 0;
/// Start capturing JS stack-traces for all JS heap allocated objects. These
/// can be accessed via \c ::createSnapshotToFile().

Просмотреть файл

@ -97,7 +97,7 @@ Instrumentation& Runtime::instrumentation() {
return std::unordered_map<std::string, int64_t>{};
}
void collectGarbage() override {}
void collectGarbage(std::string) override {}
void startTrackingHeapObjectStackTraces() override {}
void stopTrackingHeapObjectStackTraces() override {}

Просмотреть файл

@ -344,7 +344,7 @@ void JSIExecutor::handleMemoryPressure(int pressureLevel) {
// collections.
LOG(INFO) << "Memory warning (pressure level: " << levelName
<< ") received by JS VM, running a GC";
runtime_->instrumentation().collectGarbage();
runtime_->instrumentation().collectGarbage("memory warning");
break;
default:
// Use the raw number instead of the name here since the name is

Просмотреть файл

@ -31,6 +31,26 @@ JavaTurboModule::JavaTurboModule(const InitParams &params)
instance_(jni::make_global(params.instance)),
nativeInvoker_(params.nativeInvoker) {}
JavaTurboModule::~JavaTurboModule() {
/**
* TODO(T75896241): In E2E tests, instance_ is null. Investigate why. Can we
* get rid of this null check?
*/
if (!instance_) {
return;
}
nativeInvoker_->invokeAsync([instance = std::move(instance_)]() mutable {
/**
* Reset the global NativeModule ref on the NativeModules thread. Why:
* - ~JavaTurboModule() can be called on a non-JVM thread. If we reset the
* global ref in ~JavaTurboModule(), we might access the JVM from a
* non-JVM thread, which will crash the app.
*/
instance.reset();
});
}
bool JavaTurboModule::isPromiseAsyncDispatchEnabled_ = false;
void JavaTurboModule::enablePromiseAsyncDispatch(bool enable) {
isPromiseAsyncDispatchEnabled_ = enable;

Просмотреть файл

@ -41,6 +41,7 @@ class JSI_EXPORT JavaTurboModule : public TurboModule {
};
JavaTurboModule(const InitParams &params);
virtual ~JavaTurboModule();
jsi::Value invokeJavaMethod(
jsi::Runtime &runtime,
TurboModuleMethodValueKind valueKind,

Просмотреть файл

@ -171,7 +171,7 @@ convertJSIFunctionToCallback(jsi::Runtime &runtime, const jsi::Function &value,
{
auto weakWrapper = CallbackWrapper::createWeak(value.getFunction(runtime), runtime, jsInvoker);
BOOL __block wrapperWasCalled = NO;
return ^(NSArray *responses) {
RCTResponseSenderBlock callback = ^(NSArray *responses) {
if (wrapperWasCalled) {
throw std::runtime_error("callback arg cannot be called more than once");
}
@ -194,6 +194,8 @@ convertJSIFunctionToCallback(jsi::Runtime &runtime, const jsi::Function &value,
wrapperWasCalled = YES;
};
return [callback copy];
}
namespace facebook {
@ -331,12 +333,11 @@ jsi::Value ObjCTurboModule::performMethodInvocation(
bool wasMethodSync = isMethodSync(returnType);
void (^block)() = ^{
if (!weakModule) {
id<RCTTurboModule> strongModule = weakModule;
if (!strongModule) {
return;
}
id<RCTTurboModule> strongModule = weakModule;
if (wasMethodSync) {
TurboModulePerfLogger::syncMethodCallExecutionStart(moduleName, methodNameStr.c_str());
} else {
@ -635,10 +636,13 @@ jsi::Value ObjCTurboModule::invokeObjCMethod(
runtime,
jsInvoker_,
^(RCTPromiseResolveBlock resolveBlock, RCTPromiseRejectBlock rejectBlock) {
[inv setArgument:(void *)&resolveBlock atIndex:count + 2];
[inv setArgument:(void *)&rejectBlock atIndex:count + 3];
[retainedObjectsForInvocation addObject:resolveBlock];
[retainedObjectsForInvocation addObject:rejectBlock];
RCTPromiseResolveBlock resolveCopy = [resolveBlock copy];
RCTPromiseRejectBlock rejectCopy = [rejectBlock copy];
[inv setArgument:(void *)&resolveCopy atIndex:count + 2];
[inv setArgument:(void *)&rejectCopy atIndex:count + 3];
[retainedObjectsForInvocation addObject:resolveCopy];
[retainedObjectsForInvocation addObject:rejectCopy];
// The return type becomes void in the ObjC side.
performMethodInvocation(runtime, VoidKind, methodName, inv, retainedObjectsForInvocation);
})

Просмотреть файл

@ -75,6 +75,6 @@ rn_xplat_cxx_library(
],
exported_deps = [
"//xplat/jsi:jsi",
react_native_xplat_target("turbomodule/core:core"),
react_native_xplat_target("react/nativemodule/core:core"),
],
)

Просмотреть файл

@ -191,8 +191,10 @@ void LayoutAnimationDriver::animationMutationsForFrame(
// Queue up "final" mutations for all keyframes in the completed animation
for (auto const &keyframe : animation.keyFrames) {
if (!keyframe.invalidated &&
keyframe.finalMutationForKeyFrame.hasValue()) {
if (keyframe.invalidated) {
continue;
}
if (keyframe.finalMutationForKeyFrame.hasValue()) {
auto const &finalMutationForKeyFrame =
*keyframe.finalMutationForKeyFrame;
PrintMutationInstruction(
@ -207,6 +209,17 @@ void LayoutAnimationDriver::animationMutationsForFrame(
finalMutationForKeyFrame.oldChildShadowView,
finalMutationForKeyFrame.newChildShadowView,
finalMutationForKeyFrame.index});
} else {
// Issue a final UPDATE so that the final props object sent to the
// mounting layer is the same as the one on the ShadowTree. This is
// mostly to make the MountingCoordinator StubViewTree assertions
// pass.
mutationsList.push_back(
ShadowViewMutation{ShadowViewMutation::Type::Update,
keyframe.parentView,
keyframe.viewStart,
keyframe.viewEnd,
-1});
}
}

Просмотреть файл

@ -30,7 +30,8 @@ namespace facebook {
namespace react {
#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING
std::string GetMutationInstructionString(ShadowViewMutation const &mutation) {
static std::string GetMutationInstructionString(
ShadowViewMutation const &mutation) {
bool mutationIsRemove = mutation.type == ShadowViewMutation::Type::Remove;
bool mutationIsInsert = mutation.type == ShadowViewMutation::Type::Insert;
bool mutationIsDelete = mutation.type == ShadowViewMutation::Type::Delete;
@ -1253,7 +1254,10 @@ LayoutAnimationKeyFrameManager::pullTransaction(
<< "Adjust delayed mutations based on finalConflictingMutations";
#endif
for (auto &mutation : finalConflictingMutations) {
adjustDelayedMutationIndicesForMutation(surfaceId, mutation);
if (mutation.type == ShadowViewMutation::Remove ||
mutation.type == ShadowViewMutation::Insert) {
adjustDelayedMutationIndicesForMutation(surfaceId, mutation);
}
}
// Adjust keyframes based on already-delayed, existing animations, before

Просмотреть файл

@ -68,7 +68,9 @@ void TouchEventEmitter::onTouchStart(TouchEvent const &event) const {
}
void TouchEventEmitter::onTouchMove(TouchEvent const &event) const {
dispatchTouchEvent("touchMove", event, EventPriority::AsynchronousBatched);
dispatchUniqueEvent("touchMove", [event](jsi::Runtime &runtime) {
return touchEventPayload(runtime, event);
});
}
void TouchEventEmitter::onTouchEnd(TouchEvent const &event) const {

Просмотреть файл

@ -46,16 +46,31 @@ void ViewEventEmitter::onLayout(const LayoutMetrics &layoutMetrics) const {
lastLayoutMetrics_ = layoutMetrics;
}
dispatchEvent("layout", [frame = layoutMetrics.frame](jsi::Runtime &runtime) {
auto layout = jsi::Object(runtime);
layout.setProperty(runtime, "x", frame.origin.x);
layout.setProperty(runtime, "y", frame.origin.y);
layout.setProperty(runtime, "width", frame.size.width);
layout.setProperty(runtime, "height", frame.size.height);
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "layout", std::move(layout));
return payload;
});
std::atomic_uint_fast8_t *eventCounter = &eventCounter_;
uint_fast8_t expectedEventCount = ++*eventCounter;
// dispatchUniqueEvent only drops consecutive onLayout events to the same
// node. We want to drop *any* unprocessed onLayout events when there's a
// newer one.
dispatchEvent(
"layout",
[frame = layoutMetrics.frame, expectedEventCount, eventCounter](
jsi::Runtime &runtime) {
uint_fast8_t actualEventCount = eventCounter->load();
if (expectedEventCount != actualEventCount) {
// Drop stale events
return jsi::Value::null();
}
auto layout = jsi::Object(runtime);
layout.setProperty(runtime, "x", frame.origin.x);
layout.setProperty(runtime, "y", frame.origin.y);
layout.setProperty(runtime, "width", frame.size.width);
layout.setProperty(runtime, "height", frame.size.height);
auto payload = jsi::Object(runtime);
payload.setProperty(runtime, "layout", std::move(layout));
return jsi::Value(std::move(payload));
});
}
} // namespace react

Просмотреть файл

@ -40,6 +40,7 @@ class ViewEventEmitter : public TouchEventEmitter {
private:
mutable std::mutex layoutMetricsMutex_;
mutable LayoutMetrics lastLayoutMetrics_;
mutable std::atomic_uint_fast8_t eventCounter_{0};
};
} // namespace react

Просмотреть файл

@ -14,7 +14,7 @@ using Tag = EventTarget::Tag;
EventTarget::EventTarget(
jsi::Runtime &runtime,
const jsi::Value &instanceHandle,
jsi::Value const &instanceHandle,
Tag tag)
: weakInstanceHandle_(
jsi::WeakObject(runtime, instanceHandle.asObject(runtime))),

Просмотреть файл

@ -35,7 +35,7 @@ class EventTarget {
/*
* Constructs an EventTarget from a weak instance handler and a tag.
*/
EventTarget(jsi::Runtime &runtime, const jsi::Value &instanceHandle, Tag tag);
EventTarget(jsi::Runtime &runtime, jsi::Value const &instanceHandle, Tag tag);
/*
* Sets the `enabled` flag that allows creating a strong instance handle from

Просмотреть файл

@ -7,6 +7,7 @@ load(
"fb_xplat_cxx_test",
"get_apple_compiler_flags",
"get_apple_inspector_flags",
"react_native_xplat_target",
"rn_xplat_cxx_library",
"subdir_glob",
)
@ -95,6 +96,7 @@ rn_xplat_cxx_library(
tests = [":tests"],
visibility = ["PUBLIC"],
deps = [
react_native_xplat_target("better:better"),
"//third-party/glog:glog",
"//xplat/fbsystrace:fbsystrace",
"//xplat/folly:headers_only",

Просмотреть файл

@ -12,28 +12,21 @@ namespace facebook {
namespace react {
SharedColor colorFromComponents(ColorComponents components) {
const CGFloat componentsArray[] = {
components.red, components.green, components.blue, components.alpha};
auto color = CGColorCreate(CGColorSpaceCreateDeviceRGB(), componentsArray);
return SharedColor(color, CGColorRelease);
float ratio = 255.9999;
return SharedColor(
((int)(components.alpha * ratio) & 0xff) << 24 |
((int)(components.red * ratio) & 0xff) << 16 |
((int)(components.green * ratio) & 0xff) << 8 |
((int)(components.blue * ratio) & 0xff));
}
ColorComponents colorComponentsFromColor(SharedColor color) {
if (!color) {
// Empty color object can be considered as `clear` (black, fully
// transparent) color.
return ColorComponents{0, 0, 0, 0};
}
auto numberOfComponents __unused = CGColorGetNumberOfComponents(color.get());
assert(numberOfComponents == 4);
const CGFloat *components = CGColorGetComponents(color.get());
return ColorComponents{(float)components[0],
(float)components[1],
(float)components[2],
(float)components[3]};
ColorComponents colorComponentsFromColor(SharedColor sharedColor) {
float ratio = 256;
Color color = *sharedColor;
return ColorComponents{(float)((color >> 16) & 0xff) / ratio,
(float)((color >> 8) & 0xff) / ratio,
(float)((color >> 0) & 0xff) / ratio,
(float)((color >> 24) & 0xff) / ratio};
}
SharedColor clearColor() {

Просмотреть файл

@ -7,17 +7,17 @@
#pragma once
#include <memory>
#include <better/optional.h>
#include <CoreGraphics/CoreGraphics.h>
#include <react/renderer/graphics/ColorComponents.h>
#include <react/renderer/graphics/Float.h>
namespace facebook {
namespace react {
using Color = CGColor;
using SharedColor = std::shared_ptr<Color>;
using Color = int32_t;
using SharedColor = better::optional<Color>;
SharedColor colorFromComponents(ColorComponents components);
ColorComponents colorComponentsFromColor(SharedColor color);

Просмотреть файл

@ -23,13 +23,13 @@ MountingCoordinator::MountingCoordinator(
ShadowTreeRevision baseRevision,
std::weak_ptr<MountingOverrideDelegate const> delegate,
bool enableReparentingDetection)
: surfaceId_(baseRevision.getRootShadowNode().getSurfaceId()),
: surfaceId_(baseRevision.rootShadowNode->getSurfaceId()),
baseRevision_(baseRevision),
mountingOverrideDelegate_(delegate),
telemetryController_(*this),
enableReparentingDetection_(enableReparentingDetection) {
#ifdef RN_SHADOW_TREE_INTROSPECTION
stubViewTree_ = stubViewTreeFromShadowNode(baseRevision_.getRootShadowNode());
stubViewTree_ = stubViewTreeFromShadowNode(*baseRevision_.rootShadowNode);
#endif
}
@ -37,17 +37,15 @@ SurfaceId MountingCoordinator::getSurfaceId() const {
return surfaceId_;
}
void MountingCoordinator::push(ShadowTreeRevision &&revision) const {
void MountingCoordinator::push(ShadowTreeRevision const &revision) const {
{
std::lock_guard<std::mutex> lock(mutex_);
assert(
!lastRevision_.has_value() ||
revision.getNumber() != lastRevision_->getNumber());
!lastRevision_.has_value() || revision.number != lastRevision_->number);
if (!lastRevision_.has_value() ||
lastRevision_->getNumber() < revision.getNumber()) {
lastRevision_ = std::move(revision);
if (!lastRevision_.has_value() || lastRevision_->number < revision.number) {
lastRevision_ = revision;
}
}
@ -60,7 +58,7 @@ void MountingCoordinator::revoke() const {
// 1. We need to stop retaining `ShadowNode`s to not prolong their lifetime
// to prevent them from overliving `ComponentDescriptor`s.
// 2. A possible call to `pullTransaction()` should return empty optional.
baseRevision_.rootShadowNode_.reset();
baseRevision_.rootShadowNode.reset();
lastRevision_.reset();
}
@ -90,18 +88,17 @@ better::optional<MountingTransaction> MountingCoordinator::pullTransaction()
if (lastRevision_.has_value()) {
number_++;
auto telemetry = lastRevision_->getTelemetry();
auto telemetry = lastRevision_->telemetry;
telemetry.willDiff();
auto mutations = calculateShadowViewMutations(
baseRevision_.getRootShadowNode(), lastRevision_->getRootShadowNode());
*baseRevision_.rootShadowNode,
*lastRevision_->rootShadowNode,
enableReparentingDetection_);
telemetry.didDiff();
baseRevision_ = std::move(*lastRevision_);
lastRevision_.reset();
transaction = MountingTransaction{
surfaceId_, number_, std::move(mutations), telemetry};
}
@ -119,10 +116,13 @@ better::optional<MountingTransaction> MountingCoordinator::pullTransaction()
mutations = transaction->getMutations();
telemetry = transaction->getTelemetry();
} else {
number_++;
telemetry.willLayout();
telemetry.didLayout();
telemetry.willCommit();
telemetry.didCommit();
telemetry.willDiff();
telemetry.didDiff();
}
transaction = mountingOverrideDelegate->pullTransaction(
@ -141,15 +141,17 @@ better::optional<MountingTransaction> MountingCoordinator::pullTransaction()
// If the transaction was overridden, we don't have a model of the shadow
// tree therefore we cannot validate the validity of the mutation
// instructions.
if (!shouldOverridePullTransaction) {
auto line = std::string{};
if (!shouldOverridePullTransaction && lastRevision_.has_value()) {
auto stubViewTree =
stubViewTreeFromShadowNode(baseRevision_.getRootShadowNode());
stubViewTreeFromShadowNode(*lastRevision_->rootShadowNode);
if (stubViewTree_ != stubViewTree) {
bool treesEqual = stubViewTree_ == stubViewTree;
if (!treesEqual) {
// Display debug info
auto line = std::string{};
std::stringstream ssOldTree(
baseRevision_.getRootShadowNode().getDebugDescription());
baseRevision_.rootShadowNode->getDebugDescription());
while (std::getline(ssOldTree, line, '\n')) {
LOG(ERROR) << "Old tree:" << line;
}
@ -160,19 +162,21 @@ better::optional<MountingTransaction> MountingCoordinator::pullTransaction()
}
std::stringstream ssNewTree(
lastRevision_->getRootShadowNode().getDebugDescription());
lastRevision_->rootShadowNode->getDebugDescription());
while (std::getline(ssNewTree, line, '\n')) {
LOG(ERROR) << "New tree:" << line;
}
}
assert(
(stubViewTree_ == stubViewTree) &&
"Incorrect set of mutations detected.");
assert((treesEqual) && "Incorrect set of mutations detected.");
}
}
#endif
if (lastRevision_.has_value()) {
baseRevision_ = std::move(*lastRevision_);
lastRevision_.reset();
}
return transaction;
}

Просмотреть файл

@ -91,7 +91,7 @@ class MountingCoordinator final {
private:
friend class ShadowTree;
void push(ShadowTreeRevision &&revision) const;
void push(ShadowTreeRevision const &revision) const;
/*
* Revokes the last pushed `ShadowTreeRevision`.

Просмотреть файл

@ -237,17 +237,19 @@ ShadowTree::ShadowTree(
auto family = rootComponentDescriptor.createFamily(
ShadowNodeFamilyFragment{surfaceId, surfaceId, noopEventEmitter},
nullptr);
rootShadowNode_ = std::static_pointer_cast<const RootShadowNode>(
auto rootShadowNode = std::static_pointer_cast<const RootShadowNode>(
rootComponentDescriptor.createShadowNode(
ShadowNodeFragment{
/* .props = */ props,
},
family));
currentRevision_ = ShadowTreeRevision{
rootShadowNode, ShadowTreeRevision::Number{0}, TransactionTelemetry{}};
mountingCoordinator_ = std::make_shared<MountingCoordinator const>(
ShadowTreeRevision{rootShadowNode_, 0, {}},
mountingOverrideDelegate,
enableReparentingDetection);
currentRevision_, mountingOverrideDelegate, enableReparentingDetection);
}
ShadowTree::~ShadowTree() {
@ -291,15 +293,16 @@ CommitStatus ShadowTree::tryCommit(
auto telemetry = TransactionTelemetry{};
telemetry.willCommit();
RootShadowNode::Shared oldRootShadowNode;
auto oldRevision = ShadowTreeRevision{};
auto newRevision = ShadowTreeRevision{};
{
// Reading `rootShadowNode_` in shared manner.
// Reading `currentRevision_` in shared manner.
std::shared_lock<better::shared_mutex> lock(commitMutex_);
oldRootShadowNode = rootShadowNode_;
oldRevision = currentRevision_;
}
RootShadowNode::Unshared newRootShadowNode = transaction(oldRootShadowNode);
auto newRootShadowNode = transaction(*oldRevision.rootShadowNode);
if (!newRootShadowNode) {
return CommitStatus::Cancelled;
@ -307,14 +310,14 @@ CommitStatus ShadowTree::tryCommit(
if (enableStateReconciliation) {
auto updatedNewRootShadowNode =
progressState(*newRootShadowNode, *oldRootShadowNode);
progressState(*newRootShadowNode, *oldRevision.rootShadowNode);
if (updatedNewRootShadowNode) {
newRootShadowNode =
std::static_pointer_cast<RootShadowNode>(updatedNewRootShadowNode);
}
}
// Layout nodes
// Layout nodes.
std::vector<LayoutableShadowNode const *> affectedLayoutableNodes{};
affectedLayoutableNodes.reserve(1024);
@ -327,48 +330,52 @@ CommitStatus ShadowTree::tryCommit(
// Seal the shadow node so it can no longer be mutated
newRootShadowNode->sealRecursive();
auto revisionNumber = ShadowTreeRevision::Number{};
{
// Updating `rootShadowNode_` in unique manner if it hasn't changed.
// Updating `currentRevision_` in unique manner if it hasn't changed.
std::unique_lock<better::shared_mutex> lock(commitMutex_);
if (rootShadowNode_ != oldRootShadowNode) {
if (currentRevision_.number != oldRevision.number) {
return CommitStatus::Failed;
}
rootShadowNode_ = newRootShadowNode;
auto newRevisionNumber = oldRevision.number + 1;
{
std::lock_guard<std::mutex> dispatchLock(EventEmitter::DispatchMutex());
updateMountedFlag(
oldRootShadowNode->getChildren(), newRootShadowNode->getChildren());
currentRevision_.rootShadowNode->getChildren(),
newRootShadowNode->getChildren());
}
revisionNumber_++;
revisionNumber = revisionNumber_;
telemetry.didCommit();
telemetry.setRevisionNumber(newRevisionNumber);
newRevision =
ShadowTreeRevision{newRootShadowNode, newRevisionNumber, telemetry};
currentRevision_ = newRevision;
}
emitLayoutEvents(affectedLayoutableNodes);
telemetry.didCommit();
telemetry.setRevisionNumber(revisionNumber);
mountingCoordinator_->push(
ShadowTreeRevision{newRootShadowNode, revisionNumber, telemetry});
mountingCoordinator_->push(newRevision);
notifyDelegatesOfUpdates();
return CommitStatus::Succeeded;
}
ShadowTreeRevision ShadowTree::getCurrentRevision() const {
std::shared_lock<better::shared_mutex> lock(commitMutex_);
return currentRevision_;
}
void ShadowTree::commitEmptyTree() const {
commit(
[](RootShadowNode::Shared const &oldRootShadowNode)
-> RootShadowNode::Unshared {
[](RootShadowNode const &oldRootShadowNode) -> RootShadowNode::Unshared {
return std::make_shared<RootShadowNode>(
*oldRootShadowNode,
oldRootShadowNode,
ShadowNodeFragment{
/* .props = */ ShadowNodeFragment::propsPlaceholder(),
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),

Просмотреть файл

@ -24,7 +24,7 @@ namespace facebook {
namespace react {
using ShadowTreeCommitTransaction = std::function<RootShadowNode::Unshared(
RootShadowNode::Shared const &oldRootShadowNode)>;
RootShadowNode const &oldRootShadowNode)>;
/*
* Represents the shadow tree and its lifecycle.
@ -72,6 +72,12 @@ class ShadowTree final {
ShadowTreeCommitTransaction transaction,
bool enableStateReconciliation = false) const;
/*
* Returns a `ShadowTreeRevision` representing the momentary state of
* the `ShadowTree`.
*/
ShadowTreeRevision getCurrentRevision() const;
/*
* Commit an empty tree (a new `RootShadowNode` with no children).
*/
@ -101,10 +107,7 @@ class ShadowTree final {
SurfaceId const surfaceId_;
ShadowTreeDelegate const &delegate_;
mutable better::shared_mutex commitMutex_;
mutable RootShadowNode::Shared
rootShadowNode_; // Protected by `commitMutex_`.
mutable ShadowTreeRevision::Number revisionNumber_{
0}; // Protected by `commitMutex_`.
mutable ShadowTreeRevision currentRevision_; // Protected by `commitMutex_`.
MountingCoordinator::Shared mountingCoordinator_;
bool enableReparentingDetection_{false};
};

Просмотреть файл

@ -6,29 +6,3 @@
*/
#include "ShadowTreeRevision.h"
namespace facebook {
namespace react {
using Number = ShadowTreeRevision::Number;
ShadowTreeRevision::ShadowTreeRevision(
RootShadowNode::Shared const &rootShadowNode,
Number number,
TransactionTelemetry telemetry)
: rootShadowNode_(rootShadowNode), number_(number), telemetry_(telemetry) {}
TransactionTelemetry const &ShadowTreeRevision::getTelemetry() const {
return telemetry_;
}
RootShadowNode const &ShadowTreeRevision::getRootShadowNode() {
return *rootShadowNode_;
}
Number ShadowTreeRevision::getNumber() const {
return number_;
}
} // namespace react
} // namespace facebook

Просмотреть файл

@ -30,40 +30,12 @@ class ShadowTreeRevision final {
*/
using Number = int64_t;
/*
* Creates the object with given root shadow node, revision number and
* telemetry.
*/
ShadowTreeRevision(
RootShadowNode::Shared const &rootShadowNode,
Number number,
TransactionTelemetry telemetry);
/*
* Returns telemetry associated with this revision.
*/
TransactionTelemetry const &getTelemetry() const;
/*
* Methods from this section are meant to be used by
* `MountingOverrideDelegate` only.
*/
public:
RootShadowNode const &getRootShadowNode();
/*
* Methods from this section are meant to be used by `MountingCoordinator`
* only.
*/
private:
friend class ShadowTree;
friend class MountingCoordinator;
Number getNumber() const;
private:
RootShadowNode::Shared rootShadowNode_;
Number number_;
TransactionTelemetry telemetry_;
RootShadowNode::Shared rootShadowNode;
Number number;
TransactionTelemetry telemetry;
};
} // namespace react

Просмотреть файл

@ -12,18 +12,18 @@
namespace facebook {
namespace react {
using ThreadLocalTransactionTelemetry = ThreadStorage<TransactionTelemetry *>;
thread_local TransactionTelemetry *threadLocalTransactionTelemetry = nullptr;
TransactionTelemetry *TransactionTelemetry::threadLocalTelemetry() {
return ThreadLocalTransactionTelemetry::getInstance().get().value_or(nullptr);
return threadLocalTransactionTelemetry;
}
void TransactionTelemetry::setAsThreadLocal() {
ThreadLocalTransactionTelemetry::getInstance().set(this);
threadLocalTransactionTelemetry = this;
}
void TransactionTelemetry::unsetAsThreadLocal() {
ThreadLocalTransactionTelemetry::getInstance().set(nullptr);
threadLocalTransactionTelemetry = nullptr;
}
void TransactionTelemetry::willCommit() {

Просмотреть файл

@ -11,7 +11,6 @@
#include <cstdint>
#include <react/utils/Telemetry.h>
#include <react/utils/ThreadStorage.h>
namespace facebook {
namespace react {

Просмотреть файл

@ -5,11 +5,14 @@
* LICENSE file in the root directory of this source tree.
*/
#include <vector>
#include <glog/logging.h>
#include <gtest/gtest.h>
#include <react/renderer/components/root/RootComponentDescriptor.h>
#include <react/renderer/components/view/ViewComponentDescriptor.h>
#include <react/renderer/mounting/ShadowViewMutation.h>
#include <react/renderer/mounting/Differentiator.h>
#include <react/renderer/mounting/stubs.h>
@ -102,6 +105,25 @@ static void testShadowNodeTreeLifeCycle(
auto mutations = calculateShadowViewMutations(
*currentRootNode, *nextRootNode, useFlattener);
// If using flattener: make sure that in a single frame, a DELETE for a view is not
// followed by a CREATE for the same view.
if (useFlattener) {
std::vector<int> deletedTags{};
for (auto const& mutation : mutations) {
if (mutation.type == ShadowViewMutation::Type::Delete) {
deletedTags.push_back(mutation.oldChildShadowView.tag);
}
}
for (auto const& mutation : mutations) {
if (mutation.type == ShadowViewMutation::Type::Create) {
if (std::find(deletedTags.begin(), deletedTags.end(), mutation.newChildShadowView.tag) != deletedTags.end()) {
LOG(ERROR) << "Deleted tag was recreated in mutations list: [" << mutation.newChildShadowView.tag << "]";
FAIL();
}
}
}
}
// Mutating the view tree.
viewTree.mutate(mutations);

Просмотреть файл

@ -43,16 +43,8 @@ inline ShadowNode const *findDescendantNode(
inline ShadowNode const *findDescendantNode(
ShadowTree const &shadowTree,
ShadowNodeFamily const &family) {
ShadowNode const *result = nullptr;
shadowTree.tryCommit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
result = findDescendantNode(*oldRootShadowNode, family);
return nullptr;
},
false);
return result;
return findDescendantNode(
*shadowTree.getCurrentRevision().rootShadowNode, family);
}
TEST(StateReconciliationTest, testStateReconciliation) {
@ -107,7 +99,7 @@ TEST(StateReconciliationTest, testStateReconciliation) {
{}};
shadowTree.commit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
[&](RootShadowNode const &oldRootShadowNode) {
return std::static_pointer_cast<RootShadowNode>(rootShadowNodeState1);
},
true);
@ -131,7 +123,7 @@ TEST(StateReconciliationTest, testStateReconciliation) {
findDescendantNode(*rootShadowNodeState2, family)->getState(), state2);
shadowTree.commit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
[&](RootShadowNode const &oldRootShadowNode) {
return std::static_pointer_cast<RootShadowNode>(rootShadowNodeState2);
},
true);
@ -153,7 +145,7 @@ TEST(StateReconciliationTest, testStateReconciliation) {
findDescendantNode(*rootShadowNodeState3, family)->getState(), state3);
shadowTree.commit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
[&](RootShadowNode const &oldRootShadowNode) {
return std::static_pointer_cast<RootShadowNode>(rootShadowNodeState3);
},
true);
@ -168,7 +160,7 @@ TEST(StateReconciliationTest, testStateReconciliation) {
// Here we commit the old tree but we expect that the state associated with
// the node will stay the same (newer that the old tree has).
shadowTree.commit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
[&](RootShadowNode const &oldRootShadowNode) {
return std::static_pointer_cast<RootShadowNode>(rootShadowNodeState2);
},
true);

Просмотреть файл

@ -110,11 +110,17 @@ Scheduler::Scheduler(
"react_fabric:enable_reparenting_detection_android");
removeOutstandingSurfacesOnDestruction_ = reactNativeConfig_->getBool(
"react_fabric:remove_outstanding_surfaces_on_destruction_android");
uiManager_->experimentEnableStateUpdateWithAutorepeat =
reactNativeConfig_->getBool(
"react_fabric:enable_state_update_with_autorepeat_android");
#else
enableReparentingDetection_ = reactNativeConfig_->getBool(
"react_fabric:enable_reparenting_detection_ios");
removeOutstandingSurfacesOnDestruction_ = reactNativeConfig_->getBool(
"react_fabric:remove_outstanding_surfaces_on_destruction_ios");
uiManager_->experimentEnableStateUpdateWithAutorepeat =
reactNativeConfig_->getBool(
"react_fabric:enable_state_update_with_autorepeat_ios");
#endif
}
@ -220,9 +226,9 @@ void Scheduler::renderTemplateToSurface(
uiManager_->getShadowTreeRegistry().visit(
surfaceId, [=](const ShadowTree &shadowTree) {
return shadowTree.tryCommit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
[&](RootShadowNode const &oldRootShadowNode) {
return std::make_shared<RootShadowNode>(
*oldRootShadowNode,
oldRootShadowNode,
ShadowNodeFragment{
/* .props = */ ShadowNodeFragment::propsPlaceholder(),
/* .children = */
@ -272,19 +278,16 @@ Size Scheduler::measureSurface(
const LayoutContext &layoutContext) const {
SystraceSection s("Scheduler::measureSurface");
Size size;
auto currentRootShadowNode = RootShadowNode::Shared{};
uiManager_->getShadowTreeRegistry().visit(
surfaceId, [&](const ShadowTree &shadowTree) {
shadowTree.tryCommit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
auto rootShadowNode =
oldRootShadowNode->clone(layoutConstraints, layoutContext);
rootShadowNode->layoutIfNeeded();
size = rootShadowNode->getLayoutMetrics().frame.size;
return nullptr;
});
currentRootShadowNode = shadowTree.getCurrentRevision().rootShadowNode;
});
return size;
auto rootShadowNode =
currentRootShadowNode->clone(layoutConstraints, layoutContext);
rootShadowNode->layoutIfNeeded();
return rootShadowNode->getLayoutMetrics().frame.size;
}
MountingCoordinator::Shared Scheduler::findMountingCoordinator(
@ -305,8 +308,8 @@ void Scheduler::constraintSurfaceLayout(
uiManager_->getShadowTreeRegistry().visit(
surfaceId, [&](ShadowTree const &shadowTree) {
shadowTree.commit([&](RootShadowNode::Shared const &oldRootShadowNode) {
return oldRootShadowNode->clone(layoutConstraints, layoutContext);
shadowTree.commit([&](RootShadowNode const &oldRootShadowNode) {
return oldRootShadowNode.clone(layoutConstraints, layoutContext);
});
});
}

Просмотреть файл

@ -96,7 +96,24 @@ inline static NSUnderlineStyle RCTNSUnderlineStyleFromStyleAndPattern(
return style;
}
inline static UIColor *RCTUIColorFromSharedColor(const SharedColor &color)
inline static UIColor *RCTUIColorFromSharedColor(const SharedColor &sharedColor)
{
return color ? [UIColor colorWithCGColor:color.get()] : nil;
if (!sharedColor) {
return nil;
}
if (*facebook::react::clearColor() == *sharedColor) {
return [UIColor clearColor];
}
if (*facebook::react::blackColor() == *sharedColor) {
return [UIColor blackColor];
}
if (*facebook::react::whiteColor() == *sharedColor) {
return [UIColor whiteColor];
}
auto components = facebook::react::colorComponentsFromColor(sharedColor);
return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha];
}

Просмотреть файл

@ -97,9 +97,9 @@ void UIManager::completeSurface(
shadowTreeRegistry_.visit(surfaceId, [&](ShadowTree const &shadowTree) {
shadowTree.commit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
[&](RootShadowNode const &oldRootShadowNode) {
return std::make_shared<RootShadowNode>(
*oldRootShadowNode,
oldRootShadowNode,
ShadowNodeFragment{
/* .props = */ ShadowNodeFragment::propsPlaceholder(),
/* .children = */ rootChildren,
@ -139,12 +139,7 @@ ShadowNode::Shared UIManager::getNewestCloneOfShadowNode(
auto ancestorShadowNode = ShadowNode::Shared{};
shadowTreeRegistry_.visit(
shadowNode.getSurfaceId(), [&](ShadowTree const &shadowTree) {
shadowTree.tryCommit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
ancestorShadowNode = oldRootShadowNode;
return nullptr;
},
true);
ancestorShadowNode = shadowTree.getCurrentRevision().rootShadowNode;
});
if (!ancestorShadowNode) {
@ -178,9 +173,9 @@ void UIManager::setNativeProps(
shadowTreeRegistry_.visit(
shadowNode.getSurfaceId(), [&](ShadowTree const &shadowTree) {
shadowTree.tryCommit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
[&](RootShadowNode const &oldRootShadowNode) {
return std::static_pointer_cast<RootShadowNode>(
oldRootShadowNode->cloneTree(
oldRootShadowNode.cloneTree(
shadowNode.getFamily(),
[&](ShadowNode const &oldShadowNode) {
return oldShadowNode.clone({
@ -205,13 +200,9 @@ LayoutMetrics UIManager::getRelativeLayoutMetrics(
if (!ancestorShadowNode) {
shadowTreeRegistry_.visit(
shadowNode.getSurfaceId(), [&](ShadowTree const &shadowTree) {
shadowTree.tryCommit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
owningAncestorShadowNode = oldRootShadowNode;
ancestorShadowNode = oldRootShadowNode.get();
return nullptr;
},
true);
owningAncestorShadowNode =
shadowTree.getCurrentRevision().rootShadowNode;
ancestorShadowNode = owningAncestorShadowNode.get();
});
} else {
// It is possible for JavaScript (or other callers) to have a reference
@ -240,10 +231,10 @@ void UIManager::updateStateWithAutorepeat(
shadowTreeRegistry_.visit(
family->getSurfaceId(), [&](ShadowTree const &shadowTree) {
shadowTree.commit([&](RootShadowNode::Shared const &oldRootShadowNode) {
shadowTree.commit([&](RootShadowNode const &oldRootShadowNode) {
auto isValid = true;
auto rootNode = oldRootShadowNode->cloneTree(
auto rootNode = oldRootShadowNode.cloneTree(
*family, [&](ShadowNode const &oldShadowNode) {
auto newData =
callback(oldShadowNode.getState()->getDataPointer());
@ -272,7 +263,7 @@ void UIManager::updateStateWithAutorepeat(
}
void UIManager::updateState(StateUpdate const &stateUpdate) const {
if (stateUpdate.autorepeat) {
if (stateUpdate.autorepeat || experimentEnableStateUpdateWithAutorepeat) {
updateStateWithAutorepeat(stateUpdate);
return;
}
@ -283,10 +274,10 @@ void UIManager::updateState(StateUpdate const &stateUpdate) const {
shadowTreeRegistry_.visit(
family->getSurfaceId(), [&](ShadowTree const &shadowTree) {
auto status = shadowTree.tryCommit([&](RootShadowNode::Shared const
auto status = shadowTree.tryCommit([&](RootShadowNode const
&oldRootShadowNode) {
return std::static_pointer_cast<RootShadowNode>(
oldRootShadowNode->cloneTree(
oldRootShadowNode.cloneTree(
*family, [&](ShadowNode const &oldShadowNode) {
auto newData =
callback(oldShadowNode.getState()->getDataPointer());
@ -320,8 +311,8 @@ void UIManager::dispatchCommand(
void UIManager::configureNextLayoutAnimation(
jsi::Runtime &runtime,
RawValue const &config,
const jsi::Value &successCallback,
const jsi::Value &failureCallback) const {
jsi::Value const &successCallback,
jsi::Value const &failureCallback) const {
if (animationDelegate_) {
animationDelegate_->uiManagerDidConfigureNextLayoutAnimation(
runtime,

Просмотреть файл

@ -73,6 +73,11 @@ class UIManager final : public ShadowTreeDelegate {
ShadowTree const &shadowTree,
MountingCoordinator::Shared const &mountingCoordinator) const override;
/*
* Temporary flags.
*/
bool experimentEnableStateUpdateWithAutorepeat{false};
private:
friend class UIManagerBinding;
friend class Scheduler;
@ -142,8 +147,8 @@ class UIManager final : public ShadowTreeDelegate {
void configureNextLayoutAnimation(
jsi::Runtime &runtime,
RawValue const &config,
const jsi::Value &successCallback,
const jsi::Value &failureCallback) const;
jsi::Value const &successCallback,
jsi::Value const &failureCallback) const;
ShadowTreeRegistry const &getShadowTreeRegistry() const;

Просмотреть файл

@ -26,8 +26,8 @@ class UIManagerAnimationDelegate {
virtual void uiManagerDidConfigureNextLayoutAnimation(
jsi::Runtime &runtime,
RawValue const &config,
const jsi::Value &successCallback,
const jsi::Value &failureCallback) const = 0;
jsi::Value const &successCallback,
jsi::Value const &failureCallback) const = 0;
/**
* Set ComponentDescriptor registry.

Просмотреть файл

@ -17,7 +17,7 @@ namespace react {
static jsi::Object getModule(
jsi::Runtime &runtime,
const std::string &moduleName) {
std::string const &moduleName) {
auto batchedBridge =
runtime.global().getPropertyAsObject(runtime, "__fbBatchedBridge");
auto getCallableModule =
@ -82,8 +82,8 @@ void UIManagerBinding::attach(std::shared_ptr<UIManager> const &uiManager) {
void UIManagerBinding::startSurface(
jsi::Runtime &runtime,
SurfaceId surfaceId,
const std::string &moduleName,
const folly::dynamic &initalProps) const {
std::string const &moduleName,
folly::dynamic const &initalProps) const {
folly::dynamic parameters = folly::dynamic::object();
parameters["rootTag"] = surfaceId;
parameters["initialProps"] = initalProps;
@ -128,13 +128,18 @@ void UIManagerBinding::stopSurface(jsi::Runtime &runtime, SurfaceId surfaceId)
void UIManagerBinding::dispatchEvent(
jsi::Runtime &runtime,
const EventTarget *eventTarget,
const std::string &type,
const ValueFactory &payloadFactory) const {
EventTarget const *eventTarget,
std::string const &type,
ValueFactory const &payloadFactory) const {
SystraceSection s("UIManagerBinding::dispatchEvent");
auto payload = payloadFactory(runtime);
// If a payload is null, the factory has decided to cancel the event
if (payload.isNull()) {
return;
}
auto instanceHandle = eventTarget
? [&]() {
auto instanceHandle = eventTarget->getInstanceHandle(runtime);
@ -153,7 +158,7 @@ void UIManagerBinding::dispatchEvent(
: jsi::Value::null();
auto &eventHandlerWrapper =
static_cast<const EventHandlerWrapper &>(*eventHandler_);
static_cast<EventHandlerWrapper const &>(*eventHandler_);
eventHandlerWrapper.callback.call(
runtime,
@ -168,7 +173,7 @@ void UIManagerBinding::invalidate() const {
jsi::Value UIManagerBinding::get(
jsi::Runtime &runtime,
const jsi::PropNameID &name) {
jsi::PropNameID const &name) {
auto methodName = name.utf8(runtime);
// Convert shared_ptr<UIManager> to a raw ptr
@ -208,9 +213,9 @@ jsi::Value UIManagerBinding::get(
5,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
return valueFromShadowNode(
runtime,
uiManager->createNode(
@ -230,9 +235,9 @@ jsi::Value UIManagerBinding::get(
1,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
return valueFromShadowNode(
runtime,
uiManager->cloneNode(shadowNodeFromValue(runtime, arguments[0])));
@ -246,9 +251,9 @@ jsi::Value UIManagerBinding::get(
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
uiManager->setJSResponder(
shadowNodeFromValue(runtime, arguments[0]),
arguments[1].getBool());
@ -264,9 +269,9 @@ jsi::Value UIManagerBinding::get(
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
auto node = shadowNodeFromValue(runtime, arguments[0]);
auto locationX = (Float)arguments[1].getNumber();
auto locationY = (Float)arguments[2].getNumber();
@ -294,9 +299,9 @@ jsi::Value UIManagerBinding::get(
0,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
uiManager->clearJSResponder();
return jsi::Value::undefined();
@ -311,9 +316,9 @@ jsi::Value UIManagerBinding::get(
1,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
return valueFromShadowNode(
runtime,
uiManager->cloneNode(
@ -330,10 +335,10 @@ jsi::Value UIManagerBinding::get(
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
const auto &rawProps = RawProps(runtime, arguments[1]);
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
auto const &rawProps = RawProps(runtime, arguments[1]);
return valueFromShadowNode(
runtime,
uiManager->cloneNode(
@ -351,10 +356,10 @@ jsi::Value UIManagerBinding::get(
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
const auto &rawProps = RawProps(runtime, arguments[1]);
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
auto const &rawProps = RawProps(runtime, arguments[1]);
return valueFromShadowNode(
runtime,
uiManager->cloneNode(
@ -371,9 +376,9 @@ jsi::Value UIManagerBinding::get(
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
uiManager->appendChild(
shadowNodeFromValue(runtime, arguments[0]),
shadowNodeFromValue(runtime, arguments[1]));
@ -387,9 +392,9 @@ jsi::Value UIManagerBinding::get(
name,
1,
[](jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
auto shadowNodeList =
std::make_shared<SharedShadowNodeList>(SharedShadowNodeList({}));
return valueFromShadowNodeList(runtime, shadowNodeList);
@ -402,9 +407,9 @@ jsi::Value UIManagerBinding::get(
name,
2,
[](jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
auto shadowNodeList = shadowNodeListFromValue(runtime, arguments[0]);
auto shadowNode = shadowNodeFromValue(runtime, arguments[1]);
shadowNodeList->push_back(shadowNode);
@ -424,29 +429,18 @@ jsi::Value UIManagerBinding::get(
jsi::Runtime &runtime,
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) -> jsi::Value {
try {
auto surfaceId = surfaceIdFromValue(runtime, arguments[0]);
auto shadowNodeList =
shadowNodeListFromValue(runtime, arguments[1]);
size_t count) noexcept -> jsi::Value {
auto surfaceId = surfaceIdFromValue(runtime, arguments[0]);
auto shadowNodeList =
shadowNodeListFromValue(runtime, arguments[1]);
if (sharedUIManager->backgroundExecutor_) {
sharedUIManager->backgroundExecutor_(
[sharedUIManager, surfaceId, shadowNodeList] {
sharedUIManager->completeSurface(
surfaceId, shadowNodeList);
});
} else {
uiManager->completeSurface(surfaceId, shadowNodeList);
}
} catch (std::exception const &e) {
LOG(ERROR) << "Exception in UIManagerBinding::completeRoot(): "
<< e.what();
abort();
} catch (...) {
LOG(ERROR)
<< "Exception in UIManagerBinding::completeRoot(): Unknown.";
abort();
if (sharedUIManager->backgroundExecutor_) {
sharedUIManager->backgroundExecutor_(
[sharedUIManager, surfaceId, shadowNodeList] {
sharedUIManager->completeSurface(surfaceId, shadowNodeList);
});
} else {
uiManager->completeSurface(surfaceId, shadowNodeList);
}
return jsi::Value::undefined();
@ -462,20 +456,10 @@ jsi::Value UIManagerBinding::get(
jsi::Runtime &runtime,
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) -> jsi::Value {
try {
uiManager->completeSurface(
surfaceIdFromValue(runtime, arguments[0]),
shadowNodeListFromValue(runtime, arguments[1]));
} catch (std::exception const &e) {
LOG(ERROR) << "Exception in UIManagerBinding::completeRoot(): "
<< e.what();
abort();
} catch (...) {
LOG(ERROR)
<< "Exception in UIManagerBinding::completeRoot(): Unknown.";
abort();
}
size_t count) noexcept -> jsi::Value {
uiManager->completeSurface(
surfaceIdFromValue(runtime, arguments[0]),
shadowNodeListFromValue(runtime, arguments[1]));
return jsi::Value::undefined();
});
@ -489,9 +473,9 @@ jsi::Value UIManagerBinding::get(
1,
[this](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
auto eventHandler =
arguments[0].getObject(runtime).getFunction(runtime);
eventHandler_ =
@ -507,9 +491,9 @@ jsi::Value UIManagerBinding::get(
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
auto layoutMetrics = uiManager->getRelativeLayoutMetrics(
*shadowNodeFromValue(runtime, arguments[0]),
shadowNodeFromValue(runtime, arguments[1]).get(),
@ -531,9 +515,9 @@ jsi::Value UIManagerBinding::get(
3,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
uiManager->dispatchCommand(
shadowNodeFromValue(runtime, arguments[0]),
stringFromValue(runtime, arguments[1]),
@ -551,9 +535,9 @@ jsi::Value UIManagerBinding::get(
4,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
auto layoutMetrics = uiManager->getRelativeLayoutMetrics(
*shadowNodeFromValue(runtime, arguments[0]),
shadowNodeFromValue(runtime, arguments[1]).get(),
@ -587,9 +571,9 @@ jsi::Value UIManagerBinding::get(
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
auto layoutMetrics = uiManager->getRelativeLayoutMetrics(
*shadowNodeFromValue(runtime, arguments[0]),
nullptr,
@ -622,9 +606,9 @@ jsi::Value UIManagerBinding::get(
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
auto layoutMetrics = uiManager->getRelativeLayoutMetrics(
*shadowNodeFromValue(runtime, arguments[0]),
nullptr,
@ -657,9 +641,9 @@ jsi::Value UIManagerBinding::get(
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
uiManager->setNativeProps(
*shadowNodeFromValue(runtime, arguments[0]),
RawProps(runtime, arguments[1]));
@ -675,9 +659,9 @@ jsi::Value UIManagerBinding::get(
3,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
jsi::Value const &thisValue,
jsi::Value const *arguments,
size_t count) noexcept -> jsi::Value {
uiManager->configureNextLayoutAnimation(
runtime,
// TODO: pass in JSI value instead of folly::dynamic to RawValue

Просмотреть файл

@ -47,8 +47,8 @@ class UIManagerBinding : public jsi::HostObject {
void startSurface(
jsi::Runtime &runtime,
SurfaceId surfaceId,
const std::string &moduleName,
const folly::dynamic &initalProps) const;
std::string const &moduleName,
folly::dynamic const &initalProps) const;
/*
* Stops React Native Surface with given id.
@ -62,9 +62,9 @@ class UIManagerBinding : public jsi::HostObject {
*/
void dispatchEvent(
jsi::Runtime &runtime,
const EventTarget *eventTarget,
const std::string &type,
const ValueFactory &payloadFactory) const;
EventTarget const *eventTarget,
std::string const &type,
ValueFactory const &payloadFactory) const;
/*
* Invalidates the binding and underlying UIManager.
@ -78,11 +78,11 @@ class UIManagerBinding : public jsi::HostObject {
/*
* `jsi::HostObject` specific overloads.
*/
jsi::Value get(jsi::Runtime &runtime, const jsi::PropNameID &name) override;
jsi::Value get(jsi::Runtime &runtime, jsi::PropNameID const &name) override;
private:
std::shared_ptr<UIManager> uiManager_;
std::unique_ptr<const EventHandler> eventHandler_;
std::unique_ptr<EventHandler const> eventHandler_;
};
} // namespace react

Просмотреть файл

@ -42,7 +42,7 @@ struct ShadowNodeListWrapper : public jsi::HostObject {
inline static ShadowNode::Shared shadowNodeFromValue(
jsi::Runtime &runtime,
const jsi::Value &value) {
jsi::Value const &value) {
return value.getObject(runtime)
.getHostObject<ShadowNodeWrapper>(runtime)
->shadowNode;
@ -57,7 +57,7 @@ inline static jsi::Value valueFromShadowNode(
inline static SharedShadowNodeUnsharedList shadowNodeListFromValue(
jsi::Runtime &runtime,
const jsi::Value &value) {
jsi::Value const &value) {
return value.getObject(runtime)
.getHostObject<ShadowNodeListWrapper>(runtime)
->shadowNodeList;
@ -72,31 +72,31 @@ inline static jsi::Value valueFromShadowNodeList(
inline static SharedEventTarget eventTargetFromValue(
jsi::Runtime &runtime,
const jsi::Value &eventTargetValue,
const jsi::Value &tagValue) {
jsi::Value const &eventTargetValue,
jsi::Value const &tagValue) {
return std::make_shared<EventTarget>(
runtime, eventTargetValue, tagValue.getNumber());
}
inline static Tag tagFromValue(jsi::Runtime &runtime, const jsi::Value &value) {
inline static Tag tagFromValue(jsi::Runtime &runtime, jsi::Value const &value) {
return (Tag)value.getNumber();
}
inline static SurfaceId surfaceIdFromValue(
jsi::Runtime &runtime,
const jsi::Value &value) {
jsi::Value const &value) {
return (SurfaceId)value.getNumber();
}
inline static std::string stringFromValue(
jsi::Runtime &runtime,
const jsi::Value &value) {
jsi::Value const &value) {
return value.getString(runtime).utf8(runtime);
}
inline static folly::dynamic commandArgsFromValue(
jsi::Runtime &runtime,
const jsi::Value &value) {
jsi::Value const &value) {
return jsi::dynamicFromValue(runtime, value);
}

Просмотреть файл

@ -101,7 +101,7 @@
"base64-js": "^1.1.2",
"event-target-shim": "^5.0.1",
"fbjs-scripts": "^1.1.0",
"hermes-engine": "~0.6.0",
"hermes-engine": "~0.7.0",
"invariant": "^2.2.4",
"jsc-android": "^245459.0.0",
"metro-babel-register": "0.63.0",

Просмотреть файл

@ -383,7 +383,7 @@ def rn_codegen_cxx_modules(
],
visibility = ["PUBLIC"],
exported_deps = [
react_native_xplat_target("turbomodule/core:core"),
react_native_xplat_target("react/nativemodule/core:core"),
],
)

Просмотреть файл

@ -510,8 +510,8 @@ SPEC CHECKSUMS:
CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845
CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f
DoubleConversion: 2b45d0f8e156a5b02354c8a4062de64d41ccb4e0
FBLazyVector: b1685f3e7e72b3e0e03eb9cafa76f44f205986e7
FBReactNativeSpec: ac2a4d9bd03d8af7fb4813174428ad6a014e77c0
FBLazyVector: 6840df244b2930eadc86dfddd63dd586de8fbf4b
FBReactNativeSpec: 6a5b272a87d8fba0a0267169a9c69104a33f4104
Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365
Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3
@ -522,33 +522,33 @@ SPEC CHECKSUMS:
glog: 789873d01e4b200777d0a09bc23d548446758699
OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355
RCT-Folly: 55d0039b24e192081ec0b2257f7bd9f42e382fb7
RCTRequired: 205b323c68d5dfd5ce7914ffcbdeae97e24373ea
RCTTypeSafety: 6fd50970af7c4e1f684f3e73c61c755747cbcdd3
React: b2352d001abc0a4778f0c61687b4cf6d4ae5da41
React-callinvoker: 8d01a1ae67805a6dcbf1993897646e18c58f182e
React-Core: 588a1f9ff5b745676f75b3ae85423158956b5790
React-CoreModules: 51089db7201fd36061945ea3e0950b6cc0d3d46a
React-cxxreact: e9af0dd6b2f57a5383c662b18ce7047161a81af0
React-jsi: 705b31c0c4ee6c85ec4eab8c34643120e9ed0e94
React-jsiexecutor: c09397b4f5ffd8146d26a0df68223e04ee373ca6
React-jsinspector: cfa811504e00c2e8e1655ee01eef5b6609a3d841
React-perflogger: 0213f3cdfbfb0424ff459b8ecff59946f94b1470
React-RCTActionSheet: 06a968c4debb85b326da51c9ef51ad7ab2d8a2ca
React-RCTAnimation: 577d84c30d06078a09bfa98bf58d10b6dc921f74
React-RCTBlob: 9552ef5af043628d1ab1c90034a33b9fa05dbe68
React-RCTImage: 1a85a75849d8d3d624bf2c98585bd578c5c6e368
React-RCTLinking: 03d175b4ab363b66e7c043bb8099ff5a13289dba
React-RCTNetwork: 297ea48bf5b0306ed575c976d45ddf4b4a6330c6
React-RCTPushNotification: 5c92e178eb91bdcdad13c8eb14f7f7939e79c964
React-RCTSettings: a95c74817566c5eaec37865d2b22126912c256b2
React-RCTTest: cf8c5562b4713ebe315b52e9fa80194e9d4c2d30
React-RCTText: 2b28538ca0ce986efa4ec58b830b29f6e9b15149
React-RCTVibration: 9a0ea019f241a96817185b7312db86374a016791
React-runtimeexecutor: 636c0d7528206f59fbcd5d757341f3a3193257f1
RCTRequired: 8aab7fd83973da16ad4a7c320ab60d95daaf3a49
RCTTypeSafety: 1b9fcfa91c95a819c8ee11b2d78792a1a0c9f008
React: 0d0bbc598d4e4a42c2d8f501e2d85bf68f4b4c58
React-callinvoker: 7171dcdb2310bc9843acfd9e9d1ad4432f069343
React-Core: f9261d49de3292529f426b0337589cf85cbd2be5
React-CoreModules: 6cd34c4feb240e2243a2b766ac42b4922a43f272
React-cxxreact: 1d94eae472a7d96512c9616b79755f17b7d9e60d
React-jsi: 738aad53cef29cda532eb60393636882b048aa63
React-jsiexecutor: 4f307d9cf526c904c6a29a492ee0d0a56a3955ac
React-jsinspector: 9a11ad30cee910a8f9af0f93863b3583512ea3ca
React-perflogger: 869de521c7b7dbdbb1b0ef973a3c00f7c8046ff8
React-RCTActionSheet: a0bd6037f343183655817e8ccfb517d227ce89c6
React-RCTAnimation: b89077f33ada3bc4d37b4b431810a90bce548395
React-RCTBlob: ee093fdd17bea7fccea6b7798112d70f32e14590
React-RCTImage: 6260a130d38065a6b6a0053b066b693b88bfcfb1
React-RCTLinking: 455be7c2c9b567d1e8225d28cefbf0d6c3364f85
React-RCTNetwork: a1d28da57b4d322b4dfd7c036da4e3fddc220ef1
React-RCTPushNotification: 843cd5fdc428814ad09eef327f5e069e2962e5bc
React-RCTSettings: 2fd29ce8818a57f6016243ca3857a36dfda3f73a
React-RCTTest: f5e9ca6222f647dde3ca536ce5d983da1028cbb8
React-RCTText: ae07257137eb22d788947b76d7176418c18d7f8b
React-RCTVibration: 96e1d2916264a8787a2a409e9a3dd1ded5f2a404
React-runtimeexecutor: 165044764bc0b6176e07f654f57d3c84567107b8
React-TurboModuleCxx-RNW: 18bb71af41fe34c8b12a56bef60aae7ee32b0817
React-TurboModuleCxx-WinRTPort: 1573ded4b5b79b532ec047684e45824bb7819802
ReactCommon: 9eaa82ed36085dedf050938c1caf7ad87c9763ac
Yoga: 465347bf1eea8844d3ce34a1564cab46567ae9db
React-TurboModuleCxx-WinRTPort: 8a81b72f4adac4e05a468c73e399c32a65bfcee9
ReactCommon: f4ea3975534282e48263f282d0d6deb8a7e546a1
Yoga: a55415d3b511bbb13a9689678be7e18a6cac73d2
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
PODFILE CHECKSUM: cb260f8f7765c910b68f8267cbd74709f6ae6e54

Просмотреть файл

@ -13,7 +13,7 @@ Before running the app, make sure you ran:
### Running on iOS
Both macOS and Xcode are required.
- `cd packages/rn-tester`
- Install [Bundler](https://bundler.io/): `gem install bundler`. We use bundler to install the right version of [CocoaPods](https://cocoapods.org/) locally.
- Install Bundler and CocoaPods dependencies: `bundle install && bundle exec pod install`
- Open the generated `RNTesterPods.xcworkspace`. This is not checked in, as it is generated by CocoaPods. Do not open `RNTesterPods.xcodeproj` directly.

Просмотреть файл

@ -121,6 +121,8 @@ def jscFlavor = 'org.webkit:android-jsc:+'
def enableHermes = project.ext.react.get("enableHermes", false);
android {
ndkVersion rootProject.ext.ndkVersion
compileSdkVersion rootProject.ext.compileSdkVersion
compileOptions {

Просмотреть файл

@ -6,6 +6,7 @@ buildscript {
minSdkVersion = 19
compileSdkVersion = 29
targetSdkVersion = 29
ndkVersion = "20.1.5948944"
}
repositories {
google()

Просмотреть файл

@ -4680,10 +4680,10 @@ hermes-engine-darwin@~0.5.0:
resolved "https://registry.yarnpkg.com/hermes-engine-darwin/-/hermes-engine-darwin-0.5.1.tgz#697ac45c3aa2ea17a18494c9f8898d984c34c060"
integrity sha512-+IHmncqIrZfOhUYgmInujJVnTQ5qxjijdKC5ohlHalVj088/H6hnAIGr4nX84HAs1JjLCGLA0xujRfd0WY50wA==
hermes-engine@~0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.6.0.tgz#82c0738b89afb3253dcc4b322888a8bf0f52930b"
integrity sha512-WrKfVJ8zsaTz31GHuoX2rJl7AV85Y9bFQkWhqalbObwPusanSsvU+viByDXccUU3khs9CjLBTm0O6DAH3Yls8g==
hermes-engine@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.7.0.tgz#c4a13e09811d7bc975a0662bd2ca7120003d6ef8"
integrity sha512-lU9OenFWXXOzYldqn15QvbiD0kDc+uw2arhCOkR+9D+PhrLFcbEqnaXFESgchN77JYEf77KkqXncTZA8aoXw2A==
hosted-git-info@^2.1.4:
version "2.8.8"