diff --git a/.circleci/Dockerfiles/scripts/run-ci-e2e-tests.sh b/.circleci/Dockerfiles/scripts/run-ci-e2e-tests.sh index 4c12de632141fd..c930937a593021 100755 --- a/.circleci/Dockerfiles/scripts/run-ci-e2e-tests.sh +++ b/.circleci/Dockerfiles/scripts/run-ci-e2e-tests.sh @@ -67,11 +67,6 @@ while :; do shift ;; - --tvos) - RUN_IOS=1 - shift - ;; - *) break esac diff --git a/.eslintrc b/.eslintrc index 47460cc9582009..2c5a51053d8221 100644 --- a/.eslintrc +++ b/.eslintrc @@ -5,15 +5,20 @@ "./packages/eslint-config-react-native-community/index.js" ], + "plugins": [ + "@react-native/eslint-plugin-codegen" + ], + "overrides": [ { "files": [ "Libraries/**/*.js", ], - rules: { - '@react-native-community/no-haste-imports': 2, - '@react-native-community/error-subclass-name': 2, - '@react-native-community/platform-colors': 2, + "rules": { + "@react-native-community/no-haste-imports": 2, + "@react-native-community/error-subclass-name": 2, + "@react-native-community/platform-colors": 2, + "@react-native/codegen/react-native-modules": 2 } }, { @@ -40,8 +45,8 @@ ], "env": { "jasmine": true, - "jest": true, - }, - }, - ], + "jest": true + } + } + ] } diff --git a/.flowconfig b/.flowconfig index cb84f309414bcb..27765405dd858a 100644 --- a/.flowconfig +++ b/.flowconfig @@ -80,4 +80,4 @@ untyped-import untyped-type-import [version] -^0.133.0 +^0.135.0 diff --git a/.flowconfig.android b/.flowconfig.android index 9ae9688898f886..6875e21d96c375 100644 --- a/.flowconfig.android +++ b/.flowconfig.android @@ -83,4 +83,4 @@ untyped-import untyped-type-import [version] -^0.133.0 +^0.135.0 diff --git a/.flowconfig.macos b/.flowconfig.macos index 8697df66dcf171..ee803d82df17e4 100644 --- a/.flowconfig.macos +++ b/.flowconfig.macos @@ -81,4 +81,4 @@ untyped-import untyped-type-import [version] -^0.133.0 +^0.135.0 diff --git a/.gitignore b/.gitignore index fdfa1383303719..9c7f67153a111d 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ project.xcworkspace/* /build/ /packages/react-native-codegen/android/build/ /packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/build +/packages/rn-tester/android/app/.cxx/ /packages/rn-tester/android/app/build/ /packages/rn-tester/android/app/gradle/ /packages/rn-tester/android/app/gradlew diff --git a/Libraries/ActionSheetIOS/NativeActionSheetManager.js b/Libraries/ActionSheetIOS/NativeActionSheetManager.js index 0895778768b17b..9037f7ab3525e1 100644 --- a/Libraries/ActionSheetIOS/NativeActionSheetManager.js +++ b/Libraries/ActionSheetIOS/NativeActionSheetManager.js @@ -17,6 +17,12 @@ import type {ProcessedColorValue} from '../StyleSheet/processColor'; // TODO(mac export interface Spec extends TurboModule { +getConstants: () => {||}; +showActionSheetWithOptions: ( + // TODO(macOS GH#774) - For some reason, yarn lint complains here with this error: + // "Unsupported type 'object' for Spec interface. See https://fburl.com/rn-nativemodules for more details" + // The link in the error is restricted to Facebook employees, so we can't access this info. + // 3a6327a5d9ebfd0de56c37009ab7de1d0e6bdf85 apparently solves this in other places by disabling the error, + // so we might as well do the same thing here. + // eslint-disable-next-line @react-native/codegen/react-native-modules options: {| +title?: ?string, +message?: ?string, diff --git a/Libraries/ActionSheetIOS/React-RCTActionSheet.podspec b/Libraries/ActionSheetIOS/React-RCTActionSheet.podspec index ed7d9fb30ccac5..c43897e0a3c877 100644 --- a/Libraries/ActionSheetIOS/React-RCTActionSheet.podspec +++ b/Libraries/ActionSheetIOS/React-RCTActionSheet.podspec @@ -24,10 +24,10 @@ Pod::Spec.new do |s| s.documentation_url = "https://reactnative.dev/docs/actionsheetios" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.source = source s.source_files = "*.{m}" - s.preserve_paths = "package.json", "LICENSE", "LICENSE-docs" + s.preserve_paths = "package.json", "LICENSE", "LICENSE-docs" s.header_dir = "RCTActionSheet" s.dependency "React-Core/RCTActionSheetHeaders", version diff --git a/Libraries/Alert/NativeAlertManager.js b/Libraries/Alert/NativeAlertManager.js index 08b2c2d648669c..64e1c89789c588 100644 --- a/Libraries/Alert/NativeAlertManager.js +++ b/Libraries/Alert/NativeAlertManager.js @@ -32,6 +32,7 @@ export type Args = {| export interface Spec extends TurboModule { +alertWithArgs: ( + // eslint-disable-next-line @react-native/codegen/react-native-modules args: Args, callback: (id: number, value: string) => void, ) => void; diff --git a/Libraries/Animated/NativeAnimatedModule.js b/Libraries/Animated/NativeAnimatedModule.js index a42e7ba5b12bed..c16597018d1485 100644 --- a/Libraries/Animated/NativeAnimatedModule.js +++ b/Libraries/Animated/NativeAnimatedModule.js @@ -30,7 +30,7 @@ export type AnimatingNodeConfig = Object; export interface Spec extends TurboModule { +startOperationBatch: () => void; +finishOperationBatch: () => void; - + // eslint-disable-next-line @react-native/codegen/react-native-modules +createAnimatedNode: (tag: number, config: AnimatedNodeConfig) => void; +getValue: (tag: number, saveValueCallback: SaveValueCallback) => void; +startListeningToAnimatedNodeValue: (tag: number) => void; diff --git a/Libraries/Animated/NativeAnimatedTurboModule.js b/Libraries/Animated/NativeAnimatedTurboModule.js index ac6c50b45069ac..e5d5bcbeebd84f 100644 --- a/Libraries/Animated/NativeAnimatedTurboModule.js +++ b/Libraries/Animated/NativeAnimatedTurboModule.js @@ -30,7 +30,7 @@ export type AnimatingNodeConfig = Object; export interface Spec extends TurboModule { +startOperationBatch: () => void; +finishOperationBatch: () => void; - + // eslint-disable-next-line @react-native/codegen/react-native-modules +createAnimatedNode: (tag: number, config: AnimatedNodeConfig) => void; +getValue: (tag: number, saveValueCallback: SaveValueCallback) => void; +startListeningToAnimatedNodeValue: (tag: number) => void; diff --git a/Libraries/Animated/createAnimatedComponent.js b/Libraries/Animated/createAnimatedComponent.js index d5dc89b7f81f8d..ccc789c8037b46 100644 --- a/Libraries/Animated/createAnimatedComponent.js +++ b/Libraries/Animated/createAnimatedComponent.js @@ -224,7 +224,8 @@ function createAnimatedComponent( 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 diff --git a/Libraries/Animated/nodes/AnimatedDivision.js b/Libraries/Animated/nodes/AnimatedDivision.js index 437b013b525c8f..fad47ae87d1242 100644 --- a/Libraries/Animated/nodes/AnimatedDivision.js +++ b/Libraries/Animated/nodes/AnimatedDivision.js @@ -24,7 +24,7 @@ class AnimatedDivision extends AnimatedWithChildren { constructor(a: AnimatedNode | number, b: AnimatedNode | number) { super(); - if (b === 0) { + if (b === 0 || (b instanceof AnimatedNode && b.__getValue() === 0)) { console.error('Detected potential division by zero in AnimatedDivision'); } this._a = typeof a === 'number' ? new AnimatedValue(a) : a; diff --git a/Libraries/Blob/React-RCTBlob.podspec b/Libraries/Blob/React-RCTBlob.podspec index c9be6c0dd0b866..8522ca699905cb 100644 --- a/Libraries/Blob/React-RCTBlob.podspec +++ b/Libraries/Blob/React-RCTBlob.podspec @@ -26,7 +26,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "*.{h,m,mm}" diff --git a/Libraries/Components/AppleTV/TVEventHandler.js b/Libraries/Components/AppleTV/TVEventHandler.js deleted file mode 100644 index d3f475a1fdb777..00000000000000 --- a/Libraries/Components/AppleTV/TVEventHandler.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - * @flow - */ - -'use strict'; - -import NativeEventEmitter from '../../EventEmitter/NativeEventEmitter'; -import Platform from '../../Utilities/Platform'; -import {type EventSubscription} from '../../vendor/emitter/EventEmitter'; -import NativeTVNavigationEventEmitter from './NativeTVNavigationEventEmitter'; - -class TVEventHandler { - __nativeTVNavigationEventListener: ?EventSubscription = null; - __nativeTVNavigationEventEmitter: ?NativeEventEmitter = null; - - enable(component: ?any, callback: Function): void { - if (Platform.OS === 'ios' && !NativeTVNavigationEventEmitter) { - return; - } - - this.__nativeTVNavigationEventEmitter = new NativeEventEmitter( - NativeTVNavigationEventEmitter, - ); - this.__nativeTVNavigationEventListener = this.__nativeTVNavigationEventEmitter.addListener( - 'onHWKeyEvent', - data => { - if (callback) { - callback(component, data); - } - }, - ); - } - - disable(): void { - if (this.__nativeTVNavigationEventListener) { - this.__nativeTVNavigationEventListener.remove(); - delete this.__nativeTVNavigationEventListener; - } - if (this.__nativeTVNavigationEventEmitter) { - delete this.__nativeTVNavigationEventEmitter; - } - } -} - -module.exports = TVEventHandler; diff --git a/Libraries/Components/Picker/AndroidDialogPickerNativeComponent.js b/Libraries/Components/Picker/AndroidDialogPickerNativeComponent.js index bac74826a1fb11..d01757865909fc 100644 --- a/Libraries/Components/Picker/AndroidDialogPickerNativeComponent.js +++ b/Libraries/Components/Picker/AndroidDialogPickerNativeComponent.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. * * @format - * @flow strict-local + * @flow */ 'use strict'; @@ -14,6 +14,8 @@ import * as React from 'react'; import codegenNativeCommands from '../../Utilities/codegenNativeCommands'; import requireNativeComponent from '../../ReactNative/requireNativeComponent'; +import registerGeneratedViewConfig from '../../Utilities/registerGeneratedViewConfig'; +import AndroidDialogPickerViewConfig from './AndroidDialogPickerViewConfig'; import type { DirectEventHandler, @@ -64,6 +66,17 @@ export const Commands: NativeCommands = codegenNativeCommands({ supportedCommands: ['setNativeSelectedPosition'], }); -export default (requireNativeComponent( - 'AndroidDialogPicker', -): NativeType); +let AndroidDialogPickerNativeComponent; +if (global.RN$Bridgeless) { + registerGeneratedViewConfig( + 'AndroidDialogPicker', + AndroidDialogPickerViewConfig, + ); + AndroidDialogPickerNativeComponent = 'AndroidDialogPicker'; +} else { + AndroidDialogPickerNativeComponent = requireNativeComponent( + 'AndroidDialogPicker', + ); +} + +export default ((AndroidDialogPickerNativeComponent: any): NativeType); diff --git a/Libraries/Components/Picker/AndroidDialogPickerViewConfig.js b/Libraries/Components/Picker/AndroidDialogPickerViewConfig.js new file mode 100644 index 00000000000000..1108b8105e37e1 --- /dev/null +++ b/Libraries/Components/Picker/AndroidDialogPickerViewConfig.js @@ -0,0 +1,30 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +'use strict'; + +import type {GeneratedViewConfig} from '../../Utilities/registerGeneratedViewConfig'; + +const AndroidDialogPickerViewConfig = { + uiViewClassName: 'AndroidDialogPicker', + bubblingEventTypes: {}, + directEventTypes: {}, + validAttributes: { + color: {process: require('../../StyleSheet/processColor')}, + backgroundColor: {process: require('../../StyleSheet/processColor')}, + enabled: true, + items: true, + prompt: true, + selected: true, + onSelect: true, + }, +}; + +module.exports = (AndroidDialogPickerViewConfig: GeneratedViewConfig); diff --git a/Libraries/Components/Pressable/Pressable.js b/Libraries/Components/Pressable/Pressable.js index 1f88a43aa52a93..6e6a7540c08e19 100644 --- a/Libraries/Components/Pressable/Pressable.js +++ b/Libraries/Components/Pressable/Pressable.js @@ -190,6 +190,16 @@ function Pressable(props: Props, forwardedRef): React.Node { const hitSlop = normalizeRect(props.hitSlop); + const restPropsWithDefaults: React.ElementConfig = { + ...restProps, + ...android_rippleConfig?.viewProps, + acceptsFirstMouse: acceptsFirstMouse !== false && !disabled, // [TODO(macOS GH#774) + enableFocusRing: enableFocusRing !== false && !disabled, // ]TODO(macOS GH#774) + accessible: accessible !== false, + focusable: focusable !== false, + hitSlop, + }; + const config = useMemo( () => ({ disabled, @@ -239,14 +249,8 @@ function Pressable(props: Props, forwardedRef): React.Node { return ( diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index c3a252a1d5a956..cdaafbe4a6b2af 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -1315,13 +1315,12 @@ class ScrollView extends React.Component { if (refreshControl) { if (Platform.OS === 'ios') { // On iOS the RefreshControl is a child of the ScrollView. - // tvOS lacks native support for RefreshControl, so don't include it in that case return ( /* $FlowFixMe(>=0.117.0 site=react_native_fb) This comment suppresses * an error found when Flow v0.117 was deployed. To see the error, * delete this comment and run Flow. */ - {Platform.isTV ? null : refreshControl} + {refreshControl} {contentContainer} ); diff --git a/Libraries/Components/StatusBar/NativeStatusBarManagerAndroid.js b/Libraries/Components/StatusBar/NativeStatusBarManagerAndroid.js index e9168b5dc52b06..486c8bf4eb91d7 100644 --- a/Libraries/Components/StatusBar/NativeStatusBarManagerAndroid.js +++ b/Libraries/Components/StatusBar/NativeStatusBarManagerAndroid.js @@ -26,6 +26,7 @@ export interface Spec extends TurboModule { * - 'default' * - 'dark-content' */ + // eslint-disable-next-line @react-native/codegen/react-native-modules +setStyle: (statusBarStyle?: ?string) => void; +setHidden: (hidden: boolean) => void; } diff --git a/Libraries/Components/StatusBar/NativeStatusBarManagerIOS.js b/Libraries/Components/StatusBar/NativeStatusBarManagerIOS.js index b5da2851bf2436..86251253640059 100644 --- a/Libraries/Components/StatusBar/NativeStatusBarManagerIOS.js +++ b/Libraries/Components/StatusBar/NativeStatusBarManagerIOS.js @@ -31,6 +31,7 @@ export interface Spec extends TurboModule { * - 'dark-content' * - 'light-content' */ + // eslint-disable-next-line @react-native/codegen/react-native-modules +setStyle: (statusBarStyle?: ?string, animated: boolean) => void; /** * - withAnimation can be: 'none' | 'fade' | 'slide' diff --git a/Libraries/Components/Touchable/TVTouchable.js b/Libraries/Components/Touchable/TVTouchable.js deleted file mode 100644 index 01e51ba766509a..00000000000000 --- a/Libraries/Components/Touchable/TVTouchable.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - * @format - */ - -'use strict'; - -import invariant from 'invariant'; -import ReactNative from '../../Renderer/shims/ReactNative'; -import type { - BlurEvent, - FocusEvent, - PressEvent, -} from '../../Types/CoreEventTypes'; -import Platform from '../../Utilities/Platform'; -import TVEventHandler from '../../Components/AppleTV/TVEventHandler'; - -type TVTouchableConfig = $ReadOnly<{| - getDisabled: () => boolean, - onBlur: (event: BlurEvent) => mixed, - onFocus: (event: FocusEvent) => mixed, - onPress: (event: PressEvent) => mixed, -|}>; - -export default class TVTouchable { - _tvEventHandler: TVEventHandler; - - constructor(component: any, config: TVTouchableConfig) { - invariant(Platform.isTV, 'TVTouchable: Requires `Platform.isTV`.'); - this._tvEventHandler = new TVEventHandler(); - this._tvEventHandler.enable(component, (_, tvData) => { - tvData.dispatchConfig = {}; - if (ReactNative.findNodeHandle(component) === tvData.tag) { - if (tvData.eventType === 'focus') { - config.onFocus(tvData); - } else if (tvData.eventType === 'blur') { - config.onBlur(tvData); - } else if (tvData.eventType === 'select') { - if (!config.getDisabled()) { - config.onPress(tvData); - } - } - } - }); - } - - destroy(): void { - this._tvEventHandler.disable(); - } -} diff --git a/Libraries/Components/Touchable/Touchable.js b/Libraries/Components/Touchable/Touchable.js index 8bfa1cb75eeb26..8b232d13d467be 100644 --- a/Libraries/Components/Touchable/Touchable.js +++ b/Libraries/Components/Touchable/Touchable.js @@ -16,7 +16,6 @@ const Position = require('./Position'); const React = require('react'); const ReactNative = require('../../Renderer/shims/ReactNative'); const StyleSheet = require('../../StyleSheet/StyleSheet'); -const TVEventHandler = require('../AppleTV/TVEventHandler'); const UIManager = require('../../ReactNative/UIManager'); const View = require('../View/View'); const SoundManager = require('../Sound/SoundManager'); @@ -370,33 +369,12 @@ const TouchableMixin = { if (!Platform.isTV) { return; } - - this._tvEventHandler = new TVEventHandler(); - this._tvEventHandler.enable(this, function(cmp, evt) { - const myTag = ReactNative.findNodeHandle(cmp); - evt.dispatchConfig = {}; - if (myTag === evt.tag) { - if (evt.eventType === 'focus') { - cmp.touchableHandleFocus(evt); - } else if (evt.eventType === 'blur') { - cmp.touchableHandleBlur(evt); - } else if (evt.eventType === 'select' && Platform.OS !== 'android') { - cmp.touchableHandlePress && - !cmp.props.disabled && - cmp.touchableHandlePress(evt); - } - } - }); }, /** * Clear all timeouts on unmount */ componentWillUnmount: function() { - if (this._tvEventHandler) { - this._tvEventHandler.disable(); - delete this._tvEventHandler; - } this.touchableDelayTimeout && clearTimeout(this.touchableDelayTimeout); this.longPressDelayTimeout && clearTimeout(this.longPressDelayTimeout); this.pressOutDelayTimeout && clearTimeout(this.pressOutDelayTimeout); diff --git a/Libraries/Components/Touchable/TouchableBounce.js b/Libraries/Components/Touchable/TouchableBounce.js index 77d3985bb235b9..7bf17966ed8d48 100644 --- a/Libraries/Components/Touchable/TouchableBounce.js +++ b/Libraries/Components/Touchable/TouchableBounce.js @@ -15,7 +15,6 @@ import Pressability, { } from '../../Pressability/Pressability'; import {PressabilityDebugView} from '../../Pressability/PressabilityDebug'; import type {ViewStyleProp} from '../../StyleSheet/StyleSheet'; -import TVTouchable from './TVTouchable'; import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback'; import {Animated, Platform} from 'react-native'; import * as React from 'react'; @@ -38,8 +37,6 @@ type State = $ReadOnly<{| |}>; class TouchableBounce extends React.Component { - _tvTouchable: ?TVTouchable; - state: State = { pressability: new Pressability(this._createPressabilityConfig()), scale: new Animated.Value(1), @@ -203,39 +200,11 @@ class TouchableBounce extends React.Component { ); } - componentDidMount(): void { - if (Platform.isTV) { - this._tvTouchable = new TVTouchable(this, { - getDisabled: () => this.props.disabled === true, - onBlur: event => { - if (this.props.onBlur != null) { - this.props.onBlur(event); - } - }, - onFocus: event => { - if (this.props.onFocus != null) { - this.props.onFocus(event); - } - }, - onPress: event => { - if (this.props.onPress != null) { - this.props.onPress(event); - } - }, - }); - } - } - componentDidUpdate(prevProps: Props, prevState: State) { this.state.pressability.configure(this._createPressabilityConfig()); } componentWillUnmount(): void { - if (Platform.isTV) { - if (this._tvTouchable != null) { - this._tvTouchable.destroy(); - } - } this.state.pressability.reset(); } } diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js index aa81e91121b97a..0635932c93bf4a 100644 --- a/Libraries/Components/Touchable/TouchableHighlight.js +++ b/Libraries/Components/Touchable/TouchableHighlight.js @@ -16,7 +16,6 @@ import Pressability, { import {PressabilityDebugView} from '../../Pressability/PressabilityDebug'; import StyleSheet, {type ViewStyleProp} from '../../StyleSheet/StyleSheet'; import type {ColorValue} from '../../StyleSheet/StyleSheet'; -import TVTouchable from './TVTouchable'; import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback'; import Platform from '../../Utilities/Platform'; import View from '../../Components/View/View'; @@ -158,7 +157,6 @@ type State = $ReadOnly<{| class TouchableHighlight extends React.Component { _hideTimeout: ?TimeoutID; _isMounted: boolean = false; - _tvTouchable: ?TVTouchable; state: State = { pressability: new Pressability(this._createPressabilityConfig()), @@ -367,26 +365,6 @@ class TouchableHighlight extends React.Component { componentDidMount(): void { this._isMounted = true; - if (Platform.isTV) { - this._tvTouchable = new TVTouchable(this, { - getDisabled: () => this.props.disabled === true, - onBlur: event => { - if (this.props.onBlur != null) { - this.props.onBlur(event); - } - }, - onFocus: event => { - if (this.props.onFocus != null) { - this.props.onFocus(event); - } - }, - onPress: event => { - if (this.props.onPress != null) { - this.props.onPress(event); - } - }, - }); - } } componentDidUpdate(prevProps: Props, prevState: State) { @@ -398,11 +376,6 @@ class TouchableHighlight extends React.Component { if (this._hideTimeout != null) { clearTimeout(this._hideTimeout); } - if (Platform.isTV) { - if (this._tvTouchable != null) { - this._tvTouchable.destroy(); - } - } this.state.pressability.reset(); } } diff --git a/Libraries/Components/Touchable/TouchableNativeFeedback.js b/Libraries/Components/Touchable/TouchableNativeFeedback.js index 1b92392c1b6b86..05be0f481d35f0 100644 --- a/Libraries/Components/Touchable/TouchableNativeFeedback.js +++ b/Libraries/Components/Touchable/TouchableNativeFeedback.js @@ -14,7 +14,6 @@ import Pressability, { type PressabilityConfig, } from '../../Pressability/Pressability'; import {PressabilityDebugView} from '../../Pressability/PressabilityDebug'; -import TVTouchable from './TVTouchable'; import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback'; import {Commands} from 'react-native/Libraries/Components/View/ViewNativeComponent'; import ReactNative from 'react-native/Libraries/Renderer/shims/ReactNative'; @@ -178,8 +177,6 @@ class TouchableNativeFeedback extends React.Component { static canUseNativeForeground: () => boolean = () => Platform.OS === 'android' && Platform.Version >= 23; - _tvTouchable: ?TVTouchable; - state: State = { pressability: new Pressability(this._createPressabilityConfig()), }; @@ -317,39 +314,11 @@ class TouchableNativeFeedback extends React.Component { ); } - componentDidMount(): void { - if (Platform.isTV) { - this._tvTouchable = new TVTouchable(this, { - getDisabled: () => this.props.disabled === true, - onBlur: event => { - if (this.props.onBlur != null) { - this.props.onBlur(event); - } - }, - onFocus: event => { - if (this.props.onFocus != null) { - this.props.onFocus(event); - } - }, - onPress: event => { - if (this.props.onPress != null) { - this.props.onPress(event); - } - }, - }); - } - } - componentDidUpdate(prevProps: Props, prevState: State) { this.state.pressability.configure(this._createPressabilityConfig()); } componentWillUnmount(): void { - if (Platform.isTV) { - if (this._tvTouchable != null) { - this._tvTouchable.destroy(); - } - } this.state.pressability.reset(); } } diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 7ed66f75b0f0e5..f2a70c76b847b8 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -14,7 +14,6 @@ import Pressability, { type PressabilityConfig, } from '../../Pressability/Pressability'; import {PressabilityDebugView} from '../../Pressability/PressabilityDebug'; -import TVTouchable from './TVTouchable'; import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback'; import Animated from 'react-native/Libraries/Animated/Animated'; import Easing from 'react-native/Libraries/Animated/Easing'; @@ -143,8 +142,6 @@ type State = $ReadOnly<{| * */ class TouchableOpacity extends React.Component { - _tvTouchable: ?TVTouchable; - state: State = { anim: new Animated.Value(this._getChildStyleOpacityWithDefault()), pressability: new Pressability(this._createPressabilityConfig()), @@ -317,29 +314,6 @@ class TouchableOpacity extends React.Component { ); } - componentDidMount(): void { - if (Platform.isTV) { - this._tvTouchable = new TVTouchable(this, { - getDisabled: () => this.props.disabled === true, - onBlur: event => { - if (this.props.onBlur != null) { - this.props.onBlur(event); - } - }, - onFocus: event => { - if (this.props.onFocus != null) { - this.props.onFocus(event); - } - }, - onPress: event => { - if (this.props.onPress != null) { - this.props.onPress(event); - } - }, - }); - } - } - componentDidUpdate(prevProps: Props, prevState: State) { this.state.pressability.configure(this._createPressabilityConfig()); if (this.props.disabled !== prevProps.disabled) { @@ -348,11 +322,6 @@ class TouchableOpacity extends React.Component { } componentWillUnmount(): void { - if (Platform.isTV) { - if (this._tvTouchable != null) { - this._tvTouchable.destroy(); - } - } this.state.pressability.reset(); } } diff --git a/Libraries/Components/Touchable/TouchableWithoutFeedback.js b/Libraries/Components/Touchable/TouchableWithoutFeedback.js index 196dd222ffaf89..85e0707e2ad99a 100755 --- a/Libraries/Components/Touchable/TouchableWithoutFeedback.js +++ b/Libraries/Components/Touchable/TouchableWithoutFeedback.js @@ -14,7 +14,6 @@ import Pressability, { type PressabilityConfig, } from '../../Pressability/Pressability'; import {PressabilityDebugView} from '../../Pressability/PressabilityDebug'; -import TVTouchable from './TVTouchable'; import type { AccessibilityActionEvent, AccessibilityActionInfo, @@ -127,8 +126,6 @@ const PASSTHROUGH_PROPS = [ ]; class TouchableWithoutFeedback extends React.Component { - _tvTouchable: ?TVTouchable; - state: State = { pressability: new Pressability(createPressabilityConfig(this.props)), }; @@ -186,39 +183,11 @@ class TouchableWithoutFeedback extends React.Component { return React.cloneElement(element, elementProps, ...children); } - componentDidMount(): void { - if (Platform.isTV) { - this._tvTouchable = new TVTouchable(this, { - getDisabled: () => this.props.disabled === true, - onBlur: event => { - if (this.props.onBlur != null) { - this.props.onBlur(event); - } - }, - onFocus: event => { - if (this.props.onFocus != null) { - this.props.onFocus(event); - } - }, - onPress: event => { - if (this.props.onPress != null) { - this.props.onPress(event); - } - }, - }); - } - } - componentDidUpdate(): void { this.state.pressability.configure(createPressabilityConfig(this.props)); } componentWillUnmount(): void { - if (Platform.isTV) { - if (this._tvTouchable != null) { - this._tvTouchable.destroy(); - } - } this.state.pressability.reset(); } } diff --git a/Libraries/Core/NativeExceptionsManager.js b/Libraries/Core/NativeExceptionsManager.js index 798bb45686ebcd..ba4b628e5e6676 100644 --- a/Libraries/Core/NativeExceptionsManager.js +++ b/Libraries/Core/NativeExceptionsManager.js @@ -47,6 +47,7 @@ export interface Spec extends TurboModule { stack: Array, exceptionId: number, ) => void; + // eslint-disable-next-line @react-native/codegen/react-native-modules +reportException?: (data: ExceptionData) => void; +updateExceptionMessage: ( message: string, diff --git a/Libraries/DeprecatedPropTypes/DeprecatedTVViewPropTypes.js b/Libraries/DeprecatedPropTypes/DeprecatedTVViewPropTypes.js deleted file mode 100644 index 22628f0e469446..00000000000000 --- a/Libraries/DeprecatedPropTypes/DeprecatedTVViewPropTypes.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - * @flow strict-local - */ - -'use strict'; - -const PropTypes = require('prop-types'); - -const DeprecatedTVViewPropTypes = { - hasTVPreferredFocus: PropTypes.bool, - tvParallaxShiftDistanceX: PropTypes.number, - tvParallaxShiftDistanceY: PropTypes.number, - tvParallaxTiltAngle: PropTypes.number, - tvParallaxMagnification: PropTypes.number, -}; - -module.exports = DeprecatedTVViewPropTypes; diff --git a/Libraries/FBLazyVector/FBLazyVector.podspec b/Libraries/FBLazyVector/FBLazyVector.podspec index 778fbb7d785cbc..4ecf18b34ae38e 100644 --- a/Libraries/FBLazyVector/FBLazyVector.podspec +++ b/Libraries/FBLazyVector/FBLazyVector.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#774) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#774) s.source = source s.source_files = "**/*.{c,h,m,mm,cpp}" s.header_dir = "FBLazyVector" diff --git a/Libraries/FBReactNativeSpec/BUCK b/Libraries/FBReactNativeSpec/BUCK index 3ab64ac8a7a14a..96f09f1d31c205 100644 --- a/Libraries/FBReactNativeSpec/BUCK +++ b/Libraries/FBReactNativeSpec/BUCK @@ -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"), ], ) diff --git a/Libraries/FBReactNativeSpec/FBReactNativeSpec.podspec b/Libraries/FBReactNativeSpec/FBReactNativeSpec.podspec index e9c43f8400296c..6e97b674a2239d 100644 --- a/Libraries/FBReactNativeSpec/FBReactNativeSpec.podspec +++ b/Libraries/FBReactNativeSpec/FBReactNativeSpec.podspec @@ -26,7 +26,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#774) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#774) s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "**/*.{c,h,m,mm,cpp}" diff --git a/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm b/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm index 1f7227f478f872..5ca05feb84cd38 100644 --- a/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm +++ b/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm @@ -1610,46 +1610,72 @@ + (RCTManagedPointer *)JS_NativeImagePickerIOS_SpecOpenSelectDialogConfig:(id)js } // namespace react } // namespace facebook -@implementation RCTCxxConvert (NativeImageStore_SpecAddImageFromBase64ErrorCallbackError) -+ (RCTManagedPointer *)JS_NativeImageStore_SpecAddImageFromBase64ErrorCallbackError:(id)json +namespace facebook { + namespace react { + + + static facebook::jsi::Value __hostFunction_NativeImageStoreAndroidSpecJSI_getBase64ForTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getBase64ForTag", @selector(getBase64ForTag:successCallback:errorCallback:), args, count); + } + + + NativeImageStoreAndroidSpecJSI::NativeImageStoreAndroidSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_["getBase64ForTag"] = MethodMetadata {3, __hostFunction_NativeImageStoreAndroidSpecJSI_getBase64ForTag}; + + + + } + + } // namespace react +} // namespace facebook +@implementation RCTCxxConvert (NativeImageStoreIOS_SpecGetBase64ForTagErrorCallbackError) ++ (RCTManagedPointer *)JS_NativeImageStoreIOS_SpecGetBase64ForTagErrorCallbackError:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +@implementation RCTCxxConvert (NativeImageStoreIOS_SpecAddImageFromBase64ErrorCallbackError) ++ (RCTManagedPointer *)JS_NativeImageStoreIOS_SpecAddImageFromBase64ErrorCallbackError:(id)json { - return facebook::react::managedPointer(json); + return facebook::react::managedPointer(json); } @end namespace facebook { namespace react { - static facebook::jsi::Value __hostFunction_NativeImageStoreSpecJSI_getBase64ForTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + static facebook::jsi::Value __hostFunction_NativeImageStoreIOSSpecJSI_getBase64ForTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "getBase64ForTag", @selector(getBase64ForTag:successCallback:errorCallback:), args, count); } - static facebook::jsi::Value __hostFunction_NativeImageStoreSpecJSI_hasImageForTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + static facebook::jsi::Value __hostFunction_NativeImageStoreIOSSpecJSI_hasImageForTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "hasImageForTag", @selector(hasImageForTag:callback:), args, count); } - static facebook::jsi::Value __hostFunction_NativeImageStoreSpecJSI_removeImageForTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + static facebook::jsi::Value __hostFunction_NativeImageStoreIOSSpecJSI_removeImageForTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "removeImageForTag", @selector(removeImageForTag:), args, count); } - static facebook::jsi::Value __hostFunction_NativeImageStoreSpecJSI_addImageFromBase64(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + static facebook::jsi::Value __hostFunction_NativeImageStoreIOSSpecJSI_addImageFromBase64(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addImageFromBase64", @selector(addImageFromBase64:successCallback:errorCallback:), args, count); } - NativeImageStoreSpecJSI::NativeImageStoreSpecJSI(const ObjCTurboModule::InitParams ¶ms) + NativeImageStoreIOSSpecJSI::NativeImageStoreIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms) : ObjCTurboModule(params) { - methodMap_["getBase64ForTag"] = MethodMetadata {3, __hostFunction_NativeImageStoreSpecJSI_getBase64ForTag}; + methodMap_["getBase64ForTag"] = MethodMetadata {3, __hostFunction_NativeImageStoreIOSSpecJSI_getBase64ForTag}; - methodMap_["hasImageForTag"] = MethodMetadata {2, __hostFunction_NativeImageStoreSpecJSI_hasImageForTag}; + methodMap_["hasImageForTag"] = MethodMetadata {2, __hostFunction_NativeImageStoreIOSSpecJSI_hasImageForTag}; - methodMap_["removeImageForTag"] = MethodMetadata {1, __hostFunction_NativeImageStoreSpecJSI_removeImageForTag}; + methodMap_["removeImageForTag"] = MethodMetadata {1, __hostFunction_NativeImageStoreIOSSpecJSI_removeImageForTag}; - methodMap_["addImageFromBase64"] = MethodMetadata {3, __hostFunction_NativeImageStoreSpecJSI_addImageFromBase64}; + methodMap_["addImageFromBase64"] = MethodMetadata {3, __hostFunction_NativeImageStoreIOSSpecJSI_addImageFromBase64}; diff --git a/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h b/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h index 2e45270b5bb5f8..e8bc65b8b329cd 100644 --- a/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h +++ b/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h @@ -1530,9 +1530,45 @@ namespace facebook { }; } // namespace react } // namespace facebook +@protocol NativeImageStoreAndroidSpec + +- (void)getBase64ForTag:(NSString *)uri + successCallback:(RCTResponseSenderBlock)successCallback + errorCallback:(RCTResponseSenderBlock)errorCallback; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'ImageStoreAndroid' + */ + + class JSI_EXPORT NativeImageStoreAndroidSpecJSI : public ObjCTurboModule { + public: + NativeImageStoreAndroidSpecJSI(const ObjCTurboModule::InitParams ¶ms); + + }; + } // namespace react +} // namespace facebook namespace JS { - namespace NativeImageStore { + namespace NativeImageStoreIOS { + struct SpecGetBase64ForTagErrorCallbackError { + NSString *message() const; + + SpecGetBase64ForTagErrorCallbackError(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeImageStoreIOS_SpecGetBase64ForTagErrorCallbackError) ++ (RCTManagedPointer *)JS_NativeImageStoreIOS_SpecGetBase64ForTagErrorCallbackError:(id)json; +@end + +namespace JS { + namespace NativeImageStoreIOS { struct SpecAddImageFromBase64ErrorCallbackError { NSString *message() const; @@ -1543,10 +1579,10 @@ namespace JS { } } -@interface RCTCxxConvert (NativeImageStore_SpecAddImageFromBase64ErrorCallbackError) -+ (RCTManagedPointer *)JS_NativeImageStore_SpecAddImageFromBase64ErrorCallbackError:(id)json; +@interface RCTCxxConvert (NativeImageStoreIOS_SpecAddImageFromBase64ErrorCallbackError) ++ (RCTManagedPointer *)JS_NativeImageStoreIOS_SpecAddImageFromBase64ErrorCallbackError:(id)json; @end -@protocol NativeImageStoreSpec +@protocol NativeImageStoreIOSSpec - (void)getBase64ForTag:(NSString *)uri successCallback:(RCTResponseSenderBlock)successCallback @@ -1562,12 +1598,12 @@ namespace JS { namespace facebook { namespace react { /** - * ObjC++ class for module 'ImageStore' + * ObjC++ class for module 'ImageStoreIOS' */ - class JSI_EXPORT NativeImageStoreSpecJSI : public ObjCTurboModule { + class JSI_EXPORT NativeImageStoreIOSSpecJSI : public ObjCTurboModule { public: - NativeImageStoreSpecJSI(const ObjCTurboModule::InitParams ¶ms); + NativeImageStoreIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms); }; } // namespace react @@ -3315,7 +3351,12 @@ inline bool JS::NativeImagePickerIOS::SpecOpenSelectDialogConfig::showVideos() c id const p = _v[@"showVideos"]; return RCTBridgingToBool(p); } -inline NSString *JS::NativeImageStore::SpecAddImageFromBase64ErrorCallbackError::message() const +inline NSString *JS::NativeImageStoreIOS::SpecGetBase64ForTagErrorCallbackError::message() const +{ + id const p = _v[@"message"]; + return RCTBridgingToString(p); +} +inline NSString *JS::NativeImageStoreIOS::SpecAddImageFromBase64ErrorCallbackError::message() const { id const p = _v[@"message"]; return RCTBridgingToString(p); diff --git a/Libraries/Image/NativeImageEditor.js b/Libraries/Image/NativeImageEditor.js index c6fdc63f7d4812..738483b15842f1 100644 --- a/Libraries/Image/NativeImageEditor.js +++ b/Libraries/Image/NativeImageEditor.js @@ -42,6 +42,7 @@ export interface Spec extends TurboModule { +getConstants: () => {||}; +cropImage: ( uri: string, + // eslint-disable-next-line @react-native/codegen/react-native-modules cropData: Options, successCallback: (uri: string) => void, errorCallback: (error: string) => void, diff --git a/Libraries/Image/NativeImageStoreAndroid.js b/Libraries/Image/NativeImageStoreAndroid.js new file mode 100644 index 00000000000000..8ab6dc7c1a2315 --- /dev/null +++ b/Libraries/Image/NativeImageStoreAndroid.js @@ -0,0 +1,27 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +'use strict'; + +import type {TurboModule} from '../TurboModule/RCTExport'; +import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; + +export interface Spec extends TurboModule { + +getConstants: () => {||}; + +getBase64ForTag: ( + uri: string, + successCallback: (base64ImageData: string) => void, + errorCallback: (error: string) => void, + ) => void; +} + +export default (TurboModuleRegistry.getEnforcing( + 'ImageStoreManager', +): Spec); diff --git a/Libraries/Image/NativeImageStore.js b/Libraries/Image/NativeImageStoreIOS.js similarity index 75% rename from Libraries/Image/NativeImageStore.js rename to Libraries/Image/NativeImageStoreIOS.js index 38603b6aaab2cf..d94f1ef4f35095 100644 --- a/Libraries/Image/NativeImageStore.js +++ b/Libraries/Image/NativeImageStoreIOS.js @@ -15,21 +15,11 @@ import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; export interface Spec extends TurboModule { +getConstants: () => {||}; - // Common +getBase64ForTag: ( uri: string, successCallback: (base64ImageData: string) => void, - - /** - * On Android, the failure callback is called with a string. - * On iOS, the failure callback is called with an error object. - * - * TODO(T47527939) Unify this inconsistency - */ - errorCallback: (error: {|message: string|} | string) => void, + errorCallback: (error: {|message: string|}) => void, ) => void; - - // iOS-only +hasImageForTag: (uri: string, callback: (hasImage: boolean) => void) => void; +removeImageForTag: (uri: string) => void; +addImageFromBase64: ( diff --git a/Libraries/Image/RCTImageLoader.mm b/Libraries/Image/RCTImageLoader.mm index e0262b3710d06e..34975092ee07f5 100644 --- a/Libraries/Image/RCTImageLoader.mm +++ b/Libraries/Image/RCTImageLoader.mm @@ -862,19 +862,6 @@ - (NSString *)loaderModuleNameForRequestUrl:(NSURL *)url { return nil; } -- (void)trackURLImageContentDidSetForRequest:(RCTImageURLLoaderRequest *)loaderRequest -{ - if (!loaderRequest) { - return; - } - - // This delegate method is Fabric-only - id loadHandler = [self imageURLLoaderForURL:loaderRequest.imageURL]; - if ([loadHandler respondsToSelector:@selector(trackURLImageContentDidSetForRequest:)]) { - [(id)loadHandler trackURLImageContentDidSetForRequest:loaderRequest]; - } -} - - (void)trackURLImageVisibilityForRequest:(RCTImageURLLoaderRequest *)loaderRequest imageView:(RCTUIView *)imageView // TODO(macOS GH#774) { if (!loaderRequest || !imageView) { diff --git a/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h b/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h index a984605a66e283..0faff5b4ef540b 100644 --- a/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h +++ b/Libraries/Image/RCTImageLoaderWithAttributionProtocol.h @@ -36,11 +36,6 @@ RCT_EXTERN void RCTEnableImageLoadingPerfInstrumentation(BOOL enabled); completionBlock:(RCTImageLoaderCompletionBlockWithMetadata)completionBlock; #endif -/** - * Image instrumentation - notify that the image content (UIImage) has been set on the native view. - */ -- (void)trackURLImageContentDidSetForRequest:(RCTImageURLLoaderRequest *)loaderRequest; - /** * Image instrumentation - start tracking the on-screen visibility of the native image view. */ diff --git a/Libraries/Image/RCTImageStoreManager.mm b/Libraries/Image/RCTImageStoreManager.mm index 4a0940b69fa8b0..cccf9ed597b921 100644 --- a/Libraries/Image/RCTImageStoreManager.mm +++ b/Libraries/Image/RCTImageStoreManager.mm @@ -24,7 +24,7 @@ static NSString *const RCTImageStoreURLScheme = @"rct-image-store"; -@interface RCTImageStoreManager() +@interface RCTImageStoreManager() @end @implementation RCTImageStoreManager @@ -238,7 +238,7 @@ - (void)getImageForTag:(NSString *)imageTag withBlock:(void (^)(UIImage *image)) - (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params { - return std::make_shared(params); + return std::make_shared(params); } @end diff --git a/Libraries/Image/RCTImageURLLoaderWithAttribution.h b/Libraries/Image/RCTImageURLLoaderWithAttribution.h index a289ae9e40f97a..fb676bad144774 100644 --- a/Libraries/Image/RCTImageURLLoaderWithAttribution.h +++ b/Libraries/Image/RCTImageURLLoaderWithAttribution.h @@ -61,11 +61,6 @@ struct ImageURLLoaderAttribution { completionHandler:(RCTImageLoaderCompletionBlockWithMetadata)completionHandler; #endif -/** - * Image instrumentation - notify that the image content (UIImage) has been set on the native view. - */ -- (void)trackURLImageContentDidSetForRequest:(RCTImageURLLoaderRequest *)loaderRequest; - /** * Image instrumentation - start tracking the on-screen visibility of the native image view. */ diff --git a/Libraries/Image/React-RCTImage.podspec b/Libraries/Image/React-RCTImage.podspec index a74baca2cb3545..b7e7b7b765bc2d 100644 --- a/Libraries/Image/React-RCTImage.podspec +++ b/Libraries/Image/React-RCTImage.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.documentation_url = "https://reactnative.dev/docs/image" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "*.{m,mm}" diff --git a/Libraries/Inspector/PerformanceOverlay.js b/Libraries/Inspector/PerformanceOverlay.js index a7307bda9174a6..128abb43099330 100644 --- a/Libraries/Inspector/PerformanceOverlay.js +++ b/Libraries/Inspector/PerformanceOverlay.js @@ -22,7 +22,7 @@ class PerformanceOverlay extends React.Component<{...}> { const items = []; for (const key in perfLogs) { - if (perfLogs[key].totalTime) { + if (perfLogs[key]?.totalTime) { const unit = key === 'BundleSize' ? 'b' : 'ms'; items.push( diff --git a/Libraries/Interaction/PanResponder.js b/Libraries/Interaction/PanResponder.js index 1aaf9fce9ecaee..4106597cb6d40c 100644 --- a/Libraries/Interaction/PanResponder.js +++ b/Libraries/Interaction/PanResponder.js @@ -121,7 +121,7 @@ const currentCentroidY = TouchHistoryMath.currentCentroidY; * ### Working Example * * To see it in action, try the - * [PanResponder example in RNTester](https://github.com/facebook/react-native/blob/master/RNTester/js/PanResponderExample.js) + * [PanResponder example in RNTester](https://github.com/facebook/react-native/blob/master/packages/rn-tester/js/examples/PanResponder/PanResponderExample.js) */ export type GestureState = {| diff --git a/Libraries/Interaction/TaskQueue.js b/Libraries/Interaction/TaskQueue.js index 965953f64fb8d0..3fad71ae0a0d7c 100644 --- a/Libraries/Interaction/TaskQueue.js +++ b/Libraries/Interaction/TaskQueue.js @@ -156,6 +156,7 @@ class TaskQueue { // happens once it is fully processed. this._queueStack.push({tasks: [], popable: false}); const stackIdx = this._queueStack.length - 1; + const stackItem = this._queueStack[stackIdx]; DEBUG && infoLog('TaskQueue: push new queue: ', {stackIdx}); DEBUG && infoLog('TaskQueue: exec gen task ' + task.name); task @@ -166,7 +167,7 @@ class TaskQueue { stackIdx, queueStackSize: this._queueStack.length, }); - this._queueStack[stackIdx].popable = true; + stackItem.popable = true; this.hasTasksToProcess() && this._onMoreTasks(); }) .catch(ex => { diff --git a/Libraries/Interaction/__tests__/TaskQueue-test.js b/Libraries/Interaction/__tests__/TaskQueue-test.js index 5055fa2dde853b..aeed06ca0aeae6 100644 --- a/Libraries/Interaction/__tests__/TaskQueue-test.js +++ b/Libraries/Interaction/__tests__/TaskQueue-test.js @@ -150,4 +150,20 @@ describe('TaskQueue', () => { expect(task1).not.toBeCalled(); expect(taskQueue.hasTasksToProcess()).toBe(false); }); + + it('should not crash when task is cancelled between being started and resolved', () => { + const task1 = jest.fn(() => { + return new Promise(resolve => { + setTimeout(() => { + resolve(); + }, 1); + }); + }); + + taskQueue.enqueue({gen: task1, name: 'gen1'}); + taskQueue.processNext(); + taskQueue.cancelTasks([task1]); + + jest.runAllTimers(); + }); }); diff --git a/Libraries/LinkingIOS/React-RCTLinking.podspec b/Libraries/LinkingIOS/React-RCTLinking.podspec index a10514ab25d3be..95fcd9f43b2102 100644 --- a/Libraries/LinkingIOS/React-RCTLinking.podspec +++ b/Libraries/LinkingIOS/React-RCTLinking.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.documentation_url = "https://reactnative.dev/docs/linking" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "*.{m,mm}" diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedModule.mm b/Libraries/NativeAnimation/RCTNativeAnimatedModule.mm index bf4cd6e7703333..f0df049793d067 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedModule.mm +++ b/Libraries/NativeAnimation/RCTNativeAnimatedModule.mm @@ -46,6 +46,7 @@ - (instancetype)init - (void)invalidate { + [super invalidate]; [_nodesManager stopAnimationLoop]; [self.bridge.eventDispatcher removeDispatchObserver:self]; [self.bridge.uiManager.observerCoordinator removeObserver:self]; diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm b/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm index 8fac525cb143ee..1ad8e3220e2f67 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm +++ b/Libraries/NativeAnimation/RCTNativeAnimatedTurboModule.mm @@ -47,6 +47,7 @@ - (instancetype)init - (void)invalidate { + [super invalidate]; [_nodesManager stopAnimationLoop]; [self.bridge.eventDispatcher removeDispatchObserver:self]; [self.bridge.uiManager.observerCoordinator removeObserver:self]; diff --git a/Libraries/NativeAnimation/React-RCTAnimation.podspec b/Libraries/NativeAnimation/React-RCTAnimation.podspec index b79d854edf7365..65453a45ae9a8e 100644 --- a/Libraries/NativeAnimation/React-RCTAnimation.podspec +++ b/Libraries/NativeAnimation/React-RCTAnimation.podspec @@ -26,7 +26,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "{Drivers/*,Nodes/*,*}.{m,mm}" diff --git a/Libraries/NativeModules/specs/NativeDialogManagerAndroid.js b/Libraries/NativeModules/specs/NativeDialogManagerAndroid.js index fcc476b3419c33..809f488f0a1212 100644 --- a/Libraries/NativeModules/specs/NativeDialogManagerAndroid.js +++ b/Libraries/NativeModules/specs/NativeDialogManagerAndroid.js @@ -40,6 +40,7 @@ export interface Spec extends TurboModule { +buttonNeutral: DialogButtonKey, |}; +showAlert: ( + // eslint-disable-next-line @react-native/codegen/react-native-modules config: DialogOptions, onError: (error: string) => void, onAction: (action: DialogAction, buttonKey?: DialogButtonKey) => void, diff --git a/Libraries/Network/RCTNetworking.mm b/Libraries/Network/RCTNetworking.mm index b857c43a9086e4..c4dbf8a7bf3364 100644 --- a/Libraries/Network/RCTNetworking.mm +++ b/Libraries/Network/RCTNetworking.mm @@ -168,6 +168,8 @@ - (instancetype)initWithHandlersProvider:(NSArray> * (^ - (void)invalidate { + [super invalidate]; + for (NSNumber *requestID in _tasksByRequestID) { [_tasksByRequestID[requestID] cancel]; } @@ -680,7 +682,7 @@ - (RCTNetworkTask *)networkTaskWithRequest:(NSURLRequest *)request completionBlo @"timeout": @(query.timeout()), @"withCredentials": @(query.withCredentials()), }; - + // TODO: buildRequest returns a cancellation block, but there's currently // no way to invoke it, if, for example the request is cancelled while // loading a large file to build the request body diff --git a/Libraries/Network/React-RCTNetwork.podspec b/Libraries/Network/React-RCTNetwork.podspec index 0839802ee3e5d5..c7b5acfc76d3bb 100644 --- a/Libraries/Network/React-RCTNetwork.podspec +++ b/Libraries/Network/React-RCTNetwork.podspec @@ -26,7 +26,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "*.{m,mm}" diff --git a/Libraries/PermissionsAndroid/NativePermissionsAndroid.js b/Libraries/PermissionsAndroid/NativePermissionsAndroid.js index 2e834fc725cb42..2b53284da78b98 100644 --- a/Libraries/PermissionsAndroid/NativePermissionsAndroid.js +++ b/Libraries/PermissionsAndroid/NativePermissionsAndroid.js @@ -47,6 +47,7 @@ export type PermissionType = */ export interface Spec extends TurboModule { + // eslint-disable-next-line @react-native/codegen/react-native-modules +checkPermission: (permission: PermissionType) => Promise; +requestPermission: (permission: PermissionType) => Promise; +shouldShowRequestPermissionRationale: ( diff --git a/Libraries/Pressability/usePressability.js b/Libraries/Pressability/usePressability.js index 3538524239387b..9ed4cc22197437 100644 --- a/Libraries/Pressability/usePressability.js +++ b/Libraries/Pressability/usePressability.js @@ -16,11 +16,16 @@ import Pressability, { } from './Pressability'; import {useEffect, useRef} from 'react'; +/** + * Creates a persistent instance of `Pressability` that automatically configures + * itself and resets. Accepts null `config` to support lazy initialization. Once + * initialized, will not un-initialize until the component has been unmounted. + */ export default function usePressability( - config: PressabilityConfig, -): EventHandlers { + config: ?PressabilityConfig, +): ?EventHandlers { const pressabilityRef = useRef(null); - if (pressabilityRef.current == null) { + if (config != null && pressabilityRef.current == null) { pressabilityRef.current = new Pressability(config); } const pressability = pressabilityRef.current; @@ -28,16 +33,20 @@ export default function usePressability( // On the initial mount, this is a no-op. On updates, `pressability` will be // re-configured to use the new configuration. useEffect(() => { - pressability.configure(config); + if (config != null && pressability != null) { + pressability.configure(config); + } }, [config, pressability]); // On unmount, reset pending state and timers inside `pressability`. This is // a separate effect because we do not want to reset when `config` changes. useEffect(() => { - return () => { - pressability.reset(); - }; + if (pressability != null) { + return () => { + pressability.reset(); + }; + } }, [pressability]); - return pressability.getEventHandlers(); + return pressability == null ? null : pressability.getEventHandlers(); } diff --git a/Libraries/PushNotificationIOS/NativePushNotificationManagerIOS.js b/Libraries/PushNotificationIOS/NativePushNotificationManagerIOS.js index 2e2044262eefaa..941ba0a3c77f99 100644 --- a/Libraries/PushNotificationIOS/NativePushNotificationManagerIOS.js +++ b/Libraries/PushNotificationIOS/NativePushNotificationManagerIOS.js @@ -32,7 +32,7 @@ type Notification = {| |}; export interface Spec extends TurboModule { - +getConstants: () => {...}; + +getConstants: () => {||}; +onFinishRemoteNotification: ( notificationId: string, /** @@ -52,6 +52,7 @@ export interface Spec extends TurboModule { |}) => Promise; +abandonPermissions: () => void; +checkPermissions: (callback: (permissions: Permissions) => void) => void; + // eslint-disable-next-line @react-native/codegen/react-native-modules +presentLocalNotification: (notification: Notification) => void; +scheduleLocalNotification: (notification: Notification) => void; +cancelAllLocalNotifications: () => void; diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.h b/Libraries/PushNotificationIOS/RCTPushNotificationManager.h index 08b620b5f85e91..08c9a7fee034aa 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.h +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.h @@ -15,7 +15,7 @@ extern NSString *const RCTRemoteNotificationReceived; typedef void (^RCTRemoteNotificationCallback)(UIBackgroundFetchResult result); #endif // TODO(macOS GH#774) -#if !TARGET_OS_TV && !TARGET_OS_UIKITFORMAC +#if !TARGET_OS_UIKITFORMAC #if !TARGET_OS_OSX // TODO(macOS GH#774) + (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings; #endif // TODO(macOS GH#774) diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm b/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm index 43938c9eaf7ec7..47e0210cda7228 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm @@ -27,7 +27,7 @@ static NSString *const kErrorUnableToRequestPermissions = @"E_UNABLE_TO_REQUEST_PERMISSIONS"; -#if !TARGET_OS_TV +#if !TARGET_OS_UIKITFORMAC @implementation RCTConvert (NSCalendarUnit) RCT_ENUM_CONVERTER(NSCalendarUnit, @@ -120,11 +120,11 @@ + (NSUserNotification *)NSUserNotification:(id)json #else @interface RCTPushNotificationManager () @end -#endif //TARGET_OS_TV / TARGET_OS_UIKITFORMAC +#endif // TARGET_OS_UIKITFORMAC @implementation RCTPushNotificationManager -#if !TARGET_OS_TV && !TARGET_OS_UIKITFORMAC && !TARGET_OS_OSX +#if !TARGET_OS_UIKITFORMAC && !TARGET_OS_OSX static NSDictionary *RCTFormatLocalNotification(UILocalNotification *notification) { @@ -169,7 +169,7 @@ @implementation RCTPushNotificationManager return formattedNotification; } -#endif //TARGET_OS_TV / TARGET_OS_UIKITFORMAC +#endif // TARGET_OS_UIKITFORMAC #if TARGET_OS_OSX // [TODO(macOS GH#774) static NSDictionary *RCTFormatUserNotification(NSUserNotification *notification) @@ -198,7 +198,7 @@ - (dispatch_queue_t)methodQueue return dispatch_get_main_queue(); } -#if !TARGET_OS_TV && !TARGET_OS_UIKITFORMAC +#if !TARGET_OS_UIKITFORMAC - (void)startObserving { [[NSNotificationCenter defaultCenter] addObserver:self @@ -662,7 +662,7 @@ - (void)handleRemoteNotificationRegistrationError:(NSNotification *)notification #endif // ]TODO(macOS GH#774) } -#else //TARGET_OS_TV / TARGET_OS_UIKITFORMAC +#else // TARGET_OS_UIKITFORMAC RCT_EXPORT_METHOD(onFinishRemoteNotification:(NSString *)notificationId fetchResult:(NSString *)fetchResult) { @@ -747,7 +747,7 @@ - (void)handleRemoteNotificationRegistrationError:(NSNotification *)notification return @[]; } -#endif //TARGET_OS_TV / TARGET_OS_UIKITFORMAC +#endif // TARGET_OS_UIKITFORMAC - (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params { diff --git a/Libraries/PushNotificationIOS/React-RCTPushNotification.podspec b/Libraries/PushNotificationIOS/React-RCTPushNotification.podspec index 28b0ebfae93201..97e419d1f1e7f8 100644 --- a/Libraries/PushNotificationIOS/React-RCTPushNotification.podspec +++ b/Libraries/PushNotificationIOS/React-RCTPushNotification.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.documentation_url = "https://reactnative.dev/docs/pushnotificationios" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "*.{m,mm}" diff --git a/Libraries/RCTRequired/RCTRequired.podspec b/Libraries/RCTRequired/RCTRequired.podspec index 077da42b4754ca..e4d10d5d15960c 100644 --- a/Libraries/RCTRequired/RCTRequired.podspec +++ b/Libraries/RCTRequired/RCTRequired.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#774) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#774) s.source = source s.source_files = "**/*.{c,h,m,mm,cpp}" s.header_dir = "RCTRequired" diff --git a/Libraries/Settings/React-RCTSettings.podspec b/Libraries/Settings/React-RCTSettings.podspec index 42995840793f80..52f436c923e216 100644 --- a/Libraries/Settings/React-RCTSettings.podspec +++ b/Libraries/Settings/React-RCTSettings.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.documentation_url = "https://reactnative.dev/docs/settings" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "*.{m,mm}" diff --git a/Libraries/Share/NativeShareModule.js b/Libraries/Share/NativeShareModule.js index 08d313d76d7d6c..e6d8800725b30e 100644 --- a/Libraries/Share/NativeShareModule.js +++ b/Libraries/Share/NativeShareModule.js @@ -17,6 +17,7 @@ export interface Spec extends TurboModule { +getConstants: () => {||}; +share: ( content: {|title?: string, message?: string|}, + // eslint-disable-next-line @react-native/codegen/react-native-modules dialogTitle?: string, ) => Promise<{|action: string|}>; } diff --git a/Libraries/Text/React-RCTText.podspec b/Libraries/Text/React-RCTText.podspec index 7dc353d671adbf..de7d3a3b6993d8 100644 --- a/Libraries/Text/React-RCTText.podspec +++ b/Libraries/Text/React-RCTText.podspec @@ -24,7 +24,7 @@ Pod::Spec.new do |s| s.documentation_url = "https://reactnative.dev/docs/text" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.source = source s.source_files = "**/*.{h,m}" s.ios.exclude_files = "**/macOS/*" # TODO(macOS GH#774) diff --git a/Libraries/Text/Text.js b/Libraries/Text/Text.js index b571f1275847b9..681922ea13fdde 100644 --- a/Libraries/Text/Text.js +++ b/Libraries/Text/Text.js @@ -10,14 +10,13 @@ 'use strict'; +import {NativeText, NativeVirtualText} from './TextNativeComponent'; + const DeprecatedTextPropTypes = require('../DeprecatedPropTypes/DeprecatedTextPropTypes'); const React = require('react'); -const ReactNativeViewAttributes = require('../Components/View/ReactNativeViewAttributes'); const TextAncestor = require('./TextAncestor'); const Touchable = require('../Components/Touchable/Touchable'); -const UIManager = require('../ReactNative/UIManager'); -const createReactNativeComponentClass = require('../Renderer/shims/createReactNativeComponentClass'); const nullthrows = require('nullthrows'); const processColor = require('../StyleSheet/processColor'); @@ -27,7 +26,7 @@ import type {PressRetentionOffset, TextProps} from './TextProps'; type ResponseHandlers = $ReadOnly<{| onStartShouldSetResponder: () => boolean, - onResponderGrant: (event: PressEvent, dispatchID: string) => void, + onResponderGrant: (event: PressEvent) => void, onResponderMove: (event: PressEvent) => void, onResponderRelease: (event: PressEvent) => void, onResponderTerminate: (event: PressEvent) => void, @@ -36,7 +35,7 @@ type ResponseHandlers = $ReadOnly<{| type Props = $ReadOnly<{| ...TextProps, - forwardedRef: ?React.Ref<'RCTText' | 'RCTVirtualText'>, + forwardedRef: ?React.Ref, |}>; type State = {| @@ -51,37 +50,6 @@ type State = {| const PRESS_RECT_OFFSET = {top: 20, left: 20, right: 20, bottom: 30}; -const viewConfig = { - validAttributes: { - ...ReactNativeViewAttributes.UIView, - isHighlighted: true, - numberOfLines: true, - ellipsizeMode: true, - allowFontScaling: true, - maxFontSizeMultiplier: true, - disabled: true, - selectable: true, - selectionColor: true, - adjustsFontSizeToFit: true, - minimumFontScale: true, - textBreakStrategy: true, - onTextLayout: true, - onInlineViewLayout: true, - dataDetectorType: true, - tooltip: true, - android_hyphenationFrequency: true, - }, - directEventTypes: { - topTextLayout: { - registrationName: 'onTextLayout', - }, - topInlineViewLayout: { - registrationName: 'onInlineViewLayout', - }, - }, - uiViewClassName: 'RCTText', -}; - /** * A React component for displaying text. * @@ -123,21 +91,19 @@ class TouchableText extends React.Component { : null; } - static viewConfig = viewConfig; - render(): React.Node { - let props = this.props; - if (isTouchable(props)) { + let {forwardedRef, selectionColor, ...props} = this.props; + if (isTouchable(this.props)) { props = { ...props, ...this.state.responseHandlers, isHighlighted: this.state.isHighlighted, }; } - if (props.selectionColor != null) { + if (selectionColor != null) { props = { ...props, - selectionColor: processColor(props.selectionColor), + selectionColor: processColor(selectionColor), }; } if (__DEV__) { @@ -152,16 +118,17 @@ class TouchableText extends React.Component { {hasTextAncestor => hasTextAncestor ? ( - ) : ( - + ) } @@ -264,26 +231,9 @@ const isTouchable = (props: Props): boolean => props.onLongPress != null || props.onStartShouldSetResponder != null; -const RCTText = createReactNativeComponentClass( - viewConfig.uiViewClassName, - () => viewConfig, -); - -const RCTVirtualText = - UIManager.getViewManagerConfig('RCTVirtualText') == null - ? RCTText - : createReactNativeComponentClass('RCTVirtualText', () => ({ - validAttributes: { - ...ReactNativeViewAttributes.UIView, - isHighlighted: true, - maxFontSizeMultiplier: true, - }, - uiViewClassName: 'RCTVirtualText', - })); - const Text = ( props: TextProps, - forwardedRef: ?React.Ref<'RCTText' | 'RCTVirtualText'>, + forwardedRef: ?React.Ref, ) => { return ; }; diff --git a/Libraries/Text/Text/RCTTextView.m b/Libraries/Text/Text/RCTTextView.m index 30bc2594bd3ebe..c1a35e16e40eb4 100644 --- a/Libraries/Text/Text/RCTTextView.m +++ b/Libraries/Text/Text/RCTTextView.m @@ -355,7 +355,7 @@ - (void)disableContextMenu - (void)handleLongPress:(UILongPressGestureRecognizer *)gesture { // TODO: Adopt showMenuFromRect (necessary for UIKitForMac) -#if !TARGET_OS_TV && !TARGET_OS_UIKITFORMAC +#if !TARGET_OS_UIKITFORMAC UIMenuController *menuController = [UIMenuController sharedMenuController]; if (menuController.isMenuVisible) { @@ -449,7 +449,6 @@ - (BOOL)canPerformAction:(SEL)action withSender:(id)sender - (void)copy:(id)sender { -#if !TARGET_OS_TV NSAttributedString *attributedText = _textStorage; NSData *rtf = [attributedText dataFromRange:NSMakeRange(0, attributedText.length) @@ -471,7 +470,6 @@ - (void)copy:(id)sender [pasteboard clearContents]; [pasteboard writeObjects:[NSArray arrayWithObjects:attributedText.string, rtf, nil]]; #endif // TODO(macOS GH#774) -#endif } @end diff --git a/Libraries/Text/TextInput/Multiline/RCTMultilineTextInputViewManager.m b/Libraries/Text/TextInput/Multiline/RCTMultilineTextInputViewManager.m index c07bbc0d5fb9e4..e1e80dbea48104 100644 --- a/Libraries/Text/TextInput/Multiline/RCTMultilineTextInputViewManager.m +++ b/Libraries/Text/TextInput/Multiline/RCTMultilineTextInputViewManager.m @@ -19,9 +19,7 @@ - (RCTUIView *)view // TODO(macOS ISS#3536887) #pragma mark - Multiline (aka TextView) specific properties -#if !TARGET_OS_TV RCT_REMAP_NOT_OSX_VIEW_PROPERTY(dataDetectorTypes, backedTextInputView.dataDetectorTypes, UIDataDetectorTypes) // TODO(macOS GH#774) RCT_REMAP_OSX_VIEW_PROPERTY(dataDetectorTypes, backedTextInputView.enabledTextCheckingTypes, NSTextCheckingTypes) // TODO(macOS GH#774) -#endif @end diff --git a/Libraries/Text/TextInput/Multiline/RCTUITextView.m b/Libraries/Text/TextInput/Multiline/RCTUITextView.m index d9b7464ad2204d..54e19622a3616a 100644 --- a/Libraries/Text/TextInput/Multiline/RCTUITextView.m +++ b/Libraries/Text/TextInput/Multiline/RCTUITextView.m @@ -64,15 +64,24 @@ - (instancetype)initWithFrame:(CGRect)frame // on screen. self.textContainer.lineFragmentPadding = 1; #endif -#if !TARGET_OS_OSX && !TARGET_OS_TV // TODO(macOS GH#774) +#if !TARGET_OS_OSX // TODO(macOS GH#774) self.scrollsToTop = NO; -#endif +#endif // TODO(macOS GH#774) self.scrollEnabled = YES; } return self; } +- (void)setDelegate:(id)delegate { + // Delegate is set inside `[RCTBackedTextViewDelegateAdapter initWithTextView]` and + // it cannot be changed from outside. + if (super.delegate) { + return; + } + [super setDelegate:delegate]; +} + #pragma mark - Accessibility - (void)setIsAccessibilityElement:(BOOL)isAccessibilityElement diff --git a/Libraries/Text/TextInput/RCTBaseTextInputView.m b/Libraries/Text/TextInput/RCTBaseTextInputView.m index 1722a2175c73f8..beeb152d8df20a 100644 --- a/Libraries/Text/TextInput/RCTBaseTextInputView.m +++ b/Libraries/Text/TextInput/RCTBaseTextInputView.m @@ -291,7 +291,7 @@ - (void)setTextContentType:(NSString *)type }; #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */ - if (@available(iOS 11.0, tvOS 11.0, *)) { + if (@available(iOS 11.0, *)) { NSDictionary * iOS11extras = @{@"username": UITextContentTypeUsername, @"password": UITextContentTypePassword}; @@ -303,7 +303,7 @@ - (void)setTextContentType:(NSString *)type #endif #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 120000 /* __IPHONE_12_0 */ - if (@available(iOS 12.0, tvOS 12.0, *)) { + if (@available(iOS 12.0, *)) { NSDictionary * iOS12extras = @{@"newPassword": UITextContentTypeNewPassword, @"oneTimeCode": UITextContentTypeOneTimeCode}; @@ -413,17 +413,17 @@ - (BOOL)textInputShouldReturn // `onSubmitEditing` is called when "Submit" button // (the blue key on onscreen keyboard) did pressed // (no connection to any specific "submitting" process). -#if TARGET_OS_OSX // [TODO(macOS Candidate ISS#2710739) +#if TARGET_OS_OSX // [TODO(macOS GH#774) if (_blurOnSubmit) { -#endif // ]TODO(macOS Candidate ISS#2710739) +#endif // ]TODO(macOS GH#774) [_eventDispatcher sendTextEventWithType:RCTTextEventTypeSubmit - reactTag:self.reactTag - text:[self.backedTextInputView.attributedText.string copy] + reactTag:self.reactTag + text:[self.backedTextInputView.attributedText.string copy] key:nil - eventCount:_nativeEventCount]; -#if TARGET_OS_OSX // [TODO(macOS Candidate ISS#2710739) + eventCount:_nativeEventCount]; +#if TARGET_OS_OSX // [TODO(macOS GH#774) } -#endif // ]TODO(macOS Candidate ISS#2710739) +#endif // ]TODO(macOS GH#774) return _blurOnSubmit; } @@ -644,7 +644,7 @@ - (void)didSetProps:(NSArray *)changedProps - (void)setCustomInputAccessoryViewWithNativeID:(NSString *)nativeID { - #if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS GH#774) + #if !TARGET_OS_OSX // TODO(macOS GH#774) __weak RCTBaseTextInputView *weakSelf = self; [_bridge.uiManager rootViewForReactTag:self.reactTag withCompletion:^(UIView *rootView) { RCTBaseTextInputView *strongSelf = weakSelf; @@ -657,12 +657,12 @@ - (void)setCustomInputAccessoryViewWithNativeID:(NSString *)nativeID } } }]; - #endif /* !TARGET_OS_TV && !TARGET_OS_OSX TODO(macOS GH#774) */ + #endif /* !TARGET_OS_OSX TODO(macOS GH#774) */ } - (void)setDefaultInputAccessoryView { - #if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS GH#774) + #if !TARGET_OS_OSX // TODO(macOS GH#774) UIView *textInputView = self.backedTextInputView; UIKeyboardType keyboardType = textInputView.keyboardType; @@ -701,7 +701,7 @@ - (void)setDefaultInputAccessoryView textInputView.inputAccessoryView = nil; } [self reloadInputViewsIfNecessary]; - #endif /* !TARGET_OS_TV && !TARGET_OS_OSX TODO(macOS GH#774) */ + #endif /* !TARGET_OS_OSX TODO(macOS GH#774) */ } #if !TARGET_OS_OSX // TODO(macOS GH#774) diff --git a/Libraries/Text/TextInput/RCTInputAccessoryViewContent.m b/Libraries/Text/TextInput/RCTInputAccessoryViewContent.m index 81f6e364c5d5fd..6916657e5c81c9 100644 --- a/Libraries/Text/TextInput/RCTInputAccessoryViewContent.m +++ b/Libraries/Text/TextInput/RCTInputAccessoryViewContent.m @@ -31,7 +31,7 @@ - (instancetype)init _heightConstraint.active = YES; #if !TARGET_OS_OSX // TODO(macOS GH#774) - if (@available(iOS 11.0, tvOS 11.0, *)) { + if (@available(iOS 11.0, *)) { [_safeAreaContainer.bottomAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.bottomAnchor].active = YES; [_safeAreaContainer.topAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.topAnchor].active = YES; [_safeAreaContainer.leadingAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.leadingAnchor].active = YES; diff --git a/Libraries/Text/TextNativeComponent.js b/Libraries/Text/TextNativeComponent.js new file mode 100644 index 00000000000000..eafc7ddf8d1483 --- /dev/null +++ b/Libraries/Text/TextNativeComponent.js @@ -0,0 +1,68 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +import ReactNativeViewAttributes from '../Components/View/ReactNativeViewAttributes'; +import UIManager from '../ReactNative/UIManager'; +import {type HostComponent} from '../Renderer/shims/ReactNativeTypes'; +import createReactNativeComponentClass from '../Renderer/shims/createReactNativeComponentClass'; +import {type ProcessedColorValue} from '../StyleSheet/processColor'; +import {type TextProps} from './TextProps'; + +type NativeTextProps = $ReadOnly<{ + ...TextProps, + isHighlighted?: ?boolean, + selectionColor?: ?ProcessedColorValue, +}>; + +export const NativeText: HostComponent = (createReactNativeComponentClass( + 'RCTText', + () => ({ + validAttributes: { + ...ReactNativeViewAttributes.UIView, + isHighlighted: true, + numberOfLines: true, + ellipsizeMode: true, + allowFontScaling: true, + maxFontSizeMultiplier: true, + disabled: true, + selectable: true, + selectionColor: true, + adjustsFontSizeToFit: true, + minimumFontScale: true, + textBreakStrategy: true, + onTextLayout: true, + onInlineViewLayout: true, + dataDetectorType: true, + }, + directEventTypes: { + topTextLayout: { + registrationName: 'onTextLayout', + }, + topInlineViewLayout: { + registrationName: 'onInlineViewLayout', + }, + }, + uiViewClassName: 'RCTText', + }), +): any); + +export const NativeVirtualText: HostComponent = + UIManager.getViewManagerConfig('RCTVirtualText') == null + ? NativeText + : (createReactNativeComponentClass('RCTVirtualText', () => ({ + validAttributes: { + ...ReactNativeViewAttributes.UIView, + isHighlighted: true, + maxFontSizeMultiplier: true, + }, + uiViewClassName: 'RCTVirtualText', + })): any); diff --git a/Libraries/TurboModule/samples/NativeSampleTurboModule.js b/Libraries/TurboModule/samples/NativeSampleTurboModule.js index f9c4c6ef53e393..5af0c1f77c690e 100644 --- a/Libraries/TurboModule/samples/NativeSampleTurboModule.js +++ b/Libraries/TurboModule/samples/NativeSampleTurboModule.js @@ -26,6 +26,7 @@ export interface Spec extends TurboModule { +getString: (arg: string) => string; +getArray: (arg: Array) => Array; +getObject: (arg: Object) => Object; + // eslint-disable-next-line @react-native/codegen/react-native-modules +getRootTag: (arg: RootTag) => RootTag; +getValue: (x: number, y: string, z: Object) => Object; +getValueWithCallback: (callback: (value: string) => void) => void; diff --git a/Libraries/TypeSafety/RCTConvertHelpers.h b/Libraries/TypeSafety/RCTConvertHelpers.h index 1b6bf9341e7a86..0b7144b9c8ea2d 100644 --- a/Libraries/TypeSafety/RCTConvertHelpers.h +++ b/Libraries/TypeSafety/RCTConvertHelpers.h @@ -49,6 +49,7 @@ NSArray *RCTConvertOptionalVecToArray(const folly::Optional &vec) bool RCTBridgingToBool(id value); folly::Optional RCTBridgingToOptionalBool(id value); NSString *RCTBridgingToString(id value); +NSString *RCTBridgingToOptionalString(id value); folly::Optional RCTBridgingToOptionalDouble(id value); double RCTBridgingToDouble(id value); NSArray *RCTBridgingToArray(id value); diff --git a/Libraries/TypeSafety/RCTConvertHelpers.mm b/Libraries/TypeSafety/RCTConvertHelpers.mm index 69fb3e4a3de4f4..cd79c41f6fece4 100644 --- a/Libraries/TypeSafety/RCTConvertHelpers.mm +++ b/Libraries/TypeSafety/RCTConvertHelpers.mm @@ -27,6 +27,12 @@ bool RCTBridgingToBool(id value) return [RCTConvert NSString:RCTNilIfNull(value)]; } +NSString *RCTBridgingToOptionalString(id value) +{ + return RCTBridgingToString(value); +} + + folly::Optional RCTBridgingToOptionalDouble(id value) { if (!RCTNilIfNull(value)) { diff --git a/Libraries/TypeSafety/RCTTypeSafety.podspec b/Libraries/TypeSafety/RCTTypeSafety.podspec index 83dc23ae08dea5..af8dab2bbd21f4 100644 --- a/Libraries/TypeSafety/RCTTypeSafety.podspec +++ b/Libraries/TypeSafety/RCTTypeSafety.podspec @@ -26,7 +26,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#774) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#774) s.compiler_flags = folly_compiler_flags s.source = source s.source_files = "**/*.{c,h,m,mm,cpp}" diff --git a/Libraries/Utilities/BackHandler.android.js b/Libraries/Utilities/BackHandler.android.js index 61ba573ff5aada..757f2184b60b9f 100644 --- a/Libraries/Utilities/BackHandler.android.js +++ b/Libraries/Utilities/BackHandler.android.js @@ -35,10 +35,6 @@ RCTDeviceEventEmitter.addListener(DEVICE_BACK_EVENT, function() { * Android: Detect hardware back button presses, and programmatically invoke the default back button * functionality to exit the app if there are no listeners or if none of the listeners return true. * - * tvOS: Detect presses of the menu button on the TV remote. (Still to be implemented: - * programmatically disable menu button handling - * functionality to exit the app if there are no listeners or if none of the listeners return true.) - * * iOS: Not applicable. * * macOS: Not applicable. @@ -84,8 +80,7 @@ const BackHandler: TBackHandler = { /** * Adds an event handler. Supported events: * - * - `hardwareBackPress`: Fires when the Android hardware back button is pressed or when the - * tvOS menu button is pressed. + * - `hardwareBackPress`: Fires when the Android hardware back button is pressed. */ addEventListener: function( eventName: BackPressEventName, diff --git a/Libraries/Utilities/BackHandler.ios.js b/Libraries/Utilities/BackHandler.ios.js index 752cefa8dd0df7..2e1e5c4cd2a7ac 100644 --- a/Libraries/Utilities/BackHandler.ios.js +++ b/Libraries/Utilities/BackHandler.ios.js @@ -4,54 +4,18 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict-local * @format + * @flow */ -// On Apple TV, this implements back navigation using the TV remote's menu button. -// On iOS, this just implements a stub. - 'use strict'; -const Platform = require('./Platform'); -const TVEventHandler = require('../Components/AppleTV/TVEventHandler'); +module.exports = require('../Components/UnimplementedViews/UnimplementedView'); type BackPressEventName = 'backPress' | 'hardwareBackPress'; function emptyFunction(): void {} -/** - * Detect hardware button presses for back navigation. - * - * Android: Detect hardware back button presses, and programmatically invoke the default back button - * functionality to exit the app if there are no listeners or if none of the listeners return true. - * - * tvOS: Detect presses of the menu button on the TV remote. (Still to be implemented: - * programmatically disable menu button handling - * functionality to exit the app if there are no listeners or if none of the listeners return true.) - * - * iOS: Not applicable. - * - * macOS: Not applicable. - * - * The event subscriptions are called in reverse order (i.e. last registered subscription first), - * and if one subscription returns true then subscriptions registered earlier will not be called. - * - * Example: - * - * ```javascript - * BackHandler.addEventListener('hardwareBackPress', function() { - * // this.onMainScreen and this.goBack are just examples, you need to use your own implementation here - * // Typically you would use the navigator here to go to the last state. - * - * if (!this.onMainScreen()) { - * this.goBack(); - * return true; - * } - * return false; - * }); - * ``` - */ type TBackHandler = {| +exitApp: () => void, +addEventListener: ( @@ -64,65 +28,14 @@ type TBackHandler = {| ) => void, |}; -let BackHandler: TBackHandler; - -if (Platform.isTV) { - const _tvEventHandler = new TVEventHandler(); - const _backPressSubscriptions = new Set(); - - _tvEventHandler.enable(this, function(cmp, evt) { - if (evt && evt.eventType === 'menu') { - let invokeDefault = true; - const subscriptions = Array.from( - _backPressSubscriptions.values(), - ).reverse(); - - for (let i = 0; i < subscriptions.length; ++i) { - if (subscriptions[i]()) { - invokeDefault = false; - break; - } - } - - if (invokeDefault) { - BackHandler.exitApp(); - } - } - }); - - BackHandler = { - exitApp: emptyFunction, - - addEventListener: function( - eventName: BackPressEventName, - handler: () => ?boolean, - ): {remove: () => void, ...} { - _backPressSubscriptions.add(handler); - return { - remove: () => BackHandler.removeEventListener(eventName, handler), - }; - }, - - removeEventListener: function( - eventName: BackPressEventName, - handler: () => ?boolean, - ): void { - _backPressSubscriptions.delete(handler); - }, - }; -} else { - BackHandler = { - exitApp: emptyFunction, - addEventListener(_eventName: BackPressEventName, _handler: () => ?boolean) { - return { - remove: emptyFunction, - }; - }, - removeEventListener( - _eventName: BackPressEventName, - _handler: () => ?boolean, - ) {}, - }; -} +let BackHandler: TBackHandler = { + exitApp: emptyFunction, + addEventListener(_eventName: BackPressEventName, _handler: Function) { + return { + remove: emptyFunction, + }; + }, + removeEventListener(_eventName: BackPressEventName, _handler: Function) {}, +}; module.exports = BackHandler; diff --git a/Libraries/Utilities/__tests__/PerformanceLogger-test.js b/Libraries/Utilities/__tests__/PerformanceLogger-test.js index 9af931852397ab..d26cbede535bef 100644 --- a/Libraries/Utilities/__tests__/PerformanceLogger-test.js +++ b/Libraries/Utilities/__tests__/PerformanceLogger-test.js @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. * * @format + * @flow strict-local */ 'use strict'; @@ -14,30 +15,64 @@ import createPerformanceLogger from '../createPerformanceLogger'; import type {IPerformanceLogger} from '../createPerformanceLogger'; const TIMESPAN_1 = ''; -const TIMESPAN_2 = ''; -const TIMESPAN_2_DURATION = 123; const EXTRA_KEY = ''; const EXTRA_VALUE = ''; const EXTRA_VALUE_2 = ''; const POINT = ''; const POINT_TIMESTAMP = 99; const POINT_TIMESTAMP_2 = 999; +const POINT_ANNOTATION_1 = {extra: 'value1'}; +const POINT_ANNOTATION_2 = {extra: 'value2'}; describe('PerformanceLogger', () => { beforeEach(() => { GlobalPerformanceLogger.clear(); }); + describe('close() ', () => { + let perfLogger; + beforeEach(() => { + perfLogger = createPerformanceLogger(); + }); + + it('does not markPoint', () => { + perfLogger.close(); + perfLogger.markPoint(POINT, POINT_TIMESTAMP); + expect(perfLogger.getPoints()).toEqual({}); + }); + it('does not startTimespan', () => { + perfLogger.close(); + perfLogger.startTimespan(TIMESPAN_1); + expect(perfLogger.getTimespans()).toEqual({}); + }); + it('does not setExtra', () => { + perfLogger.close(); + perfLogger.setExtra('extra', 'an extra value'); + expect(perfLogger.getTimespans()).toEqual({}); + }); - it('starts & stops and adds a timespan', () => { + it('does not stopTimespan', () => { + perfLogger.startTimespan(TIMESPAN_1); + perfLogger.close(); + let timespan = perfLogger.getTimespans()[TIMESPAN_1]; + expect(timespan?.endTime).toBeUndefined(); + expect(timespan?.totalTime).toBeUndefined(); + perfLogger.stopTimespan(TIMESPAN_1); + timespan = perfLogger.getTimespans()[TIMESPAN_1]; + expect(timespan?.endTime).toBeUndefined(); + expect(timespan?.totalTime).toBeUndefined(); + }); + }); + + 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 +81,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 +100,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); }); @@ -172,4 +203,24 @@ describe('PerformanceLogger', () => { checkLogger(localPerformanceLogger2, true); checkLogger(GlobalPerformanceLogger, true); }); + + it('records extras for a timespan', () => { + let perfLogger = createPerformanceLogger(); + perfLogger.startTimespan(TIMESPAN_1, POINT_ANNOTATION_1); + perfLogger.stopTimespan(TIMESPAN_1, POINT_ANNOTATION_2); + expect(perfLogger.getTimespans()[TIMESPAN_1]?.startExtras).toEqual( + POINT_ANNOTATION_1, + ); + expect(perfLogger.getTimespans()[TIMESPAN_1]?.endExtras).toEqual( + POINT_ANNOTATION_2, + ); + }); + + it('records extras for a point', () => { + let perfLogger = createPerformanceLogger(); + perfLogger.markPoint(POINT, POINT_TIMESTAMP, POINT_ANNOTATION_1); + + expect(Object.keys(perfLogger.getPointExtras())).toEqual([POINT]); + expect(perfLogger.getPointExtras()[POINT]).toEqual(POINT_ANNOTATION_1); + }); }); diff --git a/Libraries/Utilities/createPerformanceLogger.js b/Libraries/Utilities/createPerformanceLogger.js index c44d649651df85..2e8753f9ec0877 100644 --- a/Libraries/Utilities/createPerformanceLogger.js +++ b/Libraries/Utilities/createPerformanceLogger.js @@ -13,253 +13,294 @@ 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, + startExtras?: Extras, + endExtras?: Extras, }; +// Extra values should be serializable primitives +type ExtraValue = number | string | boolean; + +type Extras = {[key: string]: ExtraValue}; + export interface IPerformanceLogger { - addTimeAnnotation( - key: string, - durationInMs: number, - description?: string, - ): void; addTimespan( key: string, startTime: number, endTime: number, - description?: string, + startExtras?: Extras, + endExtras?: Extras, ): void; - startTimespan(key: string, description?: string): void; - stopTimespan(key: string, options?: {update?: boolean}): void; clear(): void; clearCompleted(): void; + close(): void; currentTimestamp(): number; - getTimespans(): {[key: string]: Timespan, ...}; + getExtras(): {[key: string]: ?ExtraValue, ...}; + getPoints(): {[key: string]: ?number, ...}; + getPointExtras(): {[key: string]: ?Extras, ...}; + getTimespans(): {[key: string]: ?Timespan, ...}; hasTimespan(key: string): boolean; - setExtra(key: string, value: mixed): void; - getExtras(): {[key: string]: mixed, ...}; - removeExtra(key: string): ?mixed; - markPoint(key: string, timestamp?: number): void; - getPoints(): {[key: string]: number, ...}; + isClosed(): boolean; logEverything(): void; + markPoint(key: string, timestamp?: number, extras?: Extras): void; + removeExtra(key: string): ?ExtraValue; + setExtra(key: string, value: ExtraValue): void; + startTimespan(key: string, extras?: Extras): void; + stopTimespan(key: string, extras?: Extras): void; } const _cookies: {[key: string]: number, ...} = {}; const PRINT_TO_CONSOLE: false = false; // Type as false to prevent accidentally committing `true`; -/** - * 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; +class PerformanceLogger implements IPerformanceLogger { + _timespans: {[key: string]: ?Timespan} = {}; + _extras: {[key: string]: ?ExtraValue} = {}; + _points: {[key: string]: ?number} = {}; + _pointExtras: {[key: string]: ?Extras, ...} = {}; + _closed: boolean = false; + + addTimespan( + key: string, + startTime: number, + endTime: number, + startExtras?: Extras, + endExtras?: Extras, + ) { + if (this._closed) { + if (PRINT_TO_CONSOLE && __DEV__) { + infoLog('PerformanceLogger: addTimespan - has closed ignoring: ', key); + } + return; + } + 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] = { + startTime, + endTime, + totalTime: endTime - (startTime || 0), + startExtras, + endExtras, + }; + } + + 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'); + } + } + + close() { + this._closed = true; + } + + currentTimestamp() { + return performanceNow(); + } + + getExtras() { + return this._extras; + } + + getPoints() { + return this._points; + } + + getPointExtras() { + return this._pointExtras; + } + + getTimespans() { + return this._timespans; + } + + hasTimespan(key: string) { + return !!this._timespans[key]; + } - 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, - ); + isClosed() { + return this._closed; + } + + 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'); } - 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, - ); + // log extras + infoLog(this._extras); + + // log points + for (const key in this._points) { + if (this._points[key] != null) { + infoLog(key + ': ' + this._points[key] + 'ms'); } - return; } - if (timespan.endTime && !options?.update) { - if (PRINT_TO_CONSOLE && __DEV__) { - infoLog( - 'PerformanceLogger: Attempting to end a timespan that has already ended ', - key, - ); - } - return; + } + } + + markPoint(key: string, timestamp?: number, extras?: Extras) { + if (this._closed) { + if (PRINT_TO_CONSOLE && __DEV__) { + infoLog('PerformanceLogger: markPoint - has closed ignoring: ', key); + } + return; + } + if (this._points[key] != null) { + if (PRINT_TO_CONSOLE && __DEV__) { + infoLog( + 'PerformanceLogger: Attempting to mark a point that has been already logged ', + key, + ); } + return; + } + this._points[key] = timestamp ?? performanceNow(); + if (extras) { + this._pointExtras[key] = extras; + } + } - timespan.endTime = performanceNow(); - timespan.totalTime = timespan.endTime - (timespan.startTime || 0); - if (PRINT_TO_CONSOLE) { - infoLog('PerformanceLogger.js', 'end: ' + key); + removeExtra(key: string): ?ExtraValue { + const value = this._extras[key]; + delete this._extras[key]; + return value; + } + + setExtra(key: string, value: ExtraValue) { + if (this._closed) { + if (PRINT_TO_CONSOLE && __DEV__) { + infoLog('PerformanceLogger: setExtra - has closed ignoring: ', key); } + return; + } - if (_cookies[key] != null) { - Systrace.endAsyncEvent(key, _cookies[key]); - delete _cookies[key]; + 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}, + ); } - }, - - clear() { - this._timespans = {}; - this._extras = {}; - this._points = {}; - if (PRINT_TO_CONSOLE) { - infoLog('PerformanceLogger.js', 'clear'); + return; + } + this._extras[key] = value; + } + + startTimespan(key: string, extras?: Extras) { + if (this._closed) { + if (PRINT_TO_CONSOLE && __DEV__) { + infoLog( + 'PerformanceLogger: startTimespan - has closed ignoring: ', + key, + ); } - }, + return; + } - clearCompleted() { - for (const key in this._timespans) { - if (this._timespans[key].totalTime) { - delete this._timespans[key]; - } + if (this._timespans[key]) { + if (PRINT_TO_CONSOLE && __DEV__) { + infoLog( + 'PerformanceLogger: Attempting to start a timespan that already exists ', + key, + ); } - this._extras = {}; - this._points = {}; - if (PRINT_TO_CONSOLE) { - infoLog('PerformanceLogger.js', 'clearCompleted'); + return; + } + + this._timespans[key] = { + startTime: performanceNow(), + startExtras: extras, + }; + _cookies[key] = Systrace.beginAsyncEvent(key); + if (PRINT_TO_CONSOLE) { + infoLog('PerformanceLogger.js', 'start: ' + key); + } + } + + stopTimespan(key: string, extras?: Extras) { + if (this._closed) { + if (PRINT_TO_CONSOLE && __DEV__) { + infoLog('PerformanceLogger: stopTimespan - has closed ignoring: ', key); } - }, - - 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; + return; + } + + 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, + ); } - 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; + return; + } + if (timespan.endTime != null) { + if (PRINT_TO_CONSOLE && __DEV__) { + infoLog( + 'PerformanceLogger: Attempting to end a timespan that has already ended ', + key, + ); } - 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'); - } - } + return; + } - // log extras - infoLog(this._extras); + timespan.endExtras = extras; + timespan.endTime = performanceNow(); + timespan.totalTime = timespan.endTime - (timespan.startTime || 0); + if (PRINT_TO_CONSOLE) { + infoLog('PerformanceLogger.js', 'end: ' + key); + } - // log points - for (const key in this._points) { - infoLog(key + ': ' + this._points[key] + 'ms'); - } - } - }, - }; - return result; + if (_cookies[key] != null) { + Systrace.endAsyncEvent(key, _cookies[key]); + delete _cookies[key]; + } + } +} + +/** + * 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 { + return new PerformanceLogger(); } module.exports = createPerformanceLogger; diff --git a/Libraries/Vibration/React-RCTVibration.podspec b/Libraries/Vibration/React-RCTVibration.podspec index 46384ce09e6012..aa7a2d60a9797a 100644 --- a/Libraries/Vibration/React-RCTVibration.podspec +++ b/Libraries/Vibration/React-RCTVibration.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.documentation_url = "https://reactnative.dev/docs/vibration" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "*.{m,mm}" diff --git a/React-Core.podspec b/React-Core.podspec index 1d63cd2c6a5d81..5a8a765ed2f69d 100644 --- a/React-Core.podspec +++ b/React-Core.podspec @@ -41,7 +41,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#774) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#774) s.source = source s.resource_bundle = { "AccessibilityResources" => ["React/AccessibilityResources/*.lproj"]} s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags @@ -60,10 +60,9 @@ Pod::Spec.new do |s| "React/Inspector/**/*", "React/Tests/**/*", "React/CxxBridge/HermesExecutorFactory.*" # TODO(macOS GH#214) - ss.ios.exclude_files = "React/**/RCTTV*.*", # [TODO(macOS GH#774) - "**/macOS/*" + ss.ios.exclude_files = "**/macOS/*" ss.osx.exclude_files = "React/Modules/RCTRedBoxExtraDataViewController.{h,m}", "React/Modules/RCTAccessibilityManager.m", "React/Profiler/{RCTFPSGraph,RCTPerfMonitor}.*", @@ -74,13 +73,6 @@ Pod::Spec.new do |s| "React/Views/RefreshControl/*", "React/Views/SafeAreaView/*" # ]TODO(macOS GH#774) - - ss.tvos.exclude_files = "React/Modules/RCTClipboard*", - "React/Views/RCTDatePicker*", - "React/Views/RCTPicker*", - "React/Views/RCTRefreshControl*", - "React/Views/RCTSlider*", - "React/Views/RCTSwitch*", ss.private_header_files = "React/Cxx*/*.h" end diff --git a/React.podspec b/React.podspec index daf2d96cb1d394..05a332c9b1e2ea 100644 --- a/React.podspec +++ b/React.podspec @@ -36,7 +36,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.source = source s.preserve_paths = "package.json", "LICENSE", "LICENSE-docs" s.cocoapods_version = ">= 1.2.0" diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index a05a3de5cd4ba8..d84827d266c565 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -434,7 +434,6 @@ + (UIKeyboardType)UIKeyboardType:(id)json RCT_DYNAMIC return type; } -#if !TARGET_OS_TV RCT_MULTI_ENUM_CONVERTER( UIDataDetectorTypes, (@{ @@ -464,8 +463,6 @@ + (UIKeyboardType)UIKeyboardType:(id)json RCT_DYNAMIC WKDataDetectorTypePhoneNumber, unsignedLongLongValue) -#endif // !TARGET_OS_TV - RCT_ENUM_CONVERTER( UIKeyboardAppearance, (@{ @@ -518,7 +515,6 @@ + (UIKeyboardType)UIKeyboardType:(id)json RCT_DYNAMIC UIViewContentModeScaleAspectFill, integerValue) -#if !TARGET_OS_TV RCT_ENUM_CONVERTER( UIBarStyle, (@{ @@ -529,7 +525,6 @@ + (UIKeyboardType)UIKeyboardType:(id)json RCT_DYNAMIC }), UIBarStyleDefault, integerValue) -#endif #else // [TODO(macOS GH#774) RCT_MULTI_ENUM_CONVERTER(NSTextCheckingTypes, (@{ @"ortography": @(NSTextCheckingTypeOrthography), diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m index caeef7902334d8..f95378abe4370c 100644 --- a/React/Base/RCTRootView.m +++ b/React/Base/RCTRootView.m @@ -30,11 +30,6 @@ #import "RCTView.h" #import "UIView+React.h" -#if TARGET_OS_TV -#import "RCTTVNavigationEventEmitter.h" -#import "RCTTVRemoteHandler.h" -#endif - #if __has_include("RCTDevMenu.h") // [TODO(OSS Candidate ISS#2710739) #import "RCTDevMenu.h" #endif // ]TODO(OSS Candidate ISS#2710739) @@ -109,13 +104,6 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge name:RCTContentDidAppearNotification object:self]; -#if TARGET_OS_TV - self.tvRemoteHandler = [RCTTVRemoteHandler new]; - for (NSString *key in [self.tvRemoteHandler.tvRemoteGestureRecognizers allKeys]) { - [self addGestureRecognizer:self.tvRemoteHandler.tvRemoteGestureRecognizers[key]]; - } -#endif - [self showLoadingView]; // Immediately schedule the application to be started. @@ -141,16 +129,6 @@ - (instancetype)initWithBundleURL:(NSURL *)bundleURL RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame) RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder) -#if TARGET_OS_TV -- (UIView *)preferredFocusedView -{ - if (self.reactPreferredFocusedView) { - return self.reactPreferredFocusedView; - } - return [super preferredFocusedView]; -} -#endif - #pragma mark - passThroughTouches - (BOOL)passThroughTouches diff --git a/React/Base/RCTRootViewInternal.h b/React/Base/RCTRootViewInternal.h index c6a21c9ab1cb2f..8e28dd453570c1 100644 --- a/React/Base/RCTRootViewInternal.h +++ b/React/Base/RCTRootViewInternal.h @@ -7,8 +7,6 @@ #import -@class RCTTVRemoteHandler; - /** * The interface provides a set of functions that allow other internal framework * classes to change the RCTRootViews's internal state. @@ -21,14 +19,6 @@ */ @property (readwrite, nonatomic, assign) CGSize intrinsicContentSize; -/** - * TV remote gesture recognizers - */ -#if TARGET_OS_TV -@property (nonatomic, strong) RCTTVRemoteHandler *tvRemoteHandler; -@property (nonatomic, strong) UIView *reactPreferredFocusedView; -#endif - - (void)contentViewInvalidated; @end diff --git a/React/Base/RCTTVRemoteHandler.h b/React/Base/RCTTVRemoteHandler.h deleted file mode 100644 index 10d56906901677..00000000000000 --- a/React/Base/RCTTVRemoteHandler.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import - -extern NSString *const RCTTVRemoteEventMenu; -extern NSString *const RCTTVRemoteEventPlayPause; -extern NSString *const RCTTVRemoteEventSelect; - -extern NSString *const RCTTVRemoteEventLongPlayPause; -extern NSString *const RCTTVRemoteEventLongSelect; - -extern NSString *const RCTTVRemoteEventLeft; -extern NSString *const RCTTVRemoteEventRight; -extern NSString *const RCTTVRemoteEventUp; -extern NSString *const RCTTVRemoteEventDown; - -extern NSString *const RCTTVRemoteEventSwipeLeft; -extern NSString *const RCTTVRemoteEventSwipeRight; -extern NSString *const RCTTVRemoteEventSwipeUp; -extern NSString *const RCTTVRemoteEventSwipeDown; - -@interface RCTTVRemoteHandler : NSObject - -@property (nonatomic, copy, readonly) NSDictionary *tvRemoteGestureRecognizers; - -@end diff --git a/React/Base/RCTTVRemoteHandler.m b/React/Base/RCTTVRemoteHandler.m deleted file mode 100644 index aadbacec4b7eb5..00000000000000 --- a/React/Base/RCTTVRemoteHandler.m +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "RCTTVRemoteHandler.h" - -#import - -#import "RCTAssert.h" -#import "RCTBridge.h" -#import "RCTEventDispatcher.h" -#import "RCTLog.h" -#import "RCTRootView.h" -#import "RCTTVNavigationEventEmitter.h" -#import "RCTUIManager.h" -#import "RCTUtils.h" -#import "RCTView.h" -#import "UIView+React.h" - -#if __has_include("RCTDevMenu.h") -#import "RCTDevMenu.h" -#endif - -NSString *const RCTTVRemoteEventMenu = @"menu"; -NSString *const RCTTVRemoteEventPlayPause = @"playPause"; -NSString *const RCTTVRemoteEventSelect = @"select"; - -NSString *const RCTTVRemoteEventLongPlayPause = @"longPlayPause"; -NSString *const RCTTVRemoteEventLongSelect = @"longSelect"; - -NSString *const RCTTVRemoteEventLeft = @"left"; -NSString *const RCTTVRemoteEventRight = @"right"; -NSString *const RCTTVRemoteEventUp = @"up"; -NSString *const RCTTVRemoteEventDown = @"down"; - -NSString *const RCTTVRemoteEventSwipeLeft = @"swipeLeft"; -NSString *const RCTTVRemoteEventSwipeRight = @"swipeRight"; -NSString *const RCTTVRemoteEventSwipeUp = @"swipeUp"; -NSString *const RCTTVRemoteEventSwipeDown = @"swipeDown"; - -@implementation RCTTVRemoteHandler { - NSMutableDictionary *_tvRemoteGestureRecognizers; -} - -- (instancetype)init -{ - if ((self = [super init])) { - _tvRemoteGestureRecognizers = [NSMutableDictionary dictionary]; - - // Recognizers for Apple TV remote buttons - - // Play/Pause - [self addTapGestureRecognizerWithSelector:@selector(playPausePressed:) - pressType:UIPressTypePlayPause - name:RCTTVRemoteEventPlayPause]; - - // Menu - [self addTapGestureRecognizerWithSelector:@selector(menuPressed:) - pressType:UIPressTypeMenu - name:RCTTVRemoteEventMenu]; - - // Select - [self addTapGestureRecognizerWithSelector:@selector(selectPressed:) - pressType:UIPressTypeSelect - name:RCTTVRemoteEventSelect]; - - // Up - [self addTapGestureRecognizerWithSelector:@selector(tappedUp:) - pressType:UIPressTypeUpArrow - name:RCTTVRemoteEventUp]; - - // Down - [self addTapGestureRecognizerWithSelector:@selector(tappedDown:) - pressType:UIPressTypeDownArrow - name:RCTTVRemoteEventDown]; - - // Left - [self addTapGestureRecognizerWithSelector:@selector(tappedLeft:) - pressType:UIPressTypeLeftArrow - name:RCTTVRemoteEventLeft]; - - // Right - [self addTapGestureRecognizerWithSelector:@selector(tappedRight:) - pressType:UIPressTypeRightArrow - name:RCTTVRemoteEventRight]; - - // Recognizers for long button presses - // We don't intercept long menu press -- that's used by the system to go to the home screen - - [self addLongPressGestureRecognizerWithSelector:@selector(longPlayPausePressed:) - pressType:UIPressTypePlayPause - name:RCTTVRemoteEventLongPlayPause]; - - [self addLongPressGestureRecognizerWithSelector:@selector(longSelectPressed:) - pressType:UIPressTypeSelect - name:RCTTVRemoteEventLongSelect]; - - // Recognizers for Apple TV remote trackpad swipes - - // Up - [self addSwipeGestureRecognizerWithSelector:@selector(swipedUp:) - direction:UISwipeGestureRecognizerDirectionUp - name:RCTTVRemoteEventSwipeUp]; - - // Down - [self addSwipeGestureRecognizerWithSelector:@selector(swipedDown:) - direction:UISwipeGestureRecognizerDirectionDown - name:RCTTVRemoteEventSwipeDown]; - - // Left - [self addSwipeGestureRecognizerWithSelector:@selector(swipedLeft:) - direction:UISwipeGestureRecognizerDirectionLeft - name:RCTTVRemoteEventSwipeLeft]; - - // Right - [self addSwipeGestureRecognizerWithSelector:@selector(swipedRight:) - direction:UISwipeGestureRecognizerDirectionRight - name:RCTTVRemoteEventSwipeRight]; - } - - return self; -} - -- (void)playPausePressed:(UIGestureRecognizer *)r -{ - [self sendAppleTVEvent:RCTTVRemoteEventPlayPause toView:r.view]; -} - -- (void)menuPressed:(UIGestureRecognizer *)r -{ - [self sendAppleTVEvent:RCTTVRemoteEventMenu toView:r.view]; -} - -- (void)selectPressed:(UIGestureRecognizer *)r -{ - [self sendAppleTVEvent:RCTTVRemoteEventSelect toView:r.view]; -} - -- (void)longPlayPausePressed:(UIGestureRecognizer *)r -{ - [self sendAppleTVEvent:RCTTVRemoteEventLongPlayPause toView:r.view]; - -#if __has_include("RCTDevMenu.h") && RCT_DEV - // If shake to show is enabled on device, use long play/pause event to show dev menu - [[NSNotificationCenter defaultCenter] postNotificationName:RCTShowDevMenuNotification object:nil]; -#endif -} - -- (void)longSelectPressed:(UIGestureRecognizer *)r -{ - [self sendAppleTVEvent:RCTTVRemoteEventLongSelect toView:r.view]; -} - -- (void)swipedUp:(UIGestureRecognizer *)r -{ - [self sendAppleTVEvent:RCTTVRemoteEventSwipeUp toView:r.view]; -} - -- (void)swipedDown:(UIGestureRecognizer *)r -{ - [self sendAppleTVEvent:RCTTVRemoteEventSwipeDown toView:r.view]; -} - -- (void)swipedLeft:(UIGestureRecognizer *)r -{ - [self sendAppleTVEvent:RCTTVRemoteEventSwipeLeft toView:r.view]; -} - -- (void)swipedRight:(UIGestureRecognizer *)r -{ - [self sendAppleTVEvent:RCTTVRemoteEventSwipeRight toView:r.view]; -} - -- (void)tappedUp:(UIGestureRecognizer *)r -{ - [self sendAppleTVEvent:RCTTVRemoteEventUp toView:r.view]; -} - -- (void)tappedDown:(UIGestureRecognizer *)r -{ - [self sendAppleTVEvent:RCTTVRemoteEventDown toView:r.view]; -} - -- (void)tappedLeft:(UIGestureRecognizer *)r -{ - [self sendAppleTVEvent:RCTTVRemoteEventLeft toView:r.view]; -} - -- (void)tappedRight:(UIGestureRecognizer *)r -{ - [self sendAppleTVEvent:RCTTVRemoteEventRight toView:r.view]; -} - -#pragma mark - - -- (void)addLongPressGestureRecognizerWithSelector:(nonnull SEL)selector - pressType:(UIPressType)pressType - name:(NSString *)name -{ - UILongPressGestureRecognizer *recognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:selector]; - recognizer.allowedPressTypes = @[ @(pressType) ]; - - _tvRemoteGestureRecognizers[name] = recognizer; -} - -- (void)addTapGestureRecognizerWithSelector:(nonnull SEL)selector pressType:(UIPressType)pressType name:(NSString *)name -{ - UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:selector]; - recognizer.allowedPressTypes = @[ @(pressType) ]; - - _tvRemoteGestureRecognizers[name] = recognizer; -} - -- (void)addSwipeGestureRecognizerWithSelector:(nonnull SEL)selector - direction:(UISwipeGestureRecognizerDirection)direction - name:(NSString *)name -{ - UISwipeGestureRecognizer *recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:selector]; - recognizer.direction = direction; - - _tvRemoteGestureRecognizers[name] = recognizer; -} - -- (void)sendAppleTVEvent:(NSString *)eventType toView:(__unused UIView *)v -{ - [[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification - object:@{@"eventType" : eventType}]; -} - -@end diff --git a/React/CoreModules/BUCK b/React/CoreModules/BUCK index 2f9902478139d5..45b4b45f0daf1b 100644 --- a/React/CoreModules/BUCK +++ b/React/CoreModules/BUCK @@ -104,9 +104,6 @@ rn_apple_library( ) + react_module_plugin_providers( name = "LogBox", native_class_func = "RCTLogBoxCls", - ) + react_module_plugin_providers( - name = "TVNavigationEventEmitter", - native_class_func = "RCTTVNavigationEventEmitterCls", ) + react_module_plugin_providers( name = "WebSocketExecutor", native_class_func = "RCTWebSocketExecutorCls", diff --git a/React/CoreModules/CoreModulesPlugins.h b/React/CoreModules/CoreModulesPlugins.h index 509eb5af2f8dbc..b8fa8e9f0767ed 100644 --- a/React/CoreModules/CoreModulesPlugins.h +++ b/React/CoreModules/CoreModulesPlugins.h @@ -49,7 +49,6 @@ Class RCTDevMenuCls(void) __attribute__((used)); Class RCTDevSettingsCls(void) __attribute__((used)); Class RCTRedBoxCls(void) __attribute__((used)); Class RCTLogBoxCls(void) __attribute__((used)); -Class RCTTVNavigationEventEmitterCls(void) __attribute__((used)); Class RCTWebSocketExecutorCls(void) __attribute__((used)); Class RCTWebSocketModuleCls(void) __attribute__((used)); Class RCTDevLoadingViewCls(void) __attribute__((used)); diff --git a/React/CoreModules/CoreModulesPlugins.mm b/React/CoreModules/CoreModulesPlugins.mm index 50e232f43c2cd2..9fe8a4edfa4887 100644 --- a/React/CoreModules/CoreModulesPlugins.mm +++ b/React/CoreModules/CoreModulesPlugins.mm @@ -44,7 +44,6 @@ Class RCTCoreModulesClassProvider(const char *name) { #if !TARGET_OS_OSX // TODO(macOS) = Do we need these? {"LogBox", RCTLogBoxCls}, #endif // TODO(macOS) - {"TVNavigationEventEmitter", RCTTVNavigationEventEmitterCls}, {"WebSocketExecutor", RCTWebSocketExecutorCls}, {"WebSocketModule", RCTWebSocketModuleCls}, {"DevLoadingView", RCTDevLoadingViewCls}, diff --git a/React/CoreModules/RCTAppState.h b/React/CoreModules/RCTAppState.h index 6e0f19c3356f65..0921f70e44723b 100644 --- a/React/CoreModules/RCTAppState.h +++ b/React/CoreModules/RCTAppState.h @@ -7,6 +7,6 @@ #import -@interface RCTAppState : RCTEventEmitter +@interface RCTAppState : RCTEventEmitter @end diff --git a/React/CoreModules/RCTAppState.mm b/React/CoreModules/RCTAppState.mm index 6f17715e147e66..7998a6b7986f92 100644 --- a/React/CoreModules/RCTAppState.mm +++ b/React/CoreModules/RCTAppState.mm @@ -112,11 +112,6 @@ - (void)stopObserving [[NSNotificationCenter defaultCenter] removeObserver:self]; } -- (void)invalidate -{ - [self stopObserving]; -} - #pragma mark - App Notification Methods - (void)handleMemoryWarning diff --git a/React/CoreModules/RCTAsyncLocalStorage.mm b/React/CoreModules/RCTAsyncLocalStorage.mm index 3d48ea18faebf0..4f9db50abc4daf 100644 --- a/React/CoreModules/RCTAsyncLocalStorage.mm +++ b/React/CoreModules/RCTAsyncLocalStorage.mm @@ -77,7 +77,7 @@ static void RCTAppendError(NSDictionary *error, NSMutableArray * static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ #if !TARGET_OS_OSX // TODO(macOS GH#774) - // iOS and tvOS to use Caches folder. + // iOS to use Caches folder. // Don't use NSDocumentsDirectory otherwise the RCTAsyncLocalStorage_V1 will appear in apps that // expose the User's Documents folder such as Microsoft Office apps. storageDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject; @@ -252,10 +252,6 @@ - (NSDictionary *)_ensureSetup { RCTAssertThread(RCTGetMethodQueue(), @"Must be executed on storage thread"); -#if TARGET_OS_TV - RCTLogWarn(@"Persistent storage is not supported on tvOS, your data may be removed at any point."); -#endif - NSError *error = nil; if (!RCTHasCreatedStorageDirectory) { [[NSFileManager defaultManager] createDirectoryAtPath:RCTGetStorageDirectory() diff --git a/React/CoreModules/RCTDevSettings.mm b/React/CoreModules/RCTDevSettings.mm index c5ea936b3eb671..9194e4cd67b80d 100644 --- a/React/CoreModules/RCTDevSettings.mm +++ b/React/CoreModules/RCTDevSettings.mm @@ -220,6 +220,7 @@ - (dispatch_queue_t)methodQueue - (void)invalidate { + [super invalidate]; #if ENABLE_PACKAGER_CONNECTION [[RCTPackagerConnection sharedPackagerConnection] removeHandler:_reloadToken]; #endif diff --git a/React/CoreModules/RCTDeviceInfo.mm b/React/CoreModules/RCTDeviceInfo.mm index 8bf767a71a769e..a8097b05b755f4 100644 --- a/React/CoreModules/RCTDeviceInfo.mm +++ b/React/CoreModules/RCTDeviceInfo.mm @@ -25,7 +25,7 @@ @interface RCTDeviceInfo () @end @implementation RCTDeviceInfo { -#if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS GH#774) +#if !TARGET_OS_OSX // TODO(macOS GH#774) UIInterfaceOrientation _currentInterfaceOrientation; NSDictionary *_currentInterfaceDimensions; #endif @@ -54,9 +54,7 @@ - (void)setBridge:(RCTBridge *)bridge selector:@selector(didReceiveNewContentSizeMultiplier) name:RCTAccessibilityManagerDidUpdateMultiplierNotification object:_bridge.accessibilityManager]; -#endif // TODO(macOS GH#774) - -#if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS GH#774) + _currentInterfaceOrientation = [RCTSharedApplication() statusBarOrientation]; [[NSNotificationCenter defaultCenter] addObserver:self @@ -75,8 +73,7 @@ - (void)setBridge:(RCTBridge *)bridge selector:@selector(interfaceFrameDidChange) name:RCTUserInterfaceStyleDidChangeNotification object:nil]; - -#endif +#endif // TODO(macOS GH#774) } static BOOL RCTIsIPhoneX() @@ -174,7 +171,7 @@ - (void)didReceiveNewContentSizeMultiplier }); } -#if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS GH#774) +#if !TARGET_OS_OSX // TODO(macOS GH#774) - (void)interfaceOrientationDidChange { @@ -223,8 +220,7 @@ - (void)_interfaceFrameDidChange _currentInterfaceDimensions = nextInterfaceDimensions; } - -#endif // TARGET_OS_TV +#endif // TODO(macOS GH#774) - (std::shared_ptr)getTurboModule:(const ObjCTurboModule::InitParams &)params { diff --git a/React/CoreModules/RCTKeyboardObserver.mm b/React/CoreModules/RCTKeyboardObserver.mm index f01bb393efd168..268f8036a207e5 100644 --- a/React/CoreModules/RCTKeyboardObserver.mm +++ b/React/CoreModules/RCTKeyboardObserver.mm @@ -23,7 +23,7 @@ @implementation RCTKeyboardObserver - (void)startObserving { -#if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS GH#774) +#if !TARGET_OS_OSX // TODO(macOS GH#774) NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; @@ -37,8 +37,7 @@ - (void)startObserving ADD_KEYBOARD_HANDLER(UIKeyboardDidChangeFrameNotification, keyboardDidChangeFrame); #undef ADD_KEYBOARD_HANDLER - -#endif +#endif // TODO(macOS GH#774) } - (NSArray *)supportedEvents @@ -114,7 +113,7 @@ -(void)EVENT : (NSNotification *)notification static NSDictionary *RCTParseKeyboardNotification(NSNotification *notification) { -#if TARGET_OS_TV || TARGET_OS_OSX // TODO(macOS GH#774) +#if TARGET_OS_OSX // TODO(macOS GH#774) return @{}; #else NSDictionary *userInfo = notification.userInfo; @@ -132,7 +131,7 @@ -(void)EVENT : (NSNotification *)notification @"easing" : RCTAnimationNameForCurve(curve), @"isEventFromThisApp" : isLocalUserInfoKey == 1 ? @YES : @NO, }; -#endif +#endif // TODO(macOS GH#774) } Class RCTKeyboardObserverCls(void) diff --git a/React/CoreModules/RCTRedBox.mm b/React/CoreModules/RCTRedBox.mm index 12cf40747287e3..9087b40d0f58e4 100644 --- a/React/CoreModules/RCTRedBox.mm +++ b/React/CoreModules/RCTRedBox.mm @@ -109,10 +109,8 @@ - (instancetype)initWithFrame:(CGRect)frame _stackTraceTableView.delegate = self; _stackTraceTableView.dataSource = self; _stackTraceTableView.backgroundColor = [UIColor clearColor]; -#if !TARGET_OS_TV _stackTraceTableView.separatorColor = [UIColor colorWithWhite:1 alpha:0.3]; _stackTraceTableView.separatorStyle = UITableViewCellSeparatorStyleNone; -#endif _stackTraceTableView.indicatorStyle = UIScrollViewIndicatorStyleWhite; [rootView addSubview:_stackTraceTableView]; @@ -297,10 +295,8 @@ - (void)copyStack [fullStackTrace appendFormat:@" %@\n", [self formatFrameSource:stackFrame]]; } } -#if !TARGET_OS_TV UIPasteboard *pb = [UIPasteboard generalPasteboard]; [pb setString:fullStackTrace]; -#endif } - (NSString *)formatFrameSource:(RCTJSStackFrame *)stackFrame diff --git a/React/CoreModules/RCTStatusBarManager.h b/React/CoreModules/RCTStatusBarManager.h index 2d00fb0b47d5f9..2a4b7ac8a5d7bd 100644 --- a/React/CoreModules/RCTStatusBarManager.h +++ b/React/CoreModules/RCTStatusBarManager.h @@ -12,10 +12,10 @@ @interface RCTConvert (UIStatusBar) -#if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS GH#774) +#if !TARGET_OS_OSX // TODO(macOS GH#774) + (UIStatusBarStyle)UIStatusBarStyle:(id)json; + (UIStatusBarAnimation)UIStatusBarAnimation:(id)json; -#endif +#endif // TODO(macOS GH#774) @end diff --git a/React/CoreModules/RCTStatusBarManager.mm b/React/CoreModules/RCTStatusBarManager.mm index 0af98f96fb20ba..32f8c86d76d885 100644 --- a/React/CoreModules/RCTStatusBarManager.mm +++ b/React/CoreModules/RCTStatusBarManager.mm @@ -12,7 +12,7 @@ #import #import -#if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS GH#774) +#if !TARGET_OS_OSX // TODO(macOS GH#774) #import @implementation RCTConvert (UIStatusBar) @@ -58,14 +58,11 @@ + (UIStatusBarStyle)UIStatusBarStyle:(id)json RCT_DYNAMIC integerValue); @end -#endif - -#if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS GH#774) @interface RCTStatusBarManager () @end -#endif +#endif // TODO(macOS GH#774) @implementation RCTStatusBarManager @@ -94,7 +91,7 @@ + (BOOL)requiresMainQueueSetup return @[ @"statusBarFrameDidChange", @"statusBarFrameWillChange" ]; } -#if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS GH#774) +#if !TARGET_OS_OSX // TODO(macOS GH#774) - (void)startObserving { @@ -207,7 +204,7 @@ - (void)applicationWillChangeStatusBarFrame:(NSNotification *)notification return std::make_shared(params); } -#endif // TARGET_OS_TV +#endif // TODO(macOS GH#774) @end diff --git a/React/CoreModules/RCTTVNavigationEventEmitter.h b/React/CoreModules/RCTTVNavigationEventEmitter.h deleted file mode 100644 index b3bf1d43bd07a3..00000000000000 --- a/React/CoreModules/RCTTVNavigationEventEmitter.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import - -RCT_EXTERN NSString *const RCTTVNavigationEventNotification; - -@interface RCTTVNavigationEventEmitter : RCTEventEmitter - -@end diff --git a/React/CoreModules/RCTTVNavigationEventEmitter.mm b/React/CoreModules/RCTTVNavigationEventEmitter.mm deleted file mode 100644 index 58fc1ead297cfb..00000000000000 --- a/React/CoreModules/RCTTVNavigationEventEmitter.mm +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "RCTTVNavigationEventEmitter.h" - -#import -#import "CoreModulesPlugins.h" - -NSString *const RCTTVNavigationEventNotification = @"RCTTVNavigationEventNotification"; - -static NSString *const TVNavigationEventName = @"onHWKeyEvent"; - -@interface RCTTVNavigationEventEmitter () -@end - -@implementation RCTTVNavigationEventEmitter - -RCT_EXPORT_MODULE() - -+ (BOOL)requiresMainQueueSetup -{ - return NO; -} - -- (instancetype)init -{ - if (self = [super init]) { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleTVNavigationEventNotification:) - name:RCTTVNavigationEventNotification - object:nil]; - } - return self; -} - -- (NSArray *)supportedEvents -{ - return @[ TVNavigationEventName ]; -} - -- (void)handleTVNavigationEventNotification:(NSNotification *)notif -{ - if (self.bridge) { - [self sendEventWithName:TVNavigationEventName body:notif.object]; - } -} - -- (std::shared_ptr)getTurboModule: - (const facebook::react::ObjCTurboModule::InitParams &)params -{ - return std::make_shared(params); -} - -@end - -Class RCTTVNavigationEventEmitterCls(void) -{ - return RCTTVNavigationEventEmitter.class; -} diff --git a/React/CoreModules/RCTWebSocketModule.mm b/React/CoreModules/RCTWebSocketModule.mm index 4baa4a9e682a04..4a978f3679bcf7 100644 --- a/React/CoreModules/RCTWebSocketModule.mm +++ b/React/CoreModules/RCTWebSocketModule.mm @@ -53,6 +53,8 @@ - (NSArray *)supportedEvents - (void)invalidate { + [super invalidate]; + _contentHandlers = nil; for (RCTSRWebSocket *socket in _sockets.allValues) { socket.delegate = nil; diff --git a/React/CoreModules/React-CoreModules.podspec b/React/CoreModules/React-CoreModules.podspec index ada10a1335a793..3e99a3ddc5cc3f 100644 --- a/React/CoreModules/React-CoreModules.podspec +++ b/React/CoreModules/React-CoreModules.podspec @@ -26,7 +26,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#774) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#774) s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "**/*.{c,m,mm,cpp}" diff --git a/React/Fabric/Mounting/ComponentViews/ActivityIndicator/RCTActivityIndicatorViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/ActivityIndicator/RCTActivityIndicatorViewComponentView.mm index dd79102526d9e5..7b5a56999ed8be 100644 --- a/React/Fabric/Mounting/ComponentViews/ActivityIndicator/RCTActivityIndicatorViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/ActivityIndicator/RCTActivityIndicatorViewComponentView.mm @@ -7,6 +7,8 @@ #import "RCTActivityIndicatorViewComponentView.h" +#import + #import #import #import @@ -50,7 +52,7 @@ - (instancetype)initWithFrame:(CGRect)frame } 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 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & } } - 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. diff --git a/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm b/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm index a80c9914ea75ad..09dd599650f026 100644 --- a/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/Image/RCTImageComponentView.mm @@ -13,9 +13,7 @@ #import #import #import -#import #import -#import #import using namespace facebook::react; @@ -74,7 +72,7 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & // `tintColor` if (oldImageProps.tintColor != newImageProps.tintColor) { - _imageView.tintColor = [UIColor colorWithCGColor:newImageProps.tintColor.get()]; + _imageView.tintColor = RCTUIColorFromSharedColor(newImageProps.tintColor); } [super updateProps:props oldProps:oldProps]; @@ -160,11 +158,6 @@ - (void)didReceiveImage:(UIImage *)image metadata:(id)metadata fromObserver:(voi if (!data.hasValue()) { return; } - auto instrumentation = std::static_pointer_cast( - data.value().getImageRequest().getSharedImageInstrumentation()); - if (instrumentation) { - instrumentation->didSetImage(); - } }; if (imageProps.blurRadius > __FLT_EPSILON__) { diff --git a/React/Fabric/Mounting/ComponentViews/InputAccessory/RCTInputAccessoryContentView.mm b/React/Fabric/Mounting/ComponentViews/InputAccessory/RCTInputAccessoryContentView.mm index d9c4fb231b2493..1d112b51c1c88f 100644 --- a/React/Fabric/Mounting/ComponentViews/InputAccessory/RCTInputAccessoryContentView.mm +++ b/React/Fabric/Mounting/ComponentViews/InputAccessory/RCTInputAccessoryContentView.mm @@ -24,7 +24,7 @@ - (instancetype)init _heightConstraint = [_safeAreaContainer.heightAnchor constraintEqualToConstant:0]; _heightConstraint.active = YES; - if (@available(iOS 11.0, tvOS 11.0, *)) { + if (@available(iOS 11.0, *)) { [NSLayoutConstraint activateConstraints:@[ [_safeAreaContainer.bottomAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.bottomAnchor], [_safeAreaContainer.topAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.topAnchor], diff --git a/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm b/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm index 8c9a97ce6aa2ab..3262418b17832c 100644 --- a/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/LegacyViewManagerInterop/RCTLegacyViewManagerInteropComponentView.mm @@ -37,6 +37,17 @@ - (instancetype)initWithFrame:(CGRect)frame return self; } +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event +{ + UIView *result = [super hitTest:point withEvent:event]; + + if (result == _adapter.paperView) { + return self; + } + + return result; +} + + (NSMutableSet *)supportedViewManagers { static NSMutableSet *supported = [NSMutableSet setWithObjects:@"Picker", diff --git a/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.h b/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.h index 1cd88f8aa62fff..f794b067c75443 100644 --- a/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.h +++ b/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.h @@ -15,8 +15,6 @@ @property (nonatomic, weak) id delegate; -#if !TARGET_OS_TV @property (nonatomic, assign) UIInterfaceOrientationMask supportedInterfaceOrientations; -#endif @end diff --git a/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.mm b/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.mm index 95f333c2e3f7fe..dc852ff001da6d 100644 --- a/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.mm +++ b/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.mm @@ -40,7 +40,6 @@ - (void)loadView [_touchHandler attachToView:self.view]; } -#if !TARGET_OS_TV - (UIStatusBarStyle)preferredStatusBarStyle { return [RCTSharedApplication() statusBarStyle]; @@ -57,20 +56,6 @@ - (BOOL)prefersStatusBarHidden return [RCTSharedApplication() isStatusBarHidden]; } -- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)())completion -{ - UIView *snapshot = [self.view snapshotViewAfterScreenUpdates:NO]; - [self.view addSubview:snapshot]; - - [super dismissViewControllerAnimated:flag - completion:^{ - [snapshot removeFromSuperview]; - if (completion) { - completion(); - } - }]; -} - #if RCT_DEV - (UIInterfaceOrientationMask)supportedInterfaceOrientations { @@ -89,6 +74,5 @@ - (UIInterfaceOrientationMask)supportedInterfaceOrientations return _supportedInterfaceOrientations; } #endif // RCT_DEV -#endif // !TARGET_OS_TV @end diff --git a/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.h b/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.h index 16fbdfb9624ab7..b61196c4c57b0a 100644 --- a/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.h +++ b/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.h @@ -5,12 +5,13 @@ * LICENSE file in the root directory of this source tree. */ +#import #import /** * UIView class for root component. */ -@interface RCTModalHostViewComponentView : RCTViewComponentView +@interface RCTModalHostViewComponentView : RCTViewComponentView /** * Subclasses may override this method and present the modal on different view controller. diff --git a/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.mm index b4a8b7b2f5239d..01648dd2f423d9 100644 --- a/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/Modal/RCTModalHostViewComponentView.mm @@ -19,7 +19,6 @@ using namespace facebook::react; -#if !TARGET_OS_TV static UIInterfaceOrientationMask supportedOrientationsMask(ModalHostViewSupportedOrientationsMask mask) { UIInterfaceOrientationMask supportedOrientations = 0; @@ -54,7 +53,6 @@ static UIInterfaceOrientationMask supportedOrientationsMask(ModalHostViewSupport return supportedOrientations; } -#endif static std::tuple animationConfiguration(ModalHostViewAnimationType const animation) { @@ -103,6 +101,7 @@ @implementation RCTModalHostViewComponentView { ModalHostViewShadowNode::ConcreteStateTeller _stateTeller; BOOL _shouldAnimatePresentation; BOOL _isPresented; + UIView *_modalContentsSnapshot; } - (instancetype)initWithFrame:(CGRect)frame @@ -163,10 +162,22 @@ - (void)ensurePresentedOnlyIfNeeded BOOL shouldBeHidden = _isPresented && !self.superview; if (shouldBeHidden) { _isPresented = NO; + // To animate dismissal of view controller, snapshot of + // view hierarchy needs to be added to the UIViewController. + [self.viewController.view addSubview:_modalContentsSnapshot]; [self dismissViewController:self.viewController animated:_shouldAnimatePresentation]; } } +#pragma mark - RCTMountingTransactionObserving + +- (void)mountingTransactionWillMountWithMetadata:(MountingTransactionMetadata const &)metadata +{ + _modalContentsSnapshot = [self.viewController.view snapshotViewAfterScreenUpdates:NO]; +} + +#pragma mark - UIView methods + - (void)didMoveToWindow { [super didMoveToWindow]; diff --git a/React/Fabric/Mounting/ComponentViews/SafeAreaView/RCTSafeAreaViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/SafeAreaView/RCTSafeAreaViewComponentView.mm index c54ea89bcac19f..7623c1099f93b0 100644 --- a/React/Fabric/Mounting/ComponentViews/SafeAreaView/RCTSafeAreaViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/SafeAreaView/RCTSafeAreaViewComponentView.mm @@ -34,7 +34,7 @@ - (instancetype)initWithFrame:(CGRect)frame - (UIEdgeInsets)_safeAreaInsets { - if (@available(iOS 11.0, tvOS 11.0, *)) { + if (@available(iOS 11.0, *)) { return self.safeAreaInsets; } diff --git a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm index 859b4582980e8a..848e26d2678584 100644 --- a/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -498,16 +498,15 @@ - (void)_remountChildren visibleFrame.size.width *= scale; visibleFrame.size.height *= scale; +#ifndef NDEBUG + NSMutableArray *> *expectedSubviews = [NSMutableArray new]; +#endif + NSInteger mountedIndex = 0; for (UIView *componentView in _childComponentViews) { BOOL shouldBeMounted = YES; BOOL isMounted = componentView.superview != nil; - // If a view is mounted, it must be mounted exactly at `mountedIndex` position. - RCTAssert( - !isMounted || [_containerView.subviews objectAtIndex:mountedIndex] == componentView, - @"Attempt to unmount improperly mounted component view."); - // It's simpler and faster to not mess with views that are not `RCTViewComponentView` subclasses. if ([componentView isKindOfClass:[RCTViewComponentView class]]) { RCTViewComponentView *viewComponentView = (RCTViewComponentView *)componentView; @@ -529,7 +528,24 @@ - (void)_remountChildren if (shouldBeMounted) { mountedIndex++; } + +#ifndef NDEBUG + if (shouldBeMounted) { + [expectedSubviews addObject:componentView]; + } +#endif + } + +#ifndef NDEBUG + RCTAssert( + _containerView.subviews.count == expectedSubviews.count, + @"-[RCTScrollViewComponentView _remountChildren]: Inconsistency detected."); + for (NSInteger i = 0; i < expectedSubviews.count; i++) { + RCTAssert( + [_containerView.subviews objectAtIndex:i] == [expectedSubviews objectAtIndex:i], + @"-[RCTScrollViewComponentView _remountChildren]: Inconsistency detected."); } +#endif } #pragma mark - RCTScrollableProtocol diff --git a/React/Fabric/Mounting/ComponentViews/Slider/RCTSliderComponentView.mm b/React/Fabric/Mounting/ComponentViews/Slider/RCTSliderComponentView.mm index e28a7eb5da9f3b..af62366caa3be3 100644 --- a/React/Fabric/Mounting/ComponentViews/Slider/RCTSliderComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/Slider/RCTSliderComponentView.mm @@ -7,8 +7,10 @@ #import "RCTSliderComponentView.h" +#import #import #import + #import #import #import @@ -108,12 +110,6 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & const auto &oldSliderProps = *std::static_pointer_cast(_props); const auto &newSliderProps = *std::static_pointer_cast(props); - // `value` - if (oldSliderProps.value != newSliderProps.value) { - _sliderView.value = newSliderProps.value; - _previousValue = newSliderProps.value; - } - // `minimumValue` if (oldSliderProps.minimumValue != newSliderProps.minimumValue) { _sliderView.minimumValue = newSliderProps.minimumValue; @@ -124,6 +120,12 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & _sliderView.maximumValue = newSliderProps.maximumValue; } + // `value` + if (oldSliderProps.value != newSliderProps.value) { + _sliderView.value = newSliderProps.value; + _previousValue = newSliderProps.value; + } + // `disabled` if (oldSliderProps.disabled != newSliderProps.disabled) { _sliderView.enabled = !newSliderProps.disabled; @@ -131,17 +133,17 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & // `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]; @@ -298,7 +300,7 @@ - (void)onChange:(UISlider *)sender withContinuous:(BOOL)continuous const auto &props = *std::static_pointer_cast(_props); - if (props.step > 0 && value <= (props.maximumValue - props.minimumValue)) { + if (props.step > 0 && props.step <= (props.maximumValue - props.minimumValue)) { value = MAX( props.minimumValue, MIN(props.maximumValue, props.minimumValue + round((value - props.minimumValue) / props.step) * props.step)); diff --git a/React/Fabric/Mounting/ComponentViews/Switch/RCTSwitchComponentView.mm b/React/Fabric/Mounting/ComponentViews/Switch/RCTSwitchComponentView.mm index 2538f040476c78..5c89f7cbc0bab0 100644 --- a/React/Fabric/Mounting/ComponentViews/Switch/RCTSwitchComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/Switch/RCTSwitchComponentView.mm @@ -7,6 +7,8 @@ #import "RCTSwitchComponentView.h" +#import + #import #import #import @@ -75,17 +77,17 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & // `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]; diff --git a/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index 35b7858f01e177..428d8bc630e1b0 100644 --- a/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -7,10 +7,12 @@ #import "RCTViewComponentView.h" +#import +#import + #import #import #import -#import #import #import #import @@ -118,7 +120,7 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & // `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 = RCTCreateCGColorRefFromSharedColor(borderColors.top), + .left = RCTCreateCGColorRefFromSharedColor(borderColors.left), + .bottom = RCTCreateCGColorRefFromSharedColor(borderColors.bottom), + .right = RCTCreateCGColorRefFromSharedColor(borderColors.right)}; +} + +static void RCTReleaseRCTBorderColors(RCTBorderColors borderColors) { - return RCTBorderColors{.top = RCTCGColorRefUnretainedFromSharedColor(borderColors.top), - .left = RCTCGColorRefUnretainedFromSharedColor(borderColors.left), - .bottom = RCTCGColorRefUnretainedFromSharedColor(borderColors.bottom), - .right = RCTCGColorRefUnretainedFromSharedColor(borderColors.right)}; + CGColorRelease(borderColors.top); + CGColorRelease(borderColors.left); + CGColorRelease(borderColors.bottom); + CGColorRelease(borderColors.right); } static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle) @@ -412,7 +422,7 @@ - (void)invalidateLayer } 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 @@ - (void)invalidateLayer 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 { @@ -555,6 +569,11 @@ - (NSString *)accessibilityValue #pragma mark - Accessibility Events +- (BOOL)shouldGroupAccessibilityChildren +{ + return YES; +} + - (NSArray *)accessibilityCustomActions { auto const &accessibilityActions = _props->accessibilityActions; diff --git a/React/Fabric/Mounting/RCTComponentViewFactory.mm b/React/Fabric/Mounting/RCTComponentViewFactory.mm index 88489f2b3cd900..1c857b440c9d95 100644 --- a/React/Fabric/Mounting/RCTComponentViewFactory.mm +++ b/React/Fabric/Mounting/RCTComponentViewFactory.mm @@ -117,6 +117,7 @@ - (RCTComponentViewClassDescriptor)_componentViewClassDescriptorFromClass:(Class - (void)registerComponentViewClass:(Class)componentViewClass { + RCTAssert(componentViewClass, @"RCTComponentViewFactory: Provided `componentViewClass` is `nil`."); std::unique_lock lock(_mutex); auto componentDescriptorProvider = [componentViewClass componentDescriptorProvider]; diff --git a/React/Fabric/RCTConversions.h b/React/Fabric/RCTConversions.h index f1dc7ee2910f17..222e9ba85005b2 100644 --- a/React/Fabric/RCTConversions.h +++ b/React/Fabric/RCTConversions.h @@ -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; + } -inline CF_RETURNS_NOT_RETAINED CGColorRef -RCTCGColorRefUnretainedFromSharedColor(const facebook::react::SharedColor &sharedColor) -{ - return sharedColor ? sharedColor.get() : 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_RETAINED CGColorRef RCTCGColorRefFromSharedColor(const facebook::react::SharedColor &sharedColor) +inline CF_RETURNS_RETAINED CGColorRef +RCTCreateCGColorRefFromSharedColor(const facebook::react::SharedColor &sharedColor) { - return sharedColor ? CGColorCreateCopy(sharedColor.get()) : nil; + return CGColorRetain(RCTUIColorFromSharedColor(sharedColor).CGColor); } inline CGPoint RCTCGPointFromPoint(const facebook::react::Point &point) diff --git a/React/Modules/RCTEventEmitter.h b/React/Modules/RCTEventEmitter.h index 55426d4f64c0bd..b0ff7f4c233d8e 100644 --- a/React/Modules/RCTEventEmitter.h +++ b/React/Modules/RCTEventEmitter.h @@ -12,7 +12,7 @@ * RCTEventEmitter is an abstract base class to be used for modules that emit * events to be observed by JS. */ -@interface RCTEventEmitter : NSObject +@interface RCTEventEmitter : NSObject @property (nonatomic, weak) RCTBridge * _Nullable bridge; // TODO(macOS GH#774) @@ -37,6 +37,8 @@ - (void)startObserving; - (void)stopObserving; +- (void)invalidate NS_REQUIRES_SUPER; + - (void)addListener:(NSString *_Nullable)eventName; // TODO(macOS GH#774) - (void)removeListeners:(double)count; diff --git a/React/Modules/RCTEventEmitter.m b/React/Modules/RCTEventEmitter.m index 7717ddbdcac451..6e5a6ff37e7f5d 100644 --- a/React/Modules/RCTEventEmitter.m +++ b/React/Modules/RCTEventEmitter.m @@ -78,7 +78,7 @@ - (void)stopObserving // Does nothing } -- (void)dealloc +- (void)invalidate { if (_listenerCount > 0) { [self stopObserving]; diff --git a/React/Modules/RCTLayoutAnimation.m b/React/Modules/RCTLayoutAnimation.m index 80de16a018955a..31faf0bb72d4df 100644 --- a/React/Modules/RCTLayoutAnimation.m +++ b/React/Modules/RCTLayoutAnimation.m @@ -61,7 +61,6 @@ static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnim // `UIKeyboardWillChangeFrameNotification`s. + (void)initializeStatics { -#if !TARGET_OS_TV static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [[NSNotificationCenter defaultCenter] addObserver:self @@ -69,15 +68,12 @@ + (void)initializeStatics name:UIKeyboardWillChangeFrameNotification object:nil]; }); -#endif } + (void)keyboardWillChangeFrame:(NSNotification *)notification { -#if !TARGET_OS_TV NSDictionary *userInfo = notification.userInfo; _currentKeyboardAnimationCurve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]; -#endif } #endif // TODO(macOS GH#774) diff --git a/React/Modules/RCTRedBoxExtraDataViewController.m b/React/Modules/RCTRedBoxExtraDataViewController.m index 240aa5e9b033ad..9b006ec68c1ef3 100644 --- a/React/Modules/RCTRedBoxExtraDataViewController.m +++ b/React/Modules/RCTRedBoxExtraDataViewController.m @@ -33,10 +33,8 @@ - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSStr self.keyLabel.textColor = [UIColor whiteColor]; self.keyLabel.numberOfLines = 0; -#if !TARGET_OS_TV self.keyLabel.lineBreakMode = NSLineBreakByWordWrapping; self.keyLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:12.0f]; -#endif self.valueLabel = [UILabel new]; [self.contentView addSubview:self.valueLabel]; @@ -48,10 +46,8 @@ - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSStr self.valueLabel.textColor = [UIColor whiteColor]; self.valueLabel.numberOfLines = 0; -#if !TARGET_OS_TV self.valueLabel.lineBreakMode = NSLineBreakByWordWrapping; self.valueLabel.font = [UIFont fontWithName:@"Menlo-Regular" size:12.0f]; -#endif } return self; } @@ -82,9 +78,7 @@ - (instancetype)init _tableView.dataSource = self; _tableView.backgroundColor = [UIColor clearColor]; _tableView.estimatedRowHeight = 200; -#if !TARGET_OS_TV _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; -#endif _tableView.rowHeight = UITableViewAutomaticDimension; _tableView.allowsSelection = NO; diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 7368a270574529..695cab0ed79e66 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -189,14 +189,10 @@ - (void)setBridge:(RCTBridge *)bridge object:[self->_bridge moduleForName:@"AccessibilityManager" lazilyLoadIfNecessary:YES]]; }); -#endif // TODO(macOS GH#774) -#if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS GH#774) [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(namedOrientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil]; -#endif -#if !TARGET_OS_OSX // TODO(macOS GH#774) [RCTLayoutAnimation initializeStatics]; #endif // TODO(macOS GH#774) } @@ -225,7 +221,7 @@ - (void)didReceiveNewContentSizeMultiplier } #endif // TODO(macOS GH#774) -#if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS GH#774) +#if !TARGET_OS_OSX // TODO(macOS GH#774) // Names and coordinate system from html5 spec: // https://developer.mozilla.org/en-US/docs/Web/API/Screen.orientation // https://developer.mozilla.org/en-US/docs/Web/API/Screen.lockOrientation @@ -277,7 +273,7 @@ - (void)namedOrientationDidChange [_bridge.eventDispatcher sendDeviceEventWithName:@"namedOrientationDidChange" body:orientationEvent]; #pragma clang diagnostic pop } -#endif +#endif // TODO(macOS GH#774) - (dispatch_queue_t)methodQueue { diff --git a/React/Profiler/RCTProfile.m b/React/Profiler/RCTProfile.m index 1704de3b7770a4..deaf62202b609c 100644 --- a/React/Profiler/RCTProfile.m +++ b/React/Profiler/RCTProfile.m @@ -396,7 +396,6 @@ + (void)toggle:(UIButton *)target RCTProfileEnd(RCTProfilingBridge(), ^(NSString *result) { NSString *outFile = [NSTemporaryDirectory() stringByAppendingString:@"tmp_trace.json"]; [result writeToFile:outFile atomically:YES encoding:NSUTF8StringEncoding error:nil]; -#if !TARGET_OS_TV UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[ [NSURL fileURLWithPath:outFile] ] applicationActivities:nil]; @@ -413,7 +412,6 @@ + (void)toggle:(UIButton *)target animated:YES completion:nil]; }); -#endif }); } else { RCTProfileInit(RCTProfilingBridge()); @@ -761,7 +759,7 @@ void RCTProfileSendResult(RCTBridge *bridge, NSString *route, NSData *data) alert.informativeText = message; [alert addButtonWithTitle:@"OK"]; [alert runModal]; -#elif !TARGET_OS_TV // ]TODO(macOS GH#774) +#else // ]TODO(macOS GH#774) dispatch_async(dispatch_get_main_queue(), ^{ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Profile" @@ -772,7 +770,7 @@ void RCTProfileSendResult(RCTBridge *bridge, NSString *route, NSData *data) handler:nil]]; [RCTPresentedViewController() presentViewController:alertController animated:YES completion:nil]; }); -#endif +#endif // TODO(macOS GH#774) } } }]; diff --git a/React/React-RCTFabric.podspec b/React/React-RCTFabric.podspec index 59c3620869c3d2..ae5654780f8277 100644 --- a/React/React-RCTFabric.podspec +++ b/React/React-RCTFabric.podspec @@ -28,7 +28,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0" } + s.platforms = { :ios => "10.0" } s.source = source s.source_files = "Fabric/**/*.{c,h,m,mm,S,cpp}", "Tests/**/*.{mm}" diff --git a/React/Views/RCTComponentData.m b/React/Views/RCTComponentData.m index 191c5209f0da1b..e90f0686d18942 100644 --- a/React/Views/RCTComponentData.m +++ b/React/Views/RCTComponentData.m @@ -70,10 +70,8 @@ - (RCTPlatformView *)createViewWithTag:(nullable NSNumber *)tag rootTag:(nullabl RCTPlatformView *view = [self.manager view]; // TODO(macOS GH#774) view.reactTag = tag; view.rootTag = rootTag; -#if !TARGET_OS_OSX && !TARGET_OS_TV // TODO(macOS GH#774) - view.multipleTouchEnabled = YES; -#endif #if !TARGET_OS_OSX // TODO(macOS GH#774) + view.multipleTouchEnabled = YES; view.userInteractionEnabled = YES; // required for touch handling view.layer.allowsGroupOpacity = YES; // required for touch handling #endif // TODO(macOS GH#774) diff --git a/React/Views/RCTDatePicker.m b/React/Views/RCTDatePicker.m index 2f893945448229..a994ddc7b3fef0 100644 --- a/React/Views/RCTDatePicker.m +++ b/React/Views/RCTDatePicker.m @@ -7,9 +7,20 @@ #import "RCTDatePicker.h" +#import +#import + #import "RCTUtils.h" #import "UIView+React.h" +#ifndef __IPHONE_14_0 +#define __IPHONE_14_0 140000 +#endif // __IPHONE_14_0 + +#ifndef RCT_IOS_14_0_SDK_OR_LATER +#define RCT_IOS_14_0_SDK_OR_LATER (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0) +#endif // RCT_IOS_14_0_SDK_OR_LATER + @interface RCTDatePicker () @property (nonatomic, copy) RCTBubblingEventBlock onChange; @@ -29,6 +40,14 @@ - (instancetype)initWithFrame:(CGRect)frame self.action = @selector(didChange); #endif // ]TODO(macOS GH#774) _reactMinuteInterval = 1; + +#if !TARGET_OS_OSX // TODO(macOS GH#774) +#if RCT_IOS_14_0_SDK_OR_LATER + if (@available(iOS 14, *)) { + self.preferredDatePickerStyle = UIDatePickerStyleWheels; + } +#endif // RCT_IOS_14_0_SDK_OR_LATER +#endif // ]TODO(macOS GH#774) } return self; } diff --git a/React/Views/RCTModalHostView.h b/React/Views/RCTModalHostView.h index 4e61886dba54ef..c54c1c69c94413 100644 --- a/React/Views/RCTModalHostView.h +++ b/React/Views/RCTModalHostView.h @@ -13,7 +13,6 @@ @class RCTBridge; @class RCTModalHostViewController; -@class RCTTVRemoteHandler; @protocol RCTModalHostViewInteractor; @@ -32,11 +31,6 @@ @property (nonatomic, copy) NSArray *supportedOrientations; @property (nonatomic, copy) RCTDirectEventBlock onOrientationChange; -#if TARGET_OS_TV -@property (nonatomic, copy) RCTDirectEventBlock onRequestClose; -@property (nonatomic, strong) RCTTVRemoteHandler *tvRemoteHandler; -#endif - - (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; @end diff --git a/React/Views/RCTModalHostView.m b/React/Views/RCTModalHostView.m index 6a153301038853..0cfa69a99aab6f 100644 --- a/React/Views/RCTModalHostView.m +++ b/React/Views/RCTModalHostView.m @@ -16,9 +16,6 @@ #import "RCTUIManager.h" #import "RCTUtils.h" #import "UIView+React.h" -#if TARGET_OS_TV -#import "RCTTVRemoteHandler.h" -#endif @implementation RCTModalHostView { __weak RCTBridge *_bridge; @@ -26,11 +23,7 @@ @implementation RCTModalHostView { RCTModalHostViewController *_modalViewController; RCTTouchHandler *_touchHandler; UIView *_reactSubview; -#if TARGET_OS_TV - UITapGestureRecognizer *_menuButtonGestureRecognizer; -#else UIInterfaceOrientation _lastKnownOrientation; -#endif } RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame) @@ -45,12 +38,6 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; _modalViewController.view = containerView; _touchHandler = [[RCTTouchHandler alloc] initWithBridge:bridge]; -#if TARGET_OS_TV - _menuButtonGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self - action:@selector(menuButtonPressed:)]; - _menuButtonGestureRecognizer.allowedPressTypes = @[ @(UIPressTypeMenu) ]; - self.tvRemoteHandler = [RCTTVRemoteHandler new]; -#endif _isPresented = NO; __weak typeof(self) weakSelf = self; @@ -62,27 +49,6 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge return self; } -#if TARGET_OS_TV -- (void)menuButtonPressed:(__unused UIGestureRecognizer *)gestureRecognizer -{ - if (_onRequestClose) { - _onRequestClose(nil); - } -} - -- (void)setOnRequestClose:(RCTDirectEventBlock)onRequestClose -{ - _onRequestClose = onRequestClose; - if (_reactSubview) { - if (_onRequestClose && _menuButtonGestureRecognizer) { - [_reactSubview addGestureRecognizer:_menuButtonGestureRecognizer]; - } else { - [_reactSubview removeGestureRecognizer:_menuButtonGestureRecognizer]; - } - } -} -#endif - - (void)notifyForBoundsChange:(CGRect)newBounds { if (_reactSubview && _isPresented) { @@ -93,7 +59,6 @@ - (void)notifyForBoundsChange:(CGRect)newBounds - (void)notifyForOrientationChange { -#if !TARGET_OS_TV if (!_onOrientationChange) { return; } @@ -110,7 +75,6 @@ - (void)notifyForOrientationChange @"orientation" : isPortrait ? @"portrait" : @"landscape", }; _onOrientationChange(eventPayload); -#endif } - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex @@ -118,16 +82,6 @@ - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex RCTAssert(_reactSubview == nil, @"Modal view can only have one subview"); [super insertReactSubview:subview atIndex:atIndex]; [_touchHandler attachToView:subview]; -#if TARGET_OS_TV - for (NSString *key in [self.tvRemoteHandler.tvRemoteGestureRecognizers allKeys]) { - if (![key isEqualToString:RCTTVRemoteEventMenu]) { - [subview addGestureRecognizer:self.tvRemoteHandler.tvRemoteGestureRecognizers[key]]; - } - } - if (_onRequestClose) { - [subview addGestureRecognizer:_menuButtonGestureRecognizer]; - } -#endif [_modalViewController.view insertSubview:subview atIndex:0]; _reactSubview = subview; @@ -139,14 +93,6 @@ - (void)removeReactSubview:(UIView *)subview // Superclass (category) removes the `subview` from actual `superview`. [super removeReactSubview:subview]; [_touchHandler detachFromView:subview]; -#if TARGET_OS_TV - if (_menuButtonGestureRecognizer) { - [subview removeGestureRecognizer:_menuButtonGestureRecognizer]; - } - for (UIGestureRecognizer *gr in self.tvRemoteHandler.tvRemoteGestureRecognizers) { - [subview removeGestureRecognizer:gr]; - } -#endif _reactSubview = nil; } @@ -176,9 +122,8 @@ - (void)didMoveToWindow if (!_isPresented && self.window) { RCTAssert(self.reactViewController, @"Can't present modal view controller without a presenting view controller"); -#if !TARGET_OS_TV _modalViewController.supportedInterfaceOrientations = [self supportedOrientationsMask]; -#endif + if ([self.animationType isEqualToString:@"fade"]) { _modalViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; } else if ([self.animationType isEqualToString:@"slide"]) { @@ -228,7 +173,6 @@ - (void)setTransparent:(BOOL)transparent transparent ? UIModalPresentationOverFullScreen : UIModalPresentationFullScreen; } -#if !TARGET_OS_TV - (UIInterfaceOrientationMask)supportedOrientationsMask { if (_supportedOrientations.count == 0) { @@ -255,6 +199,5 @@ - (UIInterfaceOrientationMask)supportedOrientationsMask } return supportedOrientations; } -#endif @end diff --git a/React/Views/RCTModalHostViewController.h b/React/Views/RCTModalHostViewController.h index 0a4a82968075c5..a3f66b6b068336 100644 --- a/React/Views/RCTModalHostViewController.h +++ b/React/Views/RCTModalHostViewController.h @@ -11,8 +11,6 @@ @property (nonatomic, copy) void (^boundsDidChangeBlock)(CGRect newBounds); -#if !TARGET_OS_TV @property (nonatomic, assign) UIInterfaceOrientationMask supportedInterfaceOrientations; -#endif @end diff --git a/React/Views/RCTModalHostViewController.m b/React/Views/RCTModalHostViewController.m index 112bc17c4d85bf..00c149d3db8474 100644 --- a/React/Views/RCTModalHostViewController.m +++ b/React/Views/RCTModalHostViewController.m @@ -12,10 +12,8 @@ @implementation RCTModalHostViewController { CGRect _lastViewFrame; -#if !TARGET_OS_TV UIStatusBarStyle _preferredStatusBarStyle; BOOL _preferredStatusBarHidden; -#endif } - (instancetype)init @@ -31,10 +29,8 @@ - (instancetype)init } #endif -#if !TARGET_OS_TV _preferredStatusBarStyle = [RCTSharedApplication() statusBarStyle]; _preferredStatusBarHidden = [RCTSharedApplication() isStatusBarHidden]; -#endif return self; } @@ -49,7 +45,6 @@ - (void)viewDidLayoutSubviews } } -#if !TARGET_OS_TV - (UIStatusBarStyle)preferredStatusBarStyle { return _preferredStatusBarStyle; @@ -78,6 +73,5 @@ - (UIInterfaceOrientationMask)supportedInterfaceOrientations return _supportedInterfaceOrientations; } #endif // RCT_DEV -#endif // !TARGET_OS_TV @end diff --git a/React/Views/RCTModalHostViewManager.m b/React/Views/RCTModalHostViewManager.m index 05a2286dfc8a60..56d3a5b460d1f3 100644 --- a/React/Views/RCTModalHostViewManager.m +++ b/React/Views/RCTModalHostViewManager.m @@ -20,10 +20,8 @@ @implementation RCTConvert (RCTModalHostView) UIModalPresentationStyle, (@{ @"fullScreen" : @(UIModalPresentationFullScreen), -#if !TARGET_OS_TV @"pageSheet" : @(UIModalPresentationPageSheet), @"formSheet" : @(UIModalPresentationFormSheet), -#endif @"overFullScreen" : @(UIModalPresentationOverFullScreen), }), UIModalPresentationFullScreen, @@ -138,8 +136,4 @@ - (void)invalidate RCT_EXPORT_VIEW_PROPERTY(supportedOrientations, NSArray) RCT_EXPORT_VIEW_PROPERTY(onOrientationChange, RCTDirectEventBlock) -#if TARGET_OS_TV -RCT_EXPORT_VIEW_PROPERTY(onRequestClose, RCTDirectEventBlock) -#endif - @end diff --git a/React/Views/RCTProgressViewManager.m b/React/Views/RCTProgressViewManager.m index a93eeb2a9ae7d2..dd802c059a5b6e 100644 --- a/React/Views/RCTProgressViewManager.m +++ b/React/Views/RCTProgressViewManager.m @@ -22,9 +22,7 @@ @implementation RCTConvert (RCTProgressViewManager) UIProgressViewStyle, (@{ @"default" : @(UIProgressViewStyleDefault), -#if !TARGET_OS_TV @"bar" : @(UIProgressViewStyleBar), -#endif }), UIProgressViewStyleDefault, integerValue) diff --git a/React/Views/RCTTVView.h b/React/Views/RCTTVView.h deleted file mode 100644 index a421e6fc61f173..00000000000000 --- a/React/Views/RCTTVView.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import -#import - -#import - -// A RCTView with additional properties and methods for user interaction using the Apple TV focus engine. -@interface RCTTVView : RCTView - -/** - * TV event handlers - */ -@property (nonatomic, assign) BOOL isTVSelectable; // True if this view is TV-focusable - -/** - * Properties for Apple TV focus parallax effects - */ -@property (nonatomic, copy) NSDictionary *tvParallaxProperties; - -/** - * TV preferred focus - */ -@property (nonatomic, assign) BOOL hasTVPreferredFocus; - -@end diff --git a/React/Views/RCTTVView.m b/React/Views/RCTTVView.m deleted file mode 100644 index 6f06b9a1f411d2..00000000000000 --- a/React/Views/RCTTVView.m +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "RCTTVView.h" - -#import "RCTAutoInsetsProtocol.h" -#import "RCTBorderDrawing.h" -#import "RCTBridge.h" -#import "RCTConvert.h" -#import "RCTEventDispatcher.h" -#import "RCTLog.h" -#import "RCTRootViewInternal.h" -#import "RCTTVNavigationEventEmitter.h" -#import "RCTUtils.h" -#import "RCTView.h" -#import "UIView+React.h" - -@implementation RCTTVView { - UITapGestureRecognizer *_selectRecognizer; -} - -- (instancetype)initWithFrame:(CGRect)frame -{ - if (self = [super initWithFrame:frame]) { - dispatch_once(&onceToken, ^{ - defaultTVParallaxProperties = @{ - @"enabled" : @YES, - @"shiftDistanceX" : @2.0f, - @"shiftDistanceY" : @2.0f, - @"tiltAngle" : @0.05f, - @"magnification" : @1.0f, - @"pressMagnification" : @1.0f, - @"pressDuration" : @0.3f, - @"pressDelay" : @0.0f - }; - }); - self.tvParallaxProperties = defaultTVParallaxProperties; - } - - return self; -} - -static NSDictionary *defaultTVParallaxProperties = nil; -static dispatch_once_t onceToken; - -- (void)setTvParallaxProperties:(NSDictionary *)tvParallaxProperties -{ - if (_tvParallaxProperties == nil) { - _tvParallaxProperties = [defaultTVParallaxProperties copy]; - return; - } - - NSMutableDictionary *newParallaxProperties = [NSMutableDictionary dictionaryWithDictionary:_tvParallaxProperties]; - for (NSString *k in [defaultTVParallaxProperties allKeys]) { - if (tvParallaxProperties[k]) { - newParallaxProperties[k] = tvParallaxProperties[k]; - } - } - _tvParallaxProperties = [newParallaxProperties copy]; -} - -RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused) - -- (void)setIsTVSelectable:(BOOL)isTVSelectable -{ - self->_isTVSelectable = isTVSelectable; - if (isTVSelectable) { - UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self - action:@selector(handleSelect:)]; - recognizer.allowedPressTypes = @[ @(UIPressTypeSelect) ]; - _selectRecognizer = recognizer; - [self addGestureRecognizer:_selectRecognizer]; - } else { - if (_selectRecognizer) { - [self removeGestureRecognizer:_selectRecognizer]; - } - } -} - -- (void)handleSelect:(__unused UIGestureRecognizer *)r -{ - if ([self.tvParallaxProperties[@"enabled"] boolValue] == YES) { - float magnification = [self.tvParallaxProperties[@"magnification"] floatValue]; - float pressMagnification = [self.tvParallaxProperties[@"pressMagnification"] floatValue]; - - // Duration of press animation - float pressDuration = [self.tvParallaxProperties[@"pressDuration"] floatValue]; - - // Delay of press animation - float pressDelay = [self.tvParallaxProperties[@"pressDelay"] floatValue]; - - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:pressDelay]]; - - [UIView animateWithDuration:(pressDuration / 2) - animations:^{ - self.transform = CGAffineTransformMakeScale(pressMagnification, pressMagnification); - } - completion:^(__unused BOOL finished1) { - [UIView animateWithDuration:(pressDuration / 2) - animations:^{ - self.transform = CGAffineTransformMakeScale(magnification, magnification); - } - completion:^(__unused BOOL finished2) { - [[NSNotificationCenter defaultCenter] - postNotificationName:RCTTVNavigationEventNotification - object:@{@"eventType" : @"select", @"tag" : self.reactTag}]; - }]; - }]; - - } else { - [[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification - object:@{@"eventType" : @"select", @"tag" : self.reactTag}]; - } -} - -- (BOOL)isUserInteractionEnabled -{ - return YES; -} - -- (BOOL)canBecomeFocused -{ - return (self.isTVSelectable); -} - -- (void)addParallaxMotionEffects -{ - // Size of shift movements - CGFloat const shiftDistanceX = [self.tvParallaxProperties[@"shiftDistanceX"] floatValue]; - CGFloat const shiftDistanceY = [self.tvParallaxProperties[@"shiftDistanceY"] floatValue]; - - // Make horizontal movements shift the centre left and right - UIInterpolatingMotionEffect *xShift = - [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" - type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; - xShift.minimumRelativeValue = @(shiftDistanceX * -1.0f); - xShift.maximumRelativeValue = @(shiftDistanceX); - - // Make vertical movements shift the centre up and down - UIInterpolatingMotionEffect *yShift = - [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" - type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis]; - yShift.minimumRelativeValue = @(shiftDistanceY * -1.0f); - yShift.maximumRelativeValue = @(shiftDistanceY); - - // Size of tilt movements - CGFloat const tiltAngle = [self.tvParallaxProperties[@"tiltAngle"] floatValue]; - - // Now make horizontal movements effect a rotation about the Y axis for side-to-side rotation. - UIInterpolatingMotionEffect *xTilt = - [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"layer.transform" - type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; - - // CATransform3D value for minimumRelativeValue - CATransform3D transMinimumTiltAboutY = CATransform3DIdentity; - transMinimumTiltAboutY.m34 = 1.0 / 500; - transMinimumTiltAboutY = CATransform3DRotate(transMinimumTiltAboutY, tiltAngle * -1.0, 0, 1, 0); - - // CATransform3D value for minimumRelativeValue - CATransform3D transMaximumTiltAboutY = CATransform3DIdentity; - transMaximumTiltAboutY.m34 = 1.0 / 500; - transMaximumTiltAboutY = CATransform3DRotate(transMaximumTiltAboutY, tiltAngle, 0, 1, 0); - - // Set the transform property boundaries for the interpolation - xTilt.minimumRelativeValue = [NSValue valueWithCATransform3D:transMinimumTiltAboutY]; - xTilt.maximumRelativeValue = [NSValue valueWithCATransform3D:transMaximumTiltAboutY]; - - // Now make vertical movements effect a rotation about the X axis for up and down rotation. - UIInterpolatingMotionEffect *yTilt = - [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"layer.transform" - type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis]; - - // CATransform3D value for minimumRelativeValue - CATransform3D transMinimumTiltAboutX = CATransform3DIdentity; - transMinimumTiltAboutX.m34 = 1.0 / 500; - transMinimumTiltAboutX = CATransform3DRotate(transMinimumTiltAboutX, tiltAngle * -1.0, 1, 0, 0); - - // CATransform3D value for minimumRelativeValue - CATransform3D transMaximumTiltAboutX = CATransform3DIdentity; - transMaximumTiltAboutX.m34 = 1.0 / 500; - transMaximumTiltAboutX = CATransform3DRotate(transMaximumTiltAboutX, tiltAngle, 1, 0, 0); - - // Set the transform property boundaries for the interpolation - yTilt.minimumRelativeValue = [NSValue valueWithCATransform3D:transMinimumTiltAboutX]; - yTilt.maximumRelativeValue = [NSValue valueWithCATransform3D:transMaximumTiltAboutX]; - - // Add all of the motion effects to this group - self.motionEffects = @[ xShift, yShift, xTilt, yTilt ]; - - float magnification = [self.tvParallaxProperties[@"magnification"] floatValue]; - - [UIView animateWithDuration:0.2 - animations:^{ - self.transform = CGAffineTransformMakeScale(magnification, magnification); - }]; -} - -- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context - withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator -{ - if (context.nextFocusedView == self && self.isTVSelectable) { - [self becomeFirstResponder]; - [coordinator - addCoordinatedAnimations:^(void) { - if ([self.tvParallaxProperties[@"enabled"] boolValue]) { - [self addParallaxMotionEffects]; - } - [[NSNotificationCenter defaultCenter] - postNotificationName:RCTTVNavigationEventNotification - object:@{@"eventType" : @"focus", @"tag" : self.reactTag}]; - } - completion:^(void){ - }]; - } else { - [coordinator - addCoordinatedAnimations:^(void) { - [[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification - object:@{@"eventType" : @"blur", @"tag" : self.reactTag}]; - [UIView animateWithDuration:0.2 - animations:^{ - self.transform = CGAffineTransformMakeScale(1, 1); - }]; - - for (UIMotionEffect *effect in [self.motionEffects copy]) { - [self removeMotionEffect:effect]; - } - } - completion:^(void){ - }]; - [self resignFirstResponder]; - } -} - -- (void)setHasTVPreferredFocus:(BOOL)hasTVPreferredFocus -{ - _hasTVPreferredFocus = hasTVPreferredFocus; - if (hasTVPreferredFocus) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - UIView *rootview = self; - while (![rootview isReactRootView] && rootview != nil) { - rootview = [rootview superview]; - } - if (rootview == nil) - return; - - rootview = [rootview superview]; - - [rootview setNeedsFocusUpdate]; - [rootview updateFocusIfNeeded]; - }); - } -} - -@end diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index 22e04cbf62d0b8..521d62e9156ed0 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -21,10 +21,6 @@ #import "RCTView.h" #import "UIView+React.h" -#if TARGET_OS_TV -#import "RCTTVView.h" -#endif - #if !TARGET_OS_OSX // TODO(macOS GH#774) @implementation RCTConvert (UIAccessibilityTraits) @@ -92,11 +88,7 @@ - (dispatch_queue_t)methodQueue - (RCTPlatformView *)view // TODO(macOS GH#774) { -#if TARGET_OS_TV - return [RCTTVView new]; -#else return [[RCTView alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; // TODO(OSS Candidate ISS#2710739) -#endif } - (RCTShadowView *)shadowView @@ -145,13 +137,6 @@ - (RCTShadowView *)shadowView #pragma mark - View properties -#if TARGET_OS_TV -// TODO: Delete props for Apple TV. -RCT_EXPORT_VIEW_PROPERTY(isTVSelectable, BOOL) -RCT_EXPORT_VIEW_PROPERTY(hasTVPreferredFocus, BOOL) -RCT_EXPORT_VIEW_PROPERTY(tvParallaxProperties, NSDictionary) -#endif - // Accessibility related properties #if !TARGET_OS_OSX // TODO(macOS GH#774) RCT_REMAP_VIEW_PROPERTY(accessible, reactAccessibilityElement.isAccessibilityElement, BOOL) diff --git a/React/Views/ScrollView/RCTScrollView.m b/React/Views/ScrollView/RCTScrollView.m index f285346e06849d..1e8079835c25f4 100644 --- a/React/Views/ScrollView/RCTScrollView.m +++ b/React/Views/ScrollView/RCTScrollView.m @@ -20,9 +20,9 @@ #import "UIView+React.h" -#if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS GH#774) +#if !TARGET_OS_OSX // TODO(macOS GH#774) #import "RCTRefreshControl.h" -#endif +#endif // TODO(macOS GH#774) /** * Include a custom scroll view subclass because we want to limit certain @@ -37,11 +37,10 @@ @interface RCTCustomScrollView : #endif // ]TODO(macOS GH#774) @property (nonatomic, assign) BOOL centerContent; -#if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS GH#774) +#if !TARGET_OS_OSX // TODO(macOS GH#774) @property (nonatomic, strong) UIView *customRefreshControl; @property (nonatomic, assign) BOOL pinchGestureEnabled; -#endif -#if TARGET_OS_OSX // [TODO(macOS GH#774) +#else // [TODO(macOS GH#774) + (BOOL)isCompatibleWithResponsiveScrolling; @property (nonatomic, assign, getter=isScrollEnabled) BOOL scrollEnabled; @property (nonatomic, strong) NSPanGestureRecognizer *panGestureRecognizer; @@ -69,9 +68,9 @@ - (instancetype)initWithFrame:(CGRect)frame } #endif -#if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS GH#774) +#if !TARGET_OS_OSX // TODO(macOS GH#774) _pinchGestureEnabled = YES; -#endif +#endif // TODO(macOS GH#774) } return self; } @@ -296,7 +295,7 @@ - (void)setFrame:(CGRect)frame } } -#if !TARGET_OS_TV && !TARGET_OS_OSX // TODO(macOS GH#774) +#if !TARGET_OS_OSX // TODO(macOS GH#774) - (void)setCustomRefreshControl:(UIView *)refreshControl { if (_customRefreshControl) { @@ -329,7 +328,7 @@ - (void)didMoveToWindow // in the setter gets overridden when the view loads. self.pinchGestureRecognizer.enabled = _pinchGestureEnabled; } -#endif // TARGET_OS_TV +#endif // TODO(macOS GH#774) #if TARGET_OS_OSX // [TODO(macOS GH#774) - (BOOL)canBecomeFirstResponder @@ -522,15 +521,12 @@ - (void)insertReactSubview:(RCTUIView *)view atIndex:(NSInteger)atIndex // TODO( _scrollView.documentView = view; #else // ]TODO(macOS GH#774) -#if !TARGET_OS_TV if ([view conformsToProtocol:@protocol(RCTCustomRefreshContolProtocol)]) { [_scrollView setCustomRefreshControl:(UIView *)view]; if (![view isKindOfClass:[UIRefreshControl class]] && [view conformsToProtocol:@protocol(UIScrollViewDelegate)]) { [self addScrollListener:(UIView *)view]; } - } else -#endif - { + } else { RCTAssert( _contentView == nil, @"RCTScrollView may only contain a single subview, the already set subview looks like: %@", @@ -549,16 +545,13 @@ - (void)removeReactSubview:(RCTUIView *)subview // TODO(macOS ISS#3536887) _scrollView.documentView = nil; _contentSize = CGSizeZero; #else // ]TODO(macOS GH#774) -#if !TARGET_OS_TV if ([subview conformsToProtocol:@protocol(RCTCustomRefreshContolProtocol)]) { [_scrollView setCustomRefreshControl:nil]; if (![subview isKindOfClass:[UIRefreshControl class]] && [subview conformsToProtocol:@protocol(UIScrollViewDelegate)]) { [self removeScrollListener:(UIView *)subview]; } - } else -#endif - { + } else { RCTAssert(_contentView == subview, @"Attempted to remove non-existent subview"); _contentView = nil; } @@ -1349,10 +1342,8 @@ -(type)getter \ #endif // TODO(macOS GH#774) RCT_SET_AND_PRESERVE_OFFSET(setScrollEnabled, isScrollEnabled, BOOL) #if !TARGET_OS_OSX // TODO(macOS GH#774) -#if !TARGET_OS_TV RCT_SET_AND_PRESERVE_OFFSET(setPagingEnabled, isPagingEnabled, BOOL) RCT_SET_AND_PRESERVE_OFFSET(setScrollsToTop, scrollsToTop, BOOL) -#endif #endif // TODO(macOS GH#774) RCT_SET_AND_PRESERVE_OFFSET(setShowsHorizontalScrollIndicator, showsHorizontalScrollIndicator, BOOL) RCT_SET_AND_PRESERVE_OFFSET(setShowsVerticalScrollIndicator, showsVerticalScrollIndicator, BOOL) diff --git a/React/Views/ScrollView/RCTScrollViewManager.m b/React/Views/ScrollView/RCTScrollViewManager.m index acc59de59e96c6..f7be154fa7ad24 100644 --- a/React/Views/ScrollView/RCTScrollViewManager.m +++ b/React/Views/ScrollView/RCTScrollViewManager.m @@ -80,11 +80,9 @@ - (RCTPlatformView *)view // TODO(macOS GH#774) RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(maximumZoomScale, CGFloat) // TODO(macOS GH#774) RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(minimumZoomScale, CGFloat) // TODO(macOS GH#774) RCT_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL) -#if !TARGET_OS_TV RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(pagingEnabled, BOOL) // TODO(macOS GH#774) RCT_REMAP_NOT_OSX_VIEW_PROPERTY(pinchGestureEnabled, scrollView.pinchGestureEnabled, BOOL) // TODO(macOS GH#774) RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(scrollsToTop, BOOL) // TODO(macOS GH#774) -#endif RCT_EXPORT_VIEW_PROPERTY(showsHorizontalScrollIndicator, BOOL) RCT_EXPORT_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL) RCT_EXPORT_VIEW_PROPERTY(scrollEventThrottle, NSTimeInterval) diff --git a/ReactAndroid/Android-prebuilt.mk b/ReactAndroid/Android-prebuilt.mk new file mode 100644 index 00000000000000..394032a524ab95 --- /dev/null +++ b/ReactAndroid/Android-prebuilt.mk @@ -0,0 +1,94 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +# This configuration provides access to most common React Native prebuilt .so files +# to avoid recompiling each of the libraries outside of ReactAndroid NDK compilation. +# Hosting app's/library's Android.mk can include this Android-prebuilt.mk file to +# get access to those .so to depend on. +# NOTES: +# * Currently, it assumes building React Native from source. +# * Not every .so is listed here (yet). +# * Static libs are not covered here (yet). + +LOCAL_PATH := $(call my-dir) + +REACT_ANDROID_DIR := $(LOCAL_PATH) +# TODO: Find a better way without pointing to ReactAndroid/build dir. +REACT_ANDROID_BUILD_DIR := $(REACT_ANDROID_DIR)/build + +FIRST_PARTY_NDK_DIR := $(REACT_ANDROID_DIR)/src/main/jni/first-party +THIRD_PARTY_NDK_DIR := $(REACT_ANDROID_BUILD_DIR)/third-party-ndk +REACT_ANDROID_SRC_DIR := $(REACT_ANDROID_DIR)/src/main +REACT_COMMON_DIR := $(REACT_ANDROID_DIR)/../ReactCommon +REACT_GENERATED_SRC_DIR := $(REACT_ANDROID_BUILD_DIR)/generated/source +# Note: this only have .so, not .a +REACT_NDK_EXPORT_DIR := $(PROJECT_BUILD_DIR)/react-ndk/exported + +# fb +include $(CLEAR_VARS) +LOCAL_MODULE := fb +LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libfb.so +LOCAL_EXPORT_C_INCLUDES := $(FIRST_PARTY_NDK_DIR)/fb/include +include $(PREBUILT_SHARED_LIBRARY) + +# folly_json +include $(CLEAR_VARS) +LOCAL_MODULE := folly_json +LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libfolly_json.so +LOCAL_EXPORT_C_INCLUDES := \ + $(THIRD_PARTY_NDK_DIR)/boost/boost_1_63_0 \ + $(THIRD_PARTY_NDK_DIR)/double-conversion \ + $(THIRD_PARTY_NDK_DIR)/folly \ + $(THIRD_PARTY_NDK_DIR)/glog/exported +# Note: Sync with folly/Android.mk. +FOLLY_FLAGS := \ + -DFOLLY_NO_CONFIG=1 \ + -DFOLLY_HAVE_CLOCK_GETTIME=1 \ + -DFOLLY_HAVE_MEMRCHR=1 \ + -DFOLLY_USE_LIBCPP=1 \ + -DFOLLY_MOBILE=1 \ + -DFOLLY_HAVE_XSI_STRERROR_R=1 +LOCAL_CFLAGS += $(FOLLY_FLAGS) +LOCAL_EXPORT_CPPFLAGS := $(FOLLY_FLAGS) +include $(PREBUILT_SHARED_LIBRARY) + +# folly_futures +include $(CLEAR_VARS) +LOCAL_MODULE := folly_futures +LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libfolly_futures.so +LOCAL_SHARED_LIBRARIES := liblibfolly_json +include $(PREBUILT_SHARED_LIBRARY) + +# react_nativemodule_core +include $(CLEAR_VARS) +LOCAL_MODULE := react_nativemodule_core +LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libreact_nativemodule_core.so +LOCAL_EXPORT_C_INCLUDES := \ + $(REACT_ANDROID_SRC_DIR)/jni \ + $(REACT_COMMON_DIR)/callinvoker \ + $(REACT_COMMON_DIR)/jsi \ + $(REACT_COMMON_DIR)/react/nativemodule/core \ + $(REACT_COMMON_DIR)/react/nativemodule/core/platform/android +LOCAL_SHARED_LIBRARIES := libfolly_json +include $(PREBUILT_SHARED_LIBRARY) + +# turbomodulejsijni +include $(CLEAR_VARS) +LOCAL_MODULE := turbomodulejsijni +LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libturbomodulejsijni.so +LOCAL_EXPORT_C_INCLUDES := \ + $(REACT_ANDROID_SRC_DIR)/java/com/facebook/react/turbomodule/core/jni +include $(PREBUILT_SHARED_LIBRARY) + +# react_codegen_reactandroidspec +include $(CLEAR_VARS) +LOCAL_MODULE := react_codegen_reactandroidspec +LOCAL_SRC_FILES := $(REACT_NDK_EXPORT_DIR)/$(TARGET_ARCH_ABI)/libreact_codegen_reactandroidspec.so +LOCAL_EXPORT_C_INCLUDES := \ + $(REACT_GENERATED_SRC_DIR)/codegen/jni +include $(PREBUILT_SHARED_LIBRARY) + +# fbjni +include $(FIRST_PARTY_NDK_DIR)/fbjni/Android.mk diff --git a/ReactAndroid/build.gradle b/ReactAndroid/build.gradle index 1189ead6e9a4c4..1c0a3a0f9b301a 100644 --- a/ReactAndroid/build.gradle +++ b/ReactAndroid/build.gradle @@ -33,6 +33,9 @@ def thirdPartyNdkDir = new File("$buildDir/third-party-ndk") // - glog-0.3.5 def dependenciesPath = System.getenv("REACT_NATIVE_DEPENDENCIES") +// The 'USE_CODEGEN' environment variable will use codegen and compile the output. +def enableCodegen = (System.getenv('USE_CODEGEN') ?: '0').toBoolean() + // The 'USE_FABRIC' environment variable will build Fabric C++ code into the bundle // USE_FABRIC=0 will build RN excluding fabric // USE_FABRIC=1 will build RN including fabric @@ -301,6 +304,7 @@ def getNdkBuildFullPath() { def buildReactNdkLib = tasks.register("buildReactNdkLib", Exec) { dependsOn(prepareJSC, prepareHermes, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog, extractAARHeaders, extractJNIFiles) + dependsOn("generateCodegenArtifactsFromSchema"); inputs.dir("$projectDir/../ReactCommon") inputs.dir("src/main/jni") @@ -314,8 +318,10 @@ def buildReactNdkLib = tasks.register("buildReactNdkLib", Exec) { "NDK_LIBS_OUT=$buildDir/react-ndk/all", "THIRD_PARTY_NDK_DIR=$thirdPartyNdkDir", "REACT_COMMON_DIR=$projectDir/../ReactCommon", + "REACT_GENERATED_SRC_DIR=$buildDir/generated/source", "REACT_SRC_DIR=$projectDir/src/main/java/com/facebook/react", "BUILD_FABRIC=$enableFabric", + "USE_CODEGEN=$enableCodegen", "-C", file("src/main/jni/react/jni").absolutePath, "--jobs", project.findProperty("jobs") ?: Runtime.runtime.availableProcessors() ) @@ -471,7 +477,6 @@ dependencies { apply(from: "release.gradle") react { - enableCodegen = System.getenv("USE_CODEGEN") ?: false jsRootDir = file("../Libraries") reactNativeRootDir = file("$rootDir") useJavaGenerator = System.getenv("USE_CODEGEN_JAVAPOET") ?: false diff --git a/ReactAndroid/gradle.properties b/ReactAndroid/gradle.properties index 983c476d9f6e99..3997eb422934a9 100644 --- a/ReactAndroid/gradle.properties +++ b/ReactAndroid/gradle.properties @@ -8,7 +8,7 @@ POM_PACKAGING=aar MOCKITO_CORE_VERSION=2.26.0 POWERMOCK_VERSION=2.0.2 -ROBOLECTRIC_VERSION=4.3.1 +ROBOLECTRIC_VERSION=4.4 JUNIT_VERSION=4.12 FEST_ASSERT_CORE_VERSION=2.0M10 diff --git a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/BUCK b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/BUCK index 94067e4f20f33d..49f617762e45c9 100644 --- a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/BUCK @@ -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 [], diff --git a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeImageStoreAndroidSpec.java b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeImageStoreAndroidSpec.java new file mode 100644 index 00000000000000..f3c53ac8544ac5 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeImageStoreAndroidSpec.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + *

This source code is licensed under the MIT license found in the LICENSE file in the root + * directory of this source tree. + * + *

Generated by an internal genrule from Flow types. + * + * @generated + * @nolint + */ + +package com.facebook.fbreact.specs; + +import com.facebook.react.bridge.Callback; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReactModuleWithSpec; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; + +public abstract class NativeImageStoreAndroidSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule { + public NativeImageStoreAndroidSpec(ReactApplicationContext reactContext) { + super(reactContext); + } + + @ReactMethod + public abstract void getBase64ForTag(String uri, Callback successCallback, + Callback errorCallback); +} diff --git a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeImageStoreIOSSpec.java b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeImageStoreIOSSpec.java new file mode 100644 index 00000000000000..10455469ccec72 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/NativeImageStoreIOSSpec.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + *

This source code is licensed under the MIT license found in the LICENSE file in the root + * directory of this source tree. + * + *

Generated by an internal genrule from Flow types. + * + * @generated + * @nolint + */ + +package com.facebook.fbreact.specs; + +import com.facebook.react.bridge.Callback; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReactModuleWithSpec; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; + +public abstract class NativeImageStoreIOSSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule { + public NativeImageStoreIOSSpec(ReactApplicationContext reactContext) { + super(reactContext); + } + + @ReactMethod + public abstract void addImageFromBase64(String base64ImageData, Callback successCallback, + Callback errorCallback); + + @ReactMethod + public abstract void getBase64ForTag(String uri, Callback successCallback, + Callback errorCallback); + + @ReactMethod + public abstract void hasImageForTag(String uri, Callback callback); + + @ReactMethod + public abstract void removeImageForTag(String uri); +} diff --git a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/jni/FBReactNativeSpec-generated.cpp b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/jni/FBReactNativeSpec-generated.cpp index 85c52d7e79592a..e8fdde414ba7d9 100644 --- a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/jni/FBReactNativeSpec-generated.cpp +++ b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/jni/FBReactNativeSpec-generated.cpp @@ -1416,36 +1416,56 @@ namespace facebook { namespace react { - static facebook::jsi::Value __hostFunction_NativeImageStoreSpecJSI_getBase64ForTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + static facebook::jsi::Value __hostFunction_NativeImageStoreAndroidSpecJSI_getBase64ForTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { return static_cast(turboModule).invokeJavaMethod(rt, VoidKind, "getBase64ForTag", "(Ljava/lang/String;Lcom/facebook/react/bridge/Callback;Lcom/facebook/react/bridge/Callback;)V", args, count); } - static facebook::jsi::Value __hostFunction_NativeImageStoreSpecJSI_hasImageForTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + + NativeImageStoreAndroidSpecJSI::NativeImageStoreAndroidSpecJSI(const JavaTurboModule::InitParams ¶ms) + : JavaTurboModule(params) { + + methodMap_["getBase64ForTag"] = MethodMetadata {3, __hostFunction_NativeImageStoreAndroidSpecJSI_getBase64ForTag}; + + + + } + + } // namespace react +} // namespace facebook +namespace facebook { + namespace react { + + + static facebook::jsi::Value __hostFunction_NativeImageStoreIOSSpecJSI_getBase64ForTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeJavaMethod(rt, VoidKind, "getBase64ForTag", "(Ljava/lang/String;Lcom/facebook/react/bridge/Callback;Lcom/facebook/react/bridge/Callback;)V", args, count); + } + + static facebook::jsi::Value __hostFunction_NativeImageStoreIOSSpecJSI_hasImageForTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { return static_cast(turboModule).invokeJavaMethod(rt, VoidKind, "hasImageForTag", "(Ljava/lang/String;Lcom/facebook/react/bridge/Callback;)V", args, count); } - static facebook::jsi::Value __hostFunction_NativeImageStoreSpecJSI_removeImageForTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + static facebook::jsi::Value __hostFunction_NativeImageStoreIOSSpecJSI_removeImageForTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { return static_cast(turboModule).invokeJavaMethod(rt, VoidKind, "removeImageForTag", "(Ljava/lang/String;)V", args, count); } - static facebook::jsi::Value __hostFunction_NativeImageStoreSpecJSI_addImageFromBase64(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + static facebook::jsi::Value __hostFunction_NativeImageStoreIOSSpecJSI_addImageFromBase64(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { return static_cast(turboModule).invokeJavaMethod(rt, VoidKind, "addImageFromBase64", "(Ljava/lang/String;Lcom/facebook/react/bridge/Callback;Lcom/facebook/react/bridge/Callback;)V", args, count); } - NativeImageStoreSpecJSI::NativeImageStoreSpecJSI(const JavaTurboModule::InitParams ¶ms) + NativeImageStoreIOSSpecJSI::NativeImageStoreIOSSpecJSI(const JavaTurboModule::InitParams ¶ms) : JavaTurboModule(params) { - methodMap_["getBase64ForTag"] = MethodMetadata {3, __hostFunction_NativeImageStoreSpecJSI_getBase64ForTag}; + methodMap_["getBase64ForTag"] = MethodMetadata {3, __hostFunction_NativeImageStoreIOSSpecJSI_getBase64ForTag}; - methodMap_["hasImageForTag"] = MethodMetadata {2, __hostFunction_NativeImageStoreSpecJSI_hasImageForTag}; + methodMap_["hasImageForTag"] = MethodMetadata {2, __hostFunction_NativeImageStoreIOSSpecJSI_hasImageForTag}; - methodMap_["removeImageForTag"] = MethodMetadata {1, __hostFunction_NativeImageStoreSpecJSI_removeImageForTag}; + methodMap_["removeImageForTag"] = MethodMetadata {1, __hostFunction_NativeImageStoreIOSSpecJSI_removeImageForTag}; - methodMap_["addImageFromBase64"] = MethodMetadata {3, __hostFunction_NativeImageStoreSpecJSI_addImageFromBase64}; + methodMap_["addImageFromBase64"] = MethodMetadata {3, __hostFunction_NativeImageStoreIOSSpecJSI_addImageFromBase64}; diff --git a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/jni/FBReactNativeSpec.h b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/jni/FBReactNativeSpec.h index 940c22e2f62c94..49f97cc402c4d2 100644 --- a/ReactAndroid/src/main/java/com/facebook/fbreact/specs/jni/FBReactNativeSpec.h +++ b/ReactAndroid/src/main/java/com/facebook/fbreact/specs/jni/FBReactNativeSpec.h @@ -440,12 +440,26 @@ namespace facebook { namespace facebook { namespace react { /** - * C++ class for module 'ImageStore' + * C++ class for module 'ImageStoreAndroid' */ - class JSI_EXPORT NativeImageStoreSpecJSI : public JavaTurboModule { + class JSI_EXPORT NativeImageStoreAndroidSpecJSI : public JavaTurboModule { public: - NativeImageStoreSpecJSI(const JavaTurboModule::InitParams ¶ms); + NativeImageStoreAndroidSpecJSI(const JavaTurboModule::InitParams ¶ms); + + }; + } // namespace react +} // namespace facebook + +namespace facebook { + namespace react { + /** + * C++ class for module 'ImageStoreIOS' + */ + + class JSI_EXPORT NativeImageStoreIOSSpecJSI : public JavaTurboModule { + public: + NativeImageStoreIOSSpecJSI(const JavaTurboModule::InitParams ¶ms); }; } // namespace react diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java b/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java index 2af6a23fbd977d..c21bffc720a519 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java @@ -382,8 +382,6 @@ public void startReactApplication( mReactInstanceManager.createReactContextInBackground(); - attachToReactInstanceManager(); - } finally { Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactMarkerConstants.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactMarkerConstants.java index dd08b9a8eea480..8c5f38d4d89c06 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactMarkerConstants.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactMarkerConstants.java @@ -108,5 +108,8 @@ public enum ReactMarkerConstants { FABRIC_BATCH_EXECUTION_START, FABRIC_BATCH_EXECUTION_END, FABRIC_UPDATE_UI_MAIN_THREAD_START, - FABRIC_UPDATE_UI_MAIN_THREAD_END + FABRIC_UPDATE_UI_MAIN_THREAD_END, + // New markers used by bridgeless RN below this line + REACT_INSTANCE_INIT_START, + REACT_INSTANCE_INIT_END } diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK b/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK index 9ebcd431161f44..a1882a6cdbd5d1 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK @@ -39,6 +39,7 @@ rn_android_library( react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/uimanager:uimanager"), react_native_target("java/com/facebook/react/views/view:view"), + react_native_target("java/com/facebook/react/views/text:text"), react_native_target("java/com/facebook/react/touch:touch"), ], exported_deps = [ diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index bf95f6e7da3376..3e5b6c37aaba5f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -37,6 +37,7 @@ import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.ReactRootView; import com.facebook.react.bridge.LifecycleEventListener; +import com.facebook.react.bridge.NativeArray; import com.facebook.react.bridge.NativeMap; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; @@ -74,6 +75,7 @@ import com.facebook.react.fabric.mounting.mountitems.UpdateStateMountItem; import com.facebook.react.modules.core.ReactChoreographer; import com.facebook.react.modules.i18nmanager.I18nUtil; +import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactRoot; import com.facebook.react.uimanager.ReactRootViewTagGenerator; import com.facebook.react.uimanager.StateWrapper; @@ -82,6 +84,7 @@ import com.facebook.react.uimanager.ViewManagerPropertyUpdater; import com.facebook.react.uimanager.ViewManagerRegistry; import com.facebook.react.uimanager.events.EventDispatcher; +import com.facebook.react.views.text.TextLayoutManager; import com.facebook.systrace.Systrace; import java.util.ArrayDeque; import java.util.ArrayList; @@ -443,6 +446,18 @@ private MountItem createBatchMountItem( return new BatchMountItem(rootTag, items, size, commitNumber); } + @DoNotStrip + @SuppressWarnings("unused") + private NativeArray measureLines( + ReadableMap attributedString, ReadableMap paragraphAttributes, float width, float height) { + return (NativeArray) + TextLayoutManager.measureLines( + mReactApplicationContext, + attributedString, + paragraphAttributes, + PixelUtil.toPixelFromDIP(width)); + } + @DoNotStrip @SuppressWarnings("unused") private long measure( diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp index 9fb51e79fd7116..134c1c5de27daf 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp @@ -428,11 +428,12 @@ local_ref 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(jint, jint, jint, jint, jint)>( "updatePaddingMountItem"); @@ -441,12 +442,12 @@ local_ref createUpdatePaddingMountItem( auto pointScaleFactor = layoutMetrics.pointScaleFactor; auto contentInsets = layoutMetrics.contentInsets; - int left = round(contentInsets.left * pointScaleFactor); - int top = round(contentInsets.top * pointScaleFactor); - int right = round(contentInsets.right * pointScaleFactor); - int bottom = round(contentInsets.bottom * pointScaleFactor); + int left = floor(contentInsets.left * pointScaleFactor); + int top = floor(contentInsets.top * pointScaleFactor); + int right = floor(contentInsets.right * pointScaleFactor); + int bottom = floor(contentInsets.bottom * pointScaleFactor); - return updateLayoutInstruction( + return updatePaddingInstruction( javaUIManager, newChildShadowView.tag, left, top, right, bottom); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java index 4629668d4944b4..632f57af48fa38 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java @@ -63,9 +63,9 @@ public MountingManager(@NonNull ViewManagerRegistry viewManagerRegistry) { mViewManagerRegistry = viewManagerRegistry; } - private static void logViewHierarchy(ViewGroup parent) { + private static void logViewHierarchy(ViewGroup parent, boolean recurse) { int parentTag = parent.getId(); - FLog.e(TAG, " "); + FLog.e(TAG, " "); for (int i = 0; i < parent.getChildCount(); i++) { FLog.e( TAG, @@ -73,11 +73,24 @@ private static void logViewHierarchy(ViewGroup parent) { + i + " tag=" + parent.getChildAt(i).getId() - + " toString=" - + parent.getChildAt(i).toString() + + " class=" + + parent.getChildAt(i).getClass().toString() + ">"); } FLog.e(TAG, " "); + + if (recurse) { + FLog.e(TAG, "Displaying Ancestors:"); + ViewParent ancestor = parent.getParent(); + while (ancestor != null) { + ViewGroup ancestorViewGroup = (ancestor instanceof ViewGroup ? (ViewGroup) ancestor : null); + int ancestorId = ancestorViewGroup == null ? View.NO_ID : ancestorViewGroup.getId(); + FLog.e( + TAG, + ""); + ancestor = ancestor.getParent(); + } + } } /** @@ -218,7 +231,7 @@ public void addViewAt(final int parentTag, final int tag, final int index) { // Display children before inserting if (SHOW_CHANGED_VIEW_HIERARCHIES) { FLog.e(TAG, "addViewAt: [" + tag + "] -> [" + parentTag + "] idx: " + index + " BEFORE"); - logViewHierarchy(parentView); + logViewHierarchy(parentView, false); } try { @@ -243,7 +256,7 @@ public void addViewAt(final int parentTag, final int tag, final int index) { public void run() { FLog.e( TAG, "addViewAt: [" + tag + "] -> [" + parentTag + "] idx: " + index + " AFTER"); - logViewHierarchy(parentView); + logViewHierarchy(parentView, false); } }); } @@ -359,7 +372,7 @@ public void removeViewAt(final int tag, final int parentTag, final int index) { if (SHOW_CHANGED_VIEW_HIERARCHIES) { // Display children before deleting any FLog.e(TAG, "removeViewAt: [" + tag + "] -> [" + parentTag + "] idx: " + index + " BEFORE"); - logViewHierarchy(parentView); + logViewHierarchy(parentView, false); } ViewGroupManager viewGroupManager = getViewGroupManager(viewState); @@ -398,8 +411,9 @@ public void removeViewAt(final int tag, final int parentTag, final int index) { return; } + logViewHierarchy(parentView, true); throw new IllegalStateException( - "Tried to delete view [" + "Tried to remove view [" + tag + "] of parent [" + parentTag @@ -431,6 +445,8 @@ public void removeViewAt(final int tag, final int parentTag, final int index) { // enough that we shouldn't try to change this invariant, without a lot of thought. int childCount = viewGroupManager.getChildCount(parentView); + logViewHierarchy(parentView, true); + throw new IllegalStateException( "Cannot remove child at index " + index @@ -451,7 +467,7 @@ public void run() { FLog.e( TAG, "removeViewAt: [" + tag + "] -> [" + parentTag + "] idx: " + index + " AFTER"); - logViewHierarchy(parentView); + logViewHierarchy(parentView, false); } }); } @@ -577,12 +593,20 @@ public void deleteView(int reactTag) { 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); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/BatchMountItem.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/BatchMountItem.java index d685be1ae02868..fc908e2deedc29 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/BatchMountItem.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/BatchMountItem.java @@ -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 @@ private void endMarkers() { 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(); diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/camera/ImageStoreManager.java b/ReactAndroid/src/main/java/com/facebook/react/modules/camera/ImageStoreManager.java index 8bf377cfb4cc13..0752be6e554cae 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/camera/ImageStoreManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/camera/ImageStoreManager.java @@ -12,7 +12,7 @@ import android.os.AsyncTask; import android.util.Base64; import android.util.Base64OutputStream; -import com.facebook.fbreact.specs.NativeImageStoreSpec; +import com.facebook.fbreact.specs.NativeImageStoreAndroidSpec; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.GuardedAsyncTask; import com.facebook.react.bridge.ReactApplicationContext; @@ -25,7 +25,7 @@ import java.io.InputStream; @ReactModule(name = ImageStoreManager.NAME) -public class ImageStoreManager extends NativeImageStoreSpec { +public class ImageStoreManager extends NativeImageStoreAndroidSpec { public static final String NAME = "ImageStoreManager"; private static final int BUFFER_SIZE = 8192; @@ -105,20 +105,4 @@ private static void closeQuietly(Closeable closeable) { // shhh } } - - @Override - public void hasImageForTag(String uri, Callback callback) { - // iOS only - } - - @Override - public void removeImageForTag(String uri) { - // iOS only - } - - @Override - public void addImageFromBase64( - String base64ImageData, Callback successCallback, Callback errorCallback) { - // iOS only - } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/DialogModule.java b/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/DialogModule.java index e3a25072156aa6..664b359a27b63b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/DialogModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/dialog/DialogModule.java @@ -129,7 +129,8 @@ public AlertFragmentListener(Callback callback) { @Override public void onClick(DialogInterface dialog, int which) { if (!mCallbackConsumed) { - if (getReactApplicationContext().hasActiveCatalystInstance()) { + if (getReactApplicationContext().isBridgeless() + || getReactApplicationContext().hasActiveCatalystInstance()) { mCallback.invoke(ACTION_BUTTON_CLICKED, which); mCallbackConsumed = true; } @@ -139,7 +140,8 @@ public void onClick(DialogInterface dialog, int which) { @Override public void onDismiss(DialogInterface dialog) { if (!mCallbackConsumed) { - if (getReactApplicationContext().hasActiveCatalystInstance()) { + if (getReactApplicationContext().isBridgeless() + || getReactApplicationContext().hasActiveCatalystInstance()) { mCallback.invoke(ACTION_DISMISSED); mCallbackConsumed = true; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/Android.mk b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/Android.mk index 80ba4096da6480..955cc86a8b10de 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/Android.mk +++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/Android.mk @@ -5,6 +5,10 @@ LOCAL_PATH := $(call my-dir) +######################### +### callinvokerholder ### +######################### + include $(CLEAR_VARS) # Header search path for all source files in this module. @@ -15,10 +19,10 @@ LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall -LOCAL_STATIC_LIBRARIES = libcallinvoker libreactperfloggerjni - LOCAL_SHARED_LIBRARIES = libfb libfbjni +LOCAL_STATIC_LIBRARIES = libcallinvoker libreactperfloggerjni + # Name of this module. LOCAL_MODULE := callinvokerholder @@ -27,3 +31,31 @@ LOCAL_SRC_FILES := $(LOCAL_PATH)/ReactCommon/CallInvokerHolder.cpp # Build the files in this directory as a shared library include $(BUILD_STATIC_LIBRARY) + +################################## +### react_nativemodule_manager ### +################################## + +include $(CLEAR_VARS) + +# Name of this module. +# TODO: rename to react_nativemodule_manager +LOCAL_MODULE := turbomodulejsijni + +# Header search path for all source files in this module. +LOCAL_C_INCLUDES := $(LOCAL_PATH)/ReactCommon + +# Header search path for modules that depend on this module +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +LOCAL_SHARED_LIBRARIES = libfb libfbjni libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES = libcallinvokerholder libreactperfloggerjni + +# Compile all local c++ files +LOCAL_SRC_FILES := $(LOCAL_PATH)/ReactCommon/TurboModuleManager.cpp $(LOCAL_PATH)/ReactCommon/OnLoad.cpp + +# Build the files in this directory as a shared library +include $(BUILD_SHARED_LIBRARY) diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK index 84258474072b91..934dd73ac5d5c7 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/BUCK @@ -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"), ], ) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java index e5c42e059905e6..ffc46232aa9994 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java @@ -13,7 +13,7 @@ import com.facebook.react.bridge.ReadableArray; import com.facebook.react.uimanager.SimpleViewManager; import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.UIManagerModule; +import com.facebook.react.uimanager.UIManagerHelper; import com.facebook.react.uimanager.ViewProps; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.events.EventDispatcher; @@ -62,13 +62,9 @@ protected void onAfterUpdateTransaction(ReactPicker view) { @Override protected void addEventEmitters(final ThemedReactContext reactContext, final ReactPicker picker) { - UIManagerModule uiManager = reactContext.getNativeModule(UIManagerModule.class); - - if (uiManager == null) { - return; - } - - picker.setOnSelectListener(new PickerEventEmitter(picker, uiManager.getEventDispatcher())); + picker.setOnSelectListener( + new PickerEventEmitter( + picker, UIManagerHelper.getEventDispatcherForReactTag(reactContext, picker.getId()))); } @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java index 61b1a712332a5d..b632671b712b6c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java @@ -266,6 +266,7 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { int scrollToY = pendingContentOffsetY != UNSET_CONTENT_OFFSET ? pendingContentOffsetY : getScrollY(); reactScrollTo(scrollToX, scrollToY); + ReactScrollViewHelper.emitLayoutEvent(this); } /** diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java index 6497c6bce989b2..118579c886f109 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java @@ -223,6 +223,7 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { int scrollToY = pendingContentOffsetY != UNSET_CONTENT_OFFSET ? pendingContentOffsetY : getScrollY(); reactScrollTo(scrollToX, scrollToY); + ReactScrollViewHelper.emitLayoutEvent(this); } @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java index 5183f24c44bd80..c10a38ebdb02aa 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java @@ -14,6 +14,8 @@ import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReactContext; import com.facebook.react.uimanager.UIManagerHelper; +import java.util.ArrayList; +import java.util.List; /** Helper class that deals with emitting Scroll Events. */ public class ReactScrollViewHelper { @@ -23,6 +25,16 @@ public class ReactScrollViewHelper { public static final String AUTO = "auto"; public static final String OVER_SCROLL_NEVER = "never"; + public interface ScrollListener { + void onScroll( + ViewGroup scrollView, ScrollEventType scrollEventType, float xVelocity, float yVelocity); + + void onLayout(ViewGroup scrollView); + } + + // Support global native listeners for scroll events + private static List sScrollListeners = new ArrayList<>(); + // If all else fails, this is the hardcoded value in OverScroller.java, in AOSP. // The default is defined here (as of this diff): // https://android.googlesource.com/platform/frameworks/base/+/ae5bcf23b5f0875e455790d6af387184dbd009c1/core/java/android/widget/OverScroller.java#44 @@ -64,6 +76,10 @@ private static void emitScrollEvent( return; } + for (ScrollListener scrollListener : sScrollListeners) { + scrollListener.onScroll(scrollView, scrollEventType, xVelocity, yVelocity); + } + ReactContext reactContext = (ReactContext) scrollView.getContext(); UIManagerHelper.getEventDispatcherForReactTag(reactContext, scrollView.getId()) .dispatchEvent( @@ -80,6 +96,13 @@ private static void emitScrollEvent( scrollView.getHeight())); } + /** This is only for Java listeners. onLayout events emitted to JS are handled elsewhere. */ + public static void emitLayoutEvent(ViewGroup scrollView) { + for (ScrollListener scrollListener : sScrollListeners) { + scrollListener.onLayout(scrollView); + } + } + public static int parseOverScrollMode(String jsOverScrollMode) { if (jsOverScrollMode == null || jsOverScrollMode.equals(AUTO)) { return View.OVER_SCROLL_IF_CONTENT_SCROLLS; @@ -130,4 +153,12 @@ public void startScroll(int startX, int startY, int dx, int dy, int duration) { mScrollAnimationDuration = duration; } } + + public static void addScrollListener(ScrollListener listener) { + sScrollListeners.add(listener); + } + + public static void removeScrollListener(ScrollListener listener) { + sScrollListeners.remove(listener); + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java index ac84caae1e07d9..5dbd0796e53363 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextShadowNode.java @@ -144,6 +144,9 @@ public long measure( } } + if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.Q) { + layoutWidth = (float) Math.ceil(layoutWidth); + } float layoutHeight = height; if (heightMode != YogaMeasureMode.EXACTLY) { layoutHeight = layout.getLineBottom(lineCount - 1); diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java index c1143b430830e7..34ad7c4e9af018 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java @@ -27,6 +27,7 @@ import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableNativeMap; +import com.facebook.react.bridge.WritableArray; import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactAccessibilityDelegate; @@ -257,51 +258,19 @@ private static Spannable createSpannableFromAttributedString( return sb; } - public static long measureText( - Context context, - ReadableMap attributedString, - ReadableMap paragraphAttributes, + private static Layout createLayout( + Spannable text, + BoringLayout.Metrics boring, float width, YogaMeasureMode widthYogaMeasureMode, - float height, - YogaMeasureMode heightYogaMeasureMode, - ReactTextViewManagerCallback reactTextViewManagerCallback, - @Nullable float[] attachmentsPositions) { - - // TODO(5578671): Handle text direction (see View#getTextDirectionHeuristic) + boolean includeFontPadding, + int textBreakStrategy) { + Layout layout; + int spanLength = text.length(); + boolean unconstrainedWidth = widthYogaMeasureMode == YogaMeasureMode.UNDEFINED || width < 0; TextPaint textPaint = sTextPaintInstance; - Spannable text; - if (attributedString.hasKey("cacheId")) { - int cacheId = attributedString.getInt("cacheId"); - if (sTagToSpannableCache.containsKey(cacheId)) { - text = sTagToSpannableCache.get(cacheId); - } else { - return 0; - } - } else { - text = getOrCreateSpannableForText(context, attributedString, reactTextViewManagerCallback); - } - - int textBreakStrategy = - TextAttributeProps.getTextBreakStrategy( - paragraphAttributes.getString(TEXT_BREAK_STRATEGY_KEY)); - boolean includeFontPadding = - paragraphAttributes.hasKey(INCLUDE_FONT_PADDING_KEY) - ? paragraphAttributes.getBoolean(INCLUDE_FONT_PADDING_KEY) - : DEFAULT_INCLUDE_FONT_PADDING; - - if (text == null) { - throw new IllegalStateException("Spannable element has not been prepared in onBeforeLayout"); - } - - BoringLayout.Metrics boring = BoringLayout.isBoring(text, textPaint); float desiredWidth = boring == null ? Layout.getDesiredWidth(text, textPaint) : Float.NaN; - // technically, width should never be negative, but there is currently a bug in - boolean unconstrainedWidth = widthYogaMeasureMode == YogaMeasureMode.UNDEFINED || width < 0; - - Layout layout; - int spanLength = text.length(); if (boring == null && (unconstrainedWidth || (!YogaConstants.isUndefined(desiredWidth) && desiredWidth <= width))) { @@ -357,16 +326,70 @@ public static long measureText( 0.f, includeFontPadding); } else { - layout = + StaticLayout.Builder builder = StaticLayout.Builder.obtain(text, 0, spanLength, textPaint, (int) width) .setAlignment(Layout.Alignment.ALIGN_NORMAL) .setLineSpacing(0.f, 1.f) .setIncludePad(includeFontPadding) .setBreakStrategy(textBreakStrategy) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) - .build(); + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + builder.setUseLineSpacingFromFallbacks(true); + } + + layout = builder.build(); } } + return layout; + } + + public static long measureText( + Context context, + ReadableMap attributedString, + ReadableMap paragraphAttributes, + float width, + YogaMeasureMode widthYogaMeasureMode, + float height, + YogaMeasureMode heightYogaMeasureMode, + ReactTextViewManagerCallback reactTextViewManagerCallback, + @Nullable float[] attachmentsPositions) { + + // TODO(5578671): Handle text direction (see View#getTextDirectionHeuristic) + TextPaint textPaint = sTextPaintInstance; + Spannable text; + if (attributedString.hasKey("cacheId")) { + int cacheId = attributedString.getInt("cacheId"); + if (sTagToSpannableCache.containsKey(cacheId)) { + text = sTagToSpannableCache.get(cacheId); + } else { + return 0; + } + } else { + text = getOrCreateSpannableForText(context, attributedString, reactTextViewManagerCallback); + } + + int textBreakStrategy = + TextAttributeProps.getTextBreakStrategy( + paragraphAttributes.getString(TEXT_BREAK_STRATEGY_KEY)); + boolean includeFontPadding = + paragraphAttributes.hasKey(INCLUDE_FONT_PADDING_KEY) + ? paragraphAttributes.getBoolean(INCLUDE_FONT_PADDING_KEY) + : DEFAULT_INCLUDE_FONT_PADDING; + + if (text == null) { + throw new IllegalStateException("Spannable element has not been prepared in onBeforeLayout"); + } + + BoringLayout.Metrics boring = BoringLayout.isBoring(text, textPaint); + float desiredWidth = boring == null ? Layout.getDesiredWidth(text, textPaint) : Float.NaN; + + // technically, width should never be negative, but there is currently a bug in + boolean unconstrainedWidth = widthYogaMeasureMode == YogaMeasureMode.UNDEFINED || width < 0; + + Layout layout = + createLayout( + text, boring, width, widthYogaMeasureMode, includeFontPadding, textBreakStrategy); int maximumNumberOfLines = paragraphAttributes.hasKey(MAXIMUM_NUMBER_OF_LINES_KEY) @@ -408,9 +431,9 @@ public static long measureText( // follows a similar logic than used in pre-fabric (see ReactTextView.onLayout method). int attachmentIndex = 0; int lastAttachmentFoundInSpan; - for (int i = 0; i < spanLength; i = lastAttachmentFoundInSpan) { + for (int i = 0; i < text.length(); i = lastAttachmentFoundInSpan) { lastAttachmentFoundInSpan = - text.nextSpanTransition(i, spanLength, TextInlineViewPlaceholderSpan.class); + text.nextSpanTransition(i, text.length(), TextInlineViewPlaceholderSpan.class); TextInlineViewPlaceholderSpan[] placeholders = text.getSpans(i, lastAttachmentFoundInSpan, TextInlineViewPlaceholderSpan.class); for (TextInlineViewPlaceholderSpan placeholder : placeholders) { @@ -432,7 +455,7 @@ public static long measureText( // There's a bug on Samsung devices where calling getPrimaryHorizontal on // the last offset in the layout will result in an endless loop. Work around // this bug by avoiding getPrimaryHorizontal in that case. - if (start == spanLength - 1) { + if (start == text.length() - 1) { placeholderLeftPosition = isRtlParagraph // Equivalent to `layout.getLineLeft(line)` but `getLineLeft` returns incorrect @@ -502,6 +525,29 @@ public static long measureText( return YogaMeasureOutput.make(widthInSP, heightInSP); } + public static WritableArray measureLines( + @NonNull Context context, + ReadableMap attributedString, + ReadableMap paragraphAttributes, + float width) { + TextPaint textPaint = sTextPaintInstance; + Spannable text = getOrCreateSpannableForText(context, attributedString, null); + BoringLayout.Metrics boring = BoringLayout.isBoring(text, textPaint); + + int textBreakStrategy = + TextAttributeProps.getTextBreakStrategy( + paragraphAttributes.getString(TEXT_BREAK_STRATEGY_KEY)); + boolean includeFontPadding = + paragraphAttributes.hasKey(INCLUDE_FONT_PADDING_KEY) + ? paragraphAttributes.getBoolean(INCLUDE_FONT_PADDING_KEY) + : DEFAULT_INCLUDE_FONT_PADDING; + + Layout layout = + createLayout( + text, boring, width, YogaMeasureMode.EXACTLY, includeFontPadding, textBreakStrategy); + return FontMetricsUtil.getFontMetrics(text, layout, sTextPaintInstance, context); + } + // TODO T31905686: This class should be private public static class SetSpanOperation { protected int start, end; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java index 5c013a472b7f7e..c2fa76c75ca69b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java @@ -477,21 +477,17 @@ public void setCursorColor(ReactEditText view, @Nullable Integer color) { } } + private static boolean shouldHideCursorForEmailTextInput() { + String manufacturer = Build.MANUFACTURER.toLowerCase(); + return (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q && manufacturer.contains("xiaomi")); + } + @ReactProp(name = "caretHidden", defaultBoolean = false) public void setCaretHidden(ReactEditText view, boolean caretHidden) { - // Set cursor's visibility to False to fix a crash on some Xiaomi devices with Android Q. This - // crash happens when focusing on a email EditText, during which a prompt will be triggered but - // the system fail to locate it properly. Here is an example post discussing about this - // issue: https://github.com/facebook/react-native/issues/27204 - String manufacturer = Build.MANUFACTURER.toLowerCase(); - if ((view.getInputType() == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS - || view.getInputType() == InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS) - && Build.VERSION.SDK_INT == Build.VERSION_CODES.Q - && manufacturer.contains("xiaomi")) { - view.setCursorVisible(false); + if (view.getStagedInputType() == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS + && shouldHideCursorForEmailTextInput()) { return; } - view.setCursorVisible(!caretHidden); } @@ -764,6 +760,15 @@ public void setKeyboardType(ReactEditText view, @Nullable String keyboardType) { flagsToSet = INPUT_TYPE_KEYBOARD_DECIMAL_PAD; } else if (KEYBOARD_TYPE_EMAIL_ADDRESS.equalsIgnoreCase(keyboardType)) { flagsToSet = InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS | InputType.TYPE_CLASS_TEXT; + + // Set cursor's visibility to False to fix a crash on some Xiaomi devices with Android Q. This + // crash happens when focusing on a email EditText, during which a prompt will be triggered + // but + // the system fail to locate it properly. Here is an example post discussing about this + // issue: https://github.com/facebook/react-native/issues/27204 + if (shouldHideCursorForEmailTextInput()) { + view.setCursorVisible(false); + } } else if (KEYBOARD_TYPE_PHONE_PAD.equalsIgnoreCase(keyboardType)) { flagsToSet = InputType.TYPE_CLASS_PHONE; } else if (KEYBOARD_TYPE_VISIBLE_PASSWORD.equalsIgnoreCase(keyboardType)) { diff --git a/ReactAndroid/src/main/jni/Application.mk b/ReactAndroid/src/main/jni/Application.mk index 84eae80adf4c9d..8bcc6cfa8b8817 100644 --- a/ReactAndroid/src/main/jni/Application.mk +++ b/ReactAndroid/src/main/jni/Application.mk @@ -22,7 +22,7 @@ APP_MK_DIR := $(dir $(lastword $(MAKEFILE_LIST))) # Where are APP_MK_DIR, THIRD_PARTY_NDK_DIR, etc. defined? # The directories inside NDK_MODULE_PATH (ex: APP_MK_DIR, THIRD_PARTY_NDK_DIR, # etc.) are defined inside build.gradle. -NDK_MODULE_PATH := $(APP_MK_DIR)$(HOST_DIRSEP)$(THIRD_PARTY_NDK_DIR)$(HOST_DIRSEP)$(REACT_COMMON_DIR)$(HOST_DIRSEP)$(APP_MK_DIR)first-party$(HOST_DIRSEP)$(REACT_SRC_DIR) +NDK_MODULE_PATH := $(APP_MK_DIR)$(HOST_DIRSEP)$(THIRD_PARTY_NDK_DIR)$(HOST_DIRSEP)$(REACT_COMMON_DIR)$(HOST_DIRSEP)$(APP_MK_DIR)first-party$(HOST_DIRSEP)$(REACT_SRC_DIR)$(HOST_DIRSEP)$(REACT_GENERATED_SRC_DIR) APP_STL := c++_shared diff --git a/ReactAndroid/src/main/jni/react/jni/Android.mk b/ReactAndroid/src/main/jni/react/jni/Android.mk index 70b65082fac834..b4ba8ddce6a245 100644 --- a/ReactAndroid/src/main/jni/react/jni/Android.mk +++ b/ReactAndroid/src/main/jni/react/jni/Android.mk @@ -80,7 +80,7 @@ LOCAL_LDLIBS += -landroid LOCAL_SHARED_LIBRARIES := libreactnativeutilsjni libfolly_json libfb libfbjni libglog_init libyoga # The static libraries (.a files) that this module depends on. -LOCAL_STATIC_LIBRARIES := libreactnative libcallinvokerholder libruntimeexecutor +LOCAL_STATIC_LIBRARIES := libreactnative libruntimeexecutor libcallinvokerholder # Name of this module. # @@ -128,8 +128,11 @@ $(call import-module,callinvoker) $(call import-module,reactperflogger) $(call import-module,hermes) $(call import-module,runtimeexecutor) +$(call import-module,react/nativemodule/core) include $(REACT_SRC_DIR)/reactperflogger/jni/Android.mk +# TODO (T48588859): Restructure this target to align with dir structure: "react/nativemodule/..." +# Note: Update this only when ready to minimize breaking changes. include $(REACT_SRC_DIR)/turbomodule/core/jni/Android.mk ifeq ($(BUILD_FABRIC),true) @@ -145,3 +148,7 @@ include $(REACT_SRC_DIR)/jscexecutor/Android.mk include $(REACT_SRC_DIR)/../hermes/reactexecutor/Android.mk include $(REACT_SRC_DIR)/../hermes/instrumentation/Android.mk include $(REACT_SRC_DIR)/modules/blob/jni/Android.mk + +ifeq ($(USE_CODEGEN),true) + include $(REACT_GENERATED_SRC_DIR)/codegen/jni/Android.mk +endif diff --git a/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp b/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp index 7d5b1acf483f88..659c8235a64bbb 100644 --- a/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp +++ b/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp @@ -92,12 +92,6 @@ CatalystInstanceImpl::initHybrid(jni::alias_ref) { CatalystInstanceImpl::CatalystInstanceImpl() : instance_(std::make_unique()) {} -CatalystInstanceImpl::~CatalystInstanceImpl() { - if (moduleMessageQueue_ != NULL) { - moduleMessageQueue_->quitSynchronous(); - } -} - void CatalystInstanceImpl::registerNatives() { registerHybrid({ makeNativeMethod("initHybrid", CatalystInstanceImpl::initHybrid), diff --git a/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h b/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h index dd9990ef090a00..c40a691232c6a2 100644 --- a/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h +++ b/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h @@ -37,7 +37,6 @@ class CatalystInstanceImpl : public jni::HybridClass { "Lcom/facebook/react/bridge/CatalystInstanceImpl;"; static jni::local_ref initHybrid(jni::alias_ref); - ~CatalystInstanceImpl() override; static void registerNatives(); diff --git a/ReactAndroid/src/main/third-party/java/robolectric/4.3.1/BUCK b/ReactAndroid/src/main/third-party/java/robolectric/4.4/BUCK similarity index 80% rename from ReactAndroid/src/main/third-party/java/robolectric/4.3.1/BUCK rename to ReactAndroid/src/main/third-party/java/robolectric/4.4/BUCK index aeca8ed4f5bd3a..9033f5b0ce9705 100644 --- a/ReactAndroid/src/main/third-party/java/robolectric/4.3.1/BUCK +++ b/ReactAndroid/src/main/third-party/java/robolectric/4.4/BUCK @@ -56,8 +56,8 @@ rn_prebuilt_jar( fb_native.remote_file( name = "bouncycastle-binary.jar", - sha1 = "2507204241ab450456bdb8e8c0a8f986e418bd99", - url = "mvn:org.bouncycastle:bcprov-jdk15on:jar:1.59", + sha1 = "320b989112f00a63a3bcfa5a98f31a4f865a20fa", + url = "mvn:org.bouncycastle:bcprov-jdk15on:jar:1.65", ) rn_prebuilt_jar( @@ -79,8 +79,8 @@ rn_prebuilt_jar( fb_native.remote_file( name = "robolectric4.jar", - sha1 = "66e4550b96285eadcb5a45a21ad6fbe8842fa960", - url = "mvn:org.robolectric:robolectric:jar:4.3", + sha1 = "418c5bfae392fdbf71cd463a42a3e8c3b839a924", + url = "mvn:org.robolectric:robolectric:jar:4.4", ) rn_prebuilt_jar( @@ -90,8 +90,8 @@ rn_prebuilt_jar( fb_native.remote_file( name = "robolectric4-annotations.jar", - sha1 = "3db63d633be908a18db18615b594f824c034ae6d", - url = "mvn:org.robolectric:annotations:jar:4.3", + sha1 = "70fc5b1699467dfd7de606fc6c02ff9fc1816d9f", + url = "mvn:org.robolectric:annotations:jar:4.4", ) rn_prebuilt_jar( @@ -101,8 +101,8 @@ rn_prebuilt_jar( fb_native.remote_file( name = "robolectric4-junit.jar", - sha1 = "fcafc9942e8748c8bab832b022672ca21808c492", - url = "mvn:org.robolectric:junit:jar:4.3", + sha1 = "fbcda51d8e6f3a3897ae5cedc7aa481815745290", + url = "mvn:org.robolectric:junit:jar:4.4", ) rn_prebuilt_jar( @@ -112,8 +112,8 @@ rn_prebuilt_jar( fb_native.remote_file( name = "robolectric4-pluginapi.jar", - sha1 = "128acea3aed3bbe36f8fde865f3a26b920237718", - url = "mvn:org.robolectric:pluginapi:jar:4.3", + sha1 = "1ee94260f8c51620a35eac33fc1efc01350c751f", + url = "mvn:org.robolectric:pluginapi:jar:4.4", ) rn_prebuilt_jar( @@ -123,8 +123,8 @@ rn_prebuilt_jar( fb_native.remote_file( name = "robolectric4-plugins-maven-dependency-resolver.jar", - sha1 = "b1ea126cb80dbba0c2947be9234bbe2877ce2a09", - url = "mvn:org.robolectric:plugins-maven-dependency-resolver:jar:4.3", + sha1 = "9241a3c4bd01627447c76d9b67614808c78ffdd9", + url = "mvn:org.robolectric:plugins-maven-dependency-resolver:jar:4.4", ) rn_prebuilt_jar( @@ -134,8 +134,8 @@ rn_prebuilt_jar( fb_native.remote_file( name = "robolectric4-resources.jar", - sha1 = "e40030b0f6808ca378bd2c803713157ee4287ea0", - url = "mvn:org.robolectric:resources:jar:4.3", + sha1 = "a2ee1324bcb62724e6cbfa655bdb5683948a554c", + url = "mvn:org.robolectric:resources:jar:4.4", ) rn_prebuilt_jar( @@ -145,8 +145,8 @@ rn_prebuilt_jar( fb_native.remote_file( name = "robolectric4-sandbox.jar", - sha1 = "2302e406aebab5f6843dbf6c2f21952fa86ec26f", - url = "mvn:org.robolectric:sandbox:jar:4.3", + sha1 = "03cedd73c5aedaf79fb9a593552816c9fb3282f2", + url = "mvn:org.robolectric:sandbox:jar:4.4", ) rn_prebuilt_jar( @@ -156,8 +156,8 @@ rn_prebuilt_jar( fb_native.remote_file( name = "robolectric4-shadowapi.jar", - sha1 = "81dfcf4a45b623b7744e46358d01c7ce054d0fff", - url = "mvn:org.robolectric:shadowapi:jar:4.3", + sha1 = "529649474b53cf8f6f4a483044ade43aebed8a4c", + url = "mvn:org.robolectric:shadowapi:jar:4.4", ) rn_prebuilt_jar( @@ -167,8 +167,8 @@ rn_prebuilt_jar( fb_native.remote_file( name = "robolectric4-shadows-framework.jar", - sha1 = "150103d5732c432906f6130b734e7452855dd67b", - url = "mvn:org.robolectric:shadows-framework:jar:4.3", + sha1 = "90028766e71353ad6f57d7bcb56ac0d861da18c3", + url = "mvn:org.robolectric:shadows-framework:jar:4.4", ) rn_prebuilt_jar( @@ -178,8 +178,8 @@ rn_prebuilt_jar( fb_native.remote_file( name = "robolectric4-utils.jar", - sha1 = "97b0331b67d0e1dc8bf50e570b6feb017f62aed1", - url = "mvn:org.robolectric:utils:jar:4.3", + sha1 = "c54b2638d64e7bd4e1e45c4fe8038305402bd711", + url = "mvn:org.robolectric:utils:jar:4.4", ) rn_prebuilt_jar( @@ -189,8 +189,8 @@ rn_prebuilt_jar( fb_native.remote_file( name = "robolectric4-utils-reflector.jar", - sha1 = "3428887d068b66e33026ac533ae4647355167658", - url = "mvn:org.robolectric:utils-reflector:jar:4.3", + sha1 = "44c40ac0d2ef1e7c8b0f6c4e224ef26d356170f1", + url = "mvn:org.robolectric:utils-reflector:jar:4.4", ) rn_prebuilt_jar( diff --git a/ReactAndroid/src/test/java/com/facebook/react/bridge/BUCK b/ReactAndroid/src/test/java/com/facebook/react/bridge/BUCK index 2721f9cb923f4a..c774e197673e51 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/bridge/BUCK +++ b/ReactAndroid/src/test/java/com/facebook/react/bridge/BUCK @@ -15,7 +15,7 @@ rn_android_library( ], deps = [ react_native_android_toplevel_dep("third-party/java/mockito2:mockito2"), - react_native_dep("third-party/java/robolectric/4.3.1:robolectric"), + react_native_dep("third-party/java/robolectric/4.4:robolectric"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/uimanager:uimanager"), react_native_tests_target("java/org/mockito/configuration:configuration"), diff --git a/ReactCommon/React-Fabric.podspec b/ReactCommon/React-Fabric.podspec index 1937e45d71b0b6..0bf38ed0c2f62a 100644 --- a/ReactCommon/React-Fabric.podspec +++ b/ReactCommon/React-Fabric.podspec @@ -28,7 +28,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0" } + s.platforms = { :ios => "10.0" } s.source = source s.prepare_command = File.read("../scripts/generate-rncore.sh") s.source_files = "dummyFile.cpp" diff --git a/ReactCommon/ReactCommon.podspec b/ReactCommon/ReactCommon.podspec index ce62a7cfb9a539..771a9084579ab9 100644 --- a/ReactCommon/ReactCommon.podspec +++ b/ReactCommon/ReactCommon.podspec @@ -28,7 +28,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.source = source s.header_dir = "ReactCommon" # Use global header_dir for all subspecs for use_frameworks! compatibility s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags @@ -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/ReactCommon/**/*.{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/ReactCommon/**/*.{cpp,h}", + "react/nativemodule/samples/platform/ios/**/*.{mm,cpp,h}" sss.dependency "ReactCommon/turbomodule/core", version end end diff --git a/ReactCommon/callinvoker/React-callinvoker.podspec b/ReactCommon/callinvoker/React-callinvoker.podspec index 21e1d9ed7eeaab..40f6d88f771959 100644 --- a/ReactCommon/callinvoker/React-callinvoker.podspec +++ b/ReactCommon/callinvoker/React-callinvoker.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.source = source s.source_files = "**/*.{cpp,h}" s.header_dir = "ReactCommon" diff --git a/ReactCommon/cxxreact/React-cxxreact.podspec b/ReactCommon/cxxreact/React-cxxreact.podspec index 095c9875039006..94039dd0b7909f 100644 --- a/ReactCommon/cxxreact/React-cxxreact.podspec +++ b/ReactCommon/cxxreact/React-cxxreact.podspec @@ -28,7 +28,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.source = source s.source_files = "*.{cpp,h}" s.exclude_files = "SampleCxxModule.*" diff --git a/ReactCommon/hermes/inspector/tools/msggen/.babelrc b/ReactCommon/hermes/inspector/tools/msggen/.babelrc index b96eb4c692e218..8ad6d5109ddf39 100644 --- a/ReactCommon/hermes/inspector/tools/msggen/.babelrc +++ b/ReactCommon/hermes/inspector/tools/msggen/.babelrc @@ -5,5 +5,4 @@ } }] ], - "plugins": ["idx"] } diff --git a/ReactCommon/hermes/inspector/tools/msggen/package.json b/ReactCommon/hermes/inspector/tools/msggen/package.json index b16d8c68d822a8..a75ddd8384bc2b 100644 --- a/ReactCommon/hermes/inspector/tools/msggen/package.json +++ b/ReactCommon/hermes/inspector/tools/msggen/package.json @@ -33,7 +33,6 @@ }, "dependencies": { "devtools-protocol": "0.0.730699", - "idx": "^2.1.0", "yargs": "^14.2.0" }, "beachball": { diff --git a/ReactCommon/hermes/inspector/tools/msggen/yarn.lock b/ReactCommon/hermes/inspector/tools/msggen/yarn.lock index cf4d9eaa94aa58..47b1dacda9373d 100644 --- a/ReactCommon/hermes/inspector/tools/msggen/yarn.lock +++ b/ReactCommon/hermes/inspector/tools/msggen/yarn.lock @@ -2519,11 +2519,6 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -idx@^2.1.0: - version "2.5.6" - resolved "https://registry.yarnpkg.com/idx/-/idx-2.5.6.tgz#1f824595070100ae9ad585c86db08dc74f83a59d" - integrity sha512-WFXLF7JgPytbMgelpRY46nHz5tyDcedJ76pLV+RJWdb8h33bxFq4bdZau38DhNSzk5eVniBf1K3jwfK+Lb5nYA== - ieee754@^1.1.4: version "1.1.13" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" diff --git a/ReactCommon/jsi/React-jsi.podspec b/ReactCommon/jsi/React-jsi.podspec index 00fa87c93dc1f4..2c6223894c6e0e 100644 --- a/ReactCommon/jsi/React-jsi.podspec +++ b/ReactCommon/jsi/React-jsi.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.source = source s.source_files = "**/*.{cpp,h}" s.exclude_files = "**/test/*" diff --git a/ReactCommon/jsi/jsi/CMakeLists.txt b/ReactCommon/jsi/jsi/CMakeLists.txt index d04af0006af915..121d697fd8e76e 100644 --- a/ReactCommon/jsi/jsi/CMakeLists.txt +++ b/ReactCommon/jsi/jsi/CMakeLists.txt @@ -21,6 +21,9 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC") # when they go out of scope due to exceptions. list(APPEND jsi_compile_flags "/EHsc") endif() +if (HERMES_ENABLE_BITCODE) + list(APPEND jsi_compile_flags "-fembed-bitcode") +endif () target_compile_options(jsi PUBLIC ${jsi_compile_flags}) install(DIRECTORY "${PROJECT_SOURCE_DIR}/API/jsi/" DESTINATION include diff --git a/ReactCommon/jsi/jsi/decorator.h b/ReactCommon/jsi/jsi/decorator.h index 46e7414abbecb1..bef9796dc5b8af 100644 --- a/ReactCommon/jsi/jsi/decorator.h +++ b/ReactCommon/jsi/jsi/decorator.h @@ -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 { diff --git a/ReactCommon/jsi/jsi/instrumentation.h b/ReactCommon/jsi/jsi/instrumentation.h index 04c76ce2594d62..b8aa4da02b2366 100644 --- a/ReactCommon/jsi/jsi/instrumentation.h +++ b/ReactCommon/jsi/jsi/instrumentation.h @@ -49,8 +49,10 @@ class JSI_EXPORT Instrumentation { virtual std::unordered_map 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(). diff --git a/ReactCommon/jsi/jsi/jsi.cpp b/ReactCommon/jsi/jsi/jsi.cpp index e4a7e431fca00d..eac4c4b6356a8a 100644 --- a/ReactCommon/jsi/jsi/jsi.cpp +++ b/ReactCommon/jsi/jsi/jsi.cpp @@ -97,7 +97,7 @@ Instrumentation& Runtime::instrumentation() { return std::unordered_map{}; } - void collectGarbage() override {} + void collectGarbage(std::string) override {} void startTrackingHeapObjectStackTraces() override {} void stopTrackingHeapObjectStackTraces() override {} diff --git a/ReactCommon/jsiexecutor/React-jsiexecutor.podspec b/ReactCommon/jsiexecutor/React-jsiexecutor.podspec index a8ac598cbf85bd..ee90076ee3cc5e 100644 --- a/ReactCommon/jsiexecutor/React-jsiexecutor.podspec +++ b/ReactCommon/jsiexecutor/React-jsiexecutor.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.source = source s.source_files = "jsireact/*.{cpp,h}" s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags diff --git a/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp b/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp index 0d91ca7778e0b0..c4a3d90625e078 100644 --- a/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp +++ b/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp @@ -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 diff --git a/ReactCommon/jsinspector/React-jsinspector.podspec b/ReactCommon/jsinspector/React-jsinspector.podspec index ff5e71660cc408..e18cfd434e7494 100644 --- a/ReactCommon/jsinspector/React-jsinspector.podspec +++ b/ReactCommon/jsinspector/React-jsinspector.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.source = source s.source_files = "*.{cpp,h}" s.header_dir = 'jsinspector' diff --git a/ReactCommon/turbomodule/.clang-tidy b/ReactCommon/react/nativemodule/.clang-tidy similarity index 100% rename from ReactCommon/turbomodule/.clang-tidy rename to ReactCommon/react/nativemodule/.clang-tidy diff --git a/ReactCommon/react/nativemodule/core/Android.mk b/ReactCommon/react/nativemodule/core/Android.mk new file mode 100644 index 00000000000000..70f7651b4831f4 --- /dev/null +++ b/ReactCommon/react/nativemodule/core/Android.mk @@ -0,0 +1,31 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_nativemodule_core + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../ $(LOCAL_PATH)/ReactCommon $(LOCAL_PATH)/platform/android/ReactCommon + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/ReactCommon/*.cpp) $(wildcard $(LOCAL_PATH)/platform/android/ReactCommon/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/platform/android/ + +LOCAL_SHARED_LIBRARIES := libfbjni libfolly_json libreactnativejni + +LOCAL_STATIC_LIBRARIES := libjsi libreactperflogger + +LOCAL_CFLAGS := \ + -DLOG_TAG=\"ReactNative\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) + +$(call import-module,folly) +$(call import-module,jsi) +$(call import-module,reactperflogger) diff --git a/ReactCommon/turbomodule/core/BUCK b/ReactCommon/react/nativemodule/core/BUCK similarity index 93% rename from ReactCommon/turbomodule/core/BUCK rename to ReactCommon/react/nativemodule/core/BUCK index 549ddef0a9e243..1bc3b9950df7ee 100644 --- a/ReactCommon/turbomodule/core/BUCK +++ b/ReactCommon/react/nativemodule/core/BUCK @@ -4,12 +4,12 @@ load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "FBJNI_TARGET", " rn_xplat_cxx_library( name = "core", srcs = glob( - ["*.cpp"], + ["ReactCommon/**/*.cpp"], ), header_namespace = "", exported_headers = subdir_glob( [ - ("", "*.h"), + ("ReactCommon", "*.h"), ], prefix = "ReactCommon", ), @@ -26,13 +26,13 @@ rn_xplat_cxx_library( ], fbandroid_exported_headers = subdir_glob( [ - ("platform/android", "*.h"), + ("platform/android/ReactCommon", "*.h"), ], prefix = "ReactCommon", ), fbandroid_srcs = glob( [ - "platform/android/**/*.cpp", + "platform/android/ReactCommon/*.cpp", ], ), fbobjc_compiler_flags = [ diff --git a/ReactCommon/turbomodule/core/LongLivedObject.cpp b/ReactCommon/react/nativemodule/core/ReactCommon/LongLivedObject.cpp similarity index 100% rename from ReactCommon/turbomodule/core/LongLivedObject.cpp rename to ReactCommon/react/nativemodule/core/ReactCommon/LongLivedObject.cpp diff --git a/ReactCommon/turbomodule/core/LongLivedObject.h b/ReactCommon/react/nativemodule/core/ReactCommon/LongLivedObject.h similarity index 100% rename from ReactCommon/turbomodule/core/LongLivedObject.h rename to ReactCommon/react/nativemodule/core/ReactCommon/LongLivedObject.h diff --git a/ReactCommon/turbomodule/core/TurboCxxModule.cpp b/ReactCommon/react/nativemodule/core/ReactCommon/TurboCxxModule.cpp similarity index 100% rename from ReactCommon/turbomodule/core/TurboCxxModule.cpp rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboCxxModule.cpp diff --git a/ReactCommon/turbomodule/core/TurboCxxModule.h b/ReactCommon/react/nativemodule/core/ReactCommon/TurboCxxModule.h similarity index 100% rename from ReactCommon/turbomodule/core/TurboCxxModule.h rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboCxxModule.h diff --git a/ReactCommon/turbomodule/core/TurboModule.cpp b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.cpp similarity index 100% rename from ReactCommon/turbomodule/core/TurboModule.cpp rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.cpp diff --git a/ReactCommon/turbomodule/core/TurboModule.h b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.h similarity index 100% rename from ReactCommon/turbomodule/core/TurboModule.h rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.h diff --git a/ReactCommon/turbomodule/core/TurboModuleBinding.cpp b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp similarity index 100% rename from ReactCommon/turbomodule/core/TurboModuleBinding.cpp rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp diff --git a/ReactCommon/turbomodule/core/TurboModuleBinding.h b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.h similarity index 100% rename from ReactCommon/turbomodule/core/TurboModuleBinding.h rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.h diff --git a/ReactCommon/turbomodule/core/TurboModulePerfLogger.cpp b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModulePerfLogger.cpp similarity index 100% rename from ReactCommon/turbomodule/core/TurboModulePerfLogger.cpp rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboModulePerfLogger.cpp diff --git a/ReactCommon/turbomodule/core/TurboModulePerfLogger.h b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModulePerfLogger.h similarity index 100% rename from ReactCommon/turbomodule/core/TurboModulePerfLogger.h rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboModulePerfLogger.h diff --git a/ReactCommon/turbomodule/core/TurboModuleUtils.cpp b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleUtils.cpp similarity index 100% rename from ReactCommon/turbomodule/core/TurboModuleUtils.cpp rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleUtils.cpp diff --git a/ReactCommon/turbomodule/core/TurboModuleUtils.h b/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleUtils.h similarity index 100% rename from ReactCommon/turbomodule/core/TurboModuleUtils.h rename to ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleUtils.h diff --git a/ReactCommon/turbomodule/core/platform/android/JavaTurboModule.cpp b/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp similarity index 97% rename from ReactCommon/turbomodule/core/platform/android/JavaTurboModule.cpp rename to ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp index 4b5d8d6e9538b6..82fbc928fb6c1a 100644 --- a/ReactCommon/turbomodule/core/platform/android/JavaTurboModule.cpp +++ b/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.cpp @@ -31,6 +31,26 @@ JavaTurboModule::JavaTurboModule(const InitParams ¶ms) 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; diff --git a/ReactCommon/turbomodule/core/platform/android/JavaTurboModule.h b/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h similarity index 98% rename from ReactCommon/turbomodule/core/platform/android/JavaTurboModule.h rename to ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h index 642cacb194a85b..93acda48fcff56 100644 --- a/ReactCommon/turbomodule/core/platform/android/JavaTurboModule.h +++ b/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h @@ -41,6 +41,7 @@ class JSI_EXPORT JavaTurboModule : public TurboModule { }; JavaTurboModule(const InitParams ¶ms); + virtual ~JavaTurboModule(); jsi::Value invokeJavaMethod( jsi::Runtime &runtime, TurboModuleMethodValueKind valueKind, diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.h b/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.h similarity index 100% rename from ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.h rename to ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.h diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm b/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.mm similarity index 97% rename from ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm rename to ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.mm index 4e5cdc8a4ee3c6..c8950c5718fe6a 100644 --- a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModule.mm +++ b/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModule.mm @@ -171,7 +171,7 @@ static int32_t getUniqueId() { 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 @@ static int32_t getUniqueId() wrapperWasCalled = YES; }; + + return [callback copy]; } namespace facebook { @@ -331,12 +333,11 @@ static int32_t getUniqueId() bool wasMethodSync = isMethodSync(returnType); void (^block)() = ^{ - if (!weakModule) { + id strongModule = weakModule; + if (!strongModule) { return; } - id strongModule = weakModule; - if (wasMethodSync) { TurboModulePerfLogger::syncMethodCallExecutionStart(moduleName, methodNameStr.c_str()); } else { @@ -635,10 +636,13 @@ static int32_t getUniqueId() 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); }) diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.h b/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.h similarity index 100% rename from ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.h rename to ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.h diff --git a/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm b/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.mm similarity index 100% rename from ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm rename to ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.mm diff --git a/ReactCommon/turbomodule/samples/BUCK b/ReactCommon/react/nativemodule/samples/BUCK similarity index 65% rename from ReactCommon/turbomodule/samples/BUCK rename to ReactCommon/react/nativemodule/samples/BUCK index ae15f4706b606f..b6b0fe3b974e52 100644 --- a/ReactCommon/turbomodule/samples/BUCK +++ b/ReactCommon/react/nativemodule/samples/BUCK @@ -1,13 +1,15 @@ load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "OBJC_ARC_PREPROCESSOR_FLAGS", "get_preprocessor_flags_for_build_mode", "get_static_library_ios_flags") -load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "react_native_target", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob") +load("//tools/build_defs/oss:rn_defs.bzl", "ANDROID", "APPLE", "FBJNI_TARGET", "react_native_dep", "react_native_target", "react_native_xplat_target", "rn_android_library", "rn_xplat_cxx_library", "subdir_glob") rn_xplat_cxx_library( name = "samples", - srcs = glob(["*.cpp"]), + srcs = glob( + ["ReactCommon/**/*.cpp"], + ), header_namespace = "", exported_headers = subdir_glob( [ - ("", "*.h"), + ("ReactCommon", "*.h"), ], prefix = "ReactCommon", ), @@ -19,10 +21,11 @@ rn_xplat_cxx_library( ], fbandroid_deps = [ react_native_target("jni/react/jni:jni"), + FBJNI_TARGET, ], fbandroid_exported_headers = subdir_glob( [ - ("platform/android", "*.h"), + ("platform/android/ReactCommon", "*.h"), ], prefix = "ReactCommon", ), @@ -75,6 +78,27 @@ rn_xplat_cxx_library( ], exported_deps = [ "//xplat/jsi:jsi", - react_native_xplat_target("turbomodule/core:core"), + react_native_xplat_target("react/nativemodule/core:core"), + ], +) + +rn_android_library( + name = "impl", + srcs = glob(["platform/android/*.java"]), + required_for_source_only_abi = True, + visibility = [ + "PUBLIC", + ], + deps = [ + "//fbandroid/java/com/facebook/debug/log:log", + react_native_dep("third-party/java/jsr-305:jsr-305"), + react_native_dep("third-party/java/jsr-330:jsr-330"), + react_native_target("java/com/facebook/react/bridge:bridge"), + react_native_target("java/com/facebook/react/common:common"), + react_native_target("java/com/facebook/react/module/annotations:annotations"), + ":samples", + ], + exported_deps = [ + react_native_target("java/com/facebook/react/turbomodule/core/interfaces:interfaces"), ], ) diff --git a/ReactCommon/turbomodule/samples/NativeSampleTurboCxxModuleSpecJSI.cpp b/ReactCommon/react/nativemodule/samples/ReactCommon/NativeSampleTurboCxxModuleSpecJSI.cpp similarity index 100% rename from ReactCommon/turbomodule/samples/NativeSampleTurboCxxModuleSpecJSI.cpp rename to ReactCommon/react/nativemodule/samples/ReactCommon/NativeSampleTurboCxxModuleSpecJSI.cpp diff --git a/ReactCommon/turbomodule/samples/NativeSampleTurboCxxModuleSpecJSI.h b/ReactCommon/react/nativemodule/samples/ReactCommon/NativeSampleTurboCxxModuleSpecJSI.h similarity index 100% rename from ReactCommon/turbomodule/samples/NativeSampleTurboCxxModuleSpecJSI.h rename to ReactCommon/react/nativemodule/samples/ReactCommon/NativeSampleTurboCxxModuleSpecJSI.h diff --git a/ReactCommon/turbomodule/samples/SampleTurboCxxModule.cpp b/ReactCommon/react/nativemodule/samples/ReactCommon/SampleTurboCxxModule.cpp similarity index 100% rename from ReactCommon/turbomodule/samples/SampleTurboCxxModule.cpp rename to ReactCommon/react/nativemodule/samples/ReactCommon/SampleTurboCxxModule.cpp diff --git a/ReactCommon/turbomodule/samples/SampleTurboCxxModule.h b/ReactCommon/react/nativemodule/samples/ReactCommon/SampleTurboCxxModule.h similarity index 100% rename from ReactCommon/turbomodule/samples/SampleTurboCxxModule.h rename to ReactCommon/react/nativemodule/samples/ReactCommon/SampleTurboCxxModule.h diff --git a/ReactCommon/react/nativemodule/samples/platform/android/Android.mk b/ReactCommon/react/nativemodule/samples/platform/android/Android.mk new file mode 100644 index 00000000000000..46dfd7623ae029 --- /dev/null +++ b/ReactCommon/react/nativemodule/samples/platform/android/Android.mk @@ -0,0 +1,18 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := sampleturbomodule +LOCAL_C_INCLUDES := $(LOCAL_PATH) +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/ReactCommon/*.cpp) +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) +LOCAL_SHARED_LIBRARIES := libfbjni libreact_nativemodule_core +LOCAL_CFLAGS := \ + -DLOG_TAG=\"ReactNative\" +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_STATIC_LIBRARY) diff --git a/ReactCommon/react/nativemodule/samples/platform/android/NativeSampleTurboModuleSpec.java b/ReactCommon/react/nativemodule/samples/platform/android/NativeSampleTurboModuleSpec.java new file mode 100644 index 00000000000000..8475fbc4711299 --- /dev/null +++ b/ReactCommon/react/nativemodule/samples/platform/android/NativeSampleTurboModuleSpec.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// NOTE: This entire file should be codegen'ed. + +package com.facebook.fbreact.specs; + +import com.facebook.react.bridge.Callback; +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReactModuleWithSpec; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.common.build.ReactBuildConfig; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nullable; + +public abstract class NativeSampleTurboModuleSpec extends ReactContextBaseJavaModule + implements ReactModuleWithSpec, TurboModule { + public NativeSampleTurboModuleSpec(ReactApplicationContext reactContext) { + super(reactContext); + } + + @ReactMethod(isBlockingSynchronousMethod = true) + public abstract double getNumber(double arg); + + @ReactMethod(isBlockingSynchronousMethod = true) + public abstract WritableMap getValue(double x, String y, ReadableMap z); + + @ReactMethod(isBlockingSynchronousMethod = true) + public abstract WritableMap getObject(ReadableMap arg); + + @ReactMethod + public abstract void voidFunc(); + + @ReactMethod(isBlockingSynchronousMethod = true) + public abstract WritableArray getArray(ReadableArray arg); + + @ReactMethod + public abstract void getValueWithPromise(boolean error, Promise promise); + + @ReactMethod + public abstract void getValueWithCallback(Callback callback); + + @ReactMethod(isBlockingSynchronousMethod = true) + public abstract String getString(String arg); + + @ReactMethod(isBlockingSynchronousMethod = true) + public abstract double getRootTag(double arg); + + @ReactMethod(isBlockingSynchronousMethod = true) + public abstract boolean getBool(boolean arg); + + protected abstract Map getTypedExportedConstants(); + + @Override + public final @Nullable Map getConstants() { + Map constants = getTypedExportedConstants(); + if (ReactBuildConfig.DEBUG || ReactBuildConfig.IS_INTERNAL_BUILD) { + Set obligatoryFlowConstants = + new HashSet<>(Arrays.asList("const2", "const1", "const3")); + Set optionalFlowConstants = new HashSet<>(); + Set undeclaredConstants = new HashSet<>(constants.keySet()); + undeclaredConstants.removeAll(obligatoryFlowConstants); + undeclaredConstants.removeAll(optionalFlowConstants); + if (!undeclaredConstants.isEmpty()) { + throw new IllegalStateException( + String.format("Native Module Flow doesn't declare constants: %s", undeclaredConstants)); + } + undeclaredConstants = obligatoryFlowConstants; + undeclaredConstants.removeAll(constants.keySet()); + if (!undeclaredConstants.isEmpty()) { + throw new IllegalStateException( + String.format("Native Module doesn't fill in constants: %s", undeclaredConstants)); + } + } + return constants; + } +} diff --git a/ReactCommon/react/nativemodule/samples/platform/android/ReactCommon/SampleTurboModuleSpec.cpp b/ReactCommon/react/nativemodule/samples/platform/android/ReactCommon/SampleTurboModuleSpec.cpp new file mode 100644 index 00000000000000..676a79ee5e76c0 --- /dev/null +++ b/ReactCommon/react/nativemodule/samples/platform/android/ReactCommon/SampleTurboModuleSpec.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// NOTE: This entire file should be codegen'ed. + +#include + +namespace facebook { +namespace react { + +static facebook::jsi::Value +__hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc( + facebook::jsi::Runtime &rt, + TurboModule &turboModule, + const facebook::jsi::Value *args, + size_t count) { + return static_cast(turboModule) + .invokeJavaMethod(rt, VoidKind, "voidFunc", "()V", args, count); +} + +static facebook::jsi::Value +__hostFunction_NativeSampleTurboModuleSpecJSI_getBool( + facebook::jsi::Runtime &rt, + TurboModule &turboModule, + const facebook::jsi::Value *args, + size_t count) { + return static_cast(turboModule) + .invokeJavaMethod(rt, BooleanKind, "getBool", "(Z)Z", args, count); +} + +static facebook::jsi::Value +__hostFunction_NativeSampleTurboModuleSpecJSI_getNumber( + facebook::jsi::Runtime &rt, + TurboModule &turboModule, + const facebook::jsi::Value *args, + size_t count) { + return static_cast(turboModule) + .invokeJavaMethod(rt, NumberKind, "getNumber", "(D)D", args, count); +} + +static facebook::jsi::Value +__hostFunction_NativeSampleTurboModuleSpecJSI_getString( + facebook::jsi::Runtime &rt, + TurboModule &turboModule, + const facebook::jsi::Value *args, + size_t count) { + return static_cast(turboModule) + .invokeJavaMethod( + rt, + StringKind, + "getString", + "(Ljava/lang/String;)Ljava/lang/String;", + args, + count); +} + +static facebook::jsi::Value +__hostFunction_NativeSampleTurboModuleSpecJSI_getArray( + facebook::jsi::Runtime &rt, + TurboModule &turboModule, + const facebook::jsi::Value *args, + size_t count) { + return static_cast(turboModule) + .invokeJavaMethod( + rt, + ArrayKind, + "getArray", + "(Lcom/facebook/react/bridge/ReadableArray;)Lcom/facebook/react/bridge/WritableArray;", + args, + count); +} + +static facebook::jsi::Value +__hostFunction_NativeSampleTurboModuleSpecJSI_getObject( + facebook::jsi::Runtime &rt, + TurboModule &turboModule, + const facebook::jsi::Value *args, + size_t count) { + return static_cast(turboModule) + .invokeJavaMethod( + rt, + ObjectKind, + "getObject", + "(Lcom/facebook/react/bridge/ReadableMap;)Lcom/facebook/react/bridge/WritableMap;", + args, + count); +} + +static facebook::jsi::Value +__hostFunction_NativeSampleTurboModuleSpecJSI_getRootTag( + facebook::jsi::Runtime &rt, + TurboModule &turboModule, + const facebook::jsi::Value *args, + size_t count) { + return static_cast(turboModule) + .invokeJavaMethod(rt, NumberKind, "getRootTag", "(D)D", args, count); +} + +static facebook::jsi::Value +__hostFunction_NativeSampleTurboModuleSpecJSI_getValue( + facebook::jsi::Runtime &rt, + TurboModule &turboModule, + const facebook::jsi::Value *args, + size_t count) { + return static_cast(turboModule) + .invokeJavaMethod( + rt, + ObjectKind, + "getValue", + "(DLjava/lang/String;Lcom/facebook/react/bridge/ReadableMap;)Lcom/facebook/react/bridge/WritableMap;", + args, + count); +} + +static facebook::jsi::Value +__hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithCallback( + facebook::jsi::Runtime &rt, + TurboModule &turboModule, + const facebook::jsi::Value *args, + size_t count) { + return static_cast(turboModule) + .invokeJavaMethod( + rt, + VoidKind, + "getValueWithCallback", + "(Lcom/facebook/react/bridge/Callback;)V", + args, + count); +} + +static facebook::jsi::Value +__hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithPromise( + facebook::jsi::Runtime &rt, + TurboModule &turboModule, + const facebook::jsi::Value *args, + size_t count) { + return static_cast(turboModule) + .invokeJavaMethod( + rt, + PromiseKind, + "getValueWithPromise", + "(ZLcom/facebook/react/bridge/Promise;)V", + args, + count); +} + +static facebook::jsi::Value +__hostFunction_NativeSampleTurboModuleSpecJSI_getConstants( + facebook::jsi::Runtime &rt, + TurboModule &turboModule, + const facebook::jsi::Value *args, + size_t count) { + return static_cast(turboModule) + .invokeJavaMethod( + rt, ObjectKind, "getConstants", "()Ljava/util/Map;", args, count); +} + +NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI( + const JavaTurboModule::InitParams ¶ms) + : JavaTurboModule(params) { + methodMap_["voidFunc"] = + MethodMetadata{0, __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc}; + + methodMap_["getBool"] = + MethodMetadata{1, __hostFunction_NativeSampleTurboModuleSpecJSI_getBool}; + + methodMap_["getNumber"] = MethodMetadata{ + 1, __hostFunction_NativeSampleTurboModuleSpecJSI_getNumber}; + + methodMap_["getString"] = MethodMetadata{ + 1, __hostFunction_NativeSampleTurboModuleSpecJSI_getString}; + + methodMap_["getArray"] = + MethodMetadata{1, __hostFunction_NativeSampleTurboModuleSpecJSI_getArray}; + + methodMap_["getObject"] = MethodMetadata{ + 1, __hostFunction_NativeSampleTurboModuleSpecJSI_getObject}; + + methodMap_["getRootTag"] = MethodMetadata{ + 1, __hostFunction_NativeSampleTurboModuleSpecJSI_getRootTag}; + + methodMap_["getValue"] = + MethodMetadata{3, __hostFunction_NativeSampleTurboModuleSpecJSI_getValue}; + + methodMap_["getValueWithCallback"] = MethodMetadata{ + 1, __hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithCallback}; + + methodMap_["getValueWithPromise"] = MethodMetadata{ + 1, __hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithPromise}; + + methodMap_["getConstants"] = MethodMetadata{ + 0, __hostFunction_NativeSampleTurboModuleSpecJSI_getConstants}; +} + +std::shared_ptr SampleTurboModuleSpec_ModuleProvider( + const std::string moduleName, + const JavaTurboModule::InitParams ¶ms) { + if (moduleName == "SampleTurboModule") { + return std::make_shared(params); + } + return nullptr; +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/react/nativemodule/samples/platform/android/ReactCommon/SampleTurboModuleSpec.h b/ReactCommon/react/nativemodule/samples/platform/android/ReactCommon/SampleTurboModuleSpec.h new file mode 100644 index 00000000000000..6baefae6d890d9 --- /dev/null +++ b/ReactCommon/react/nativemodule/samples/platform/android/ReactCommon/SampleTurboModuleSpec.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// NOTE: This entire file should be codegen'ed. + +#pragma once + +#include +#include +#include + +namespace facebook { +namespace react { + +/** + * C++ class for module 'SampleTurboModule' + */ +class JSI_EXPORT NativeSampleTurboModuleSpecJSI : public JavaTurboModule { + public: + NativeSampleTurboModuleSpecJSI(const JavaTurboModule::InitParams ¶ms); +}; + +std::shared_ptr SampleTurboModuleSpec_ModuleProvider( + const std::string moduleName, + const JavaTurboModule::InitParams ¶ms); + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/react/nativemodule/samples/platform/android/SampleTurboModule.java b/ReactCommon/react/nativemodule/samples/platform/android/SampleTurboModule.java new file mode 100644 index 00000000000000..bc357d2baa3d9b --- /dev/null +++ b/ReactCommon/react/nativemodule/samples/platform/android/SampleTurboModule.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.fbreact.specs; + +import android.app.Activity; +import android.util.DisplayMetrics; +import android.widget.Toast; +import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.Callback; +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.bridge.WritableNativeArray; +import com.facebook.react.bridge.WritableNativeMap; +import com.facebook.react.common.MapBuilder; +import com.facebook.react.module.annotations.ReactModule; +import java.util.HashMap; +import java.util.Map; + +@ReactModule(name = SampleTurboModule.NAME) +public class SampleTurboModule extends NativeSampleTurboModuleSpec { + + public static final String NAME = "SampleTurboModule"; + + private static final String TAG = SampleTurboModule.class.getName(); + private final ReactApplicationContext mContext; + private Toast mToast; + + public SampleTurboModule(ReactApplicationContext context) { + super(context); + mContext = context; + } + + @DoNotStrip + @SuppressWarnings("unused") + @Override + public boolean getBool(boolean arg) { + log("getBool", arg, arg); + return arg; + } + + @Override + protected Map getTypedExportedConstants() { + Map result = new HashMap<>(); + DisplayMetrics displayMetrics = new DisplayMetrics(); + Activity activity = mContext.getCurrentActivity(); + if (activity != null) { + activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); + result.put("const2", displayMetrics.widthPixels); + } + result.put("const1", true); + result.put("const3", "something"); + log("constantsToExport", "", result); + return result; + } + + @DoNotStrip + @SuppressWarnings("unused") + @Override + public double getNumber(double arg) { + log("getNumber", arg, arg); + return arg; + } + + @DoNotStrip + @SuppressWarnings("unused") + @Override + public String getString(String arg) { + log("getString", arg, arg); + return arg; + } + + @DoNotStrip + @SuppressWarnings("unused") + @Override + public double getRootTag(double arg) { + log("getRootTag", arg, arg); + return arg; + } + + @DoNotStrip + @SuppressWarnings("unused") + @Override + public void voidFunc() { + log("voidFunc", "", ""); + return; + } + + // This function returns {@link WritableMap} instead of {@link Map} for backward compat with + // existing native modules that use this Writable* as return types or in events. {@link + // WritableMap} is modified in the Java side, and read (or consumed) on the C++ side. + // In the future, all native modules should ideally return an immutable Map + @DoNotStrip + @Override + @SuppressWarnings("unused") + public WritableMap getObject(ReadableMap arg) { + WritableNativeMap map = new WritableNativeMap(); + map.merge(arg); + log("getObject", arg, map); + return map; + } + + @DoNotStrip + @SuppressWarnings("unused") + @Override + public WritableMap getValue(double numberArg, String stringArg, ReadableMap mapArg) { + WritableMap map = new WritableNativeMap(); + map.putDouble("x", numberArg); + map.putString("y", stringArg); + WritableMap zMap = new WritableNativeMap(); + zMap.merge(mapArg); + map.putMap("z", zMap); + log( + "getValue", + MapBuilder.of("1-numberArg", numberArg, "2-stringArg", stringArg, "3-mapArg", mapArg), + map); + return map; + } + + @DoNotStrip + @SuppressWarnings("unused") + @Override + public void getValueWithCallback(final Callback callback) { + String result = "Value From Callback"; + log("Callback", "Return Time", result); + callback.invoke(result); + } + + @DoNotStrip + @SuppressWarnings("unused") + @Override + public WritableArray getArray(ReadableArray arg) { + if (arg == null || Arguments.toList(arg) == null) { + // Returning an empty array, since the super class always returns non-null + return new WritableNativeArray(); + } + WritableArray result = Arguments.makeNativeArray(Arguments.toList(arg)); + log("getArray", arg, result); + return result; + } + + @Override + @DoNotStrip + @SuppressWarnings("unused") + public void getValueWithPromise(boolean error, Promise promise) { + if (error) { + promise.reject( + "code 1", + "intentional promise rejection", + new Throwable("promise intentionally rejected")); + } else { + promise.resolve("result"); + } + } + + private void log(String method, Object input, Object output) { + if (mToast != null) { + mToast.cancel(); + } + StringBuilder message = new StringBuilder("Method :"); + message + .append(method) + .append("\nInputs: ") + .append(input.toString()) + .append("\nOutputs: ") + .append(output.toString()); + mToast = Toast.makeText(mContext, message.toString(), Toast.LENGTH_LONG); + mToast.show(); + } + + public void invalidate() {} + + @Override + public String getName() { + return NAME; + } +} diff --git a/ReactCommon/turbomodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.h b/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.h similarity index 100% rename from ReactCommon/turbomodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.h rename to ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.h diff --git a/ReactCommon/turbomodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm b/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm similarity index 100% rename from ReactCommon/turbomodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm rename to ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm diff --git a/ReactCommon/turbomodule/samples/platform/ios/RCTSampleTurboCxxModule.h b/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboCxxModule.h similarity index 100% rename from ReactCommon/turbomodule/samples/platform/ios/RCTSampleTurboCxxModule.h rename to ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboCxxModule.h diff --git a/ReactCommon/turbomodule/samples/platform/ios/RCTSampleTurboCxxModule.mm b/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboCxxModule.mm similarity index 100% rename from ReactCommon/turbomodule/samples/platform/ios/RCTSampleTurboCxxModule.mm rename to ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboCxxModule.mm diff --git a/ReactCommon/turbomodule/samples/platform/ios/RCTSampleTurboModule.h b/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboModule.h similarity index 100% rename from ReactCommon/turbomodule/samples/platform/ios/RCTSampleTurboModule.h rename to ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboModule.h diff --git a/ReactCommon/turbomodule/samples/platform/ios/RCTSampleTurboModule.mm b/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboModule.mm similarity index 100% rename from ReactCommon/turbomodule/samples/platform/ios/RCTSampleTurboModule.mm rename to ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboModule.mm diff --git a/ReactCommon/turbomodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.cpp b/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.cpp similarity index 100% rename from ReactCommon/turbomodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.cpp rename to ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.cpp diff --git a/ReactCommon/turbomodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.h b/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.h similarity index 100% rename from ReactCommon/turbomodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.h rename to ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.h diff --git a/ReactCommon/react/renderer/animations/LayoutAnimationDriver.cpp b/ReactCommon/react/renderer/animations/LayoutAnimationDriver.cpp index 583176fefac4dd..d6b46c68a01207 100644 --- a/ReactCommon/react/renderer/animations/LayoutAnimationDriver.cpp +++ b/ReactCommon/react/renderer/animations/LayoutAnimationDriver.cpp @@ -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( @@ -201,12 +203,28 @@ void LayoutAnimationDriver::animationMutationsForFrame( // Copy so that if something else mutates the inflight animations, it // won't change this mutation after this point. + ShadowView oldShadowView{}; + if (finalMutationForKeyFrame.type != + ShadowViewMutation::Type::Update) { + oldShadowView = finalMutationForKeyFrame.oldChildShadowView; + } mutationsList.push_back( ShadowViewMutation{finalMutationForKeyFrame.type, finalMutationForKeyFrame.parentShadowView, - finalMutationForKeyFrame.oldChildShadowView, + oldShadowView, 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.viewEnd, + -1}); } } diff --git a/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp b/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp index 66bcab91d50292..4264be5d3c613b 100644 --- a/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp +++ b/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -30,7 +31,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; @@ -395,9 +397,12 @@ void LayoutAnimationKeyFrameManager:: adjustImmediateMutationIndicesForDelayedMutations( SurfaceId surfaceId, ShadowViewMutation &mutation, - ShadowViewMutationList *auxiliaryMutations) const { + ConsecutiveAdjustmentMetadata &consecutiveAdjustmentMetadata, + bool skipLastAnimation, + bool lastAnimationOnly) const { bool isRemoveMutation = mutation.type == ShadowViewMutation::Type::Remove; - assert(isRemoveMutation || mutation.type == ShadowViewMutation::Type::Insert); + bool isInsertMutation = mutation.type == ShadowViewMutation::Type::Insert; + assert(isRemoveMutation || isInsertMutation); // TODO: turn all of this into a lambda and share code? if (mutatedViewIsVirtual(mutation)) { @@ -411,36 +416,35 @@ void LayoutAnimationKeyFrameManager:: "[IndexAdjustment] Calling adjustImmediateMutationIndicesForDelayedMutations for:", mutation); + // When adjusting INSERTs, we want to batch adjacent inserts so they're all + // adjacent to each other in the resulting view. For instance, if we're + // processing INSERTS into positions 2,3,4,5,6, etc, it is possible that + // delayed mutations would otherwise cause the inserts to be adjusted to + // positions 2, 4, 6, 8... etc, creating a striping effect. We want to prevent + // that. + if (isInsertMutation && + mutation.parentShadowView.tag == + consecutiveAdjustmentMetadata.lastAdjustedParent && + mutation.index == (consecutiveAdjustmentMetadata.lastIndexOriginal + 1)) { + PrintMutationInstruction( + std::string( + "[IndexAdjustment] adjustImmediateMutationIndicesForDelayedMutations: Adjusting consecutive INSERT mutation by ") + + std::to_string(consecutiveAdjustmentMetadata.lastAdjustedDelta), + mutation); + consecutiveAdjustmentMetadata.lastIndexOriginal = mutation.index; + mutation.index += consecutiveAdjustmentMetadata.lastAdjustedDelta; + return; + } + // First, collect all final mutations that could impact this immediate // mutation. std::vector candidateMutations{}; - if (auxiliaryMutations != nullptr) { - for (auto &auxMutation : *auxiliaryMutations) { - if (auxMutation.parentShadowView.tag != mutation.parentShadowView.tag) { - continue; - } - if (auxMutation.type != ShadowViewMutation::Type::Remove) { - continue; - } - if (mutatedViewIsVirtual(auxMutation)) { - continue; - } - if (auxMutation.oldChildShadowView.tag == - (isRemoveMutation ? mutation.oldChildShadowView.tag - : mutation.newChildShadowView.tag)) { - continue; - } - - PrintMutationInstructionRelative( - "[IndexAdjustment] adjustImmediateMutationIndicesForDelayedMutations auxiliary CANDIDATE for:", - mutation, - auxMutation); - candidateMutations.push_back(&auxMutation); - } - } - - for (auto &inflightAnimation : inflightAnimations_) { + for (auto inflightAnimationIt = + inflightAnimations_.rbegin() + (skipLastAnimation ? 1 : 0); + inflightAnimationIt != inflightAnimations_.rend(); + inflightAnimationIt++) { + auto &inflightAnimation = *inflightAnimationIt; if (inflightAnimation.surfaceId != surfaceId) { continue; } @@ -463,9 +467,6 @@ void LayoutAnimationKeyFrameManager:: continue; } - if (animatedKeyFrame.type != AnimationConfigurationType::Noop) { - continue; - } if (!animatedKeyFrame.finalMutationForKeyFrame.has_value()) { continue; } @@ -490,21 +491,33 @@ void LayoutAnimationKeyFrameManager:: delayedMutation); candidateMutations.push_back(&delayedMutation); } + + if (lastAnimationOnly) { + break; + } } // While the mutation keeps being affected, keep checking. We use the vector // so we only perform one adjustment per delayed mutation. See comments at // bottom of adjustDelayedMutationIndicesForMutation for further explanation. bool changed = true; + int adjustedDelta = 0; + if (isInsertMutation) { + consecutiveAdjustmentMetadata.lastAdjustedParent = + mutation.parentShadowView.tag; + consecutiveAdjustmentMetadata.lastIndexOriginal = mutation.index; + } while (changed) { changed = false; candidateMutations.erase( std::remove_if( candidateMutations.begin(), candidateMutations.end(), - [&mutation, &changed](ShadowViewMutation *candidateMutation) { - if (candidateMutation->index <= mutation.index) { + [&](ShadowViewMutation *candidateMutation) { + bool indexConflicts = candidateMutation->index <= mutation.index; + if (indexConflicts) { mutation.index++; + adjustedDelta++; changed = true; PrintMutationInstructionRelative( "[IndexAdjustment] adjustImmediateMutationIndicesForDelayedMutations: Adjusting mutation UPWARD", @@ -516,19 +529,17 @@ void LayoutAnimationKeyFrameManager:: }), candidateMutations.end()); } -} - -void LayoutAnimationKeyFrameManager:: - adjustLastAnimationDelayedMutationIndicesForMutation( - SurfaceId surfaceId, - ShadowViewMutation const &mutation) const { - adjustDelayedMutationIndicesForMutation(surfaceId, mutation, true); + if (isInsertMutation) { + consecutiveAdjustmentMetadata.lastAdjustedDelta = adjustedDelta; + } else { + consecutiveAdjustmentMetadata.lastAdjustedParent = -1; + } } void LayoutAnimationKeyFrameManager::adjustDelayedMutationIndicesForMutation( SurfaceId surfaceId, ShadowViewMutation const &mutation, - bool lastAnimationOnly) const { + bool skipLastAnimation) const { bool isRemoveMutation = mutation.type == ShadowViewMutation::Type::Remove; bool isInsertMutation = mutation.type == ShadowViewMutation::Type::Insert; assert(isRemoveMutation || isInsertMutation); @@ -544,7 +555,8 @@ void LayoutAnimationKeyFrameManager::adjustDelayedMutationIndicesForMutation( // mutation. std::vector candidateMutations{}; - for (auto inflightAnimationIt = inflightAnimations_.rbegin(); + for (auto inflightAnimationIt = + inflightAnimations_.rbegin() + (skipLastAnimation ? 1 : 0); inflightAnimationIt != inflightAnimations_.rend(); inflightAnimationIt++) { auto &inflightAnimation = *inflightAnimationIt; @@ -571,9 +583,6 @@ void LayoutAnimationKeyFrameManager::adjustDelayedMutationIndicesForMutation( continue; } - if (animatedKeyFrame.type != AnimationConfigurationType::Noop) { - continue; - } if (!animatedKeyFrame.finalMutationForKeyFrame.has_value()) { continue; } @@ -586,19 +595,19 @@ void LayoutAnimationKeyFrameManager::adjustDelayedMutationIndicesForMutation( continue; } - if (!mutatedViewIsVirtual(*animatedKeyFrame.finalMutationForKeyFrame) && - finalAnimationMutation.type == ShadowViewMutation::Type::Remove) { - PrintMutationInstructionRelative( - "[IndexAdjustment] adjustDelayedMutationIndicesForMutation: CANDIDATE:", - mutation, - *animatedKeyFrame.finalMutationForKeyFrame); - candidateMutations.push_back( - animatedKeyFrame.finalMutationForKeyFrame.get_pointer()); + if (finalAnimationMutation.type != ShadowViewMutation::Type::Remove) { + continue; + } + if (mutatedViewIsVirtual(*animatedKeyFrame.finalMutationForKeyFrame)) { + continue; } - } - if (lastAnimationOnly) { - break; + PrintMutationInstructionRelative( + "[IndexAdjustment] adjustDelayedMutationIndicesForMutation: CANDIDATE:", + mutation, + *animatedKeyFrame.finalMutationForKeyFrame); + candidateMutations.push_back( + animatedKeyFrame.finalMutationForKeyFrame.get_pointer()); } } @@ -684,18 +693,6 @@ LayoutAnimationKeyFrameManager::getAndEraseConflictingAnimations( continue; } - // bool hasFinalMutation = - // animatedKeyFrame.finalMutationForKeyFrame.hasValue(); - // int finalMutationTag = hasFinalMutation - // ? (((*animatedKeyFrame.finalMutationForKeyFrame).type == - // ShadowViewMutation::Create || - // (*animatedKeyFrame.finalMutationForKeyFrame).type == - // ShadowViewMutation::Insert) - // ? (*animatedKeyFrame.finalMutationForKeyFrame) - // .newChildShadowView.tag - // : (*animatedKeyFrame.finalMutationForKeyFrame) - // .oldChildShadowView.tag) - // : -1; bool conflicting = animatedKeyFrame.tag == baselineShadowView.tag || ((mutation.type == ShadowViewMutation::Type::Delete || mutation.type == ShadowViewMutation::Type::Create) && @@ -703,20 +700,6 @@ LayoutAnimationKeyFrameManager::getAndEraseConflictingAnimations( finalMutationTag == baselineShadowView.tag*/ ; - // In some bizarre situations, there can be an ongoing Delete - // animation, and then a conflicting mutation to create and/or delete - // the same tag. In actuality this "bizarre" situation is just the - // animation of repeatedly flattening and unflattening a view; but - // it's not clear how to gracefully recover from this, so we just - // ensure that the Deletion is never executed in those cases. In these - // cases, the ongoing animation will stop; the view still exists; and - // then either a "Create" or "delete" animation will be recreated and - // executed for that tag. - bool shouldExecuteFinalMutation = - !(animatedKeyFrame.finalMutationForKeyFrame.hasValue() && - (*animatedKeyFrame.finalMutationForKeyFrame).type == - ShadowViewMutation::Delete); - // Conflicting animation detected: if we're mutating a tag under // animation, or deleting the parent of a tag under animation, or // reparenting. @@ -734,7 +717,9 @@ LayoutAnimationKeyFrameManager::getAndEraseConflictingAnimations( animatedKeyFrame.invalidated = true; - if (shouldExecuteFinalMutation) { + if (animatedKeyFrame.finalMutationForKeyFrame.has_value() && + !mutatedViewIsVirtual( + *animatedKeyFrame.finalMutationForKeyFrame)) { conflictingAnimations.push_back(std::make_tuple( animatedKeyFrame, *mutationConfig, &inflightAnimation)); } @@ -747,7 +732,7 @@ LayoutAnimationKeyFrameManager::getAndEraseConflictingAnimations( *animatedKeyFrame.finalMutationForKeyFrame); } else { PrintMutationInstruction( - "Found mutation that conflicts with existing in-flight animation:", + "Found mutation that conflicts with existing in-flight animation (no final mutation):", mutation); } #endif @@ -755,13 +740,6 @@ LayoutAnimationKeyFrameManager::getAndEraseConflictingAnimations( // Delete from existing animation it = inflightAnimation.keyFrames.erase(it); } else { - //#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING - // if (hasFinalMutation) { - // PrintMutationInstructionRelative("getAndEraseConflictingAnimations, - // NOT erasing non-conflicting mutation of ", mutation, - // *animatedKeyFrame.finalMutationForKeyFrame); - // } - //#endif it++; } } @@ -843,7 +821,8 @@ LayoutAnimationKeyFrameManager::pullTransaction( if (keyframe.invalidated) { continue; } - if (keyframe.finalMutationForKeyFrame) { + if (keyframe.finalMutationForKeyFrame && + !mutatedViewIsVirtual(*keyframe.finalMutationForKeyFrame)) { std::string msg = "Animation " + std::to_string(i) + " keyframe " + std::to_string(j) + ": Final Animation"; PrintMutationInstruction(msg, *keyframe.finalMutationForKeyFrame); @@ -898,15 +877,23 @@ LayoutAnimationKeyFrameManager::pullTransaction( // being moves on the Differ level, since we know that there? We could use // TinyMap here, but it's not exposed by Differentiator (yet). std::vector insertedTags; - std::vector createdTags; + std::vector deletedTags; + std::vector reparentedTags; // tags that are deleted and recreated std::unordered_map movedTags; - std::vector reparentedTags; for (const auto &mutation : mutations) { if (mutation.type == ShadowViewMutation::Type::Insert) { insertedTags.push_back(mutation.newChildShadowView.tag); } + if (mutation.type == ShadowViewMutation::Type::Delete) { + deletedTags.push_back(mutation.oldChildShadowView.tag); + } if (mutation.type == ShadowViewMutation::Type::Create) { - createdTags.push_back(mutation.newChildShadowView.tag); + if (std::find( + deletedTags.begin(), + deletedTags.end(), + mutation.newChildShadowView.tag) != deletedTags.end()) { + reparentedTags.push_back(mutation.newChildShadowView.tag); + } } } @@ -953,26 +940,15 @@ LayoutAnimationKeyFrameManager::pullTransaction( // This should eventually be optimized out of the diffing algorithm, but // for now we detect reparenting and prevent the corresponding // Delete/Create instructions from being animated. - bool isReparented = - (mutation.type == ShadowViewMutation::Delete && - std::find( - createdTags.begin(), - createdTags.end(), - mutation.oldChildShadowView.tag) != createdTags.end()) || - (mutation.type == ShadowViewMutation::Create && - std::find( - reparentedTags.begin(), - reparentedTags.end(), - mutation.newChildShadowView.tag) != reparentedTags.end()); + bool isReparented = std::find( + reparentedTags.begin(), + reparentedTags.end(), + baselineShadowView.tag) != reparentedTags.end(); if (isRemoveReinserted) { movedTags.insert({mutation.oldChildShadowView.tag, mutation}); } - if (isReparented && mutation.type == ShadowViewMutation::Delete) { - reparentedTags.push_back(mutation.oldChildShadowView.tag); - } - // Inserts that follow a "remove" of the same tag should be treated as // an update (move) animation. bool wasInsertedTagRemoved = false; @@ -1204,30 +1180,31 @@ LayoutAnimationKeyFrameManager::pullTransaction( } #ifdef RN_SHADOW_TREE_INTROSPECTION +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING { - std::stringstream ss(getDebugDescription(immediateMutations, {})); - std::string to; - while (std::getline(ss, to, '\n')) { - LOG(ERROR) - << "LayoutAnimationKeyFrameManager.cpp: got IMMEDIATE list: Line: " - << to; + int idx = 0; + for (auto &mutation : immediateMutations) { + PrintMutationInstruction( + std::string("IMMEDIATE list: ") + std::to_string(idx) + "/" + + std::to_string(immediateMutations.size()), + mutation); + idx++; } } { + int idx = 0; for (const auto &keyframe : keyFramesToAnimate) { - if (keyframe.finalMutationForKeyFrame) { - std::stringstream ss( - getDebugDescription(*keyframe.finalMutationForKeyFrame, {})); - std::string to; - while (std::getline(ss, to, '\n')) { - LOG(ERROR) - << "LayoutAnimationKeyFrameManager.cpp: got FINAL list: Line: " - << to; - } + if (keyframe.finalMutationForKeyFrame.has_value()) { + PrintMutationInstruction( + std::string("FINAL list: ") + std::to_string(idx) + "/" + + std::to_string(keyFramesToAnimate.size()), + *keyframe.finalMutationForKeyFrame); } + idx++; } } +#endif #endif auto finalConflictingMutations = ShadowViewMutationList{}; @@ -1235,7 +1212,16 @@ LayoutAnimationKeyFrameManager::pullTransaction( auto &keyFrame = std::get<0>(conflictingKeyframeTuple); if (keyFrame.finalMutationForKeyFrame.hasValue()) { auto &mutation = *keyFrame.finalMutationForKeyFrame; - finalConflictingMutations.push_back(mutation); + if (mutation.type == ShadowViewMutation::Type::Update) { + finalConflictingMutations.push_back( + ShadowViewMutation::UpdateMutation( + mutation.parentShadowView, + {}, + mutation.newChildShadowView, + mutation.index)); + } else { + finalConflictingMutations.push_back(mutation); + } } } @@ -1246,56 +1232,121 @@ LayoutAnimationKeyFrameManager::pullTransaction( finalConflictingMutations.end(), &shouldFirstComeBeforeSecondMutation); - // Use "final conflicting mutations" to adjust delayed mutations *before* - // we adjust immediate mutations based on delayed mutations + std::stable_sort( + immediateMutations.begin(), + immediateMutations.end(), + &shouldFirstComeBeforeSecondRemovesOnly); + + animation.keyFrames = keyFramesToAnimate; + inflightAnimations_.push_back(std::move(animation)); + + // At this point, we have the following information and knowledge graph: + // Knowledge Graph: + // [ImmediateMutations] -> assumes [FinalConflicting], [FrameDelayed], + // [Delayed] already executed [FrameDelayed] -> assumes + // [FinalConflicting], [Delayed] already executed [FinalConflicting] -> is + // adjusted based on [Delayed], no dependency on [FinalConflicting], + // [FrameDelayed] [Delayed] -> assumes [FinalConflicting], + // [ImmediateMutations] not executed yet + ConsecutiveAdjustmentMetadata consecutiveAdjustmentMetadata{}; + + // Adjust [Delayed] based on [FinalConflicting] + // Knowledge Graph: + // [ImmediateMutations] -> assumes [FinalConflicting], [FrameDelayed], + // [Delayed] already executed [FrameDelayed] -> assumes + // [FinalConflicting], [Delayed] already executed [FinalConflicting] -> is + // adjusted based on [Delayed], no dependency on [FinalConflicting], + // [FrameDelayed] [Delayed] -> adjusted for [FinalConflicting]; assumes + // [ImmediateMutations] not executed yet #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING - LOG(ERROR) - << "Adjust delayed mutations based on finalConflictingMutations"; + LOG(ERROR) << "Adjust [Delayed] based on [FinalConflicting]"; #endif for (auto &mutation : finalConflictingMutations) { - adjustDelayedMutationIndicesForMutation(surfaceId, mutation); + if (mutation.type == ShadowViewMutation::Type::Insert || + mutation.type == ShadowViewMutation::Type::Remove) { + adjustDelayedMutationIndicesForMutation(surfaceId, mutation, true); + } } - // Adjust keyframes based on already-delayed, existing animations, before - // queueing. We adjust them as if finalConflictingMutations have already - // been executed - in all cases, finalConflictingMutations will be - // executed before any of these delayed mutations are. + // Adjust [FrameDelayed] based on [Delayed] + // Knowledge Graph: + // [ImmediateExecutions] -> assumes [FinalConflicting], [Delayed], + // [FrameDelayed] already executed [FrameDelayed] -> adjusted for + // [Delayed]; assumes [FinalConflicting] already executed + // [FinalConflicting] -> is adjusted based on [Delayed], no dependency on + // [FinalConflicting], [FrameDelayed] [Delayed] -> adjusted for + // [FinalConflicting]; assumes [ImmediateExecutions] not executed yet #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING - LOG(ERROR) - << "Adjust immediate keyFramesToAnimate based on delayed mutations and finalConflictingMutations"; + LOG(ERROR) << "Adjust [FrameDelayed] based on [Delayed]"; #endif - for (auto &keyframe : keyFramesToAnimate) { + for (auto &keyframe : inflightAnimations_.back().keyFrames) { if (keyframe.finalMutationForKeyFrame.has_value()) { - auto &delayedMutation = *keyframe.finalMutationForKeyFrame; - if (delayedMutation.type == ShadowViewMutation::Type::Remove) { + auto &mutation = *keyframe.finalMutationForKeyFrame; + if (mutation.type == ShadowViewMutation::Type::Insert || + mutation.type == ShadowViewMutation::Type::Remove) { + // When adjusting, skip adjusting against last animation - because + // all `mutation`s here come from the last animation, so we can't + // adjust a batch against itself. adjustImmediateMutationIndicesForDelayedMutations( - surfaceId, delayedMutation /*, &finalConflictingMutations*/); + surfaceId, mutation, consecutiveAdjustmentMetadata, true); } } } - // REMOVE mutations from this animation batch *cannot* be impacted by - // other REMOVEs from this batch, since they're already taken into - // account. INSERTs can impact delayed REMOVEs; see below. + // Adjust [ImmediateExecutions] based on [Delayed] + // Knowledge Graph: + // [ImmediateExecutions] -> adjusted for [FrameDelayed], [Delayed]; + // assumes [FinalConflicting] already executed [FrameDelayed] -> adjusted + // for [Delayed]; assumes [FinalConflicting] already executed + // [FinalConflicting] -> is adjusted based on [Delayed], no dependency on + // [FinalConflicting], [FrameDelayed] [Delayed] -> adjusted for + // [FinalConflicting]; assumes [ImmediateExecutions] not executed yet #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING - LOG(ERROR) - << "Adjust immediateMutations REMOVEs only, based on previously delayed mutations, without most-recent animation"; + LOG(ERROR) << "Adjust [ImmediateExecutions] based on [Delayed]"; #endif - std::stable_sort( - immediateMutations.begin(), - immediateMutations.end(), - &shouldFirstComeBeforeSecondRemovesOnly); + consecutiveAdjustmentMetadata = ConsecutiveAdjustmentMetadata{}; for (auto &mutation : immediateMutations) { - if (mutation.type == ShadowViewMutation::Type::Remove) { + // Note: when adjusting [ImmediateExecutions] based on [FrameDelayed], + // we need only adjust Inserts. Since inserts are executed + // highest-index-first, lower indices being delayed does not impact the + // higher-index removals; and conversely, higher indices being delayed + // cannot impact lower index removal, regardless of order. + if (mutation.type == ShadowViewMutation::Type::Insert || + mutation.type == ShadowViewMutation::Type::Remove) { adjustImmediateMutationIndicesForDelayedMutations( - surfaceId, mutation); - adjustDelayedMutationIndicesForMutation(surfaceId, mutation); + surfaceId, + mutation, + consecutiveAdjustmentMetadata, + mutation.type == ShadowViewMutation::Type::Remove); } } - animation.keyFrames = keyFramesToAnimate; - inflightAnimations_.push_back(std::move(animation)); + // Adjust [Delayed] based on [ImmediateExecutions] and [FinalConflicting] + // Knowledge Graph: + // [ImmediateExecutions] -> adjusted for [FrameDelayed], [Delayed]; + // assumes [FinalConflicting] already executed [FrameDelayed] -> adjusted + // for [Delayed]; assumes [FinalConflicting] already executed + // [FinalConflicting] -> is adjusted based on [Delayed], no dependency on + // [FinalConflicting], [FrameDelayed] [Delayed] -> adjusted for + // [FinalConflicting], [ImmediateExecutions] +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING + LOG(ERROR) + << "Adjust [Delayed] based on [ImmediateExecutions] and [FinalConflicting]"; +#endif + for (auto &mutation : immediateMutations) { + if (mutation.type == ShadowViewMutation::Type::Insert || + mutation.type == ShadowViewMutation::Type::Remove) { + // Here we need to adjust both Delayed and FrameDelayed mutations. + // Delayed Removes can be impacted by non-delayed Inserts from the + // same frame. + adjustDelayedMutationIndicesForMutation(surfaceId, mutation); + } + } + // If the knowledge graph progression above is correct, it is now safe to + // execute finalConflictingMutations and immediateMutations in that order, + // and to queue the delayed animations from this frame. + // // Execute the conflicting, delayed operations immediately. Any UPDATE // operations that smoothly transition into another animation will be // overridden by generated UPDATE operations at the end of the list, and @@ -1303,72 +1354,75 @@ LayoutAnimationKeyFrameManager::pullTransaction( // Additionally, this should allow us to avoid performing index adjustment // between this list of conflicting animations and the batch we're about // to execute. - mutations = ShadowViewMutationList{}; - for (auto &mutation : finalConflictingMutations) { - mutations.push_back(mutation); + finalConflictingMutations.insert( + finalConflictingMutations.end(), + immediateMutations.begin(), + immediateMutations.end()); + mutations = finalConflictingMutations; + } /* if (currentAnimation) */ else { + // If there's no "next" animation, make sure we queue up "final" + // operations from all ongoing, conflicting animations. +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING + LOG(ERROR) << "No Animation: Queue up final conflicting animations"; +#endif + ShadowViewMutationList finalMutationsForConflictingAnimations{}; + for (auto &conflictingKeyframeTuple : conflictingAnimations) { + auto &keyFrame = std::get<0>(conflictingKeyframeTuple); + if (keyFrame.finalMutationForKeyFrame.hasValue()) { + PrintMutationInstruction( + "No Animation: Queueing final mutation instruction", + *keyFrame.finalMutationForKeyFrame); + finalMutationsForConflictingAnimations.push_back( + *keyFrame.finalMutationForKeyFrame); + } } - // Before computing mutations based on animations / final mutations for - // this frame, we want to update any pending final mutations since they - // will execute *after* this batch of immediate mutations. Important case - // to consider (as an example, there are other interesting cases): there's - // a delayed "Remove", then an immediate "insert" is scheduled for an - // earlier index with the same parent. The remove needs to be adjusted - // upward here. Conversely, Inserts at later indices will assume the - // remove has already been executed, which may not be the case. + // Make sure that all operations execute in the proper order. + // REMOVE operations with highest indices must operate first. + std::stable_sort( + finalMutationsForConflictingAnimations.begin(), + finalMutationsForConflictingAnimations.end(), + &shouldFirstComeBeforeSecondMutation); + #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING LOG(ERROR) - << "Adjust immediateMutations and delayed mutations, including just-queued animations, based on each one"; + << "No Animation: Adjust delayed mutations based on all finalMutationsForConflictingAnimations"; #endif - for (auto &mutation : immediateMutations) { - if (mutation.type == ShadowViewMutation::Type::Remove) { - adjustLastAnimationDelayedMutationIndicesForMutation( - surfaceId, mutation); - } else if (mutation.type == ShadowViewMutation::Type::Insert) { - adjustImmediateMutationIndicesForDelayedMutations( - surfaceId, mutation); + for (auto &mutation : finalMutationsForConflictingAnimations) { + if (mutation.type == ShadowViewMutation::Type::Remove || + mutation.type == ShadowViewMutation::Type::Insert) { adjustDelayedMutationIndicesForMutation(surfaceId, mutation); } } - // These will be executed immediately. These should already be sorted - // properly. - mutations.insert( - mutations.end(), - immediateMutations.begin(), - immediateMutations.end()); - } /* if (currentAnimation) */ else { // The ShadowTree layer doesn't realize that certain operations have been // delayed, so we must adjust all Remove and Insert operations based on // what else has been deferred, whether we are executing this immediately // or later. +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING + LOG(ERROR) + << "No Animation: Adjust mutations based on remaining delayed mutations"; +#endif + ConsecutiveAdjustmentMetadata consecutiveAdjustmentMetadata{}; for (auto &mutation : mutations) { if (mutation.type == ShadowViewMutation::Type::Remove || mutation.type == ShadowViewMutation::Type::Insert) { adjustImmediateMutationIndicesForDelayedMutations( - surfaceId, mutation); - adjustDelayedMutationIndicesForMutation(surfaceId, mutation); + surfaceId, mutation, consecutiveAdjustmentMetadata); } } - // If there's no "next" animation, make sure we queue up "final" - // operations from all ongoing, conflicting animations. - ShadowViewMutationList finalMutationsForConflictingAnimations{}; - for (auto &conflictingKeyframeTuple : conflictingAnimations) { - auto &keyFrame = std::get<0>(conflictingKeyframeTuple); - if (keyFrame.finalMutationForKeyFrame.hasValue()) { - finalMutationsForConflictingAnimations.push_back( - *keyFrame.finalMutationForKeyFrame); +#ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING + LOG(ERROR) + << "No Animation: Adjust delayed mutations based on all immediate mutations"; +#endif + for (auto &mutation : mutations) { + if (mutation.type == ShadowViewMutation::Type::Remove || + mutation.type == ShadowViewMutation::Type::Insert) { + adjustDelayedMutationIndicesForMutation(surfaceId, mutation); } } - // Make sure that all operations execute in the proper order. - // REMOVE operations with highest indices must operate first. - std::stable_sort( - finalMutationsForConflictingAnimations.begin(), - finalMutationsForConflictingAnimations.end(), - &shouldFirstComeBeforeSecondMutation); - // Append mutations to this list and swap - so that the final // conflicting mutations happen before any other mutations finalMutationsForConflictingAnimations.insert( @@ -1388,13 +1442,6 @@ LayoutAnimationKeyFrameManager::pullTransaction( ShadowViewMutationList mutationsForAnimation{}; animationMutationsForFrame(surfaceId, mutationsForAnimation, now); - // Erase any remaining animations that conflict with these mutations - // In some marginal cases, a DELETE animation can be queued up and a final - // DELETE mutation be executed by the animation driver. These cases deserve - // further scrutiny, but for now to prevent crashes, just make sure the queued - // DELETE operations are removed. - getAndEraseConflictingAnimations(surfaceId, mutationsForAnimation, true); - // If any delayed removes were executed, update remaining delayed keyframes #ifdef LAYOUT_ANIMATION_VERBOSE_LOGGING LOG(ERROR) @@ -1427,7 +1474,8 @@ LayoutAnimationKeyFrameManager::pullTransaction( if (keyframe.invalidated) { continue; } - if (keyframe.finalMutationForKeyFrame) { + if (keyframe.finalMutationForKeyFrame && + !mutatedViewIsVirtual(*keyframe.finalMutationForKeyFrame)) { std::string msg = "Animation " + std::to_string(i) + " keyframe " + std::to_string(j) + ": Final Animation"; PrintMutationInstruction(msg, *keyframe.finalMutationForKeyFrame); diff --git a/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h b/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h index 686fcaa9da9e21..891d25d46d11ea 100644 --- a/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h +++ b/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h @@ -110,6 +110,12 @@ struct AnimationKeyFrame { bool invalidated{false}; }; +struct ConsecutiveAdjustmentMetadata { + Tag lastAdjustedParent{-1}; + int lastAdjustedDelta{0}; + int lastIndexOriginal{0}; +}; + class LayoutAnimationCallbackWrapper { public: LayoutAnimationCallbackWrapper(jsi::Function &&callback) @@ -212,16 +218,14 @@ class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate, void adjustImmediateMutationIndicesForDelayedMutations( SurfaceId surfaceId, ShadowViewMutation &mutation, - ShadowViewMutationList *auxiliaryMutations = nullptr) const; + ConsecutiveAdjustmentMetadata &consecutiveAdjustmentMetadata, + bool skipLastAnimation = false, + bool lastAnimationOnly = false) const; void adjustDelayedMutationIndicesForMutation( SurfaceId surfaceId, ShadowViewMutation const &mutation, - bool lastAnimationOnly = false) const; - - void adjustLastAnimationDelayedMutationIndicesForMutation( - SurfaceId surfaceId, - ShadowViewMutation const &mutation) const; + bool skipLastAnimation = false) const; std::vector> getAndEraseConflictingAnimations( @@ -304,6 +308,14 @@ static inline bool shouldFirstComeBeforeSecondMutation( return true; } + // Update comes last, before deletes + if (rhs.type == ShadowViewMutation::Type::Update) { + return true; + } + if (lhs.type == ShadowViewMutation::Type::Update) { + return false; + } + // Remove comes before insert if (lhs.type == ShadowViewMutation::Type::Remove && rhs.type == ShadowViewMutation::Type::Insert) { @@ -313,6 +325,16 @@ static inline bool shouldFirstComeBeforeSecondMutation( lhs.type == ShadowViewMutation::Type::Insert) { return false; } + + // Create comes before insert + if (lhs.type == ShadowViewMutation::Type::Create && + rhs.type == ShadowViewMutation::Type::Insert) { + return true; + } + if (rhs.type == ShadowViewMutation::Type::Create && + lhs.type == ShadowViewMutation::Type::Insert) { + return false; + } } else { // Make sure that removes on the same level are sorted - highest indices // must come first. diff --git a/ReactCommon/react/renderer/components/image/ImageShadowNode.h b/ReactCommon/react/renderer/components/image/ImageShadowNode.h index 12490840b611bd..0e60f78deef2dd 100644 --- a/ReactCommon/react/renderer/components/image/ImageShadowNode.h +++ b/ReactCommon/react/renderer/components/image/ImageShadowNode.h @@ -46,7 +46,7 @@ class ImageShadowNode final : public ConcreteViewShadowNode< ShadowNodeFamilyFragment const &familyFragment, ComponentDescriptor const &componentDescriptor) { auto imageSource = ImageSource{ImageSource::Type::Invalid}; - return {imageSource, {imageSource, nullptr, nullptr}, 0}; + return {imageSource, {imageSource, nullptr}, 0}; } #pragma mark - LayoutableShadowNode diff --git a/ReactCommon/react/renderer/components/slider/SliderShadowNode.h b/ReactCommon/react/renderer/components/slider/SliderShadowNode.h index c012ff45a517fa..82ab4d0b1dc6c2 100644 --- a/ReactCommon/react/renderer/components/slider/SliderShadowNode.h +++ b/ReactCommon/react/renderer/components/slider/SliderShadowNode.h @@ -44,13 +44,13 @@ class SliderShadowNode final : public ConcreteViewShadowNode< ComponentDescriptor const &componentDescriptor) { auto imageSource = ImageSource{ImageSource::Type::Invalid}; return {imageSource, - {imageSource, nullptr, nullptr}, + {imageSource, nullptr}, imageSource, - {imageSource, nullptr, nullptr}, + {imageSource, nullptr}, imageSource, - {imageSource, nullptr, nullptr}, + {imageSource, nullptr}, imageSource, - {imageSource, nullptr, nullptr}}; + {imageSource, nullptr}}; } #pragma mark - LayoutableShadowNode diff --git a/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp b/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp index 1295108286f14b..ee16f5d02655c9 100644 --- a/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp +++ b/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.cpp @@ -38,12 +38,17 @@ static jsi::Value linesMeasurementsPayload( void ParagraphEventEmitter::onTextLayout( LinesMeasurements const &linesMeasurements) const { - dispatchEvent( - "textLayout", - [linesMeasurements](jsi::Runtime &runtime) { - return linesMeasurementsPayload(runtime, linesMeasurements); - }, - EventPriority::AsynchronousBatched); + { + std::lock_guard guard(linesMeasurementsMutex_); + if (linesMeasurementsMetrics_ == linesMeasurements) { + return; + } + linesMeasurementsMetrics_ = linesMeasurements; + } + + dispatchEvent("textLayout", [linesMeasurements](jsi::Runtime &runtime) { + return linesMeasurementsPayload(runtime, linesMeasurements); + }); } } // namespace react diff --git a/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.h b/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.h index b73eb967d5afb2..597ef6baa2ddcb 100644 --- a/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.h +++ b/ReactCommon/react/renderer/components/text/ParagraphEventEmitter.h @@ -18,6 +18,10 @@ class ParagraphEventEmitter : public ViewEventEmitter { using ViewEventEmitter::ViewEventEmitter; void onTextLayout(LinesMeasurements const &linesMeasurements) const; + + private: + mutable std::mutex linesMeasurementsMutex_; + mutable LinesMeasurements linesMeasurementsMetrics_; }; } // namespace react diff --git a/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp b/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp index b4fc65923a619d..87745126463ca9 100644 --- a/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp +++ b/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp @@ -164,24 +164,11 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) { updateStateIfNeeded(content); - if (content.attachments.empty()) { -#ifndef ANDROID - if (getConcreteProps().onTextLayout) { - // `onTextLayout` needs to be called even if text is empty - // to be compatible with Paper. - getConcreteEventEmitter().onTextLayout({}); - } -#endif - // No attachments, nothing to layout. - return; - } - auto measurement = textLayoutManager_->measure( AttributedStringBox{content.attributedString}, content.paragraphAttributes, layoutConstraints); -#ifndef ANDROID if (getConcreteProps().onTextLayout) { auto linesMeasurements = textLayoutManager_->measureLines( content.attributedString, @@ -189,7 +176,11 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) { measurement.size); getConcreteEventEmitter().onTextLayout(linesMeasurements); } -#endif + + if (content.attachments.empty()) { + // No attachments to layout. + return; + } // Iterating on attachments, we clone shadow nodes and moving // `paragraphShadowNode` that represents clones of `this` object. diff --git a/ReactCommon/react/renderer/components/view/TouchEventEmitter.cpp b/ReactCommon/react/renderer/components/view/TouchEventEmitter.cpp index 4b582c6ba47bb3..738dec8d6467c7 100644 --- a/ReactCommon/react/renderer/components/view/TouchEventEmitter.cpp +++ b/ReactCommon/react/renderer/components/view/TouchEventEmitter.cpp @@ -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 { diff --git a/ReactCommon/react/renderer/components/view/ViewEventEmitter.cpp b/ReactCommon/react/renderer/components/view/ViewEventEmitter.cpp index fe2a83f5f43d8d..e26f7c143a6dfc 100644 --- a/ReactCommon/react/renderer/components/view/ViewEventEmitter.cpp +++ b/ReactCommon/react/renderer/components/view/ViewEventEmitter.cpp @@ -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 diff --git a/ReactCommon/react/renderer/components/view/ViewEventEmitter.h b/ReactCommon/react/renderer/components/view/ViewEventEmitter.h index 0aa1f5e588eba2..237c9a53b1038b 100644 --- a/ReactCommon/react/renderer/components/view/ViewEventEmitter.h +++ b/ReactCommon/react/renderer/components/view/ViewEventEmitter.h @@ -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 diff --git a/ReactCommon/react/renderer/core/EventQueue.cpp b/ReactCommon/react/renderer/core/EventQueue.cpp index 2861d7a14f05ec..2db952292ff9ea 100644 --- a/ReactCommon/react/renderer/core/EventQueue.cpp +++ b/ReactCommon/react/renderer/core/EventQueue.cpp @@ -8,6 +8,7 @@ #include "EventQueue.h" #include "EventEmitter.h" +#include "ShadowNodeFamily.h" namespace facebook { namespace react { @@ -35,6 +36,12 @@ void EventQueue::enqueueEvent(const RawEvent &rawEvent) const { void EventQueue::enqueueStateUpdate(const StateUpdate &stateUpdate) const { { std::lock_guard lock(queueMutex_); + if (!stateUpdateQueue_.empty()) { + auto const position = stateUpdateQueue_.back(); + if (stateUpdate.family == position.family) { + stateUpdateQueue_.pop_back(); + } + } stateUpdateQueue_.push_back(stateUpdate); } diff --git a/ReactCommon/react/renderer/core/EventTarget.cpp b/ReactCommon/react/renderer/core/EventTarget.cpp index c9f418c2d88fa6..01a5a77bd50f48 100644 --- a/ReactCommon/react/renderer/core/EventTarget.cpp +++ b/ReactCommon/react/renderer/core/EventTarget.cpp @@ -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))), diff --git a/ReactCommon/react/renderer/core/EventTarget.h b/ReactCommon/react/renderer/core/EventTarget.h index 8bd7a03a1995ac..92f382a96c4965 100644 --- a/ReactCommon/react/renderer/core/EventTarget.h +++ b/ReactCommon/react/renderer/core/EventTarget.h @@ -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 diff --git a/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp b/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp index 51b29867787525..3809740c83e72d 100644 --- a/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp +++ b/ReactCommon/react/renderer/core/LayoutableShadowNode.cpp @@ -238,7 +238,17 @@ ShadowNode::Shared LayoutableShadowNode::findNodeAtPoint( auto newPoint = point - transformedFrame.origin - layoutableShadowNode->getContentOriginOffset(); - for (const auto &childShadowNode : node->getChildren()) { + + auto sortedChildren = node->getChildren(); + std::stable_sort( + sortedChildren.begin(), + sortedChildren.end(), + [](auto const &lhs, auto const &rhs) -> bool { + return lhs->getOrderIndex() < rhs->getOrderIndex(); + }); + + for (auto it = sortedChildren.rbegin(); it != sortedChildren.rend(); it++) { + auto const &childShadowNode = *it; auto hitView = findNodeAtPoint(childShadowNode, newPoint); if (hitView) { return hitView; diff --git a/ReactCommon/react/renderer/core/tests/FindNodeAtPointTest.cpp b/ReactCommon/react/renderer/core/tests/FindNodeAtPointTest.cpp index 79787b4bf9bfef..37f1c4106db603 100644 --- a/ReactCommon/react/renderer/core/tests/FindNodeAtPointTest.cpp +++ b/ReactCommon/react/renderer/core/tests/FindNodeAtPointTest.cpp @@ -6,134 +6,224 @@ */ #include +#include +#include + #include "TestComponent.h" using namespace facebook::react; -/* - *┌─────────────────────────┐ - *│nodeA_ │ - *│ │ - *│ │ - *│ │ - *│ │ - *│ │ - *│ │ - *│ ┌────────────────┐ │ - *│ │nodeAA_ │ │ - *│ │ │ │ - *│ │ ┌───────┐ │ │ - *│ │ │nodeAA_│ │ │ - *│ │ │ │ │ │ - *│ │ └───────┘ │ │ - *│ └────────────────┘ │ - *└─────────────────────────┘ - */ -class FindNodeAtPointTest : public ::testing::Test { - protected: - FindNodeAtPointTest() - : eventDispatcher_(std::shared_ptr()), - componentDescriptor_(TestComponentDescriptor({eventDispatcher_})) { - auto traits = TestShadowNode::BaseTraits(); - - auto familyA = std::make_shared( - ShadowNodeFamilyFragment{ - /* .tag = */ 9, - /* .surfaceId = */ 1, - /* .eventEmitter = */ nullptr, - }, - eventDispatcher_, - componentDescriptor_); - - nodeA_ = std::make_shared( - ShadowNodeFragment{ - /* .props = */ std::make_shared(), - /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), - }, - familyA, - traits); - - auto familyAA = std::make_shared( - ShadowNodeFamilyFragment{ - /* .tag = */ 10, - /* .surfaceId = */ 1, - /* .eventEmitter = */ nullptr, - }, - eventDispatcher_, - componentDescriptor_); - - nodeAA_ = std::make_shared( - ShadowNodeFragment{ - /* .props = */ std::make_shared(), - /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), - }, - familyAA, - traits); - - auto familyAAA = std::make_shared( - ShadowNodeFamilyFragment{ - /* .tag = */ 11, - /* .surfaceId = */ 1, - /* .eventEmitter = */ nullptr, - }, - eventDispatcher_, - componentDescriptor_); - - nodeAAA_ = std::make_shared( - ShadowNodeFragment{ - /* .props = */ std::make_shared(), - /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), - }, - familyAAA, - traits); - - nodeA_->appendChild(nodeAA_); - nodeAA_->appendChild(nodeAAA_); - - auto layoutMetrics = EmptyLayoutMetrics; - - layoutMetrics.frame = facebook::react::Rect{ - facebook::react::Point{0, 0}, facebook::react::Size{1000, 1000}}; - nodeA_->setLayoutMetrics(layoutMetrics); - - layoutMetrics.frame = facebook::react::Rect{ - facebook::react::Point{100, 100}, facebook::react::Size{100, 100}}; - nodeAA_->setLayoutMetrics(layoutMetrics); - - layoutMetrics.frame = facebook::react::Rect{facebook::react::Point{10, 10}, - facebook::react::Size{10, 10}}; - nodeAAA_->setLayoutMetrics(layoutMetrics); - } - - std::shared_ptr eventDispatcher_; - std::shared_ptr nodeA_; - std::shared_ptr nodeAA_; - std::shared_ptr nodeAAA_; - TestComponentDescriptor componentDescriptor_; -}; - -TEST_F(FindNodeAtPointTest, withoutTransform) { +TEST(FindNodeAtPointTest, withoutTransform) { + auto builder = simpleComponentBuilder(); + + // clang-format off + auto element = + Element() + .tag(1) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.size = {1000, 1000}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + .children({ + Element() + .tag(2) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {100, 100}; + layoutMetrics.frame.size = {100, 100}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + .children({ + Element() + .tag(3) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {10, 10}; + layoutMetrics.frame.size = {10, 10}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + }) + }); + + auto parentShadowNode = builder.build(element); + EXPECT_EQ( - LayoutableShadowNode::findNodeAtPoint(nodeA_, {115, 115}), nodeAAA_); - EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(nodeA_, {105, 105}), nodeAA_); - EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(nodeA_, {900, 900}), nodeA_); + LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {115, 115})->getTag(), 3); + EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {105, 105})->getTag(), 2); + EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {900, 900})->getTag(), 1); EXPECT_EQ( - LayoutableShadowNode::findNodeAtPoint(nodeA_, {1001, 1001}), nullptr); + LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {1001, 1001}), nullptr); } -TEST_F(FindNodeAtPointTest, viewIsTranslated) { - nodeA_->_contentOriginOffset = {-100, -100}; +TEST(FindNodeAtPointTest, viewIsTranslated) { + auto builder = simpleComponentBuilder(); + + // clang-format off + auto element = + Element() + .tag(1) + .finalize([](ScrollViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.size = {1000, 1000}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + .stateData([](ScrollViewState &data) { + data.contentOffset = {100, 100}; + }) + .children({ + Element() + .tag(2) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {100, 100}; + layoutMetrics.frame.size = {100, 100}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + .children({ + Element() + .tag(3) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {10, 10}; + layoutMetrics.frame.size = {10, 10}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + }) + }); + + auto parentShadowNode = builder.build(element); EXPECT_EQ( - LayoutableShadowNode::findNodeAtPoint(nodeA_, {15, 15})->getTag(), - nodeAAA_->getTag()); - EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(nodeA_, {5, 5}), nodeAA_); + LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {15, 15})->getTag(), + 3); + EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {5, 5})->getTag(), 2); } -TEST_F(FindNodeAtPointTest, viewIsScaled) { - nodeAAA_->_transform = Transform::Identity() * Transform::Scale(0.5, 0.5, 0); +TEST(FindNodeAtPointTest, viewIsScaled) { + auto builder = simpleComponentBuilder(); + + // clang-format off + auto element = + Element() + .tag(1) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.size = {1000, 1000}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + .children({ + Element() + .tag(2) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {100, 100}; + layoutMetrics.frame.size = {100, 100}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + .children({ + Element() + .tag(3) + .props([] { + auto sharedProps = std::make_shared(); + sharedProps->transform = Transform::Scale(0.5, 0.5, 0); + return sharedProps; + }) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {10, 10}; + layoutMetrics.frame.size = {10, 10}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + }) + }); + + auto parentShadowNode = builder.build(element); + + EXPECT_EQ( + LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {119, 119})->getTag(), + 2); +} +TEST(FindNodeAtPointTest, overlappingViews) { + auto builder = simpleComponentBuilder(); + + // clang-format off + auto element = + Element() + .tag(1) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.size = {100, 100}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + .children({ + Element() + .tag(2) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {25, 25}; + layoutMetrics.frame.size = {50, 50}; + shadowNode.setLayoutMetrics(layoutMetrics); + }), + Element() + .tag(3) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {50, 50}; + layoutMetrics.frame.size = {50, 50}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + }); + + auto parentShadowNode = builder.build(element); + EXPECT_EQ( - LayoutableShadowNode::findNodeAtPoint(nodeA_, {119, 119})->getTag(), - nodeAA_->getTag()); + LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {50, 50})->getTag(), 3); } + +TEST(FindNodeAtPointTest, overlappingViewsWithZIndex) { + auto builder = simpleComponentBuilder(); + + // clang-format off + auto element = + Element() + .tag(1) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.size = {100, 100}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + .children({ + Element() + .tag(2) + .props([] { + auto sharedProps = std::make_shared(); + sharedProps->zIndex = 1; + auto &yogaStyle = sharedProps->yogaStyle; + yogaStyle.positionType() = YGPositionTypeAbsolute; + return sharedProps; + }) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {25, 25}; + layoutMetrics.frame.size = {50, 50}; + shadowNode.setLayoutMetrics(layoutMetrics); + }), + Element() + .tag(3) + .finalize([](ViewShadowNode &shadowNode){ + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {50, 50}; + layoutMetrics.frame.size = {50, 50}; + shadowNode.setLayoutMetrics(layoutMetrics); + }) + }); + + auto parentShadowNode = builder.build(element); + + EXPECT_EQ( + LayoutableShadowNode::findNodeAtPoint(parentShadowNode, {50, 50})->getTag(), 2); +} + + diff --git a/ReactCommon/react/renderer/graphics/BUCK b/ReactCommon/react/renderer/graphics/BUCK index 9b7fd05c2d0b46..87064ce89c0436 100644 --- a/ReactCommon/react/renderer/graphics/BUCK +++ b/ReactCommon/react/renderer/graphics/BUCK @@ -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", diff --git a/ReactCommon/react/renderer/graphics/React-graphics.podspec b/ReactCommon/react/renderer/graphics/React-graphics.podspec index 5a2643f61f98fd..62a5939f25b085 100644 --- a/ReactCommon/react/renderer/graphics/React-graphics.podspec +++ b/ReactCommon/react/renderer/graphics/React-graphics.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "9.0", :tvos => "10.0" } + s.platforms = { :ios => "9.0" } s.source = source s.library = "stdc++" s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags diff --git a/ReactCommon/react/renderer/graphics/platform/ios/Color.cpp b/ReactCommon/react/renderer/graphics/platform/ios/Color.cpp index 8cbdeee7fdc60b..c1023384cac7bd 100644 --- a/ReactCommon/react/renderer/graphics/platform/ios/Color.cpp +++ b/ReactCommon/react/renderer/graphics/platform/ios/Color.cpp @@ -12,28 +12,27 @@ 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) { +ColorComponents colorComponentsFromColor(SharedColor sharedColor) { + if (!sharedColor) { // 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]}; + 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() { diff --git a/ReactCommon/react/renderer/graphics/platform/ios/Color.h b/ReactCommon/react/renderer/graphics/platform/ios/Color.h index abaf98f4245e56..0b8ab406b40f50 100644 --- a/ReactCommon/react/renderer/graphics/platform/ios/Color.h +++ b/ReactCommon/react/renderer/graphics/platform/ios/Color.h @@ -7,17 +7,17 @@ #pragma once -#include +#include -#include #include #include namespace facebook { namespace react { -using Color = CGColor; -using SharedColor = std::shared_ptr; +using Color = int32_t; + +using SharedColor = better::optional; SharedColor colorFromComponents(ColorComponents components); ColorComponents colorComponentsFromColor(SharedColor color); diff --git a/ReactCommon/react/renderer/imagemanager/BUCK b/ReactCommon/react/renderer/imagemanager/BUCK index a9797d2884b068..52f99b3fec6fc0 100644 --- a/ReactCommon/react/renderer/imagemanager/BUCK +++ b/ReactCommon/react/renderer/imagemanager/BUCK @@ -70,7 +70,6 @@ rn_xplat_cxx_library( ios_exported_headers = subdir_glob( [ ("", "*.h"), - ("platform/ios", "RCTImageInstrumentationProxy.h"), ("platform/ios", "RCTImagePrimitivesConversions.h"), ], prefix = "react/renderer/imagemanager", diff --git a/ReactCommon/react/renderer/imagemanager/ImageInstrumentation.h b/ReactCommon/react/renderer/imagemanager/ImageInstrumentation.h deleted file mode 100644 index c1671cb6790376..00000000000000 --- a/ReactCommon/react/renderer/imagemanager/ImageInstrumentation.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -namespace facebook { -namespace react { - -/* - * A base class for performing image loading instrumentation. - * The actual instrumentation is app, platform, and image loader-specific. - */ -class ImageInstrumentation { - public: - virtual ~ImageInstrumentation() noexcept = default; - - /** - * Mark that the image content is set on the native image component on screen. - */ - virtual void didSetImage() const = 0; - - /** - * Mark that the image view starts to be visible on screen. - */ - virtual void didEnterVisibilityRange() const = 0; - - /** - * Mark that the image view is no longer visible on screen. - */ - virtual void didExitVisibilityRange() const = 0; -}; - -} // namespace react -} // namespace facebook diff --git a/ReactCommon/react/renderer/imagemanager/ImageRequest.h b/ReactCommon/react/renderer/imagemanager/ImageRequest.h index a77c1f76c794dd..e3f3047566429a 100644 --- a/ReactCommon/react/renderer/imagemanager/ImageRequest.h +++ b/ReactCommon/react/renderer/imagemanager/ImageRequest.h @@ -7,7 +7,6 @@ #pragma once -#include #include #include #include @@ -31,8 +30,7 @@ class ImageRequest final { */ ImageRequest( const ImageSource &imageSource, - std::shared_ptr telemetry, - std::shared_ptr instrumentation); + std::shared_ptr telemetry); /* * The move constructor. @@ -78,20 +76,6 @@ class ImageRequest final { */ const std::shared_ptr &getSharedTelemetry() const; - /* - * Returns stored image instrumentation object as a shared pointer. - * Retain this *or* `ImageRequest` to ensure a correct lifetime of the object. - */ - const std::shared_ptr - &getSharedImageInstrumentation() const; - - /* - * Returns the image instrumentation object specific to this request. - * Use this if a correct lifetime of the object is ensured in some other way - * (e.g. by retaining an `ImageRequest`). - */ - const ImageInstrumentation &getImageInstrumentation() const; - private: /* * Image source associated with the request. @@ -108,11 +92,6 @@ class ImageRequest final { */ std::shared_ptr coordinator_{}; - /* - * Image instrumentation specific to the request. - */ - std::shared_ptr instrumentation_; - /* * Function we can call to cancel image request (see destructor). */ diff --git a/ReactCommon/react/renderer/imagemanager/ImageTelemetry.h b/ReactCommon/react/renderer/imagemanager/ImageTelemetry.h index b85ea7afad8a11..312d19b19e9862 100644 --- a/ReactCommon/react/renderer/imagemanager/ImageTelemetry.h +++ b/ReactCommon/react/renderer/imagemanager/ImageTelemetry.h @@ -5,6 +5,8 @@ * LICENSE file in the root directory of this source tree. */ +#pragma once + #include #include diff --git a/ReactCommon/react/renderer/imagemanager/platform/cxx/react/renderer/imagemanager/ImageManager.cpp b/ReactCommon/react/renderer/imagemanager/platform/cxx/react/renderer/imagemanager/ImageManager.cpp index c1d4d1ac63e7b4..245c3b5e7a783b 100644 --- a/ReactCommon/react/renderer/imagemanager/platform/cxx/react/renderer/imagemanager/ImageManager.cpp +++ b/ReactCommon/react/renderer/imagemanager/platform/cxx/react/renderer/imagemanager/ImageManager.cpp @@ -24,7 +24,7 @@ ImageRequest ImageManager::requestImage( const ImageSource &imageSource, SurfaceId surfaceId) const { // Not implemented. - return ImageRequest(imageSource, nullptr, nullptr); + return ImageRequest(imageSource, nullptr); } } // namespace react diff --git a/ReactCommon/react/renderer/imagemanager/platform/cxx/react/renderer/imagemanager/ImageRequest.cpp b/ReactCommon/react/renderer/imagemanager/platform/cxx/react/renderer/imagemanager/ImageRequest.cpp index 9cdbc7fdd0dda0..9f811bc4d4a3d2 100644 --- a/ReactCommon/react/renderer/imagemanager/platform/cxx/react/renderer/imagemanager/ImageRequest.cpp +++ b/ReactCommon/react/renderer/imagemanager/platform/cxx/react/renderer/imagemanager/ImageRequest.cpp @@ -12,19 +12,15 @@ namespace react { ImageRequest::ImageRequest( const ImageSource &imageSource, - std::shared_ptr telemetry, - std::shared_ptr instrumentation) - : imageSource_(imageSource), - telemetry_(telemetry), - instrumentation_(instrumentation) { + std::shared_ptr telemetry) + : imageSource_(imageSource), telemetry_(telemetry) { // Not implemented. } ImageRequest::ImageRequest(ImageRequest &&other) noexcept : imageSource_(std::move(other.imageSource_)), telemetry_(std::move(other.telemetry_)), - coordinator_(std::move(other.coordinator_)), - instrumentation_(std::move(other.instrumentation_)) { + coordinator_(std::move(other.coordinator_)) { // Not implemented. } @@ -44,16 +40,5 @@ const std::shared_ptr abort(); } -const std::shared_ptr - &ImageRequest::getSharedImageInstrumentation() const { - // Not implemented - abort(); -} - -const ImageInstrumentation &ImageRequest::getImageInstrumentation() const { - // Not implemented - abort(); -} - } // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/imagemanager/platform/ios/ImageRequest.cpp b/ReactCommon/react/renderer/imagemanager/platform/ios/ImageRequest.cpp index 216e1ffdfd986d..ce7f9b2d04549f 100644 --- a/ReactCommon/react/renderer/imagemanager/platform/ios/ImageRequest.cpp +++ b/ReactCommon/react/renderer/imagemanager/platform/ios/ImageRequest.cpp @@ -12,24 +12,19 @@ namespace react { ImageRequest::ImageRequest( const ImageSource &imageSource, - std::shared_ptr telemetry, - std::shared_ptr instrumentation) - : imageSource_(imageSource), - telemetry_(telemetry), - instrumentation_(instrumentation) { + std::shared_ptr telemetry) + : imageSource_(imageSource), telemetry_(telemetry) { coordinator_ = std::make_shared(); } ImageRequest::ImageRequest(ImageRequest &&other) noexcept : imageSource_(std::move(other.imageSource_)), telemetry_(std::move(other.telemetry_)), - coordinator_(std::move(other.coordinator_)), - instrumentation_(std::move(other.instrumentation_)) { + coordinator_(std::move(other.coordinator_)) { other.moved_ = true; other.coordinator_ = nullptr; other.cancelRequest_ = nullptr; other.telemetry_ = nullptr; - other.instrumentation_ = nullptr; } ImageRequest::~ImageRequest() { @@ -58,14 +53,5 @@ const std::shared_ptr return coordinator_; } -const std::shared_ptr - &ImageRequest::getSharedImageInstrumentation() const { - return instrumentation_; -} - -const ImageInstrumentation &ImageRequest::getImageInstrumentation() const { - return *instrumentation_; -} - } // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImageInstrumentationProxy.h b/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImageInstrumentationProxy.h deleted file mode 100644 index 95726a5ff0cccf..00000000000000 --- a/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImageInstrumentationProxy.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -#include -#include - -NS_ASSUME_NONNULL_BEGIN - -namespace facebook { -namespace react { - -class RCTImageInstrumentationProxy final : public ImageInstrumentation { - public: - RCTImageInstrumentationProxy(id imageLoader); - ~RCTImageInstrumentationProxy(); - - void didSetImage() const override; - void didEnterVisibilityRange() const override; - void didExitVisibilityRange() const override; - - void trackNativeImageView(UIView *imageView) const; - void setImageURLLoaderRequest(RCTImageURLLoaderRequest *request); - - private: - __weak id imageLoader_; - RCTImageURLLoaderRequest *imageURLLoaderRequest_; -}; - -} // namespace react -} // namespace facebook - -NS_ASSUME_NONNULL_END diff --git a/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImageInstrumentationProxy.mm b/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImageInstrumentationProxy.mm deleted file mode 100644 index 1ed24f9faacda3..00000000000000 --- a/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImageInstrumentationProxy.mm +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "RCTImageInstrumentationProxy.h" - -namespace facebook { -namespace react { - -RCTImageInstrumentationProxy::RCTImageInstrumentationProxy(id imageLoader) - : imageLoader_(imageLoader) -{ -} - -RCTImageInstrumentationProxy::~RCTImageInstrumentationProxy() -{ - if (!imageURLLoaderRequest_) { - return; - } - [imageLoader_ trackURLImageDidDestroy:imageURLLoaderRequest_]; -} - -void RCTImageInstrumentationProxy::didSetImage() const -{ - if (!RCTImageLoadingPerfInstrumentationEnabled()) { - return; - } - - if (!imageURLLoaderRequest_) { - return; - } - - [imageLoader_ trackURLImageContentDidSetForRequest:imageURLLoaderRequest_]; -} - -void RCTImageInstrumentationProxy::didEnterVisibilityRange() const -{ - if (!RCTImageLoadingPerfInstrumentationEnabled()) { - return; - } - - // TODO (T58941612): Not yet supported. - if (!imageURLLoaderRequest_) { - return; - } -} - -void RCTImageInstrumentationProxy::didExitVisibilityRange() const -{ - if (!RCTImageLoadingPerfInstrumentationEnabled()) { - return; - } - - // TODO (T58941612): Not yet supported. - if (!imageURLLoaderRequest_) { - return; - } -} - -void RCTImageInstrumentationProxy::trackNativeImageView(UIView *imageView) const -{ - if (!RCTImageLoadingPerfInstrumentationEnabled()) { - return; - } - - if (!imageURLLoaderRequest_) { - return; - } - [imageLoader_ trackURLImageVisibilityForRequest:imageURLLoaderRequest_ imageView:imageView]; -} - -void RCTImageInstrumentationProxy::setImageURLLoaderRequest(RCTImageURLLoaderRequest *request) -{ - imageURLLoaderRequest_ = request; -} - -} // namespace react -} // namespace facebook diff --git a/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImageManager.mm b/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImageManager.mm index b719be1158fbb8..08e0c255885d3f 100644 --- a/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImageManager.mm +++ b/ReactCommon/react/renderer/imagemanager/platform/ios/RCTImageManager.mm @@ -16,7 +16,6 @@ #import #import -#import "RCTImageInstrumentationProxy.h" #import "RCTImagePrimitivesConversions.h" using namespace facebook::react; @@ -41,10 +40,9 @@ - (ImageRequest)requestImage:(ImageSource)imageSource surfaceId:(SurfaceId)surfa { SystraceSection s("RCTImageManager::requestImage"); - auto imageInstrumentation = std::make_shared(_imageLoader); auto telemetry = std::make_shared(surfaceId); telemetry->willRequestUrl(); - auto imageRequest = ImageRequest(imageSource, telemetry, imageInstrumentation); + auto imageRequest = ImageRequest(imageSource, telemetry); auto weakObserverCoordinator = (std::weak_ptr)imageRequest.getSharedObserverCoordinator(); @@ -108,10 +106,6 @@ - (ImageRequest)requestImage:(ImageSource)imageSource surfaceId:(SurfaceId)surfa completionBlock:completionBlock]; RCTImageLoaderCancellationBlock cancelationBlock = loaderRequest.cancellationBlock; sharedCancelationFunction.assign([cancelationBlock]() { cancelationBlock(); }); - - if (imageInstrumentation) { - imageInstrumentation->setImageURLLoaderRequest(loaderRequest); - } }); return imageRequest; diff --git a/ReactCommon/react/renderer/imagemanager/platform/ios/RCTSyncImageManager.mm b/ReactCommon/react/renderer/imagemanager/platform/ios/RCTSyncImageManager.mm index df89da51cecf31..7a36d42ea4a283 100644 --- a/ReactCommon/react/renderer/imagemanager/platform/ios/RCTSyncImageManager.mm +++ b/ReactCommon/react/renderer/imagemanager/platform/ios/RCTSyncImageManager.mm @@ -37,7 +37,7 @@ - (instancetype)initWithImageLoader:(id)i - (ImageRequest)requestImage:(ImageSource)imageSource surfaceId:(SurfaceId)surfaceId { auto telemetry = std::make_shared(surfaceId); - auto imageRequest = ImageRequest(imageSource, telemetry, nullptr); + auto imageRequest = ImageRequest(imageSource, telemetry); auto weakObserverCoordinator = (std::weak_ptr)imageRequest.getSharedObserverCoordinator(); diff --git a/ReactCommon/react/renderer/mounting/Differentiator.cpp b/ReactCommon/react/renderer/mounting/Differentiator.cpp index 300c38059f8a64..d7fee08924385b 100644 --- a/ReactCommon/react/renderer/mounting/Differentiator.cpp +++ b/ReactCommon/react/renderer/mounting/Differentiator.cpp @@ -1076,11 +1076,7 @@ static void calculateShadowViewMutationsV2( for (auto &oldFlattenedNode : oldFlattenedNodes) { auto unvisitedOldChildPairIt = unvisitedOldChildPairs.find( oldFlattenedNode.shadowView.tag); - if (unvisitedOldChildPairIt != unvisitedOldChildPairs.end()) { - // Node unvisited - delete it entirely - deleteMutations.push_back(ShadowViewMutation::DeleteMutation( - oldFlattenedNode.shadowView)); - } else { + if (unvisitedOldChildPairIt == unvisitedOldChildPairs.end()) { // Node was visited - make sure to remove it from // "newRemainingPairs" map auto newRemainingIt = @@ -1202,11 +1198,7 @@ static void calculateShadowViewMutationsV2( for (auto &oldFlattenedNode : oldFlattenedNodes) { auto unvisitedOldChildPairIt = unvisitedOldChildPairs.find( oldFlattenedNode.shadowView.tag); - if (unvisitedOldChildPairIt != unvisitedOldChildPairs.end()) { - // Node unvisited - delete it entirely - deleteMutations.push_back(ShadowViewMutation::DeleteMutation( - oldFlattenedNode.shadowView)); - } else { + if (unvisitedOldChildPairIt == unvisitedOldChildPairs.end()) { // Node was visited - make sure to remove it from // "newRemainingPairs" map auto newRemainingIt = diff --git a/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp b/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp index 7ad2b08e5e5700..2c9af008a80447 100644 --- a/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp +++ b/ReactCommon/react/renderer/mounting/MountingCoordinator.cpp @@ -23,13 +23,13 @@ MountingCoordinator::MountingCoordinator( ShadowTreeRevision baseRevision, std::weak_ptr 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 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 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 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 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); + + bool treesEqual = stubViewTree_ == stubViewTree; - if (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 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; } diff --git a/ReactCommon/react/renderer/mounting/MountingCoordinator.h b/ReactCommon/react/renderer/mounting/MountingCoordinator.h index 54bb0a3a63d4a7..4563dc7bd0aa5c 100644 --- a/ReactCommon/react/renderer/mounting/MountingCoordinator.h +++ b/ReactCommon/react/renderer/mounting/MountingCoordinator.h @@ -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`. diff --git a/ReactCommon/react/renderer/mounting/ShadowTree.cpp b/ReactCommon/react/renderer/mounting/ShadowTree.cpp index 59896ad7b1e257..d7d3a8461542bd 100644 --- a/ReactCommon/react/renderer/mounting/ShadowTree.cpp +++ b/ReactCommon/react/renderer/mounting/ShadowTree.cpp @@ -237,17 +237,19 @@ ShadowTree::ShadowTree( auto family = rootComponentDescriptor.createFamily( ShadowNodeFamilyFragment{surfaceId, surfaceId, noopEventEmitter}, nullptr); - rootShadowNode_ = std::static_pointer_cast( + + auto rootShadowNode = std::static_pointer_cast( rootComponentDescriptor.createShadowNode( ShadowNodeFragment{ /* .props = */ props, }, family)); + currentRevision_ = ShadowTreeRevision{ + rootShadowNode, ShadowTreeRevision::Number{0}, TransactionTelemetry{}}; + mountingCoordinator_ = std::make_shared( - 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 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(updatedNewRootShadowNode); } } - // Layout nodes + // Layout nodes. std::vector 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 lock(commitMutex_); - if (rootShadowNode_ != oldRootShadowNode) { + if (currentRevision_.number != oldRevision.number) { return CommitStatus::Failed; } - rootShadowNode_ = newRootShadowNode; + auto newRevisionNumber = oldRevision.number + 1; { std::lock_guard 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 lock(commitMutex_); + return currentRevision_; +} + void ShadowTree::commitEmptyTree() const { commit( - [](RootShadowNode::Shared const &oldRootShadowNode) - -> RootShadowNode::Unshared { + [](RootShadowNode const &oldRootShadowNode) -> RootShadowNode::Unshared { return std::make_shared( - *oldRootShadowNode, + oldRootShadowNode, ShadowNodeFragment{ /* .props = */ ShadowNodeFragment::propsPlaceholder(), /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), diff --git a/ReactCommon/react/renderer/mounting/ShadowTree.h b/ReactCommon/react/renderer/mounting/ShadowTree.h index ac984fa8fb5b18..1a2851d57345f4 100644 --- a/ReactCommon/react/renderer/mounting/ShadowTree.h +++ b/ReactCommon/react/renderer/mounting/ShadowTree.h @@ -24,7 +24,7 @@ namespace facebook { namespace react { using ShadowTreeCommitTransaction = std::function; + 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}; }; diff --git a/ReactCommon/react/renderer/mounting/ShadowTreeRevision.cpp b/ReactCommon/react/renderer/mounting/ShadowTreeRevision.cpp index 998464886ae021..f56d03ec2023e0 100644 --- a/ReactCommon/react/renderer/mounting/ShadowTreeRevision.cpp +++ b/ReactCommon/react/renderer/mounting/ShadowTreeRevision.cpp @@ -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 diff --git a/ReactCommon/react/renderer/mounting/ShadowTreeRevision.h b/ReactCommon/react/renderer/mounting/ShadowTreeRevision.h index d2a63a00aa487e..d58dbd643288d7 100644 --- a/ReactCommon/react/renderer/mounting/ShadowTreeRevision.h +++ b/ReactCommon/react/renderer/mounting/ShadowTreeRevision.h @@ -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 diff --git a/ReactCommon/react/renderer/mounting/StubViewTree.cpp b/ReactCommon/react/renderer/mounting/StubViewTree.cpp index fc68777f349fda..698af4374f2522 100644 --- a/ReactCommon/react/renderer/mounting/StubViewTree.cpp +++ b/ReactCommon/react/renderer/mounting/StubViewTree.cpp @@ -83,13 +83,15 @@ void StubViewTree::mutate( STUB_VIEW_ASSERT(registry.find(parentTag) != registry.end()); auto parentStubView = registry[parentTag]; auto childTag = mutation.newChildShadowView.tag; - STUB_VIEW_LOG({ - LOG(ERROR) << "StubView: Insert: " << childTag << " into " - << parentTag << " at " << mutation.index; - }); STUB_VIEW_ASSERT(registry.find(childTag) != registry.end()); auto childStubView = registry[childTag]; childStubView->update(mutation.newChildShadowView); + STUB_VIEW_LOG({ + LOG(ERROR) << "StubView: Insert: " << childTag << " into " + << parentTag << " at " << mutation.index << "(" + << parentStubView->children.size() << " children)"; + }); + STUB_VIEW_ASSERT(parentStubView->children.size() >= mutation.index); parentStubView->children.insert( parentStubView->children.begin() + mutation.index, childStubView); break; @@ -106,6 +108,7 @@ void StubViewTree::mutate( << parentTag << " at index " << mutation.index << " with " << parentStubView->children.size() << " children"; }); + STUB_VIEW_ASSERT(parentStubView->children.size() > mutation.index); STUB_VIEW_ASSERT(registry.find(childTag) != registry.end()); auto childStubView = registry[childTag]; bool childIsCorrect = @@ -130,8 +133,15 @@ void StubViewTree::mutate( STUB_VIEW_LOG({ LOG(ERROR) << "StubView: Update: " << mutation.newChildShadowView.tag; }); + + // We don't have a strict requirement that oldChildShadowView has any + // data. In particular, LayoutAnimations can produce UPDATEs with only a + // new node. STUB_VIEW_ASSERT( - mutation.newChildShadowView.tag == mutation.oldChildShadowView.tag); + mutation.newChildShadowView.tag == + mutation.oldChildShadowView.tag || + mutation.oldChildShadowView.tag == 0); + STUB_VIEW_ASSERT( registry.find(mutation.newChildShadowView.tag) != registry.end()); auto stubView = registry[mutation.newChildShadowView.tag]; diff --git a/ReactCommon/react/renderer/mounting/TransactionTelemetry.cpp b/ReactCommon/react/renderer/mounting/TransactionTelemetry.cpp index f1ac3f659feaa7..d81ed7c6bf44b1 100644 --- a/ReactCommon/react/renderer/mounting/TransactionTelemetry.cpp +++ b/ReactCommon/react/renderer/mounting/TransactionTelemetry.cpp @@ -12,18 +12,18 @@ namespace facebook { namespace react { -using ThreadLocalTransactionTelemetry = ThreadStorage; +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() { diff --git a/ReactCommon/react/renderer/mounting/TransactionTelemetry.h b/ReactCommon/react/renderer/mounting/TransactionTelemetry.h index b71e10bae33e78..88ef2a23b0e3c4 100644 --- a/ReactCommon/react/renderer/mounting/TransactionTelemetry.h +++ b/ReactCommon/react/renderer/mounting/TransactionTelemetry.h @@ -11,7 +11,6 @@ #include #include -#include namespace facebook { namespace react { diff --git a/ReactCommon/react/renderer/mounting/tests/ShadowTreeLifeCycleTest.cpp b/ReactCommon/react/renderer/mounting/tests/ShadowTreeLifeCycleTest.cpp index 851231da950841..1ecfd21a69b670 100644 --- a/ReactCommon/react/renderer/mounting/tests/ShadowTreeLifeCycleTest.cpp +++ b/ReactCommon/react/renderer/mounting/tests/ShadowTreeLifeCycleTest.cpp @@ -5,12 +5,15 @@ * LICENSE file in the root directory of this source tree. */ +#include + #include #include #include #include #include +#include #include #include "Entropy.h" @@ -102,6 +105,29 @@ 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 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); diff --git a/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp b/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp index c122047fa4f92e..99b86ca357b1b7 100644 --- a/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp +++ b/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp @@ -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(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(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(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(rootShadowNodeState2); }, true); diff --git a/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/ReactCommon/react/renderer/scheduler/Scheduler.cpp index 37b724d0814753..1f0c617ef8d58a 100644 --- a/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -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( - *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); }); }); } diff --git a/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp b/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp index 70904ed8b64de8..d3d9845f93c518 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp +++ b/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.cpp @@ -8,5 +8,58 @@ #include "TextMeasureCache.h" namespace facebook { -namespace react {} // namespace react +namespace react { + +static Rect rectFromDynamic(folly::dynamic const &data) { + Point origin; + origin.x = data.getDefault("x", 0).getDouble(); + origin.y = data.getDefault("y", 0).getDouble(); + Size size; + size.width = data.getDefault("width", 0).getDouble(); + size.height = data.getDefault("height", 0).getDouble(); + Rect frame; + frame.origin = origin; + frame.size = size; + return frame; +} + +LineMeasurement::LineMeasurement( + std::string text, + Rect frame, + Float descender, + Float capHeight, + Float ascender, + Float xHeight) + : text(text), + frame(frame), + descender(descender), + capHeight(capHeight), + ascender(ascender) {} + +LineMeasurement::LineMeasurement(folly::dynamic const &data) + : text(data.getDefault("text", "").getString()), + frame(rectFromDynamic(data)), + descender(data.getDefault("descender", 0).getDouble()), + capHeight(data.getDefault("capHeight", 0).getDouble()), + ascender(data.getDefault("ascender", 0).getDouble()), + xHeight(data.getDefault("xHeight", 0).getDouble()) {} + +bool LineMeasurement::operator==(LineMeasurement const &rhs) const { + return std::tie( + this->text, + this->frame, + this->descender, + this->capHeight, + this->ascender, + this->xHeight) == + std::tie( + rhs.text, + rhs.frame, + rhs.descender, + rhs.capHeight, + rhs.ascender, + rhs.xHeight); +} + +} // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h b/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h index 4a15c17925684b..d0e71872810d75 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h +++ b/ReactCommon/react/renderer/textlayoutmanager/TextMeasureCache.h @@ -23,6 +23,18 @@ struct LineMeasurement { Float capHeight; Float ascender; Float xHeight; + + LineMeasurement( + std::string text, + Rect frame, + Float descender, + Float capHeight, + Float ascender, + Float xHeight); + + LineMeasurement(folly::dynamic const &data); + + bool operator==(LineMeasurement const &rhs) const; }; using LinesMeasurements = std::vector; diff --git a/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp b/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp index 3d69b4a84ed1d5..be0201dec74d43 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp +++ b/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp @@ -98,6 +98,50 @@ TextMeasurement TextLayoutManager::measureCachedSpannableById( return TextMeasurement{size, attachments}; } +LinesMeasurements TextLayoutManager::measureLines( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + Size size) const { + const jni::global_ref &fabricUIManager = + contextContainer_->at>("FabricUIManager"); + static auto measureLines = + jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") + ->getMethod("measureLines"); + + auto serializedAttributedString = toDynamic(attributedString); + + local_ref attributedStringRNM = + ReadableNativeMap::newObjectCxxArgs(serializedAttributedString); + local_ref paragraphAttributesRNM = + ReadableNativeMap::newObjectCxxArgs(toDynamic(paragraphAttributes)); + + local_ref attributedStringRM = make_local( + reinterpret_cast(attributedStringRNM.get())); + local_ref paragraphAttributesRM = make_local( + reinterpret_cast(paragraphAttributesRNM.get())); + + auto array = measureLines( + fabricUIManager, + attributedStringRM.get(), + paragraphAttributesRM.get(), + size.width, + size.height); + + auto dynamicArray = cthis(array)->consume(); + LinesMeasurements lineMeasurements; + lineMeasurements.reserve(dynamicArray.size()); + + for (auto const &data : dynamicArray) { + lineMeasurements.push_back(LineMeasurement(data)); + } + + return lineMeasurements; +} + TextMeasurement TextLayoutManager::doMeasure( AttributedString attributedString, ParagraphAttributes paragraphAttributes, diff --git a/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h b/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h index e1ec4b122f9a7e..681c54e22fb793 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h +++ b/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h @@ -46,6 +46,15 @@ class TextLayoutManager { ParagraphAttributes paragraphAttributes, LayoutConstraints layoutConstraints) const; + /* + * Measures lines of `attributedString` using native text rendering + * infrastructure. + */ + LinesMeasurements measureLines( + AttributedString attributedString, + ParagraphAttributes paragraphAttributes, + Size size) const; + /* * Returns an opaque pointer to platform-specific TextLayoutManager. * Is used on a native views layer to delegate text rendering to the manager. diff --git a/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTTextPrimitivesConversions.h b/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTTextPrimitivesConversions.h index fdfb6e2ce61c68..c78daec7b7a3d5 100644 --- a/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTTextPrimitivesConversions.h +++ b/ReactCommon/react/renderer/textlayoutmanager/platform/ios/RCTTextPrimitivesConversions.h @@ -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]; } diff --git a/ReactCommon/react/renderer/uimanager/UIManager.cpp b/ReactCommon/react/renderer/uimanager/UIManager.cpp index 6c2e6253950b8f..a8b4c6964808fc 100644 --- a/ReactCommon/react/renderer/uimanager/UIManager.cpp +++ b/ReactCommon/react/renderer/uimanager/UIManager.cpp @@ -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( - *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( - 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( - 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, diff --git a/ReactCommon/react/renderer/uimanager/UIManager.h b/ReactCommon/react/renderer/uimanager/UIManager.h index e2ba6f41630551..1235cf3532af14 100644 --- a/ReactCommon/react/renderer/uimanager/UIManager.h +++ b/ReactCommon/react/renderer/uimanager/UIManager.h @@ -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; diff --git a/ReactCommon/react/renderer/uimanager/UIManagerAnimationDelegate.h b/ReactCommon/react/renderer/uimanager/UIManagerAnimationDelegate.h index cfbb0c00e558ea..9e55d1f3862e91 100644 --- a/ReactCommon/react/renderer/uimanager/UIManagerAnimationDelegate.h +++ b/ReactCommon/react/renderer/uimanager/UIManagerAnimationDelegate.h @@ -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. diff --git a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp index 10a9dce3cdaa86..14eee6775f405b 100644 --- a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp +++ b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp @@ -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 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(*eventHandler_); + static_cast(*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 to a raw ptr @@ -207,10 +212,10 @@ jsi::Value UIManagerBinding::get( name, 5, [uiManager]( - jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Runtime & runtime, + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept->jsi::Value { return valueFromShadowNode( runtime, uiManager->createNode( @@ -229,10 +234,10 @@ jsi::Value UIManagerBinding::get( name, 1, [uiManager]( - jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Runtime & runtime, + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept->jsi::Value { return valueFromShadowNode( runtime, uiManager->cloneNode(shadowNodeFromValue(runtime, arguments[0]))); @@ -245,10 +250,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Runtime & runtime, + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept->jsi::Value { uiManager->setJSResponder( shadowNodeFromValue(runtime, arguments[0]), arguments[1].getBool()); @@ -263,10 +268,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Runtime & runtime, + 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(); @@ -293,10 +298,10 @@ jsi::Value UIManagerBinding::get( name, 0, [uiManager]( - jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Runtime & runtime, + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept->jsi::Value { uiManager->clearJSResponder(); return jsi::Value::undefined(); @@ -310,10 +315,10 @@ jsi::Value UIManagerBinding::get( name, 1, [uiManager]( - jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Runtime & runtime, + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept->jsi::Value { return valueFromShadowNode( runtime, uiManager->cloneNode( @@ -329,11 +334,11 @@ jsi::Value UIManagerBinding::get( name, 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::Runtime & runtime, + 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( @@ -350,11 +355,11 @@ jsi::Value UIManagerBinding::get( name, 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::Runtime & runtime, + 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( @@ -370,10 +375,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Runtime & runtime, + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept->jsi::Value { uiManager->appendChild( shadowNodeFromValue(runtime, arguments[0]), shadowNodeFromValue(runtime, arguments[1])); @@ -386,10 +391,10 @@ jsi::Value UIManagerBinding::get( runtime, name, 1, - [](jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + [](jsi::Runtime & runtime, + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept->jsi::Value { auto shadowNodeList = std::make_shared(SharedShadowNodeList({})); return valueFromShadowNodeList(runtime, shadowNodeList); @@ -401,10 +406,10 @@ jsi::Value UIManagerBinding::get( runtime, name, 2, - [](jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + [](jsi::Runtime & runtime, + 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); @@ -420,33 +425,22 @@ jsi::Value UIManagerBinding::get( runtime, name, 2, - [uiManager, sharedUIManager = uiManager_]( - jsi::Runtime &runtime, + [ uiManager, sharedUIManager = uiManager_ ]( + 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]); - - 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(); + 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); } return jsi::Value::undefined(); @@ -459,23 +453,13 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, + 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(); }); @@ -488,10 +472,10 @@ jsi::Value UIManagerBinding::get( name, 1, [this]( - jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Runtime & runtime, + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept->jsi::Value { auto eventHandler = arguments[0].getObject(runtime).getFunction(runtime); eventHandler_ = @@ -506,10 +490,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Runtime & runtime, + 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(), @@ -530,10 +514,10 @@ jsi::Value UIManagerBinding::get( name, 3, [uiManager]( - jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Runtime & runtime, + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept->jsi::Value { uiManager->dispatchCommand( shadowNodeFromValue(runtime, arguments[0]), stringFromValue(runtime, arguments[1]), @@ -550,10 +534,10 @@ jsi::Value UIManagerBinding::get( name, 4, [uiManager]( - jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Runtime & runtime, + 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(), @@ -586,10 +570,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Runtime & runtime, + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept->jsi::Value { auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNodeFromValue(runtime, arguments[0]), nullptr, @@ -621,10 +605,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Runtime & runtime, + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept->jsi::Value { auto layoutMetrics = uiManager->getRelativeLayoutMetrics( *shadowNodeFromValue(runtime, arguments[0]), nullptr, @@ -656,10 +640,10 @@ jsi::Value UIManagerBinding::get( name, 2, [uiManager]( - jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Runtime & runtime, + jsi::Value const &thisValue, + jsi::Value const *arguments, + size_t count) noexcept->jsi::Value { uiManager->setNativeProps( *shadowNodeFromValue(runtime, arguments[0]), RawProps(runtime, arguments[1])); @@ -674,10 +658,10 @@ jsi::Value UIManagerBinding::get( name, 3, [uiManager]( - jsi::Runtime &runtime, - const jsi::Value &thisValue, - const jsi::Value *arguments, - size_t count) -> jsi::Value { + jsi::Runtime & runtime, + 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 diff --git a/ReactCommon/react/renderer/uimanager/UIManagerBinding.h b/ReactCommon/react/renderer/uimanager/UIManagerBinding.h index d4e133f59d38d4..d5616310716a19 100644 --- a/ReactCommon/react/renderer/uimanager/UIManagerBinding.h +++ b/ReactCommon/react/renderer/uimanager/UIManagerBinding.h @@ -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_; - std::unique_ptr eventHandler_; + std::unique_ptr eventHandler_; }; } // namespace react diff --git a/ReactCommon/react/renderer/uimanager/primitives.h b/ReactCommon/react/renderer/uimanager/primitives.h index cd9dde4a662c69..9fb2ea92786844 100644 --- a/ReactCommon/react/renderer/uimanager/primitives.h +++ b/ReactCommon/react/renderer/uimanager/primitives.h @@ -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(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(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( 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); } diff --git a/ReactCommon/reactperflogger/React-perflogger.podspec b/ReactCommon/reactperflogger/React-perflogger.podspec index 2bb69eb70c605f..a2f47038c42908 100644 --- a/ReactCommon/reactperflogger/React-perflogger.podspec +++ b/ReactCommon/reactperflogger/React-perflogger.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#774) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#774) s.source = source s.source_files = "**/*.{cpp,h}" s.header_dir = "reactperflogger" diff --git a/ReactCommon/runtimeexecutor/React-runtimeexecutor.podspec b/ReactCommon/runtimeexecutor/React-runtimeexecutor.podspec index 83f57ab1f5ea09..83a68c472a5ee7 100644 --- a/ReactCommon/runtimeexecutor/React-runtimeexecutor.podspec +++ b/ReactCommon/runtimeexecutor/React-runtimeexecutor.podspec @@ -27,7 +27,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.source = source s.source_files = "**/*.{cpp,h}" s.header_dir = "ReactCommon" diff --git a/ReactCommon/yoga/Yoga.podspec b/ReactCommon/yoga/Yoga.podspec index 38e087939f71ad..34f4ea25696505 100644 --- a/ReactCommon/yoga/Yoga.podspec +++ b/ReactCommon/yoga/Yoga.podspec @@ -43,7 +43,7 @@ Pod::Spec.new do |spec| ] # Pinning to the same version as React.podspec. - spec.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } + spec.platforms = { :ios => "10.0", :osx => "10.14" } # Set this environment variable when *not* using the `:path` option to install the pod. # E.g. when publishing this spec to a spec repo. diff --git a/ReactTurboModuleCxx/React-TurboModuleCxx-RNW.podspec b/ReactTurboModuleCxx/React-TurboModuleCxx-RNW.podspec index 42dac31a0dcced..8fecb219b26c29 100644 --- a/ReactTurboModuleCxx/React-TurboModuleCxx-RNW.podspec +++ b/ReactTurboModuleCxx/React-TurboModuleCxx-RNW.podspec @@ -13,7 +13,7 @@ Pod::Spec.new do |s| s.homepage = "https://github.com/microsoft/react-native-windows" s.license = package["license"] s.author = "Microsoft Corporation" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } + s.platforms = { :ios => "10.0", :osx => "10.14" } s.compiler_flags = folly_compiler_flags s.source = { :git => 'https://github.com/microsoft/react-native-windows.git', :commit => "d9077991441889ddaa18a8af6a2cc8514ca7714d" } diff --git a/ReactTurboModuleCxx/React-TurboModuleCxx-WinRTPort.podspec b/ReactTurboModuleCxx/React-TurboModuleCxx-WinRTPort.podspec index 8e0bfc601d8107..0412b925cda1e9 100644 --- a/ReactTurboModuleCxx/React-TurboModuleCxx-WinRTPort.podspec +++ b/ReactTurboModuleCxx/React-TurboModuleCxx-WinRTPort.podspec @@ -21,7 +21,7 @@ Pod::Spec.new do |s| s.homepage = "https://github.com/microsoft/react-native-macos" s.license = package["license"] s.author = "Microsoft Corporation" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } + s.platforms = { :ios => "10.0", :osx => "10.14" } s.compiler_flags = folly_compiler_flags s.source = source diff --git a/android-patches/patches/Build/ReactAndroid/build.gradle b/android-patches/patches/Build/ReactAndroid/build.gradle index e0dfb8a21820fc..51c4f03823c85c 100644 --- a/android-patches/patches/Build/ReactAndroid/build.gradle +++ b/android-patches/patches/Build/ReactAndroid/build.gradle @@ -1,6 +1,6 @@ --- "E:\\github\\rnm-63-fresh\\ReactAndroid\\build.gradle" 2020-10-27 20:26:16.652167500 -0700 +++ "E:\\github\\rnm-63\\ReactAndroid\\build.gradle" 2020-10-13 22:03:22.833541000 -0700 -@@ -43,6 +43,8 @@ +@@ -46,6 +46,8 @@ // and the build will use that. def boostPath = dependenciesPath ?: System.getenv("REACT_NATIVE_BOOST_PATH") @@ -9,7 +9,7 @@ // Setup build type for NDK, supported values: {debug, release} def nativeBuildType = System.getenv("NATIVE_BUILD_TYPE") ?: "release" -@@ -92,11 +92,22 @@ +@@ -95,11 +97,22 @@ dest(new File(downloadsDir, "folly-${FOLLY_VERSION}.tar.gz")) } @@ -32,7 +32,7 @@ includeEmptyDirs = false into("$thirdPartyNdkDir/folly") } -@@ -119,6 +138,14 @@ +@@ -122,6 +135,14 @@ into "$thirdPartyNdkDir/hermes" } @@ -47,24 +47,24 @@ task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) { src("https://github.com/google/glog/archive/v${GLOG_VERSION}.tar.gz") onlyIfNewer(true) -@@ -300,7 +327,7 @@ +@@ -303,7 +324,7 @@ } def buildReactNdkLib = tasks.register("buildReactNdkLib", Exec) { - dependsOn(prepareJSC, prepareHermes, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog, extractAARHeaders, extractJNIFiles) + dependsOn(prepareJSC, prepareHermes, prepareV8, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog, extractAARHeaders, extractJNIFiles) - + dependsOn("generateCodegenArtifactsFromSchema"); + inputs.dir("$projectDir/../ReactCommon") - inputs.dir("src/main/jni") -@@ -313,6 +340,7 @@ +@@ -317,6 +338,7 @@ "NDK_OUT=" + temporaryDir, "NDK_LIBS_OUT=$buildDir/react-ndk/all", "THIRD_PARTY_NDK_DIR=$thirdPartyNdkDir", + "V8_NUGET_DIR=$projectDir/$V8Path", "REACT_COMMON_DIR=$projectDir/../ReactCommon", + "REACT_GENERATED_SRC_DIR=$buildDir/generated/source", "REACT_SRC_DIR=$projectDir/src/main/java/com/facebook/react", - "BUILD_FABRIC=$enableFabric", -@@ -322,6 +351,7 @@ +@@ -333,6 +355,7 @@ commandLine(getNdkBuildFullPath(), "NDK_APPLICATION_MK=$projectDir/src/main/jni/Application.mk", "THIRD_PARTY_NDK_DIR=$thirdPartyNdkDir", @@ -72,7 +72,7 @@ "REACT_COMMON_DIR=$projectDir/../ReactCommon", "-C", file("src/main/jni/react/jni").absolutePath, "clean") -@@ -333,10 +363,16 @@ +@@ -345,10 +368,16 @@ def packageReactNdkLibs = tasks.register("packageReactNdkLibs", Copy) { dependsOn(buildReactNdkLib) diff --git a/android-patches/patches/OfficeRNHost/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp b/android-patches/patches/OfficeRNHost/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp index a22041a20e393c..623c191bb20a03 100644 --- a/android-patches/patches/OfficeRNHost/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp +++ b/android-patches/patches/OfficeRNHost/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp @@ -1,6 +1,6 @@ --- "E:\\github\\rnm-63-fresh\\ReactAndroid\\src\\main\\jni\\react\\jni\\CatalystInstanceImpl.cpp" 2020-10-27 20:26:17.024172000 -0700 +++ "E:\\github\\rnm-63\\ReactAndroid\\src\\main\\jni\\react\\jni\\CatalystInstanceImpl.cpp" 2020-10-13 21:50:14.117742800 -0700 -@@ -101,6 +101,7 @@ +@@ -95,6 +95,7 @@ void CatalystInstanceImpl::registerNatives() { registerHybrid({ makeNativeMethod("initHybrid", CatalystInstanceImpl::initHybrid), @@ -8,7 +8,7 @@ makeNativeMethod( "initializeBridge", CatalystInstanceImpl::initializeBridge), makeNativeMethod( -@@ -133,26 +134,39 @@ +@@ -127,26 +128,39 @@ CatalystInstanceImpl::handleMemoryPressure), makeNativeMethod( "getRuntimeExecutor", CatalystInstanceImpl::getRuntimeExecutor), @@ -56,7 +56,7 @@ // This used to be: // -@@ -171,12 +185,13 @@ +@@ -165,12 +179,13 @@ // don't need jsModuleDescriptions any more, all the way up and down the // stack. @@ -75,7 +75,7 @@ instance_->initializeBridge( std::make_unique(callback, moduleMessageQueue_), jseh->getExecutorFactory(), -@@ -286,6 +301,10 @@ +@@ -280,6 +295,10 @@ instance_->handleMemoryPressure(pressureLevel); } diff --git a/android-patches/patches/OfficeRNHost/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h b/android-patches/patches/OfficeRNHost/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h index 1c14e02b1e11df..54aff948758208 100644 --- a/android-patches/patches/OfficeRNHost/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h +++ b/android-patches/patches/OfficeRNHost/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.h @@ -1,6 +1,6 @@ --- "E:\\github\\rnm-63-fresh\\ReactAndroid\\src\\main\\jni\\react\\jni\\CatalystInstanceImpl.h" 2020-10-27 20:26:17.024172000 -0700 +++ "E:\\github\\rnm-63\\ReactAndroid\\src\\main\\jni\\react\\jni\\CatalystInstanceImpl.h" 2020-10-13 21:51:15.981376100 -0700 -@@ -50,17 +50,16 @@ +@@ -49,17 +49,16 @@ CatalystInstanceImpl(); @@ -24,7 +24,7 @@ void extendNativeModules( jni::alias_ref=10" }, @@ -68,6 +65,7 @@ "format": "npm run prettier && npm run clang-format", "prettier": "prettier --write \"./**/*.{js,md,yml}\"", "format-check": "prettier --list-different \"./**/*.{js,md,yml}\"", + "update-lock": "npx yarn-deduplicate", "docker-setup-android": "docker pull reactnativecommunity/react-native-android", "docker-build-android": "docker build -t reactnativeci/android -f .circleci/Dockerfiles/Dockerfile.android .", "test-android-run-instrumentation": "docker run --cap-add=SYS_ADMIN -it reactnativeci/android bash .circleci/Dockerfiles/scripts/run-android-docker-instrumentation-tests.sh", @@ -101,7 +99,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", @@ -123,7 +121,7 @@ "ws": "^6.1.4" }, "devDependencies": { - "flow-bin": "^0.133.0", + "flow-bin": "^0.135.0", "react": "16.13.1" }, "detox": { diff --git a/packages/assets/package.json b/packages/assets/package.json index 13064909c99bb9..885e7175338867 100644 --- a/packages/assets/package.json +++ b/packages/assets/package.json @@ -4,7 +4,8 @@ "description": "Asset support code for React Native.", "repository": { "type": "git", - "url": "git@github.com:facebook/react-native.git" + "url": "git@github.com:facebook/react-native.git", + "directory": "packages/assets" }, "license": "MIT" } diff --git a/packages/babel-plugin-codegen/package.json b/packages/babel-plugin-codegen/package.json index dc635c5337b446..b2dc0f85bd63ee 100644 --- a/packages/babel-plugin-codegen/package.json +++ b/packages/babel-plugin-codegen/package.json @@ -4,7 +4,8 @@ "description": "Babel plugin to generate native module and view manager code for React Native.", "repository": { "type": "git", - "url": "git@github.com:facebook/react-native.git" + "url": "git@github.com:facebook/react-native.git", + "directory": "packages/babel-plugin-codegen" }, "dependencies": { "react-native-codegen": "*" diff --git a/packages/eslint-config-react-native-community/package.json b/packages/eslint-config-react-native-community/package.json index 260cde0f7b70d9..860112def9cb14 100644 --- a/packages/eslint-config-react-native-community/package.json +++ b/packages/eslint-config-react-native-community/package.json @@ -6,7 +6,8 @@ "license": "MIT", "repository": { "type": "git", - "url": "git@github.com:facebook/react-native.git" + "url": "git@github.com:facebook/react-native.git", + "directory": "packages/eslint-config-react-native-community" }, "homepage": "https://github.com/facebook/react-native/tree/master/packages/eslint-config-react-native-community#readme", "dependencies": { diff --git a/packages/eslint-plugin-codegen/BUCK b/packages/eslint-plugin-codegen/BUCK new file mode 100644 index 00000000000000..8fd38289431bdb --- /dev/null +++ b/packages/eslint-plugin-codegen/BUCK @@ -0,0 +1,23 @@ +load("@fbsource//tools/build_defs/third_party:yarn_defs.bzl", "yarn_workspace") + +yarn_workspace( + name = "yarn-workspace", + srcs = glob( + ["**/*.js"], + exclude = [ + "**/__fixtures__/**", + "**/__flowtests__/**", + "**/__mocks__/**", + "**/__server_snapshot_tests__/**", + "**/__tests__/**", + "**/node_modules/**", + "**/node_modules/.bin/**", + "**/.*", + "**/.*/**", + "**/.*/.*", + "**/*.xcodeproj/**", + "**/*.xcworkspace/**", + ], + ), + visibility = ["PUBLIC"], +) diff --git a/packages/eslint-plugin-codegen/__tests__/eslint-tester.js b/packages/eslint-plugin-codegen/__tests__/eslint-tester.js new file mode 100644 index 00000000000000..ea767a705b790f --- /dev/null +++ b/packages/eslint-plugin-codegen/__tests__/eslint-tester.js @@ -0,0 +1,22 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +'use strict'; + +const ESLintTester = require('eslint').RuleTester; + +ESLintTester.setDefaultConfig({ + parser: require.resolve('babel-eslint'), + parserOptions: { + ecmaVersion: 6, + sourceType: 'module', + }, +}); + +module.exports = ESLintTester; diff --git a/packages/eslint-plugin-codegen/__tests__/react-native-modules-test.js b/packages/eslint-plugin-codegen/__tests__/react-native-modules-test.js new file mode 100644 index 00000000000000..8d38ab0e1aff81 --- /dev/null +++ b/packages/eslint-plugin-codegen/__tests__/react-native-modules-test.js @@ -0,0 +1,397 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @emails react_native + * @format + */ + +'use strict'; + +const ESLintTester = require('./eslint-tester.js'); + +const rule = require('../react-native-modules'); + +const NATIVE_MODULES_DIR = __dirname; + +const eslintTester = new ESLintTester(); + +const VALID_SPECS = [ + // Standard specification will all supported param types. + { + code: ` +'use strict'; + +import {TurboModuleRegistry, type TurboModule} from 'react-native'; + +export interface Spec extends TurboModule { + func1(): void, + func2(a: number, b: string, c: boolean): void, + func3(a: Object, b: Array, c: () => void): void, + func4(a: ?string): void, + func5(a: ?Object, b: ?Array, c: ?() => void): void, + func6(a: string[], b: ?number[]): void, + func7(): string, + func8(): Object, + func9(): Promise, + func10(): number, + func11(): boolean, + func12(a: {|x: string|}): void, + func13(a: $ReadOnlyArray): void, + func14(): {| + x: number, + y: string, + |}, + a: number, + b: string, + c: {a: number, b: string}, +} + +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + }, + + // With convenience API wrapper + { + code: ` +'use strict'; + +import {TurboModuleRegistry, type TurboModule} from 'react-native'; + +export interface Spec extends TurboModule { + func1(a: Object): void, +} + +const NativeModule = TurboModuleRegistry.get('XYZ'); +const NativeXYZ = { + func1(a?: number): void { + NativeModule.func1(a || {}); + }, +}; +export default NativeXYZ; +`, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + }, + + // Non-spec JS file. + { + code: ` +'use strict'; + +import {Platform} from 'react-native'; + +export default Platform.OS; +`, + }, +]; + +const INVALID_SPECS = [ + // Haste module name doesn't start with "Native" + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +export interface Spec extends TurboModule { + func1(): void, +} +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/XYZ.js`, + errors: [ + { + message: rule.errors.invalidHasteName('XYZ'), + }, + ], + }, + + // Invalid Spec interface name + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +export interface Foo extends TurboModule { + func1(): void, +} +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + errors: [ + { + message: rule.errors.invalidNativeModuleInterfaceName('Foo'), + }, + { + message: rule.errors.specNotDeclaredInFile(), + }, + ], + output: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +export interface Spec extends TurboModule { + func1(): void, +} +export default TurboModuleRegistry.get('XYZ'); + `, + }, + + // Missing method in Spec + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +export interface Spec extends TurboModule { +} +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + errors: [ + { + message: rule.errors.missingSpecInterfaceMethod(), + }, + ], + }, + + // Invalid Spec method return type + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +export interface Spec extends TurboModule { + func1(): XYZ, +} +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + errors: [ + { + message: rule.errors.unsupportedMethodReturnType('XYZ'), + }, + ], + }, + + // Unsupported Spec property + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +export interface Spec extends TurboModule { + id: Map, +} +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + errors: [ + { + message: rule.errors.unsupportedType('Map'), + }, + ], + }, + + // Unsupported nested Spec property + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +export interface Spec extends TurboModule { + a: { + b: number, + c: () => number, + }, +} +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + errors: [ + { + message: rule.errors.unsupportedType('Function'), + }, + ], + }, + + // Unsupported Spec method arg type + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +type SomeType = {}; +export interface Spec extends TurboModule { + func1(a: SomeType): void, +} +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + errors: [ + { + message: rule.errors.unsupportedType('SomeType'), + }, + ], + }, + + // Unsupported Spec method arg type: optional + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +export interface Spec extends TurboModule { + func1(a?: string): void, +} +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + errors: [ + { + message: rule.errors.unsupportedType('optional string'), + }, + ], + }, + + // Unsupported Spec method arg type: unsupported nullable + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +type Foo = {}; +export interface Spec extends TurboModule { + func1(a: ?Foo): void, +} +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + errors: [ + { + message: rule.errors.unsupportedType('nullable Foo'), + }, + ], + }, + + // Unsupported Spec method arg type: generic Function + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +export interface Spec extends TurboModule { + func1(a: Function): void, +} +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + errors: [ + { + message: rule.errors.unsupportedType('Function'), + }, + ], + }, + + // Unsupported Spec method arg type: nullable generic Function + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +export interface Spec extends TurboModule { + func1(a: ?Function): void, +} +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + errors: [ + { + message: rule.errors.unsupportedType('nullable Function'), + }, + ], + }, + + // Spec method object return type must be exact + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +export interface Spec extends TurboModule { + func1(a: string): {}, +} +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + errors: [ + { + message: rule.errors.inexactObjectReturnType(), + }, + ], + }, + + // Untyped NativeModule require + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + errors: [ + { + message: rule.errors.untypedModuleRequire('get'), + }, + ], + }, + + // Incorrectly typed NativeModule require: 0 types + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +export default TurboModuleRegistry.get<>('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + errors: [ + { + message: rule.errors.incorrectlyTypedModuleRequire('get'), + }, + ], + }, + + // Incorrectly typed NativeModule require: 1 type, but wrong + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +export interface Spec extends TurboModule { + func1(a: string): {||}, +} + +// According to Flow, this also conforms to TurboModule +type Spec1 = {| + getConstants: () => {...} +|}; + +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + errors: [ + { + message: rule.errors.incorrectlyTypedModuleRequire('get'), + }, + ], + }, + + // Incorrectly typed NativeModule require: > 1 type + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +export interface Spec extends TurboModule { + func1(a: string): {||}, +} +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + errors: [ + { + message: rule.errors.incorrectlyTypedModuleRequire('get'), + }, + ], + }, + + // NativeModule spec not declared in file + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +import type {Spec} from 'NativeFoo'; +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + errors: [ + { + message: rule.errors.specNotDeclaredInFile(), + }, + ], + }, +]; + +eslintTester.run('../react-native-modules', rule, { + valid: VALID_SPECS, + invalid: INVALID_SPECS, +}); diff --git a/packages/eslint-plugin-codegen/index.js b/packages/eslint-plugin-codegen/index.js new file mode 100644 index 00000000000000..60a5f864f7ac42 --- /dev/null +++ b/packages/eslint-plugin-codegen/index.js @@ -0,0 +1,19 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @emails react_native + * @format + */ + +'use strict'; + +const reactNativeModules = require('./react-native-modules'); + +module.exports = { + rules: { + 'react-native-modules': reactNativeModules, + }, +}; diff --git a/packages/eslint-plugin-codegen/package.json b/packages/eslint-plugin-codegen/package.json new file mode 100644 index 00000000000000..bc2b272dfe81af --- /dev/null +++ b/packages/eslint-plugin-codegen/package.json @@ -0,0 +1,12 @@ +{ + "name": "@react-native/eslint-plugin-codegen", + "version": "0.0.1", + "description": "ESLint rules to validate NativeModule and Component Specs", + "main": "index.js", + "repository": { + "type": "git", + "url": "git@github.com:facebook/react-native.git", + "directory": "packages/eslint-plugin-codegen" + }, + "license": "MIT" +} diff --git a/packages/eslint-plugin-codegen/react-native-modules.js b/packages/eslint-plugin-codegen/react-native-modules.js new file mode 100644 index 00000000000000..fd55aca4db660e --- /dev/null +++ b/packages/eslint-plugin-codegen/react-native-modules.js @@ -0,0 +1,467 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @emails react_native + * @format + */ + +'use strict'; + +const path = require('path'); + +const supportedTypes = [ + 'ArrayTypeAnnotation', + 'BooleanTypeAnnotation', + 'NumberTypeAnnotation', + 'StringTypeAnnotation', +]; + +const supportedTypeAliases = { + BooleanTypeAnnotation: 'boolean', + FunctionTypeAnnotation: 'Function', + NumberTypeAnnotation: 'number', + StringTypeAnnotation: 'string', + ObjectTypeAnnotation: 'object', +}; + +const supportedGenericTypes = [ + 'Array', + 'Object', + 'RootTag', + '$ReadOnly', + '$ReadOnlyArray', +]; + +const supportedNullableTypes = [ + 'ArrayTypeAnnotation', + 'BooleanTypeAnnotation', + 'FunctionTypeAnnotation', + 'NumberTypeAnnotation', + 'ObjectTypeAnnotation', + 'StringTypeAnnotation', +]; + +const supportedMethodReturnTypes = [ + 'BooleanTypeAnnotation', + 'NumberTypeAnnotation', + 'ObjectTypeAnnotation', + 'StringTypeAnnotation', + 'VoidTypeAnnotation', +]; + +const errors = { + invalidNativeModuleInterfaceName(interfaceName) { + return ( + "NativeModule interfaces must be named 'Spec', " + + `got '${interfaceName}'.` + ); + }, + inexactObjectReturnType() { + return 'Spec interface method object return type must be exact.'; + }, + invalidHasteName(hasteName) { + return ( + 'Module name for a NativeModule JS wrapper must start with ' + + `'Native', got '${hasteName}' instead.` + ); + }, + missingSpecInterfaceMethod() { + return 'NativeModule Spec interface must define at least one method'; + }, + unsupportedMethodReturnType(typeName) { + return ( + `Spec interface method has unsupported return type '${typeName}'. ` + + 'See https://fburl.com/rn-nativemodules for more details.' + ); + }, + unsupportedType(typeName) { + return ( + `Unsupported type '${typeName}' for Spec interface. ` + + 'See https://fburl.com/rn-nativemodules for more details.' + ); + }, + untypedModuleRequire(requireMethodName) { + return ( + 'NativeModule require not type-safe. Please require with the NativeModule interface ' + + `'Spec': TurboModuleRegistry.${requireMethodName}` + ); + }, + incorrectlyTypedModuleRequire(requireMethodName) { + return ( + 'NativeModule require incorrectly typed. Please require with the NativeModule interface identifier ' + + `'Spec', and nothing else: TurboModuleRegistry.${requireMethodName}` + ); + }, + specNotDeclaredInFile() { + return "The NativeModule interface 'Spec' wasn't declared in this NativeModule spec file."; + }, +}; + +function interfaceExtendsFrom(node, superInterfaceName) { + return ( + node.type === 'InterfaceDeclaration' && + node.extends[0] && + node.extends[0].id.name === superInterfaceName + ); +} + +function isSupportedFunctionParam(node) { + if (node.type !== 'FunctionTypeParam') { + return false; + } + + if (node.optional) { + return false; + } + + return findUnsupportedType(node.typeAnnotation, true) == null; +} + +function findUnsupportedType(typeAnnotation, supportCallbacks) { + if (supportedTypes.includes(typeAnnotation.type)) { + return null; + } + + if (typeAnnotation.type === 'NullableTypeAnnotation') { + if (supportedNullableTypes.includes(typeAnnotation.typeAnnotation.type)) { + return null; + } + typeAnnotation = typeAnnotation.typeAnnotation; + } + + if (typeAnnotation.type === 'FunctionTypeAnnotation' && supportCallbacks) { + return null; + } + + if (typeAnnotation.type === 'GenericTypeAnnotation') { + if (!supportedGenericTypes.includes(typeAnnotation.id.name)) { + return typeAnnotation; + } + if ( + !isGenericArrayTypeAnnotation(typeAnnotation) && + typeAnnotation.typeParameters + ) { + for (const param of typeAnnotation.typeParameters.params) { + const unsupported = findUnsupportedType(param, supportCallbacks); + if (unsupported != null) { + return unsupported; + } + } + } + return null; + } + + if (typeAnnotation.type === 'ObjectTypeAnnotation') { + for (const prop of typeAnnotation.properties) { + const unsupported = findUnsupportedType(prop.value, supportCallbacks); + if (unsupported != null) { + return unsupported; + } + } + return null; + } + + return typeAnnotation; +} + +function functionParamTypeName(node) { + if (node.type !== 'FunctionTypeParam') { + return null; + } + + const parts = []; + if (node.optional) { + parts.push('optional'); + } + parts.push(functionParamTypeAnnotationName(node.typeAnnotation)); + return parts.join(' '); +} + +function functionParamTypeAnnotationName(typeAnnotation) { + const {id, type} = typeAnnotation; + if (type === 'GenericTypeAnnotation') { + return id.name; + } + + const parts = []; + if (type === 'NullableTypeAnnotation') { + parts.push('nullable'); + parts.push(functionParamTypeAnnotationName(typeAnnotation.typeAnnotation)); + } else { + parts.push(supportedTypeAliases[type] || type); + } + return parts.join(' '); +} + +function getTypeName(typeAnnotation) { + const {id, type} = typeAnnotation; + if (type === 'GenericTypeAnnotation') { + return id.name; + } + return supportedTypeAliases[type]; +} + +function checkSupportedSpecProperty(context, node) { + if (node.type !== 'FunctionTypeAnnotation') { + const unsupportedNode = findUnsupportedType(node, false); + if (unsupportedNode != null) { + context.report({ + node: unsupportedNode, + message: errors.unsupportedType( + functionParamTypeAnnotationName(unsupportedNode), + ), + }); + return false; + } + return true; + } + + if (!isSupportedMethodReturnTypeAnnotation(node.returnType)) { + context.report({ + node: node.returnType, + message: errors.unsupportedMethodReturnType(getTypeName(node.returnType)), + }); + return false; + } + + // Check for exact object return type. + if ( + node.returnType.type === 'ObjectTypeAnnotation' && + !node.returnType.exact + ) { + context.report({ + node: node.returnType, + message: errors.inexactObjectReturnType(), + }); + } + + for (const param of node.params) { + if (!isSupportedFunctionParam(param)) { + context.report({ + node: param.typeAnnotation, + message: errors.unsupportedType(functionParamTypeName(param)), + }); + return false; + } + } + + return true; +} + +function isPromiseTypeAnnotation(typeAnnotation) { + return ( + typeAnnotation.type === 'GenericTypeAnnotation' && + typeAnnotation.id && + typeAnnotation.id.name === 'Promise' + ); +} + +function isGenericArrayTypeAnnotation(typeAnnotation) { + return ( + typeAnnotation.type === 'GenericTypeAnnotation' && + typeAnnotation.id && + typeAnnotation.id.name === 'Array' + ); +} + +function isGenericObjectTypeAnnotation(typeAnnotation) { + return ( + typeAnnotation.type === 'GenericTypeAnnotation' && + typeAnnotation.id && + typeAnnotation.id.name === 'Object' + ); +} + +function isSupportedMethodReturnTypeAnnotation(typeAnnotation) { + const resolvedType = + typeAnnotation.type === 'NullableTypeAnnotation' + ? typeAnnotation.typeAnnotation + : typeAnnotation; + return ( + supportedMethodReturnTypes.includes(resolvedType.type) || + isGenericArrayTypeAnnotation(resolvedType) || + isGenericObjectTypeAnnotation(resolvedType) || + isPromiseTypeAnnotation(resolvedType) + ); +} + +const VALID_SPEC_NAMES = /^Native\S+$/; + +function isModuleRequire(node) { + if (node.type !== 'CallExpression') { + return false; + } + + const callExpression = node; + + if (callExpression.callee.type !== 'MemberExpression') { + return false; + } + + const memberExpression = callExpression.callee; + if ( + !( + memberExpression.object.type === 'Identifier' && + memberExpression.object.name === 'TurboModuleRegistry' + ) + ) { + return false; + } + + if ( + !( + memberExpression.property.type === 'Identifier' && + (memberExpression.property.name === 'get' || + memberExpression.property.name === 'getEnforcing') + ) + ) { + return false; + } + return true; +} + +function isGeneratedFile(context) { + return ( + context + .getSourceCode() + .getText() + .indexOf('@' + 'generated SignedSource<<') !== -1 + ); +} + +/** + * A lint rule to guide best practices in writing type safe React NativeModules. + */ +function rule(context) { + const filename = context.getFilename(); + + if (isGeneratedFile(context)) { + return {}; + } + + const sourceCode = context.getSourceCode().getText(); + if (!sourceCode.includes('TurboModuleRegistry')) { + return {}; + } + + const specIdentifierUsages = []; + const declaredModuleInterfaces = []; + + return { + 'Program:exit': function() { + if ( + specIdentifierUsages.length > 0 && + declaredModuleInterfaces.length === 0 + ) { + specIdentifierUsages.forEach(specNode => { + context.report({ + node: specNode, + message: errors.specNotDeclaredInFile(), + }); + }); + } + }, + CallExpression(node) { + if (!isModuleRequire(node)) { + return; + } + + /** + * Validate that NativeModule requires are typed + */ + + const {typeArguments} = node; + + if (typeArguments == null) { + const methodName = node.callee.property.name; + context.report({ + node, + message: errors.untypedModuleRequire(methodName), + }); + return; + } + + if (typeArguments.type !== 'TypeParameterInstantiation') { + return; + } + + const [param] = typeArguments.params; + + /** + * Validate that NativeModule requires are correctly typed + */ + + if ( + typeArguments.params.length !== 1 || + param.type !== 'GenericTypeAnnotation' || + param.id.name !== 'Spec' + ) { + const methodName = node.callee.property.name; + context.report({ + node, + message: errors.incorrectlyTypedModuleRequire(methodName), + }); + return; + } + + specIdentifierUsages.push(param); + return true; + }, + InterfaceDeclaration(node) { + if ( + !interfaceExtendsFrom(node, 'DEPRECATED_RCTExport') && + !interfaceExtendsFrom(node, 'TurboModule') + ) { + return; + } + + const basename = path.basename(filename, '.js'); + if ( + basename && + basename !== 'RCTExport' && + !VALID_SPEC_NAMES.test(basename) + ) { + context.report({ + loc: {start: {line: 0, column: 0}}, + message: errors.invalidHasteName(basename), + }); + } + + if (node.id.name !== 'Spec') { + context.report({ + node, + message: errors.invalidNativeModuleInterfaceName(node.id.name), + fix: fixer => fixer.replaceText(node.id, 'Spec'), + }); + return; + } + + declaredModuleInterfaces.push(node); + + if (!node.body.properties.length) { + context.report({ + node: node.body, + message: errors.missingSpecInterfaceMethod(), + }); + return; + } + + let hasUnsupportedProp = false; + node.body.properties.forEach(prop => { + if (hasUnsupportedProp) { + return; + } + if (!checkSupportedSpecProperty(context, prop.value)) { + hasUnsupportedProp = true; + } + }); + }, + }; +} + +rule.errors = errors; + +module.exports = rule; diff --git a/packages/eslint-plugin-react-native-community/package.json b/packages/eslint-plugin-react-native-community/package.json index a4a01429fa9e8c..33c3575d38542b 100644 --- a/packages/eslint-plugin-react-native-community/package.json +++ b/packages/eslint-plugin-react-native-community/package.json @@ -5,7 +5,8 @@ "main": "index.js", "repository": { "type": "git", - "url": "git@github.com:facebook/react-native.git" + "url": "git@github.com:facebook/react-native.git", + "directory": "packages/eslint-plugin-react-native-community" }, "license": "MIT", "beachball": { diff --git a/packages/normalize-color/package.json b/packages/normalize-color/package.json index a9fd8fcef1b23d..9ff2e8a0733d9f 100644 --- a/packages/normalize-color/package.json +++ b/packages/normalize-color/package.json @@ -4,7 +4,8 @@ "description": "Color normalization code for React Native.", "repository": { "type": "git", - "url": "git@github.com:facebook/react-native.git" + "url": "git@github.com:facebook/react-native.git", + "directory": "packages/normalize-color" }, "license": "MIT" } diff --git a/packages/polyfills/package.json b/packages/polyfills/package.json index ed96b9f671dea5..6f00c64c7fca72 100644 --- a/packages/polyfills/package.json +++ b/packages/polyfills/package.json @@ -4,7 +4,8 @@ "description": "Polyfills for React Native.", "repository": { "type": "git", - "url": "git@github.com:facebook/react-native.git" + "url": "git@github.com:facebook/react-native.git", + "directory": "packages/polyfills" }, "license": "MIT" } diff --git a/packages/react-native-codegen/.babelrc b/packages/react-native-codegen/.babelrc index b8ac81899b77e0..7a6194fbaa55d8 100644 --- a/packages/react-native-codegen/.babelrc +++ b/packages/react-native-codegen/.babelrc @@ -5,6 +5,7 @@ "@babel/plugin-transform-destructuring", "@babel/plugin-transform-flow-strip-types", "@babel/plugin-syntax-dynamic-import", + "@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-nullish-coalescing-operator", "@babel/plugin-proposal-optional-chaining" ] diff --git a/packages/react-native-codegen/DEFS.bzl b/packages/react-native-codegen/DEFS.bzl index da49dac7fbdb32..4da9f3c420c39e 100644 --- a/packages/react-native-codegen/DEFS.bzl +++ b/packages/react-native-codegen/DEFS.bzl @@ -50,7 +50,7 @@ def rn_codegen_modules( rn_xplat_cxx_library( name = "generated_objcpp_modules-{}".format(name), - header_namespace = native_module_spec_name, + header_namespace = "", apple_sdks = (IOS), compiler_flags = [ "-fexceptions", @@ -61,8 +61,7 @@ def rn_codegen_modules( fbobjc_compiler_flags = get_apple_compiler_flags(), fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), ios_exported_headers = { - "{}.h".format(native_module_spec_name): ":{}".format(generate_module_hobjcpp_name), - "{}-generated.mm".format(native_module_spec_name): ":{}".format(generate_module_mm_name), + "{}/{}.h".format(native_module_spec_name, native_module_spec_name): ":{}".format(generate_module_hobjcpp_name), }, ios_headers = [ ":{}".format(generate_module_hobjcpp_name), @@ -383,7 +382,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"), ], ) diff --git a/packages/react-native-codegen/android/build.gradle b/packages/react-native-codegen/android/build.gradle index 69934710e3bdca..e0cc63d645bbcf 100644 --- a/packages/react-native-codegen/android/build.gradle +++ b/packages/react-native-codegen/android/build.gradle @@ -12,7 +12,7 @@ buildscript { jcenter() } dependencies { - classpath("com.android.tools.build:gradle:3.6.4") + classpath("com.android.tools.build:gradle:4.0.1") } } diff --git a/packages/react-native-codegen/android/generator/build.gradle b/packages/react-native-codegen/android/generator/build.gradle deleted file mode 100644 index 1ba58144a415db..00000000000000 --- a/packages/react-native-codegen/android/generator/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -plugins { - id 'java' - id 'application' -} - -dependencies { - implementation 'com.squareup:javapoet:1.13.0' -} - -application { - mainClassName = 'com.facebook.react.codegen.JavaGeneratorMain' -} diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/build.gradle b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/build.gradle index 616590c65c97b6..dd269383ec1416 100644 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/build.gradle +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/build.gradle @@ -19,7 +19,7 @@ gradlePlugin { } dependencies { - implementation 'com.android.tools.build:gradle:3.6.4' + implementation 'com.android.tools.build:gradle:4.0.1' // Use the same Gson version that `com.android.tools.build:gradle` depends on. implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.guava:guava:29.0-jre' diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/PromiseType.java b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/PromiseType.java index b4051ac81d7f61..c243cae7d73a1e 100644 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/PromiseType.java +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/PromiseType.java @@ -8,7 +8,7 @@ package com.facebook.react.codegen.generator.model; public final class PromiseType extends Type { - public static final String TYPE_NAME = "GenericPromiseTypeAnnotation"; + public static final String TYPE_NAME = "PromiseTypeAnnotation"; public PromiseType(final TypeId typeId) { super(typeId); diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPlugin.java b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPlugin.java index 68cd4139a64ec0..1d58a2e561259d 100644 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPlugin.java +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPlugin.java @@ -25,6 +25,12 @@ public class CodegenPlugin implements Plugin { public void apply(final Project project) { + // This flag should have been defined in CodegenPluginExtension, but the extension values + // resolution is pending project full evaluation. Given that no codegen actual task should + // be defined if the flag is not enabled, read directly from env var here. + final String useCodegenVar = System.getenv("USE_CODEGEN"); + final boolean enableCodegen = + useCodegenVar != null && (Boolean.parseBoolean(useCodegenVar) || useCodegenVar.equals("1")); final CodegenPluginExtension extension = project.getExtensions().create("react", CodegenPluginExtension.class, project); @@ -39,7 +45,8 @@ public void apply(final Project project) { "generateCodegenSchemaFromJavaScript", Exec.class, task -> { - if (!extension.enableCodegen) { + if (!enableCodegen) { + task.commandLine("echo", "Skipping: not using react-native-codegen."); return; } @@ -79,7 +86,8 @@ public void apply(final Project project) { "generateCodegenArtifactsFromSchema", Exec.class, task -> { - if (!extension.enableCodegen) { + if (!enableCodegen) { + task.commandLine("echo", "Skipping: not using react-native-codegen."); return; } @@ -116,7 +124,7 @@ public void apply(final Project project) { // Note: This last step needs to happen after the project has been evaluated. project.afterEvaluate( s -> { - if (!extension.enableCodegen) { + if (!enableCodegen) { return; } @@ -142,7 +150,6 @@ public void apply(final Project project) { .getByName("main") .getJava() .srcDir(new File(generatedSrcDir, "java")); - // TODO: Add JNI sources. }); } diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPluginExtension.java b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPluginExtension.java index 97817617d947a5..fe540dd353dc92 100644 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPluginExtension.java +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPluginExtension.java @@ -15,7 +15,6 @@ public class CodegenPluginExtension { // TODO: Remove beta. public String codegenJavaPackageName = "com.facebook.fbreact.specs.beta"; - public boolean enableCodegen = false; public File jsRootDir; public String libraryName; public String[] nodeExecutableAndArgs = {"node"}; diff --git a/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeOptionalObjectTurboModule.js b/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeOptionalObjectTurboModule.js index b697ab954dd573..02784457c3bdfb 100644 --- a/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeOptionalObjectTurboModule.js +++ b/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeOptionalObjectTurboModule.js @@ -17,7 +17,6 @@ export interface Spec extends TurboModule { +getConstants: () => {| D?: ?boolean, A?: Array, - G?: any, E?: ?{| D?: ?boolean, E?: ?{| diff --git a/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeSampleTurboModule.js b/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeSampleTurboModule.js index ee6c0983afb75f..c950ed9e288972 100644 --- a/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeSampleTurboModule.js +++ b/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeSampleTurboModule.js @@ -10,9 +10,16 @@ 'use strict'; -import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport'; +import type { + RootTag, + TurboModule, +} from 'react-native/Libraries/TurboModule/RCTExport'; import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry'; +type Animal = {| + name: string, +|}; + export interface Spec extends TurboModule { // Exported methods. +getConstants: () => {| @@ -26,7 +33,14 @@ export interface Spec extends TurboModule { +getString: (arg: string) => string; +getArray: (arg: Array) => Array; +getObject: (arg: Object) => Object; - +getValue: (x: number, y: string, z: Object) => Object; + +getObjectShape: (arg: {|prop: number|}) => {|prop: number|}; + +getAlias: (arg: Animal) => Animal; + +getRootTag: (arg: RootTag) => RootTag; + +getValue: ( + x: number, + getValuegetValuegetValuegetValuegetValuey: string, + z: Object, + ) => Object; +getValueWithCallback: (callback: (value: string) => void) => void; +getValueWithPromise: (error: boolean) => Promise; } diff --git a/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeSampleTurboModuleArrays.js b/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeSampleTurboModuleArrays.js new file mode 100644 index 00000000000000..fce6f7c0f0fb6a --- /dev/null +++ b/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeSampleTurboModuleArrays.js @@ -0,0 +1,51 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +import type { + RootTag, + TurboModule, +} from 'react-native/Libraries/TurboModule/RCTExport'; +import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry'; + +type Animal = {| + name: string, +|}; + +export interface Spec extends TurboModule { + // Exported methods. + +getConstants: () => {| + const1: Array, + const2: Array, + const3: Array, + id?: Array, + |}; + +voidFunc: () => void; + +getBool: (id: Array) => Array; + +getNumber: (arg: Array) => Array; + +getString: (arg: Array) => Array; + +getArray: (arg: Array>) => Array>; + +getObject: (arg: Array) => Array; + +getObjectShape: (arg: Array<{|prop: number|}>) => Array<{|prop: number|}>; + +getAlias: (arg: Array) => Array; + +getRootTag: (arg: Array) => Array; + +getValue: ( + x: Array, + y: Array, + z: Array, + ) => Array; + +getValueWithCallback: (callback: (value: Array) => void) => void; + +getValueWithPromise: (error: Array) => Promise>; +} + +export default (TurboModuleRegistry.getEnforcing( + 'SampleTurboModuleArrays', +): Spec); diff --git a/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeSampleTurboModuleNullable.js b/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeSampleTurboModuleNullable.js new file mode 100644 index 00000000000000..ac7b57e386a2d0 --- /dev/null +++ b/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeSampleTurboModuleNullable.js @@ -0,0 +1,46 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +import type { + RootTag, + TurboModule, +} from 'react-native/Libraries/TurboModule/RCTExport'; +import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry'; + +type Animal = ?{| + name: ?string, +|}; + +export interface Spec extends TurboModule { + // Exported methods. + +getConstants: () => {| + const1: ?boolean, + const2: ?number, + const3: ?string, + |}; + +voidFunc: () => void; + +getBool: (arg: ?boolean) => ?boolean; + +getNumber: (arg: ?number) => ?number; + +getString: (arg: ?string) => ?string; + +getArray: (arg: ?Array) => ?Array; + +getObject: (arg: ?Object) => ?Object; + +getObjectShape: (arg: ?{|prop: ?number|}) => ?{|prop: ?number|}; + +getAlias: (arg: ?Animal) => ?Animal; + +getRootTag: (arg: ?RootTag) => ?RootTag; + +getValue: (x: ?number, y: ?string, z: ?Object) => ?Object; + +getValueWithCallback: (callback: (value: ?string) => void) => void; + +getValueWithPromise: (error: ?boolean) => ?Promise; +} + +export default (TurboModuleRegistry.getEnforcing( + 'SampleTurboModuleNullable', +): Spec); diff --git a/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeSampleTurboModuleNullableAndOptional.js b/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeSampleTurboModuleNullableAndOptional.js new file mode 100644 index 00000000000000..29e54d8a5c3693 --- /dev/null +++ b/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeSampleTurboModuleNullableAndOptional.js @@ -0,0 +1,46 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +import type { + RootTag, + TurboModule, +} from 'react-native/Libraries/TurboModule/RCTExport'; +import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry'; + +type Animal = ?{| + name?: ?string, +|}; + +export interface Spec extends TurboModule { + // Exported methods. + +getConstants?: () => {| + const1?: ?boolean, + const2?: ?number, + const3?: ?string, + |}; + +voidFunc?: () => void; + +getBool?: (arg?: ?boolean) => ?boolean; + +getNumber?: (arg?: ?number) => ?number; + +getString?: (arg?: ?string) => ?string; + +getArray?: (arg?: ?Array) => ?Array; + +getObject?: (arg?: ?Object) => ?Object; + +getObjectShape?: (arg?: {|prop?: ?number|}) => {|prop?: ?number|}; + +getAlias?: (arg?: ?Animal) => ?Animal; + +getRootTag?: (arg?: ?RootTag) => ?RootTag; + +getValue?: (x?: ?number, y?: ?string, z?: ?Object) => ?Object; + +getValueWithCallback?: (callback?: ?(value?: ?string) => void) => void; + +getValueWithPromise?: (error?: ?boolean) => ?Promise; +} + +export default (TurboModuleRegistry.getEnforcing( + 'SampleTurboModuleNullableAndOptional', +): Spec); diff --git a/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeSampleTurboModuleOptional.js b/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeSampleTurboModuleOptional.js new file mode 100644 index 00000000000000..9f12225a2d883b --- /dev/null +++ b/packages/react-native-codegen/e2e/__test_fixtures__/modules/NativeSampleTurboModuleOptional.js @@ -0,0 +1,46 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +import type { + RootTag, + TurboModule, +} from 'react-native/Libraries/TurboModule/RCTExport'; +import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry'; + +type Animal = {| + name?: string, +|}; + +export interface Spec extends TurboModule { + // Exported methods. + +getConstants?: () => {| + const1?: boolean, + const2?: number, + const3?: string, + |}; + +voidFunc?: () => void; + +getBool?: (arg?: boolean) => boolean; + +getNumber?: (arg?: number) => number; + +getString?: (arg?: string) => string; + +getArray?: (arg?: Array) => Array; + +getObject?: (arg?: Object) => Object; + +getObjectShape?: (arg?: {|prop?: number|}) => {|prop?: number|}; + +getAlias?: (arg?: Animal) => Animal; + +getRootTag?: (arg?: RootTag) => RootTag; + +getValue?: (x?: number, y?: string, z?: Object) => Object; + +getValueWithCallback?: (callback?: (value?: string) => void) => void; + +getValueWithPromise?: (error?: boolean) => Promise; +} + +export default (TurboModuleRegistry.getEnforcing( + 'SampleTurboModuleOptional', +): Spec); diff --git a/packages/react-native-codegen/e2e/__tests__/modules/BUCK b/packages/react-native-codegen/e2e/__tests__/modules/BUCK new file mode 100644 index 00000000000000..29df252313e6ea --- /dev/null +++ b/packages/react-native-codegen/e2e/__tests__/modules/BUCK @@ -0,0 +1,11 @@ +load("@fbsource//xplat/js/RKJSModules/Libraries/NativeModules:defs.bzl", "react_native_module_flow_types_library") + +react_native_module_flow_types_library( + name = "RNCodegenModuleFixtures", + android_package_name = "com.facebook.fbreact.specs", + js_srcs = glob( + [ + "../../__test_fixtures__/modules/*.js", + ], + ), +) diff --git a/packages/react-native-codegen/e2e/__tests__/modules/GenerateModuleObjCpp-test.js b/packages/react-native-codegen/e2e/__tests__/modules/GenerateModuleObjCpp-test.js new file mode 100644 index 00000000000000..2b2deec0b0fe33 --- /dev/null +++ b/packages/react-native-codegen/e2e/__tests__/modules/GenerateModuleObjCpp-test.js @@ -0,0 +1,50 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @emails oncall+react_native + * @flow strict-local + * @format + */ + +'use strict'; + +const parser = require('../../../src/parsers/flow'); +const generator = require('../../../src/generators/modules/GenerateModuleObjCpp'); +const fs = require('fs'); + +import type {SchemaType} from '../../../src/CodegenSchema'; + +const FIXTURE_DIR = `${__dirname}/../../__test_fixtures__/modules`; + +function getModules(): SchemaType { + const filenames: Array = fs.readdirSync(FIXTURE_DIR); + return filenames.reduce( + (accumulator, file) => { + const schema = parser.parseFile(`${FIXTURE_DIR}/${file}`); + return { + modules: { + ...accumulator.modules, + ...schema.modules, + }, + }; + }, + {modules: {}}, + ); +} + +describe('GenerateModuleObjCpp', () => { + it('can generate a header file NativeModule specs', () => { + const libName = 'RNCodegenModuleFixtures'; + const output = generator.generate(libName, getModules(), libName); + expect(output.get(libName + '.h')).toMatchSnapshot(); + }); + + it('can generate an implementation file NativeModule specs', () => { + const libName = 'RNCodegenModuleFixtures'; + const output = generator.generate(libName, getModules(), libName); + expect(output.get(libName + '-generated.mm')).toMatchSnapshot(); + }); +}); diff --git a/packages/react-native-codegen/e2e/__tests__/modules/__snapshots__/GenerateModuleObjCpp-test.js.snap b/packages/react-native-codegen/e2e/__tests__/modules/__snapshots__/GenerateModuleObjCpp-test.js.snap new file mode 100644 index 00000000000000..c0df45acc209bc --- /dev/null +++ b/packages/react-native-codegen/e2e/__tests__/modules/__snapshots__/GenerateModuleObjCpp-test.js.snap @@ -0,0 +1,2358 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`GenerateModuleObjCpp can generate a header file NativeModule specs 1`] = ` +" +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @generated by codegen project: GenerateModuleHObjCpp.js + */ + +#ifndef __cplusplus +#error This file must be compiled as Obj-C++. If you are importing it, you must change your file extension to .mm. +#endif + +#import + +#import + +#import + +#import +#import +#import + +#import +#import +#import + +#import + + + +@protocol NativeArrayTurboModuleSpec + +- (NSArray *)getArray:(NSArray *)a; +- (NSArray *)getReadOnlyArray:(NSArray *)a; +- (NSArray *)getArrayWithAlias:(NSArray *)a + b:(NSArray *)b; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'ArrayTurboModule' + */ + class JSI_EXPORT NativeArrayTurboModuleSpecJSI : public ObjCTurboModule { + public: + NativeArrayTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + + + +@protocol NativeBooleanTurboModuleSpec + +- (NSNumber *)getBoolean:(BOOL)arg; +- (NSNumber *)getBooleanWithAlias:(BOOL)arg; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'BooleanTurboModule' + */ + class JSI_EXPORT NativeBooleanTurboModuleSpecJSI : public ObjCTurboModule { + public: + NativeBooleanTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + + + +@protocol NativeCallbackTurboModuleSpec + +- (void)getValueWithCallback:(RCTResponseSenderBlock)callback; +- (void)getValueWithCallbackWithAlias:(RCTResponseSenderBlock)c; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'CallbackTurboModule' + */ + class JSI_EXPORT NativeCallbackTurboModuleSpecJSI : public ObjCTurboModule { + public: + NativeCallbackTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + + + +@protocol NativeNullableTurboModuleSpec + +- (NSNumber * _Nullable)getBool:(NSNumber *)a; +- (NSNumber * _Nullable)getNumber:(NSNumber *)a; +- (NSString * _Nullable)getString:(NSNumber *)a; +- (NSArray> * _Nullable)getArray:(NSArray * _Nullable)a; +- (NSDictionary * _Nullable)getObject:(NSDictionary * _Nullable)a; +- (void)getValueWithPromise:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NullableTurboModule' + */ + class JSI_EXPORT NativeNullableTurboModuleSpecJSI : public ObjCTurboModule { + public: + NativeNullableTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + + + +@protocol NativeNumberTurboModuleSpec + +- (NSNumber *)getNumber:(double)arg; +- (NSNumber *)getNumberWithAlias:(double)arg; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'NumberTurboModule' + */ + class JSI_EXPORT NativeNumberTurboModuleSpecJSI : public ObjCTurboModule { + public: + NativeNumberTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + + + +namespace JS { + namespace NativeObjectTurboModule { + struct SpecDifficultObjectAE { + bool D() const; + double E() const; + NSString *F() const; + + SpecDifficultObjectAE(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeObjectTurboModule_SpecDifficultObjectAE) ++ (RCTManagedPointer *)JS_NativeObjectTurboModule_SpecDifficultObjectAE:(id)json; +@end + + +namespace JS { + namespace NativeObjectTurboModule { + struct SpecDifficultObjectA { + bool D() const; + JS::NativeObjectTurboModule::SpecDifficultObjectAE E() const; + NSString *F() const; + + SpecDifficultObjectA(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeObjectTurboModule_SpecDifficultObjectA) ++ (RCTManagedPointer *)JS_NativeObjectTurboModule_SpecDifficultObjectA:(id)json; +@end + + +namespace JS { + namespace NativeObjectTurboModule { + struct ConstantsEEE { + + struct Builder { + struct Input { + RCTRequired D; + RCTRequired E; + RCTRequired F; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing ConstantsEEE */ + Builder(ConstantsEEE i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static ConstantsEEE fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + ConstantsEEE(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} + +namespace JS { + namespace NativeObjectTurboModule { + struct ConstantsEE { + + struct Builder { + struct Input { + RCTRequired D; + RCTRequired E; + RCTRequired F; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing ConstantsEE */ + Builder(ConstantsEE i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static ConstantsEE fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + ConstantsEE(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} + +namespace JS { + namespace NativeObjectTurboModule { + struct ConstantsE { + + struct Builder { + struct Input { + RCTRequired D; + RCTRequired E; + RCTRequired F; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing ConstantsE */ + Builder(ConstantsE i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static ConstantsE fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + ConstantsE(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} + +namespace JS { + namespace NativeObjectTurboModule { + struct Constants { + + struct Builder { + struct Input { + RCTRequired D; + RCTRequired E; + RCTRequired F; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing Constants */ + Builder(Constants i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + Constants(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +@protocol NativeObjectTurboModuleSpec + +- (NSDictionary *)getGenericObject:(NSDictionary *)arg; +- (NSDictionary *)getGenericObjectReadOnly:(NSDictionary *)arg; +- (NSDictionary *)getGenericObjectWithAlias:(NSDictionary *)arg; +- (NSDictionary *)difficultObject:(JS::NativeObjectTurboModule::SpecDifficultObjectA &)A; +- (facebook::react::ModuleConstants)constantsToExport; +- (facebook::react::ModuleConstants)getConstants; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'ObjectTurboModule' + */ + class JSI_EXPORT NativeObjectTurboModuleSpecJSI : public ObjCTurboModule { + public: + NativeObjectTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + + + +namespace JS { + namespace NativeOptionalObjectTurboModule { + struct ConstantsEEE { + + struct Builder { + struct Input { + folly::Optional D; + folly::Optional E; + NSString *F; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing ConstantsEEE */ + Builder(ConstantsEEE i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static ConstantsEEE fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + ConstantsEEE(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} + +namespace JS { + namespace NativeOptionalObjectTurboModule { + struct ConstantsEE { + + struct Builder { + struct Input { + folly::Optional D; + folly::Optional E; + NSString *F; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing ConstantsEE */ + Builder(ConstantsEE i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static ConstantsEE fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + ConstantsEE(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} + +namespace JS { + namespace NativeOptionalObjectTurboModule { + struct ConstantsE { + + struct Builder { + struct Input { + folly::Optional D; + folly::Optional E; + NSString *F; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing ConstantsE */ + Builder(ConstantsE i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static ConstantsE fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + ConstantsE(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} + +namespace JS { + namespace NativeOptionalObjectTurboModule { + struct Constants { + + struct Builder { + struct Input { + folly::Optional D; + id _Nullable A; + folly::Optional E; + NSString *F; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing Constants */ + Builder(Constants i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + Constants(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +@protocol NativeOptionalObjectTurboModuleSpec + +- (facebook::react::ModuleConstants)constantsToExport; +- (facebook::react::ModuleConstants)getConstants; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'OptionalObjectTurboModule' + */ + class JSI_EXPORT NativeOptionalObjectTurboModuleSpecJSI : public ObjCTurboModule { + public: + NativeOptionalObjectTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + + + +@protocol NativePromiseTurboModuleSpec + +- (void)getValueWithPromise:(BOOL)error + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)getValueWithPromiseWithAlias:(NSString *)arg + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'PromiseTurboModule' + */ + class JSI_EXPORT NativePromiseTurboModuleSpecJSI : public ObjCTurboModule { + public: + NativePromiseTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + + + +namespace JS { + namespace NativeSampleTurboModule { + struct SpecGetObjectShapeArg { + double prop() const; + + SpecGetObjectShapeArg(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeSampleTurboModule_SpecGetObjectShapeArg) ++ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecGetObjectShapeArg:(id)json; +@end + + +namespace JS { + namespace NativeSampleTurboModule { + struct Animal { + NSString *name() const; + + Animal(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeSampleTurboModule_Animal) ++ (RCTManagedPointer *)JS_NativeSampleTurboModule_Animal:(id)json; +@end + + +namespace JS { + namespace NativeSampleTurboModule { + struct Constants { + + struct Builder { + struct Input { + RCTRequired const1; + RCTRequired const2; + RCTRequired const3; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing Constants */ + Builder(Constants i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + Constants(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +@protocol NativeSampleTurboModuleSpec + +- (void)voidFunc; +- (NSNumber *)getBool:(BOOL)arg; +- (NSNumber *)getNumber:(double)arg; +- (NSString *)getString:(NSString *)arg; +- (NSArray> *)getArray:(NSArray *)arg; +- (NSDictionary *)getObject:(NSDictionary *)arg; +- (NSDictionary *)getObjectShape:(JS::NativeSampleTurboModule::SpecGetObjectShapeArg &)arg; +- (NSDictionary *)getAlias:(JS::NativeSampleTurboModule::Animal &)arg; +- (NSNumber *)getRootTag:(double)arg; +- (NSDictionary *)getValue:(double)x +getValuegetValuegetValuegetValuegetValuey:(NSString *)getValuegetValuegetValuegetValuegetValuey + z:(NSDictionary *)z; +- (void)getValueWithCallback:(RCTResponseSenderBlock)callback; +- (void)getValueWithPromise:(BOOL)error + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (facebook::react::ModuleConstants)constantsToExport; +- (facebook::react::ModuleConstants)getConstants; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'SampleTurboModule' + */ + class JSI_EXPORT NativeSampleTurboModuleSpecJSI : public ObjCTurboModule { + public: + NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + + + +namespace JS { + namespace NativeSampleTurboModuleArrays { + struct ConstantsIdElement { + + struct Builder { + struct Input { + RCTRequired prop; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing ConstantsIdElement */ + Builder(ConstantsIdElement i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static ConstantsIdElement fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + ConstantsIdElement(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} + +namespace JS { + namespace NativeSampleTurboModuleArrays { + struct Constants { + + struct Builder { + struct Input { + RCTRequired> const1; + RCTRequired> const2; + RCTRequired> const3; + folly::Optional>> id_; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing Constants */ + Builder(Constants i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + Constants(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +@protocol NativeSampleTurboModuleArraysSpec + +- (void)voidFunc; +- (NSArray *)getBool:(NSArray *)id; +- (NSArray *)getNumber:(NSArray *)arg; +- (NSArray *)getString:(NSArray *)arg; +- (NSArray> *> *)getArray:(NSArray *)arg; +- (NSArray *)getObject:(NSArray *)arg; +- (NSArray *)getObjectShape:(NSArray *)arg; +- (NSArray *)getAlias:(NSArray *)arg; +- (NSArray *)getRootTag:(NSArray *)arg; +- (NSArray *)getValue:(NSArray *)x + y:(NSArray *)y + z:(NSArray *)z; +- (void)getValueWithCallback:(RCTResponseSenderBlock)callback; +- (void)getValueWithPromise:(NSArray *)error + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (facebook::react::ModuleConstants)constantsToExport; +- (facebook::react::ModuleConstants)getConstants; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'SampleTurboModuleArrays' + */ + class JSI_EXPORT NativeSampleTurboModuleArraysSpecJSI : public ObjCTurboModule { + public: + NativeSampleTurboModuleArraysSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + + + +namespace JS { + namespace NativeSampleTurboModuleNullable { + struct SpecGetObjectShapeArg { + folly::Optional prop() const; + + SpecGetObjectShapeArg(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeSampleTurboModuleNullable_SpecGetObjectShapeArg) ++ (RCTManagedPointer *)JS_NativeSampleTurboModuleNullable_SpecGetObjectShapeArg:(id)json; +@end + + +namespace JS { + namespace NativeSampleTurboModuleNullable { + struct Animal { + NSString *name() const; + + Animal(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeSampleTurboModuleNullable_Animal) ++ (RCTManagedPointer *)JS_NativeSampleTurboModuleNullable_Animal:(id)json; +@end + + +namespace JS { + namespace NativeSampleTurboModuleNullable { + struct Constants { + + struct Builder { + struct Input { + RCTRequired> const1; + RCTRequired> const2; + RCTRequired const3; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing Constants */ + Builder(Constants i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + Constants(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +@protocol NativeSampleTurboModuleNullableSpec + +- (void)voidFunc; +- (NSNumber * _Nullable)getBool:(NSNumber *)arg; +- (NSNumber * _Nullable)getNumber:(NSNumber *)arg; +- (NSString * _Nullable)getString:(NSString * _Nullable)arg; +- (NSArray> * _Nullable)getArray:(NSArray * _Nullable)arg; +- (NSDictionary * _Nullable)getObject:(NSDictionary * _Nullable)arg; +- (NSDictionary * _Nullable)getObjectShape:(JS::NativeSampleTurboModuleNullable::SpecGetObjectShapeArg &)arg; +- (NSDictionary * _Nullable)getAlias:(JS::NativeSampleTurboModuleNullable::Animal &)arg; +- (NSNumber * _Nullable)getRootTag:(NSNumber *)arg; +- (NSDictionary * _Nullable)getValue:(NSNumber *)x + y:(NSString * _Nullable)y + z:(NSDictionary * _Nullable)z; +- (void)getValueWithCallback:(RCTResponseSenderBlock)callback; +- (void)getValueWithPromise:(NSNumber *)error + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (facebook::react::ModuleConstants)constantsToExport; +- (facebook::react::ModuleConstants)getConstants; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'SampleTurboModuleNullable' + */ + class JSI_EXPORT NativeSampleTurboModuleNullableSpecJSI : public ObjCTurboModule { + public: + NativeSampleTurboModuleNullableSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + + + +namespace JS { + namespace NativeSampleTurboModuleNullableAndOptional { + struct SpecGetObjectShapeArg { + folly::Optional prop() const; + + SpecGetObjectShapeArg(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeSampleTurboModuleNullableAndOptional_SpecGetObjectShapeArg) ++ (RCTManagedPointer *)JS_NativeSampleTurboModuleNullableAndOptional_SpecGetObjectShapeArg:(id)json; +@end + + +namespace JS { + namespace NativeSampleTurboModuleNullableAndOptional { + struct Animal { + NSString *name() const; + + Animal(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeSampleTurboModuleNullableAndOptional_Animal) ++ (RCTManagedPointer *)JS_NativeSampleTurboModuleNullableAndOptional_Animal:(id)json; +@end + + +namespace JS { + namespace NativeSampleTurboModuleNullableAndOptional { + struct Constants { + + struct Builder { + struct Input { + folly::Optional const1; + folly::Optional const2; + NSString *const3; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing Constants */ + Builder(Constants i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + Constants(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +@protocol NativeSampleTurboModuleNullableAndOptionalSpec + +- (void)voidFunc; +- (NSNumber * _Nullable)getBool:(NSNumber *)arg; +- (NSNumber * _Nullable)getNumber:(NSNumber *)arg; +- (NSString * _Nullable)getString:(NSString * _Nullable)arg; +- (NSArray> * _Nullable)getArray:(NSArray * _Nullable)arg; +- (NSDictionary * _Nullable)getObject:(NSDictionary * _Nullable)arg; +- (NSDictionary *)getObjectShape:(JS::NativeSampleTurboModuleNullableAndOptional::SpecGetObjectShapeArg &)arg; +- (NSDictionary * _Nullable)getAlias:(JS::NativeSampleTurboModuleNullableAndOptional::Animal &)arg; +- (NSNumber * _Nullable)getRootTag:(NSNumber *)arg; +- (NSDictionary * _Nullable)getValue:(NSNumber *)x + y:(NSString * _Nullable)y + z:(NSDictionary * _Nullable)z; +- (void)getValueWithCallback:(RCTResponseSenderBlock)callback; +- (void)getValueWithPromise:(NSNumber *)error + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (facebook::react::ModuleConstants)constantsToExport; +- (facebook::react::ModuleConstants)getConstants; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'SampleTurboModuleNullableAndOptional' + */ + class JSI_EXPORT NativeSampleTurboModuleNullableAndOptionalSpecJSI : public ObjCTurboModule { + public: + NativeSampleTurboModuleNullableAndOptionalSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + + + +namespace JS { + namespace NativeSampleTurboModuleOptional { + struct SpecGetObjectShapeArg { + folly::Optional prop() const; + + SpecGetObjectShapeArg(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeSampleTurboModuleOptional_SpecGetObjectShapeArg) ++ (RCTManagedPointer *)JS_NativeSampleTurboModuleOptional_SpecGetObjectShapeArg:(id)json; +@end + + +namespace JS { + namespace NativeSampleTurboModuleOptional { + struct Animal { + NSString *name() const; + + Animal(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (NativeSampleTurboModuleOptional_Animal) ++ (RCTManagedPointer *)JS_NativeSampleTurboModuleOptional_Animal:(id)json; +@end + + +namespace JS { + namespace NativeSampleTurboModuleOptional { + struct Constants { + + struct Builder { + struct Input { + folly::Optional const1; + folly::Optional const2; + NSString *const3; + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing Constants */ + Builder(Constants i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + Constants(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +} +@protocol NativeSampleTurboModuleOptionalSpec + +- (void)voidFunc; +- (NSNumber *)getBool:(NSNumber *)arg; +- (NSNumber *)getNumber:(NSNumber *)arg; +- (NSString *)getString:(NSString *)arg; +- (NSArray> *)getArray:(NSArray *)arg; +- (NSDictionary *)getObject:(NSDictionary *)arg; +- (NSDictionary *)getObjectShape:(JS::NativeSampleTurboModuleOptional::SpecGetObjectShapeArg &)arg; +- (NSDictionary *)getAlias:(JS::NativeSampleTurboModuleOptional::Animal &)arg; +- (NSNumber *)getRootTag:(NSNumber *)arg; +- (NSDictionary *)getValue:(NSNumber *)x + y:(NSString *)y + z:(NSDictionary *)z; +- (void)getValueWithCallback:(RCTResponseSenderBlock)callback; +- (void)getValueWithPromise:(NSNumber *)error + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (facebook::react::ModuleConstants)constantsToExport; +- (facebook::react::ModuleConstants)getConstants; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'SampleTurboModuleOptional' + */ + class JSI_EXPORT NativeSampleTurboModuleOptionalSpecJSI : public ObjCTurboModule { + public: + NativeSampleTurboModuleOptionalSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + + + +@protocol NativeStringTurboModuleSpec + +- (NSString *)getString:(NSString *)arg; +- (NSString *)getStringWithAlias:(NSString *)arg; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'StringTurboModule' + */ + class JSI_EXPORT NativeStringTurboModuleSpecJSI : public ObjCTurboModule { + public: + NativeStringTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + + + + + + + + +inline bool JS::NativeObjectTurboModule::SpecDifficultObjectAE::D() const +{ + id const p = _v[@\\"D\\"]; + return RCTBridgingToBool(p); +} + + +inline double JS::NativeObjectTurboModule::SpecDifficultObjectAE::E() const +{ + id const p = _v[@\\"E\\"]; + return RCTBridgingToDouble(p); +} + + +inline NSString *JS::NativeObjectTurboModule::SpecDifficultObjectAE::F() const +{ + id const p = _v[@\\"F\\"]; + return RCTBridgingToString(p); +} + + +inline bool JS::NativeObjectTurboModule::SpecDifficultObjectA::D() const +{ + id const p = _v[@\\"D\\"]; + return RCTBridgingToBool(p); +} + + +inline JS::NativeObjectTurboModule::SpecDifficultObjectAE JS::NativeObjectTurboModule::SpecDifficultObjectA::E() const +{ + id const p = _v[@\\"E\\"]; + return JS::NativeObjectTurboModule::SpecDifficultObjectAE(p); +} + + +inline NSString *JS::NativeObjectTurboModule::SpecDifficultObjectA::F() const +{ + id const p = _v[@\\"F\\"]; + return RCTBridgingToString(p); +} + + +inline JS::NativeObjectTurboModule::ConstantsEEE::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto D = i.D.get(); + d[@\\"D\\"] = @(D); + auto E = i.E.get(); + d[@\\"E\\"] = @(E); + auto F = i.F.get(); + d[@\\"F\\"] = F; + return d; +}) {} +inline JS::NativeObjectTurboModule::ConstantsEEE::Builder::Builder(ConstantsEEE i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + +inline JS::NativeObjectTurboModule::ConstantsEE::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto D = i.D.get(); + d[@\\"D\\"] = @(D); + auto E = i.E.get(); + d[@\\"E\\"] = E.buildUnsafeRawValue(); + auto F = i.F.get(); + d[@\\"F\\"] = F; + return d; +}) {} +inline JS::NativeObjectTurboModule::ConstantsEE::Builder::Builder(ConstantsEE i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + +inline JS::NativeObjectTurboModule::ConstantsE::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto D = i.D.get(); + d[@\\"D\\"] = @(D); + auto E = i.E.get(); + d[@\\"E\\"] = E.buildUnsafeRawValue(); + auto F = i.F.get(); + d[@\\"F\\"] = F; + return d; +}) {} +inline JS::NativeObjectTurboModule::ConstantsE::Builder::Builder(ConstantsE i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + +inline JS::NativeObjectTurboModule::Constants::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto D = i.D.get(); + d[@\\"D\\"] = @(D); + auto E = i.E.get(); + d[@\\"E\\"] = E.buildUnsafeRawValue(); + auto F = i.F.get(); + d[@\\"F\\"] = F; + return d; +}) {} +inline JS::NativeObjectTurboModule::Constants::Builder::Builder(Constants i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + +inline JS::NativeOptionalObjectTurboModule::ConstantsEEE::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto D = i.D; + d[@\\"D\\"] = D.hasValue() ? @((BOOL)D.value()) : nil; + auto E = i.E; + d[@\\"E\\"] = E.hasValue() ? @((double)E.value()) : nil; + auto F = i.F; + d[@\\"F\\"] = F; + return d; +}) {} +inline JS::NativeOptionalObjectTurboModule::ConstantsEEE::Builder::Builder(ConstantsEEE i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + +inline JS::NativeOptionalObjectTurboModule::ConstantsEE::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto D = i.D; + d[@\\"D\\"] = D.hasValue() ? @((BOOL)D.value()) : nil; + auto E = i.E; + d[@\\"E\\"] = E.hasValue() ? E.value().buildUnsafeRawValue() : nil; + auto F = i.F; + d[@\\"F\\"] = F; + return d; +}) {} +inline JS::NativeOptionalObjectTurboModule::ConstantsEE::Builder::Builder(ConstantsEE i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + +inline JS::NativeOptionalObjectTurboModule::ConstantsE::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto D = i.D; + d[@\\"D\\"] = D.hasValue() ? @((BOOL)D.value()) : nil; + auto E = i.E; + d[@\\"E\\"] = E.hasValue() ? E.value().buildUnsafeRawValue() : nil; + auto F = i.F; + d[@\\"F\\"] = F; + return d; +}) {} +inline JS::NativeOptionalObjectTurboModule::ConstantsE::Builder::Builder(ConstantsE i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + +inline JS::NativeOptionalObjectTurboModule::Constants::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto D = i.D; + d[@\\"D\\"] = D.hasValue() ? @((BOOL)D.value()) : nil; + auto A = i.A; + d[@\\"A\\"] = A; + auto E = i.E; + d[@\\"E\\"] = E.hasValue() ? E.value().buildUnsafeRawValue() : nil; + auto F = i.F; + d[@\\"F\\"] = F; + return d; +}) {} +inline JS::NativeOptionalObjectTurboModule::Constants::Builder::Builder(Constants i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + + +inline double JS::NativeSampleTurboModule::SpecGetObjectShapeArg::prop() const +{ + id const p = _v[@\\"prop\\"]; + return RCTBridgingToDouble(p); +} + + +inline NSString *JS::NativeSampleTurboModule::Animal::name() const +{ + id const p = _v[@\\"name\\"]; + return RCTBridgingToString(p); +} + + +inline JS::NativeSampleTurboModule::Constants::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto const1 = i.const1.get(); + d[@\\"const1\\"] = @(const1); + auto const2 = i.const2.get(); + d[@\\"const2\\"] = @(const2); + auto const3 = i.const3.get(); + d[@\\"const3\\"] = const3; + return d; +}) {} +inline JS::NativeSampleTurboModule::Constants::Builder::Builder(Constants i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + +inline JS::NativeSampleTurboModuleArrays::ConstantsIdElement::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto prop = i.prop.get(); + d[@\\"prop\\"] = @(prop); + return d; +}) {} +inline JS::NativeSampleTurboModuleArrays::ConstantsIdElement::Builder::Builder(ConstantsIdElement i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + +inline JS::NativeSampleTurboModuleArrays::Constants::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto const1 = i.const1.get(); + d[@\\"const1\\"] = RCTConvertVecToArray(const1, ^id(bool el_) { return @(el_); }); + auto const2 = i.const2.get(); + d[@\\"const2\\"] = RCTConvertVecToArray(const2, ^id(double el_) { return @(el_); }); + auto const3 = i.const3.get(); + d[@\\"const3\\"] = RCTConvertVecToArray(const3, ^id(NSString * el_) { return el_; }); + auto id_ = i.id_; + d[@\\"id_\\"] = RCTConvertOptionalVecToArray(id_, ^id(folly::Optional el_) { return el_.hasValue() ? el_.value().buildUnsafeRawValue() : nil; }); + return d; +}) {} +inline JS::NativeSampleTurboModuleArrays::Constants::Builder::Builder(Constants i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + +inline folly::Optional JS::NativeSampleTurboModuleNullable::SpecGetObjectShapeArg::prop() const +{ + id const p = _v[@\\"prop\\"]; + return RCTBridgingToOptionalDouble(p); +} + + +inline NSString *JS::NativeSampleTurboModuleNullable::Animal::name() const +{ + id const p = _v[@\\"name\\"]; + return RCTBridgingToOptionalString(p); +} + + +inline JS::NativeSampleTurboModuleNullable::Constants::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto const1 = i.const1.get(); + d[@\\"const1\\"] = const1.hasValue() ? @((BOOL)const1.value()) : nil; + auto const2 = i.const2.get(); + d[@\\"const2\\"] = const2.hasValue() ? @((double)const2.value()) : nil; + auto const3 = i.const3.get(); + d[@\\"const3\\"] = const3; + return d; +}) {} +inline JS::NativeSampleTurboModuleNullable::Constants::Builder::Builder(Constants i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + +inline folly::Optional JS::NativeSampleTurboModuleNullableAndOptional::SpecGetObjectShapeArg::prop() const +{ + id const p = _v[@\\"prop\\"]; + return RCTBridgingToOptionalDouble(p); +} + + +inline NSString *JS::NativeSampleTurboModuleNullableAndOptional::Animal::name() const +{ + id const p = _v[@\\"name\\"]; + return RCTBridgingToOptionalString(p); +} + + +inline JS::NativeSampleTurboModuleNullableAndOptional::Constants::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto const1 = i.const1; + d[@\\"const1\\"] = const1.hasValue() ? @((BOOL)const1.value()) : nil; + auto const2 = i.const2; + d[@\\"const2\\"] = const2.hasValue() ? @((double)const2.value()) : nil; + auto const3 = i.const3; + d[@\\"const3\\"] = const3; + return d; +}) {} +inline JS::NativeSampleTurboModuleNullableAndOptional::Constants::Builder::Builder(Constants i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + +inline folly::Optional JS::NativeSampleTurboModuleOptional::SpecGetObjectShapeArg::prop() const +{ + id const p = _v[@\\"prop\\"]; + return RCTBridgingToOptionalDouble(p); +} + + +inline NSString *JS::NativeSampleTurboModuleOptional::Animal::name() const +{ + id const p = _v[@\\"name\\"]; + return RCTBridgingToOptionalString(p); +} + + +inline JS::NativeSampleTurboModuleOptional::Constants::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto const1 = i.const1; + d[@\\"const1\\"] = const1.hasValue() ? @((BOOL)const1.value()) : nil; + auto const2 = i.const2; + d[@\\"const2\\"] = const2.hasValue() ? @((double)const2.value()) : nil; + auto const3 = i.const3; + d[@\\"const3\\"] = const3; + return d; +}) {} +inline JS::NativeSampleTurboModuleOptional::Constants::Builder::Builder(Constants i) : _factory(^{ + return i.unsafeRawValue(); +}) {} + +" +`; + +exports[`GenerateModuleObjCpp can generate an implementation file NativeModule specs 1`] = ` +" +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @generated by an internal genrule from Flow types. + * + * We create an umbrella header (and corresponding implementation) here since + * Cxx compilation in BUCK has a limitation: source-code producing genrule()s + * must have a single output. More files => more genrule()s => slower builds. + */ + +#import \\"RNCodegenModuleFixtures.h\\" + + + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeArrayTurboModuleSpecJSI_getArray(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getArray\\", @selector(getArray:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeArrayTurboModuleSpecJSI_getReadOnlyArray(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getReadOnlyArray\\", @selector(getReadOnlyArray:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeArrayTurboModuleSpecJSI_getArrayWithAlias(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getArrayWithAlias\\", @selector(getArrayWithAlias:b:), args, count); + } + + + NativeArrayTurboModuleSpecJSI::NativeArrayTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_[\\"getArray\\"] = MethodMetadata {1, __hostFunction_NativeArrayTurboModuleSpecJSI_getArray}; + + + + methodMap_[\\"getReadOnlyArray\\"] = MethodMetadata {1, __hostFunction_NativeArrayTurboModuleSpecJSI_getReadOnlyArray}; + + + + methodMap_[\\"getArrayWithAlias\\"] = MethodMetadata {2, __hostFunction_NativeArrayTurboModuleSpecJSI_getArrayWithAlias}; + + + } + } // namespace react +} // namespace facebook + + + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeBooleanTurboModuleSpecJSI_getBoolean(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getBoolean\\", @selector(getBoolean:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeBooleanTurboModuleSpecJSI_getBooleanWithAlias(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getBooleanWithAlias\\", @selector(getBooleanWithAlias:), args, count); + } + + + NativeBooleanTurboModuleSpecJSI::NativeBooleanTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_[\\"getBoolean\\"] = MethodMetadata {1, __hostFunction_NativeBooleanTurboModuleSpecJSI_getBoolean}; + + + + methodMap_[\\"getBooleanWithAlias\\"] = MethodMetadata {1, __hostFunction_NativeBooleanTurboModuleSpecJSI_getBooleanWithAlias}; + + + } + } // namespace react +} // namespace facebook + + + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeCallbackTurboModuleSpecJSI_getValueWithCallback(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"getValueWithCallback\\", @selector(getValueWithCallback:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeCallbackTurboModuleSpecJSI_getValueWithCallbackWithAlias(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"getValueWithCallbackWithAlias\\", @selector(getValueWithCallbackWithAlias:), args, count); + } + + + NativeCallbackTurboModuleSpecJSI::NativeCallbackTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_[\\"getValueWithCallback\\"] = MethodMetadata {1, __hostFunction_NativeCallbackTurboModuleSpecJSI_getValueWithCallback}; + + + + methodMap_[\\"getValueWithCallbackWithAlias\\"] = MethodMetadata {1, __hostFunction_NativeCallbackTurboModuleSpecJSI_getValueWithCallbackWithAlias}; + + + } + } // namespace react +} // namespace facebook + + + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeNullableTurboModuleSpecJSI_getBool(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getBool\\", @selector(getBool:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeNullableTurboModuleSpecJSI_getNumber(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getNumber\\", @selector(getNumber:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeNullableTurboModuleSpecJSI_getString(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, StringKind, \\"getString\\", @selector(getString:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeNullableTurboModuleSpecJSI_getArray(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getArray\\", @selector(getArray:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeNullableTurboModuleSpecJSI_getObject(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getObject\\", @selector(getObject:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeNullableTurboModuleSpecJSI_getValueWithPromise(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, \\"getValueWithPromise\\", @selector(getValueWithPromise:reject:), args, count); + } + + + NativeNullableTurboModuleSpecJSI::NativeNullableTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_[\\"getBool\\"] = MethodMetadata {1, __hostFunction_NativeNullableTurboModuleSpecJSI_getBool}; + + + + methodMap_[\\"getNumber\\"] = MethodMetadata {1, __hostFunction_NativeNullableTurboModuleSpecJSI_getNumber}; + + + + methodMap_[\\"getString\\"] = MethodMetadata {1, __hostFunction_NativeNullableTurboModuleSpecJSI_getString}; + + + + methodMap_[\\"getArray\\"] = MethodMetadata {1, __hostFunction_NativeNullableTurboModuleSpecJSI_getArray}; + + + + methodMap_[\\"getObject\\"] = MethodMetadata {1, __hostFunction_NativeNullableTurboModuleSpecJSI_getObject}; + + + + methodMap_[\\"getValueWithPromise\\"] = MethodMetadata {0, __hostFunction_NativeNullableTurboModuleSpecJSI_getValueWithPromise}; + + + } + } // namespace react +} // namespace facebook + + + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeNumberTurboModuleSpecJSI_getNumber(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getNumber\\", @selector(getNumber:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeNumberTurboModuleSpecJSI_getNumberWithAlias(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getNumberWithAlias\\", @selector(getNumberWithAlias:), args, count); + } + + + NativeNumberTurboModuleSpecJSI::NativeNumberTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_[\\"getNumber\\"] = MethodMetadata {1, __hostFunction_NativeNumberTurboModuleSpecJSI_getNumber}; + + + + methodMap_[\\"getNumberWithAlias\\"] = MethodMetadata {1, __hostFunction_NativeNumberTurboModuleSpecJSI_getNumberWithAlias}; + + + } + } // namespace react +} // namespace facebook + + + +@implementation RCTCxxConvert (NativeObjectTurboModule_SpecDifficultObjectAE) ++ (RCTManagedPointer *)JS_NativeObjectTurboModule_SpecDifficultObjectAE:(id)json +{ + return facebook::react::managedPointer(json); +} +@end + + +@implementation RCTCxxConvert (NativeObjectTurboModule_SpecDifficultObjectA) ++ (RCTManagedPointer *)JS_NativeObjectTurboModule_SpecDifficultObjectA:(id)json +{ + return facebook::react::managedPointer(json); +} +@end + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeObjectTurboModuleSpecJSI_getGenericObject(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getGenericObject\\", @selector(getGenericObject:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeObjectTurboModuleSpecJSI_getGenericObjectReadOnly(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getGenericObjectReadOnly\\", @selector(getGenericObjectReadOnly:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeObjectTurboModuleSpecJSI_getGenericObjectWithAlias(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getGenericObjectWithAlias\\", @selector(getGenericObjectWithAlias:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeObjectTurboModuleSpecJSI_difficultObject(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"difficultObject\\", @selector(difficultObject:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeObjectTurboModuleSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getConstants\\", @selector(getConstants), args, count); + } + + + NativeObjectTurboModuleSpecJSI::NativeObjectTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_[\\"getGenericObject\\"] = MethodMetadata {1, __hostFunction_NativeObjectTurboModuleSpecJSI_getGenericObject}; + + + + methodMap_[\\"getGenericObjectReadOnly\\"] = MethodMetadata {1, __hostFunction_NativeObjectTurboModuleSpecJSI_getGenericObjectReadOnly}; + + + + methodMap_[\\"getGenericObjectWithAlias\\"] = MethodMetadata {1, __hostFunction_NativeObjectTurboModuleSpecJSI_getGenericObjectWithAlias}; + + + + methodMap_[\\"difficultObject\\"] = MethodMetadata {1, __hostFunction_NativeObjectTurboModuleSpecJSI_difficultObject}; + setMethodArgConversionSelector(@\\"difficultObject\\", 0, @\\"JS_NativeObjectTurboModule_SpecDifficultObjectA:\\"); + + + methodMap_[\\"getConstants\\"] = MethodMetadata {0, __hostFunction_NativeObjectTurboModuleSpecJSI_getConstants}; + + + } + } // namespace react +} // namespace facebook + + + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeOptionalObjectTurboModuleSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getConstants\\", @selector(getConstants), args, count); + } + + + NativeOptionalObjectTurboModuleSpecJSI::NativeOptionalObjectTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_[\\"getConstants\\"] = MethodMetadata {0, __hostFunction_NativeOptionalObjectTurboModuleSpecJSI_getConstants}; + + + } + } // namespace react +} // namespace facebook + + + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativePromiseTurboModuleSpecJSI_getValueWithPromise(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, \\"getValueWithPromise\\", @selector(getValueWithPromise:resolve:reject:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativePromiseTurboModuleSpecJSI_getValueWithPromiseWithAlias(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, \\"getValueWithPromiseWithAlias\\", @selector(getValueWithPromiseWithAlias:resolve:reject:), args, count); + } + + + NativePromiseTurboModuleSpecJSI::NativePromiseTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_[\\"getValueWithPromise\\"] = MethodMetadata {1, __hostFunction_NativePromiseTurboModuleSpecJSI_getValueWithPromise}; + + + + methodMap_[\\"getValueWithPromiseWithAlias\\"] = MethodMetadata {1, __hostFunction_NativePromiseTurboModuleSpecJSI_getValueWithPromiseWithAlias}; + + + } + } // namespace react +} // namespace facebook + + + +@implementation RCTCxxConvert (NativeSampleTurboModule_SpecGetObjectShapeArg) ++ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecGetObjectShapeArg:(id)json +{ + return facebook::react::managedPointer(json); +} +@end + + +@implementation RCTCxxConvert (NativeSampleTurboModule_Animal) ++ (RCTManagedPointer *)JS_NativeSampleTurboModule_Animal:(id)json +{ + return facebook::react::managedPointer(json); +} +@end + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getBool(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getBool\\", @selector(getBool:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getNumber(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getNumber\\", @selector(getNumber:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getString(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, StringKind, \\"getString\\", @selector(getString:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getArray(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getArray\\", @selector(getArray:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getObject(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getObject\\", @selector(getObject:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getObjectShape(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getObjectShape\\", @selector(getObjectShape:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getAlias(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getAlias\\", @selector(getAlias:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getRootTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getRootTag\\", @selector(getRootTag:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getValue\\", @selector(getValue:getValuegetValuegetValuegetValuegetValuey:z:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithCallback(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"getValueWithCallback\\", @selector(getValueWithCallback:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithPromise(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, \\"getValueWithPromise\\", @selector(getValueWithPromise:resolve:reject:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getConstants\\", @selector(getConstants), args, count); + } + + + NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc}; + + + + methodMap_[\\"getBool\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getBool}; + + + + methodMap_[\\"getNumber\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getNumber}; + + + + methodMap_[\\"getString\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getString}; + + + + methodMap_[\\"getArray\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getArray}; + + + + methodMap_[\\"getObject\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getObject}; + + + + methodMap_[\\"getObjectShape\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getObjectShape}; + setMethodArgConversionSelector(@\\"getObjectShape\\", 0, @\\"JS_NativeSampleTurboModule_SpecGetObjectShapeArg:\\"); + + + methodMap_[\\"getAlias\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getAlias}; + setMethodArgConversionSelector(@\\"getAlias\\", 0, @\\"JS_NativeSampleTurboModule_Animal:\\"); + + + methodMap_[\\"getRootTag\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getRootTag}; + + + + methodMap_[\\"getValue\\"] = MethodMetadata {3, __hostFunction_NativeSampleTurboModuleSpecJSI_getValue}; + + + + methodMap_[\\"getValueWithCallback\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithCallback}; + + + + methodMap_[\\"getValueWithPromise\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithPromise}; + + + + methodMap_[\\"getConstants\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleSpecJSI_getConstants}; + + + } + } // namespace react +} // namespace facebook + + + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleArraysSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getBool(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getBool\\", @selector(getBool:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getNumber(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getNumber\\", @selector(getNumber:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getString(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getString\\", @selector(getString:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getArray(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getArray\\", @selector(getArray:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getObject(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getObject\\", @selector(getObject:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getObjectShape(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getObjectShape\\", @selector(getObjectShape:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getAlias(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getAlias\\", @selector(getAlias:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getRootTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getRootTag\\", @selector(getRootTag:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getValue\\", @selector(getValue:y:z:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getValueWithCallback(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"getValueWithCallback\\", @selector(getValueWithCallback:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getValueWithPromise(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, \\"getValueWithPromise\\", @selector(getValueWithPromise:resolve:reject:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getConstants\\", @selector(getConstants), args, count); + } + + + NativeSampleTurboModuleArraysSpecJSI::NativeSampleTurboModuleArraysSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleArraysSpecJSI_voidFunc}; + + + + methodMap_[\\"getBool\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getBool}; + + + + methodMap_[\\"getNumber\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getNumber}; + + + + methodMap_[\\"getString\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getString}; + + + + methodMap_[\\"getArray\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getArray}; + + + + methodMap_[\\"getObject\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getObject}; + + + + methodMap_[\\"getObjectShape\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getObjectShape}; + + + + methodMap_[\\"getAlias\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getAlias}; + + + + methodMap_[\\"getRootTag\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getRootTag}; + + + + methodMap_[\\"getValue\\"] = MethodMetadata {3, __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getValue}; + + + + methodMap_[\\"getValueWithCallback\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getValueWithCallback}; + + + + methodMap_[\\"getValueWithPromise\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getValueWithPromise}; + + + + methodMap_[\\"getConstants\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleArraysSpecJSI_getConstants}; + + + } + } // namespace react +} // namespace facebook + + + +@implementation RCTCxxConvert (NativeSampleTurboModuleNullable_SpecGetObjectShapeArg) ++ (RCTManagedPointer *)JS_NativeSampleTurboModuleNullable_SpecGetObjectShapeArg:(id)json +{ + return facebook::react::managedPointer(json); +} +@end + + +@implementation RCTCxxConvert (NativeSampleTurboModuleNullable_Animal) ++ (RCTManagedPointer *)JS_NativeSampleTurboModuleNullable_Animal:(id)json +{ + return facebook::react::managedPointer(json); +} +@end + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getBool(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getBool\\", @selector(getBool:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getNumber(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getNumber\\", @selector(getNumber:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getString(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, StringKind, \\"getString\\", @selector(getString:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getArray(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getArray\\", @selector(getArray:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getObject(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getObject\\", @selector(getObject:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getObjectShape(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getObjectShape\\", @selector(getObjectShape:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getAlias(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getAlias\\", @selector(getAlias:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getRootTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getRootTag\\", @selector(getRootTag:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getValue\\", @selector(getValue:y:z:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getValueWithCallback(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"getValueWithCallback\\", @selector(getValueWithCallback:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getValueWithPromise(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, \\"getValueWithPromise\\", @selector(getValueWithPromise:resolve:reject:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getConstants\\", @selector(getConstants), args, count); + } + + + NativeSampleTurboModuleNullableSpecJSI::NativeSampleTurboModuleNullableSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleNullableSpecJSI_voidFunc}; + + + + methodMap_[\\"getBool\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getBool}; + + + + methodMap_[\\"getNumber\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getNumber}; + + + + methodMap_[\\"getString\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getString}; + + + + methodMap_[\\"getArray\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getArray}; + + + + methodMap_[\\"getObject\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getObject}; + + + + methodMap_[\\"getObjectShape\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getObjectShape}; + setMethodArgConversionSelector(@\\"getObjectShape\\", 0, @\\"JS_NativeSampleTurboModuleNullable_SpecGetObjectShapeArg:\\"); + + + methodMap_[\\"getAlias\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getAlias}; + setMethodArgConversionSelector(@\\"getAlias\\", 0, @\\"JS_NativeSampleTurboModuleNullable_Animal:\\"); + + + methodMap_[\\"getRootTag\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getRootTag}; + + + + methodMap_[\\"getValue\\"] = MethodMetadata {3, __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getValue}; + + + + methodMap_[\\"getValueWithCallback\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getValueWithCallback}; + + + + methodMap_[\\"getValueWithPromise\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getValueWithPromise}; + + + + methodMap_[\\"getConstants\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleNullableSpecJSI_getConstants}; + + + } + } // namespace react +} // namespace facebook + + + +@implementation RCTCxxConvert (NativeSampleTurboModuleNullableAndOptional_SpecGetObjectShapeArg) ++ (RCTManagedPointer *)JS_NativeSampleTurboModuleNullableAndOptional_SpecGetObjectShapeArg:(id)json +{ + return facebook::react::managedPointer(json); +} +@end + + +@implementation RCTCxxConvert (NativeSampleTurboModuleNullableAndOptional_Animal) ++ (RCTManagedPointer *)JS_NativeSampleTurboModuleNullableAndOptional_Animal:(id)json +{ + return facebook::react::managedPointer(json); +} +@end + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getBool(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getBool\\", @selector(getBool:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getNumber(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getNumber\\", @selector(getNumber:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getString(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, StringKind, \\"getString\\", @selector(getString:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getArray(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getArray\\", @selector(getArray:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getObject(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getObject\\", @selector(getObject:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getObjectShape(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getObjectShape\\", @selector(getObjectShape:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getAlias(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getAlias\\", @selector(getAlias:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getRootTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getRootTag\\", @selector(getRootTag:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getValue\\", @selector(getValue:y:z:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getValueWithCallback(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"getValueWithCallback\\", @selector(getValueWithCallback:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getValueWithPromise(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, \\"getValueWithPromise\\", @selector(getValueWithPromise:resolve:reject:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getConstants\\", @selector(getConstants), args, count); + } + + + NativeSampleTurboModuleNullableAndOptionalSpecJSI::NativeSampleTurboModuleNullableAndOptionalSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_voidFunc}; + + + + methodMap_[\\"getBool\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getBool}; + + + + methodMap_[\\"getNumber\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getNumber}; + + + + methodMap_[\\"getString\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getString}; + + + + methodMap_[\\"getArray\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getArray}; + + + + methodMap_[\\"getObject\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getObject}; + + + + methodMap_[\\"getObjectShape\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getObjectShape}; + setMethodArgConversionSelector(@\\"getObjectShape\\", 0, @\\"JS_NativeSampleTurboModuleNullableAndOptional_SpecGetObjectShapeArg:\\"); + + + methodMap_[\\"getAlias\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getAlias}; + setMethodArgConversionSelector(@\\"getAlias\\", 0, @\\"JS_NativeSampleTurboModuleNullableAndOptional_Animal:\\"); + + + methodMap_[\\"getRootTag\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getRootTag}; + + + + methodMap_[\\"getValue\\"] = MethodMetadata {3, __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getValue}; + + + + methodMap_[\\"getValueWithCallback\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getValueWithCallback}; + + + + methodMap_[\\"getValueWithPromise\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getValueWithPromise}; + + + + methodMap_[\\"getConstants\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleNullableAndOptionalSpecJSI_getConstants}; + + + } + } // namespace react +} // namespace facebook + + + +@implementation RCTCxxConvert (NativeSampleTurboModuleOptional_SpecGetObjectShapeArg) ++ (RCTManagedPointer *)JS_NativeSampleTurboModuleOptional_SpecGetObjectShapeArg:(id)json +{ + return facebook::react::managedPointer(json); +} +@end + + +@implementation RCTCxxConvert (NativeSampleTurboModuleOptional_Animal) ++ (RCTManagedPointer *)JS_NativeSampleTurboModuleOptional_Animal:(id)json +{ + return facebook::react::managedPointer(json); +} +@end + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getBool(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getBool\\", @selector(getBool:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getNumber(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getNumber\\", @selector(getNumber:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getString(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, StringKind, \\"getString\\", @selector(getString:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getArray(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getArray\\", @selector(getArray:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getObject(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getObject\\", @selector(getObject:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getObjectShape(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getObjectShape\\", @selector(getObjectShape:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getAlias(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getAlias\\", @selector(getAlias:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getRootTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getRootTag\\", @selector(getRootTag:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getValue\\", @selector(getValue:y:z:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getValueWithCallback(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"getValueWithCallback\\", @selector(getValueWithCallback:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getValueWithPromise(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, \\"getValueWithPromise\\", @selector(getValueWithPromise:resolve:reject:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getConstants\\", @selector(getConstants), args, count); + } + + + NativeSampleTurboModuleOptionalSpecJSI::NativeSampleTurboModuleOptionalSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_voidFunc}; + + + + methodMap_[\\"getBool\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getBool}; + + + + methodMap_[\\"getNumber\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getNumber}; + + + + methodMap_[\\"getString\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getString}; + + + + methodMap_[\\"getArray\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getArray}; + + + + methodMap_[\\"getObject\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getObject}; + + + + methodMap_[\\"getObjectShape\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getObjectShape}; + setMethodArgConversionSelector(@\\"getObjectShape\\", 0, @\\"JS_NativeSampleTurboModuleOptional_SpecGetObjectShapeArg:\\"); + + + methodMap_[\\"getAlias\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getAlias}; + setMethodArgConversionSelector(@\\"getAlias\\", 0, @\\"JS_NativeSampleTurboModuleOptional_Animal:\\"); + + + methodMap_[\\"getRootTag\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getRootTag}; + + + + methodMap_[\\"getValue\\"] = MethodMetadata {3, __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getValue}; + + + + methodMap_[\\"getValueWithCallback\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getValueWithCallback}; + + + + methodMap_[\\"getValueWithPromise\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getValueWithPromise}; + + + + methodMap_[\\"getConstants\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleOptionalSpecJSI_getConstants}; + + + } + } // namespace react +} // namespace facebook + + + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeStringTurboModuleSpecJSI_getString(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, StringKind, \\"getString\\", @selector(getString:), args, count); + } + + + static facebook::jsi::Value __hostFunction_NativeStringTurboModuleSpecJSI_getStringWithAlias(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, StringKind, \\"getStringWithAlias\\", @selector(getStringWithAlias:), args, count); + } + + + NativeStringTurboModuleSpecJSI::NativeStringTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_[\\"getString\\"] = MethodMetadata {1, __hostFunction_NativeStringTurboModuleSpecJSI_getString}; + + + + methodMap_[\\"getStringWithAlias\\"] = MethodMetadata {1, __hostFunction_NativeStringTurboModuleSpecJSI_getStringWithAlias}; + + + } + } // namespace react +} // namespace facebook + +" +`; diff --git a/packages/react-native-codegen/package.json b/packages/react-native-codegen/package.json index f0a19aa194a297..57d3fe3369d34a 100644 --- a/packages/react-native-codegen/package.json +++ b/packages/react-native-codegen/package.json @@ -5,7 +5,8 @@ "homepage": "https://github.com/facebook/react-native/tree/master/packages/react-native-codegen", "repository": { "type": "git", - "url": "git@github.com:facebook/react-native.git" + "url": "git@github.com:facebook/react-native.git", + "directory": "packages/react-native-codegen" }, "scripts": { "build": "yarn clean && node scripts/build.js --verbose", @@ -26,6 +27,7 @@ }, "devDependencies": { "@babel/core": "^7.0.0", + "@babel/plugin-proposal-class-properties": "^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0", "@babel/plugin-proposal-object-rest-spread": "^7.0.0", "@babel/plugin-proposal-optional-chaining": "^7.0.0", diff --git a/packages/react-native-codegen/src/CodegenSchema.js b/packages/react-native-codegen/src/CodegenSchema.js index ffd3a1a4666173..7e35b52207aac7 100644 --- a/packages/react-native-codegen/src/CodegenSchema.js +++ b/packages/react-native-codegen/src/CodegenSchema.js @@ -248,7 +248,7 @@ export type SchemaType = $ReadOnly<{| [module: string]: $ReadOnly<{| components?: $ReadOnly<{[component: string]: ComponentShape, ...}>, nativeModules?: $ReadOnly<{ - [nativeModule: string]: NativeModuleShape, + [nativeModule: string]: NativeModuleSchema, ..., }>, |}>, @@ -258,112 +258,152 @@ export type SchemaType = $ReadOnly<{| /** * NativeModule Types + * + * TODO(T71923114): Remove nullable from NativeModule type annotations. + * This is necessary for us to start unifying the notion of a "type + * annotation" across the codegen schema as a whole. */ -export type NativeModuleShape = $ReadOnly<{| +export type Required = $ReadOnly<{...T, nullable: false}>; + +export type NativeModuleSchema = $ReadOnly<{| // We only support aliases to Objects - aliases: $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}>, - properties: $ReadOnlyArray, + aliases: NativeModuleAliasMap, + properties: $ReadOnlyArray, |}>; -export type ObjectTypeAliasTypeShape = $ReadOnly<{| - type: 'ObjectTypeAnnotation', - properties: $ReadOnlyArray, +export type NativeModuleAliasMap = $ReadOnly<{| + [aliasName: string]: Required, |}>; -export type NativeModuleMethodTypeShape = $ReadOnly<{| +export type NativeModulePropertySchema = $ReadOnly<{| name: string, - typeAnnotation: FunctionTypeAnnotation, + optional: boolean, + typeAnnotation: NativeModuleFunctionTypeAnnotation, |}>; -export type FunctionTypeAnnotation = $ReadOnly<{| +export type NativeModuleFunctionTypeAnnotation = $ReadOnly<{| type: 'FunctionTypeAnnotation', - params: $ReadOnlyArray, - returnTypeAnnotation: FunctionTypeAnnotationReturn, + params: $ReadOnlyArray, + returnTypeAnnotation: NativeModuleReturnTypeAnnotation, + nullable: boolean, +|}>; + +export type NativeModuleMethodParamSchema = $ReadOnly<{| + name: string, optional: boolean, + typeAnnotation: NativeModuleParamTypeAnnotation, |}>; -export type FunctionTypeAnnotationReturn = - | $ReadOnly<{| - nullable: boolean, - type: - | 'GenericPromiseTypeAnnotation' - | 'VoidTypeAnnotation' - | PrimitiveTypeAnnotationType, - |}> - | $ReadOnly<{| - nullable: boolean, - type: 'ReservedFunctionValueTypeAnnotation', - name: ReservedFunctionValueTypeName, - |}> - | $ReadOnly<{| - nullable: boolean, - type: 'ArrayTypeAnnotation', - elementType: ?FunctionTypeAnnotationReturnArrayElementType, - |}> - | $ReadOnly<{| - nullable: boolean, - type: 'ObjectTypeAnnotation', - properties: ?$ReadOnlyArray, - |}>; +export type NativeModuleObjectTypeAnnotation = $ReadOnly<{| + type: 'ObjectTypeAnnotation', + properties: $ReadOnlyArray< + $ReadOnly<{| + name: string, + optional: boolean, + typeAnnotation: NativeModuleBaseTypeAnnotation, + |}>, + >, + nullable: boolean, +|}>; -export type FunctionTypeAnnotationReturnArrayElementType = - | FunctionTypeAnnotationParamTypeAnnotation // TODO: What does FunctionTypeAnnotationParamTypeAnnotation have to do with function returns? - | TypeAliasTypeAnnotation; +export type NativeModuleArrayTypeAnnotation< + T = NativeModuleBaseTypeAnnotation, +> = $ReadOnly<{| + type: 'ArrayTypeAnnotation', + /** + * TODO(T72031674): Migrate all our NativeModule specs to not use + * invalid Array ElementTypes. Then, make the elementType required. + */ + elementType?: T, + nullable: boolean, +|}>; -export type TypeAliasTypeAnnotation = $ReadOnly<{| - type: 'TypeAliasTypeAnnotation', - name: string, +export type NativeModuleStringTypeAnnotation = $ReadOnly<{| + type: 'StringTypeAnnotation', + nullable: boolean, |}>; -export type FunctionTypeAnnotationParam = $ReadOnly<{| +export type NativeModuleNumberTypeAnnotation = $ReadOnly<{| + type: 'NumberTypeAnnotation', nullable: boolean, - name: string, - typeAnnotation: - | FunctionTypeAnnotationParamTypeAnnotation - | TypeAliasTypeAnnotation, |}>; -export type FunctionTypeAnnotationParamTypeAnnotation = - | $ReadOnly<{| - type: - | 'AnyTypeAnnotation' - | 'FunctionTypeAnnotation' - | PrimitiveTypeAnnotationType, - |}> - | $ReadOnly<{| - type: 'ReservedFunctionValueTypeAnnotation', - name: ReservedFunctionValueTypeName, - |}> - | $ReadOnly<{| - type: 'ArrayTypeAnnotation', - elementType: - | ?FunctionTypeAnnotationParamTypeAnnotation - | ?TypeAliasTypeAnnotation, - |}> - | $ReadOnly<{| - type: 'ObjectTypeAnnotation', - properties: ?$ReadOnlyArray, - |}>; +export type NativeModuleInt32TypeAnnotation = $ReadOnly<{| + type: 'Int32TypeAnnotation', + nullable: boolean, +|}>; -export type PrimitiveTypeAnnotationType = - | 'StringTypeAnnotation' - | 'NumberTypeAnnotation' - | 'Int32TypeAnnotation' - | 'DoubleTypeAnnotation' - | 'FloatTypeAnnotation' - | 'BooleanTypeAnnotation' - | 'GenericObjectTypeAnnotation'; +export type NativeModuleDoubleTypeAnnotation = $ReadOnly<{| + type: 'DoubleTypeAnnotation', + nullable: boolean, +|}>; -export type PrimitiveTypeAnnotation = $ReadOnly<{| - type: PrimitiveTypeAnnotationType, +export type NativeModuleFloatTypeAnnotation = $ReadOnly<{| + type: 'FloatTypeAnnotation', + nullable: boolean, |}>; -export type ReservedFunctionValueTypeName = 'RootTag'; // Union with more custom types. +export type NativeModuleBooleanTypeAnnotation = $ReadOnly<{| + type: 'BooleanTypeAnnotation', + nullable: boolean, +|}>; -export type ObjectParamTypeAnnotation = $ReadOnly<{| - optional: boolean, +export type NativeModuleGenericObjectTypeAnnotation = $ReadOnly<{| + type: 'GenericObjectTypeAnnotation', + nullable: boolean, +|}>; + +export type NativeModuleReservedFunctionValueTypeAnnotation = $ReadOnly<{| + type: 'ReservedFunctionValueTypeAnnotation', + name: ReservedFunctionValueTypeName, + nullable: boolean, +|}>; + +export type NativeModuleTypeAliasTypeAnnotation = $ReadOnly<{| + type: 'TypeAliasTypeAnnotation', name: string, - typeAnnotation?: - | FunctionTypeAnnotationParamTypeAnnotation - | TypeAliasTypeAnnotation, // TODO (T67898313): Workaround for NativeLinking's use of union type, typeAnnotations should not be optional + nullable: boolean, |}>; + +export type NativeModulePromiseTypeAnnotation = $ReadOnly<{| + type: 'PromiseTypeAnnotation', + nullable: boolean, +|}>; + +export type NativeModuleVoidTypeAnnotation = $ReadOnly<{| + type: 'VoidTypeAnnotation', + nullable: boolean, +|}>; + +export type NativeModuleBaseTypeAnnotation = + | NativeModuleStringTypeAnnotation + | NativeModuleNumberTypeAnnotation + | NativeModuleInt32TypeAnnotation + | NativeModuleDoubleTypeAnnotation + | NativeModuleFloatTypeAnnotation + | NativeModuleBooleanTypeAnnotation + | NativeModuleGenericObjectTypeAnnotation + | NativeModuleReservedFunctionValueTypeAnnotation + | NativeModuleTypeAliasTypeAnnotation + | NativeModuleArrayTypeAnnotation<> + | NativeModuleObjectTypeAnnotation; + +export type NativeModuleParamTypeAnnotation = + | NativeModuleBaseTypeAnnotation + | NativeModuleParamOnlyTypeAnnotation; + +export type NativeModuleReturnTypeAnnotation = + | NativeModuleBaseTypeAnnotation + | NativeModuleReturnOnlyTypeAnnotation; + +export type NativeModuleTypeAnnotation = + | NativeModuleBaseTypeAnnotation + | NativeModuleParamOnlyTypeAnnotation + | NativeModuleReturnOnlyTypeAnnotation; + +type NativeModuleParamOnlyTypeAnnotation = NativeModuleFunctionTypeAnnotation; +type NativeModuleReturnOnlyTypeAnnotation = + | NativeModulePromiseTypeAnnotation + | NativeModuleVoidTypeAnnotation; + +export type ReservedFunctionValueTypeName = 'RootTag'; // Union with more custom types. diff --git a/packages/react-native-codegen/src/cli/combine/combine-js-to-schema-cli.js b/packages/react-native-codegen/src/cli/combine/combine-js-to-schema-cli.js index f9a0a07d8c90af..30443a09c35381 100644 --- a/packages/react-native-codegen/src/cli/combine/combine-js-to-schema-cli.js +++ b/packages/react-native-codegen/src/cli/combine/combine-js-to-schema-cli.js @@ -32,6 +32,9 @@ fileList.forEach(file => { // NativeUIManager will be deprecated by Fabric UIManager. // For now, ignore this spec completely because the types are not fully supported. !f.endsWith('NativeUIManager.js') && + // NativeSampleTurboModule is for demo purpose. It should be added manually to the + // app for now. + !f.endsWith('NativeSampleTurboModule.js') && !f.includes('__tests'), ); allFiles.push(...dirFiles); diff --git a/packages/react-native-codegen/src/generators/RNCodegen.js b/packages/react-native-codegen/src/generators/RNCodegen.js index 576d984abd8d71..b6f6e02a922255 100644 --- a/packages/react-native-codegen/src/generators/RNCodegen.js +++ b/packages/react-native-codegen/src/generators/RNCodegen.js @@ -25,11 +25,10 @@ const generatePropsCpp = require('./components/GeneratePropsCpp.js'); const generatePropsH = require('./components/GeneratePropsH.js'); const generateModuleH = require('./modules/GenerateModuleH.js'); const generateModuleCpp = require('./modules/GenerateModuleCpp.js'); -const generateModuleHObjCpp = require('./modules/GenerateModuleHObjCpp.js'); +const generateModuleObjCpp = require('./modules/GenerateModuleObjCpp'); const generateModuleJavaSpec = require('./modules/GenerateModuleJavaSpec.js'); const GenerateModuleJniCpp = require('./modules/GenerateModuleJniCpp.js'); const GenerateModuleJniH = require('./modules/GenerateModuleJniH.js'); -const generateModuleMm = require('./modules/GenerateModuleMm.js'); const generatePropsJavaInterface = require('./components/GeneratePropsJavaInterface.js'); const generatePropsJavaDelegate = require('./components/GeneratePropsJavaDelegate.js'); const generateTests = require('./components/GenerateTests.js'); @@ -74,8 +73,7 @@ const GENERATORS = { modules: [ generateModuleCpp.generate, generateModuleH.generate, - generateModuleHObjCpp.generate, - generateModuleMm.generate, + generateModuleObjCpp.generate, ], // TODO: Refactor this to consolidate various C++ output variation instead of forking Android. modulesAndroid: [ diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js index cf02b4095b1c92..deb38646197c44 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js @@ -10,8 +10,15 @@ 'use strict'; -import type {SchemaType} from '../../CodegenSchema'; -const {getTypeAliasTypeAnnotation} = require('./Utils'); +import type { + SchemaType, + NativeModulePropertySchema, + NativeModuleMethodParamSchema, +} from '../../CodegenSchema'; + +import type {AliasResolver} from './Utils'; +const {createAliasResolver, getModules} = require('./Utils'); + type FilesOutput = Map; const propertyHeaderTemplate = @@ -44,7 +51,7 @@ Native::_MODULE_NAME_::CxxSpecJSI::Native::_MODULE_NAME_::CxxSpecJSI(std::shared const template = ` /** - * Copyright (c) Facebook, Inc. and its affiliates. + * ${'C'}opyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. @@ -64,16 +71,21 @@ namespace react { } // namespace facebook `; -function traverseArg(arg, index, aliases): string { +function traverseArg( + arg: NativeModuleMethodParamSchema, + index: number, + resolveAlias: AliasResolver, +): string { function wrap(suffix) { return `args[${index}]${suffix}`; } const {typeAnnotation} = arg; - const realTypeAnnotation = - typeAnnotation.type === 'TypeAliasTypeAnnotation' - ? getTypeAliasTypeAnnotation(typeAnnotation.name, aliases) - : typeAnnotation; + let realTypeAnnotation = typeAnnotation; + if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { + realTypeAnnotation = resolveAlias(realTypeAnnotation.name); + } + switch (realTypeAnnotation.type) { case 'ReservedFunctionValueTypeAnnotation': switch (realTypeAnnotation.name) { @@ -90,7 +102,11 @@ function traverseArg(arg, index, aliases): string { case 'BooleanTypeAnnotation': return wrap('.getBool()'); case 'NumberTypeAnnotation': + return wrap('.getNumber()'); case 'FloatTypeAnnotation': + return wrap('.getNumber()'); + case 'DoubleTypeAnnotation': + return wrap('.getNumber()'); case 'Int32TypeAnnotation': return wrap('.getNumber()'); case 'ArrayTypeAnnotation': @@ -98,26 +114,27 @@ function traverseArg(arg, index, aliases): string { case 'FunctionTypeAnnotation': return `std::move(${wrap('.getObject(rt).getFunction(rt)')})`; case 'GenericObjectTypeAnnotation': + return wrap('.getObject(rt)'); case 'ObjectTypeAnnotation': return wrap('.getObject(rt)'); - case 'AnyTypeAnnotation': - throw new Error(`Any type is not allowed in params for "${arg.name}"`); default: - // TODO (T65847278): Figure out why this does not work. - // (type: empty); + (realTypeAnnotation.type: empty); throw new Error( `Unknown prop type for "${arg.name}, found: ${realTypeAnnotation.type}"`, ); } } -function traverseProperty(property, aliases): string { +function traverseProperty( + property: NativeModulePropertySchema, + resolveAlias: AliasResolver, +): string { const propertyTemplate = property.typeAnnotation.returnTypeAnnotation.type === 'VoidTypeAnnotation' ? voidPropertyTemplate : nonvoidPropertyTemplate; const traversedArgs = property.typeAnnotation.params - .map((p, i) => traverseArg(p, i, aliases)) + .map((p, i) => traverseArg(p, i, resolveAlias)) .join(', '); return propertyTemplate .replace(/::_PROPERTY_NAME_::/g, property.name) @@ -130,23 +147,14 @@ module.exports = { schema: SchemaType, moduleSpecName: string, ): FilesOutput { - const nativeModules = Object.keys(schema.modules) - .map(moduleName => { - const modules = schema.modules[moduleName].nativeModules; - if (modules == null) { - return null; - } - - return modules; - }) - .filter(Boolean) - .reduce((acc, modules) => Object.assign(acc, modules), {}); + const nativeModules = getModules(schema); const modules = Object.keys(nativeModules) .map(name => { const {aliases, properties} = nativeModules[name]; + const resolveAlias = createAliasResolver(aliases); const traversedProperties = properties - .map(property => traverseProperty(property, aliases)) + .map(property => traverseProperty(property, resolveAlias)) .join('\n'); return moduleTemplate .replace(/::_MODULE_PROPERTIES_::/g, traversedProperties) diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js index 7342ad01a7c32a..420b6f6a33fa18 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js @@ -10,15 +10,10 @@ 'use strict'; -import type { - SchemaType, - FunctionTypeAnnotationParamTypeAnnotation, - FunctionTypeAnnotationReturn, - TypeAliasTypeAnnotation, - ObjectTypeAliasTypeShape, -} from '../../CodegenSchema'; +import type {SchemaType, NativeModuleTypeAnnotation} from '../../CodegenSchema'; -const {getTypeAliasTypeAnnotation} = require('./Utils'); +import type {AliasResolver} from './Utils'; +const {createAliasResolver, getModules} = require('./Utils'); type FilesOutput = Map; @@ -32,8 +27,9 @@ public: };`; -const template = `/** - * Copyright (c) Facebook, Inc. and its affiliates. +const template = ` +/** + * ${'C'}opyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. @@ -54,17 +50,15 @@ namespace react { `; function translatePrimitiveJSTypeToCpp( - typeAnnotation: - | FunctionTypeAnnotationParamTypeAnnotation - | FunctionTypeAnnotationReturn - | TypeAliasTypeAnnotation, + typeAnnotation: NativeModuleTypeAnnotation, createErrorMessage: (typeName: string) => string, - aliases: $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}>, + resolveAlias: AliasResolver, ) { - const realTypeAnnotation = - typeAnnotation.type === 'TypeAliasTypeAnnotation' - ? getTypeAliasTypeAnnotation(typeAnnotation.name, aliases) - : typeAnnotation; + let realTypeAnnotation = typeAnnotation; + if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { + realTypeAnnotation = resolveAlias(realTypeAnnotation.name); + } + switch (realTypeAnnotation.type) { case 'ReservedFunctionValueTypeAnnotation': switch (realTypeAnnotation.name) { @@ -79,6 +73,9 @@ function translatePrimitiveJSTypeToCpp( case 'StringTypeAnnotation': return 'jsi::String'; case 'NumberTypeAnnotation': + return 'double'; + case 'DoubleTypeAnnotation': + return 'double'; case 'FloatTypeAnnotation': return 'double'; case 'Int32TypeAnnotation': @@ -86,17 +83,17 @@ function translatePrimitiveJSTypeToCpp( case 'BooleanTypeAnnotation': return 'bool'; case 'GenericObjectTypeAnnotation': + return 'jsi::Object'; case 'ObjectTypeAnnotation': return 'jsi::Object'; case 'ArrayTypeAnnotation': return 'jsi::Array'; case 'FunctionTypeAnnotation': return 'jsi::Function'; - case 'GenericPromiseTypeAnnotation': + case 'PromiseTypeAnnotation': return 'jsi::Value'; default: - // TODO (T65847278): Figure out why this does not work. - // (type: empty); + (realTypeAnnotation.type: empty); throw new Error(createErrorMessage(realTypeAnnotation.type)); } } @@ -110,21 +107,13 @@ module.exports = { schema: SchemaType, moduleSpecName: string, ): FilesOutput { - const nativeModules = Object.keys(schema.modules) - .map(moduleName => { - const modules = schema.modules[moduleName].nativeModules; - if (modules == null) { - return null; - } - - return modules; - }) - .filter(Boolean) - .reduce((acc, components) => Object.assign(acc, components), {}); + const nativeModules = getModules(schema); const modules = Object.keys(nativeModules) .map(name => { const {aliases, properties} = nativeModules[name]; + const resolveAlias = createAliasResolver(aliases); + const traversedProperties = properties .map(prop => { const traversedArgs = prop.typeAnnotation.params @@ -133,7 +122,7 @@ module.exports = { param.typeAnnotation, typeName => `Unsupported type for param "${param.name}" in ${prop.name}. Found: ${typeName}`, - aliases, + resolveAlias, ); const isObject = translatedParam.startsWith('jsi::'); return ( @@ -151,7 +140,7 @@ module.exports = { prop.typeAnnotation.returnTypeAnnotation, typeName => `Unsupported return type for ${prop.name}. Found: ${typeName}`, - aliases, + resolveAlias, ), ) .replace( diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleHObjCpp.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleHObjCpp.js deleted file mode 100644 index c9f173df868e37..00000000000000 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleHObjCpp.js +++ /dev/null @@ -1,415 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict - * @format - */ - -'use strict'; - -import type { - SchemaType, - FunctionTypeAnnotationParam, - FunctionTypeAnnotationReturn, - ObjectParamTypeAnnotation, - ObjectTypeAliasTypeShape, -} from '../../CodegenSchema'; - -const { - translateObjectsForStructs, - capitalizeFirstLetter, - getNamespacedStructName, -} = require('./ObjCppUtils/GenerateStructs'); - -const {getTypeAliasTypeAnnotation} = require('./Utils'); - -type FilesOutput = Map; - -const moduleTemplate = ` /** - * ObjC++ class for module '::_MODULE_NAME_::' - */ - class JSI_EXPORT Native::_MODULE_NAME_::SpecJSI : public ObjCTurboModule { - public: - Native::_MODULE_NAME_::SpecJSI(const ObjCTurboModule::InitParams ¶ms); - };`; - -const protocolTemplate = `::_STRUCTS_:: - -@protocol Native::_MODULE_NAME_::Spec -::_MODULE_PROPERTIES_:: -@end -`; - -const callbackArgs = prop => - prop.typeAnnotation.returnTypeAnnotation.type === - 'GenericPromiseTypeAnnotation' - ? `${ - prop.typeAnnotation.params.length === 0 ? '' : '\n resolve' - }:(RCTPromiseResolveBlock)resolve - reject:(RCTPromiseRejectBlock)reject` - : ''; - -const template = ` -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * ${'@'}generated by codegen project: GenerateModuleHObjCpp.js - */ - -#ifndef __cplusplus -#error This file must be compiled as Obj-C++. If you are importing it, you must change your file extension to .mm. -#endif - -#import - -#import - -#import - -#import -#import -#import - -#import -#import -#import - -#import - -::_PROTOCOLS_:: - -namespace facebook { - namespace react { -::_MODULES_:: - } // namespace react -} // namespace facebook -`; - -type ObjectForGeneratingStructs = $ReadOnly<{| - name: string, - object: $ReadOnly<{| - type: 'ObjectTypeAnnotation', - properties: $ReadOnlyArray, - |}>, -|}>; - -const constants = `- (facebook::react::ModuleConstants)constantsToExport; -- (facebook::react::ModuleConstants)getConstants;`; - -function translatePrimitiveJSTypeToObjCType( - param: FunctionTypeAnnotationParam, - createErrorMessage: (typeName: string) => string, - aliases: $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}>, -) { - const {nullable, typeAnnotation} = param; - - function wrapIntoNullableIfNeeded(generatedType: string) { - return nullable ? `${generatedType} _Nullable` : generatedType; - } - - const realTypeAnnotation = - typeAnnotation.type === 'TypeAliasTypeAnnotation' - ? getTypeAliasTypeAnnotation(typeAnnotation.name, aliases) - : typeAnnotation; - switch (realTypeAnnotation.type) { - case 'ReservedFunctionValueTypeAnnotation': - switch (realTypeAnnotation.name) { - case 'RootTag': - return nullable ? 'NSNumber *' : 'double'; - default: - (realTypeAnnotation.name: empty); - throw new Error(createErrorMessage(realTypeAnnotation.name)); - } - case 'StringTypeAnnotation': - return wrapIntoNullableIfNeeded('NSString *'); - case 'NumberTypeAnnotation': - case 'FloatTypeAnnotation': - case 'Int32TypeAnnotation': - return nullable ? 'NSNumber *' : 'double'; - case 'BooleanTypeAnnotation': - return nullable ? 'NSNumber * _Nullable' : 'BOOL'; - case 'ObjectTypeAnnotation': - if (typeAnnotation.type === 'TypeAliasTypeAnnotation') { - return getNamespacedStructName(typeAnnotation.name) + ' &'; - } - return wrapIntoNullableIfNeeded('NSDictionary *'); - case 'GenericObjectTypeAnnotation': - return wrapIntoNullableIfNeeded('NSDictionary *'); - case 'ArrayTypeAnnotation': - return wrapIntoNullableIfNeeded('NSArray *'); - case 'FunctionTypeAnnotation': - return 'RCTResponseSenderBlock'; - default: - // TODO (T65847278): Figure out why this does not work. - // (type: empty); - throw new Error(createErrorMessage(realTypeAnnotation.type)); - } -} - -function translatePrimitiveJSTypeToObjCTypeForReturn( - typeAnnotation: FunctionTypeAnnotationReturn, - createErrorMessage: (typeName: string) => string, -) { - function wrapIntoNullableIfNeeded(generatedType: string) { - return typeAnnotation.nullable - ? `${generatedType} _Nullable` - : generatedType; - } - switch (typeAnnotation.type) { - case 'ReservedFunctionValueTypeAnnotation': - switch (typeAnnotation.name) { - case 'RootTag': - return wrapIntoNullableIfNeeded('NSNumber *'); - default: - (typeAnnotation.name: empty); - throw new Error(createErrorMessage(typeAnnotation.name)); - } - case 'VoidTypeAnnotation': - case 'GenericPromiseTypeAnnotation': - return 'void'; - case 'StringTypeAnnotation': - return wrapIntoNullableIfNeeded('NSString *'); - case 'NumberTypeAnnotation': - case 'FloatTypeAnnotation': - case 'Int32TypeAnnotation': - return wrapIntoNullableIfNeeded('NSNumber *'); - case 'BooleanTypeAnnotation': - return typeAnnotation.nullable ? 'NSNumber * _Nullable' : 'BOOL'; - case 'GenericObjectTypeAnnotation': - return wrapIntoNullableIfNeeded('NSDictionary *'); - case 'ArrayTypeAnnotation': - return wrapIntoNullableIfNeeded('NSArray> *'); - case 'ObjectTypeAnnotation': - return wrapIntoNullableIfNeeded('NSDictionary *'); - default: - // TODO (T65847278): Figure out why this does not work. - // (typeAnnotation.type: empty); - throw new Error(createErrorMessage(typeAnnotation.type)); - } -} - -function handleArrayOfObjects( - objectForGeneratingStructs: Array, - propOrParam: FunctionTypeAnnotationParam, - name: string, -) { - if ( - propOrParam.typeAnnotation.type === 'ArrayTypeAnnotation' && - propOrParam.typeAnnotation.elementType - ) { - const typeAnnotation = propOrParam.typeAnnotation.elementType; - const type = typeAnnotation.type; - - if ( - type === 'ObjectTypeAnnotation' && - typeAnnotation.properties && - typeAnnotation.properties.length > 0 - ) { - objectForGeneratingStructs.push({ - name, - object: { - type: 'ObjectTypeAnnotation', - properties: typeAnnotation.properties, - }, - }); - } - } -} - -const methodImplementationTemplate = - '- (::_RETURN_VALUE_::) ::_PROPERTY_NAME_::::_ARGS_::;'; - -module.exports = { - generate( - libraryName: string, - schema: SchemaType, - moduleSpecName: string, - ): FilesOutput { - const nativeModules = Object.keys(schema.modules) - .sort() - .map(moduleName => { - const modules = schema.modules[moduleName].nativeModules; - if (modules == null) { - return null; - } - - return modules; - }) - .filter(Boolean) - .reduce((acc, components) => Object.assign(acc, components), {}); - - const modules = Object.keys(nativeModules) - .map(name => moduleTemplate.replace(/::_MODULE_NAME_::/g, name)) - .join('\n'); - - const protocols = Object.keys(nativeModules) - .sort() - .map(name => { - const objectForGeneratingStructs: Array = []; - const {aliases, properties} = nativeModules[name]; - const implementations = properties - .map(prop => { - const nativeArgs = prop.typeAnnotation.params - .map((param, i) => { - let paramObjCType; - if ( - param.typeAnnotation.type === 'ObjectTypeAnnotation' && - param.typeAnnotation.properties - ) { - const variableName = - capitalizeFirstLetter(prop.name) + - capitalizeFirstLetter(param.name); - const structName = 'Spec' + variableName; - objectForGeneratingStructs.push({ - name: structName, - object: { - type: 'ObjectTypeAnnotation', - properties: param.typeAnnotation.properties, - }, - }); - paramObjCType = getNamespacedStructName(structName) + ' &'; - - param.typeAnnotation.properties.map(aProp => { - return handleArrayOfObjects( - objectForGeneratingStructs, - aProp, - 'Spec' + - capitalizeFirstLetter(prop.name) + - capitalizeFirstLetter(param.name) + - capitalizeFirstLetter(aProp.name) + - 'Element', - ); - }); - } else if ( - param.typeAnnotation.type === 'TypeAliasTypeAnnotation' - ) { - const typeAnnotation = getTypeAliasTypeAnnotation( - param.typeAnnotation.name, - aliases, - ); - if (typeAnnotation.type === 'ObjectTypeAnnotation') { - paramObjCType = - getNamespacedStructName(param.typeAnnotation.name) + ' &'; - } else { - throw Error( - `Unsupported type for "${param.typeAnnotation.name}". Found: ${typeAnnotation.type}`, - ); - } - } else { - paramObjCType = translatePrimitiveJSTypeToObjCType( - param, - typeName => - `Unsupported type for param "${param.name}" in ${prop.name}. Found: ${typeName}`, - aliases, - ); - - handleArrayOfObjects( - objectForGeneratingStructs, - param, - 'Spec' + - capitalizeFirstLetter(prop.name) + - capitalizeFirstLetter(param.name) + - 'Element', - ); - } - return `${i === 0 ? '' : param.name}:(${paramObjCType})${ - param.name - }`; - }) - .join('\n ') - .concat(callbackArgs(prop)); - const {returnTypeAnnotation} = prop.typeAnnotation; - if ( - returnTypeAnnotation.type === 'ObjectTypeAnnotation' && - returnTypeAnnotation.properties - ) { - objectForGeneratingStructs.push({ - name: 'Spec' + capitalizeFirstLetter(prop.name) + 'ReturnType', - object: { - type: 'ObjectTypeAnnotation', - properties: returnTypeAnnotation.properties, - }, - }); - } - const implementation = methodImplementationTemplate - .replace('::_PROPERTY_NAME_::', prop.name) - .replace( - '::_RETURN_VALUE_::', - translatePrimitiveJSTypeToObjCTypeForReturn( - returnTypeAnnotation, - typeName => - `Unsupported return type for ${prop.name}. Found: ${typeName}`, - ), - ) - .replace('::_ARGS_::', nativeArgs); - if (prop.name === 'getConstants') { - if ( - prop.typeAnnotation.returnTypeAnnotation.properties && - prop.typeAnnotation.returnTypeAnnotation.properties.length === 0 - ) { - return ''; - } - return constants.replace(/::_MODULE_NAME_::/, name); - } - return implementation; - }) - .join('\n'); - - Object.keys(aliases) - .reverse() - .map((aliasName, i) => { - const alias = aliases[aliasName]; - - let paramObjCType = ''; - - switch (alias.type) { - case 'ObjectTypeAnnotation': - if (alias.properties) { - objectForGeneratingStructs.push({ - name: aliasName, - object: { - type: 'ObjectTypeAnnotation', - properties: alias.properties, - }, - }); - paramObjCType = getNamespacedStructName(alias.name) + ' &'; - } - break; - default: - throw Error( - `Unsupported type for "${aliasName}". Found: ${alias.type}`, - ); - } - return `${i === 0 ? '' : aliasName}:(${paramObjCType})${aliasName}`; - }) - .join('\n'); - - return protocolTemplate - .replace( - /::_STRUCTS_::/g, - translateObjectsForStructs( - objectForGeneratingStructs, - name, - aliases, - ), - ) - .replace(/::_MODULE_PROPERTIES_::/g, implementations) - .replace(/::_MODULE_NAME_::/g, name) - .replace('::_PROPERTIES_MAP_::', ''); - }) - .join('\n'); - - const fileName = `${moduleSpecName}.h`; - const replacedTemplate = template - .replace(/::_MODULES_::/g, modules) - .replace(/::_PROTOCOLS_::/g, protocols); - - return new Map([[fileName, replacedTemplate]]); - }, -}; diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js index 5054146e4ffc56..69958ca41d6950 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js @@ -11,18 +11,20 @@ 'use strict'; import type { - FunctionTypeAnnotationParam, - FunctionTypeAnnotationReturn, - NativeModuleMethodTypeShape, - ObjectTypeAliasTypeShape, SchemaType, + NativeModulePropertySchema, + NativeModuleMethodParamSchema, + NativeModuleReturnTypeAnnotation, } from '../../CodegenSchema'; -const {getTypeAliasTypeAnnotation} = require('./Utils'); + +import type {AliasResolver} from './Utils'; +const {createAliasResolver, getModules} = require('./Utils'); type FilesOutput = Map; -const moduleTemplate = `/** - * Copyright (c) Facebook, Inc. and its affiliates. +const moduleTemplate = ` +/** + * ${'C'}opyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the LICENSE file in the root * directory of this source tree. @@ -46,30 +48,32 @@ public abstract class ::_CLASSNAME_:: extends ReactContextBaseJavaModule impleme `; function translateFunctionParamToJavaType( - param: FunctionTypeAnnotationParam, + param: NativeModuleMethodParamSchema, createErrorMessage: (typeName: string) => string, - aliases: $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}>, + resolveAlias: AliasResolver, imports: Set, ): string { - const {nullable, typeAnnotation} = param; + const {optional, typeAnnotation} = param; + const isRequired = !optional && !typeAnnotation.nullable; function wrapIntoNullableIfNeeded(generatedType: string) { - if (nullable) { + if (!isRequired) { imports.add('javax.annotation.Nullable'); return `@Nullable ${generatedType}`; } return generatedType; } - const realTypeAnnotation = - typeAnnotation.type === 'TypeAliasTypeAnnotation' - ? getTypeAliasTypeAnnotation(typeAnnotation.name, aliases) - : typeAnnotation; + let realTypeAnnotation = typeAnnotation; + if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { + realTypeAnnotation = resolveAlias(realTypeAnnotation.name); + } + switch (realTypeAnnotation.type) { case 'ReservedFunctionValueTypeAnnotation': switch (realTypeAnnotation.name) { case 'RootTag': - return nullable ? 'Double' : 'double'; + return !isRequired ? 'Double' : 'double'; default: (realTypeAnnotation.name: empty); throw new Error(createErrorMessage(realTypeAnnotation.name)); @@ -77,11 +81,15 @@ function translateFunctionParamToJavaType( case 'StringTypeAnnotation': return wrapIntoNullableIfNeeded('String'); case 'NumberTypeAnnotation': + return !isRequired ? 'Double' : 'double'; case 'FloatTypeAnnotation': + return !isRequired ? 'Double' : 'double'; + case 'DoubleTypeAnnotation': + return !isRequired ? 'Double' : 'double'; case 'Int32TypeAnnotation': - return nullable ? 'Double' : 'double'; + return !isRequired ? 'Double' : 'double'; case 'BooleanTypeAnnotation': - return nullable ? 'Boolean' : 'boolean'; + return !isRequired ? 'Boolean' : 'boolean'; case 'ObjectTypeAnnotation': imports.add('com.facebook.react.bridge.ReadableMap'); if (typeAnnotation.type === 'TypeAliasTypeAnnotation') { @@ -100,13 +108,15 @@ function translateFunctionParamToJavaType( imports.add('com.facebook.react.bridge.Callback'); return 'Callback'; default: + (realTypeAnnotation.type: empty); throw new Error(createErrorMessage(realTypeAnnotation.type)); } } function translateFunctionReturnTypeToJavaType( - returnTypeAnnotation: FunctionTypeAnnotationReturn, + returnTypeAnnotation: NativeModuleReturnTypeAnnotation, createErrorMessage: (typeName: string) => string, + resolveAlias: AliasResolver, imports: Set, ): string { const {nullable} = returnTypeAnnotation; @@ -119,23 +129,32 @@ function translateFunctionReturnTypeToJavaType( return generatedType; } - // TODO: Support aliased return type. This doesn't exist in React Native Android yet. - switch (returnTypeAnnotation.type) { + let realTypeAnnotation = returnTypeAnnotation; + if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { + realTypeAnnotation = resolveAlias(realTypeAnnotation.name); + } + + switch (realTypeAnnotation.type) { case 'ReservedFunctionValueTypeAnnotation': - switch (returnTypeAnnotation.name) { + switch (realTypeAnnotation.name) { case 'RootTag': return nullable ? 'Double' : 'double'; default: - (returnTypeAnnotation.name: empty); - throw new Error(createErrorMessage(returnTypeAnnotation.name)); + (realTypeAnnotation.name: empty); + throw new Error(createErrorMessage(realTypeAnnotation.name)); } case 'VoidTypeAnnotation': - case 'GenericPromiseTypeAnnotation': + return 'void'; + case 'PromiseTypeAnnotation': return 'void'; case 'StringTypeAnnotation': return wrapIntoNullableIfNeeded('String'); case 'NumberTypeAnnotation': + return nullable ? 'Double' : 'double'; case 'FloatTypeAnnotation': + return nullable ? 'Double' : 'double'; + case 'DoubleTypeAnnotation': + return nullable ? 'Double' : 'double'; case 'Int32TypeAnnotation': return nullable ? 'Double' : 'double'; case 'BooleanTypeAnnotation': @@ -150,13 +169,14 @@ function translateFunctionReturnTypeToJavaType( imports.add('com.facebook.react.bridge.WritableArray'); return 'WritableArray'; default: - throw new Error(createErrorMessage(returnTypeAnnotation.type)); + (realTypeAnnotation.type: empty); + throw new Error(createErrorMessage(realTypeAnnotation.type)); } } // Build special-cased runtime check for getConstants(). function buildGetConstantsMethod( - method: NativeModuleMethodTypeShape, + method: NativeModulePropertySchema, imports: Set, ): string { if ( @@ -240,20 +260,11 @@ module.exports = { const files = new Map(); // TODO: Allow package configuration. const packageName = 'com.facebook.fbreact.specs.beta'; - const nativeModules = Object.keys(schema.modules) - .map(moduleName => { - const modules = schema.modules[moduleName].nativeModules; - if (modules == null) { - return null; - } - - return modules; - }) - .filter(Boolean) - .reduce((acc, components) => Object.assign(acc, components), {}); + const nativeModules = getModules(schema); Object.keys(nativeModules).forEach(name => { const {aliases, properties} = nativeModules[name]; + const resolveAlias = createAliasResolver(aliases); const className = `Native${name}Spec`; const imports: Set = new Set([ @@ -275,11 +286,12 @@ module.exports = { method.typeAnnotation.returnTypeAnnotation, typeName => `Unsupported return type for method ${method.name}. Found: ${typeName}`, + resolveAlias, imports, ); const returningPromise = method.typeAnnotation.returnTypeAnnotation.type === - 'GenericPromiseTypeAnnotation'; + 'PromiseTypeAnnotation'; const isSyncMethod = method.typeAnnotation.returnTypeAnnotation.type !== 'VoidTypeAnnotation' && !returningPromise; @@ -290,7 +302,7 @@ module.exports = { param, typeName => `Unsupported type for param "${param.name}" in ${method.name}. Found: ${typeName}`, - aliases, + resolveAlias, imports, ); return `${translatedParam} ${param.name}`; diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js index 1af032bbc26e1e..72b48a7fcdab72 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js @@ -11,14 +11,14 @@ 'use strict'; import type { - FunctionTypeAnnotationParam, - FunctionTypeAnnotationReturn, - NativeModuleShape, - ObjectTypeAliasTypeShape, SchemaType, + NativeModulePropertySchema, + NativeModuleMethodParamSchema, + NativeModuleReturnTypeAnnotation, } from '../../CodegenSchema'; -const {getTypeAliasTypeAnnotation} = require('./Utils'); +import type {AliasResolver} from './Utils'; +const {createAliasResolver, getModules} = require('./Utils'); type FilesOutput = Map; @@ -50,7 +50,7 @@ const oneModuleLookupTemplate = ` if (moduleName == "::_MODULE_NAME_::") { const template = ` /** - * Copyright (c) Facebook, Inc. and its affiliates. + * ${'C'}opyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. @@ -67,6 +67,7 @@ namespace react { std::shared_ptr ::_LIBRARY_NAME_::_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) { ::_MODULE_LOOKUP_:: + return nullptr; } } // namespace react @@ -74,17 +75,23 @@ std::shared_ptr ::_LIBRARY_NAME_::_ModuleProvider(const std::string `; function translateReturnTypeToKind( - typeAnnotation: FunctionTypeAnnotationReturn, + typeAnnotation: NativeModuleReturnTypeAnnotation, + resolveAlias: AliasResolver, ): string { - switch (typeAnnotation.type) { + let realTypeAnnotation = typeAnnotation; + if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { + realTypeAnnotation = resolveAlias(realTypeAnnotation.name); + } + + switch (realTypeAnnotation.type) { case 'ReservedFunctionValueTypeAnnotation': - switch (typeAnnotation.name) { + switch (realTypeAnnotation.name) { case 'RootTag': return 'NumberKind'; default: - (typeAnnotation.name: empty); + (realTypeAnnotation.name: empty); throw new Error( - `Invalid ReservedFunctionValueTypeName name, got ${typeAnnotation.name}`, + `Invalid ReservedFunctionValueTypeName name, got ${realTypeAnnotation.name}`, ); } case 'VoidTypeAnnotation': @@ -94,62 +101,66 @@ function translateReturnTypeToKind( case 'BooleanTypeAnnotation': return 'BooleanKind'; case 'NumberTypeAnnotation': + return 'NumberKind'; case 'DoubleTypeAnnotation': + return 'NumberKind'; case 'FloatTypeAnnotation': + return 'NumberKind'; case 'Int32TypeAnnotation': return 'NumberKind'; - case 'GenericPromiseTypeAnnotation': + case 'PromiseTypeAnnotation': return 'PromiseKind'; case 'GenericObjectTypeAnnotation': + return 'ObjectKind'; case 'ObjectTypeAnnotation': return 'ObjectKind'; case 'ArrayTypeAnnotation': return 'ArrayKind'; default: - // TODO (T65847278): Figure out why this does not work. - // (typeAnnotation.type: empty); + (realTypeAnnotation.type: empty); throw new Error( - `Unknown prop type for returning value, found: ${typeAnnotation.type}"`, + `Unknown prop type for returning value, found: ${realTypeAnnotation.type}"`, ); } } function translateParamTypeToJniType( - param: FunctionTypeAnnotationParam, - aliases: $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}>, + param: NativeModuleMethodParamSchema, + resolveAlias: AliasResolver, ): string { - const {nullable, typeAnnotation} = param; + const {optional, typeAnnotation} = param; + const isRequired = !optional && !typeAnnotation.nullable; - const realTypeAnnotation = - typeAnnotation.type === 'TypeAliasTypeAnnotation' - ? getTypeAliasTypeAnnotation(typeAnnotation.name, aliases) - : typeAnnotation; + let realTypeAnnotation = typeAnnotation; + if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { + realTypeAnnotation = resolveAlias(realTypeAnnotation.name); + } switch (realTypeAnnotation.type) { case 'ReservedFunctionValueTypeAnnotation': switch (realTypeAnnotation.name) { case 'RootTag': - return 'D'; + return !isRequired ? 'Ljava/lang/Double;' : 'D'; default: (realTypeAnnotation.name: empty); throw new Error( `Invalid ReservedFunctionValueTypeName name, got ${realTypeAnnotation.name}`, ); } - case 'VoidTypeAnnotation': - return 'V'; case 'StringTypeAnnotation': return 'Ljava/lang/String;'; case 'BooleanTypeAnnotation': - return nullable ? 'Ljava/lang/Boolean' : 'Z'; + return !isRequired ? 'Ljava/lang/Boolean' : 'Z'; case 'NumberTypeAnnotation': + return !isRequired ? 'Ljava/lang/Double;' : 'D'; case 'DoubleTypeAnnotation': + return !isRequired ? 'Ljava/lang/Double;' : 'D'; case 'FloatTypeAnnotation': + return !isRequired ? 'Ljava/lang/Double;' : 'D'; case 'Int32TypeAnnotation': - return nullable ? 'Ljava/lang/Double;' : 'D'; - case 'GenericPromiseTypeAnnotation': - return 'Lcom/facebook/react/bridge/Promise;'; + return !isRequired ? 'Ljava/lang/Double;' : 'D'; case 'GenericObjectTypeAnnotation': + return 'Lcom/facebook/react/bridge/ReadableMap;'; case 'ObjectTypeAnnotation': return 'Lcom/facebook/react/bridge/ReadableMap;'; case 'ArrayTypeAnnotation': @@ -157,6 +168,7 @@ function translateParamTypeToJniType( case 'FunctionTypeAnnotation': return 'Lcom/facebook/react/bridge/Callback;'; default: + (realTypeAnnotation.type: empty); throw new Error( `Unknown prop type for method arg, found: ${realTypeAnnotation.type}"`, ); @@ -164,19 +176,25 @@ function translateParamTypeToJniType( } function translateReturnTypeToJniType( - typeAnnotation: FunctionTypeAnnotationReturn, + typeAnnotation: NativeModuleReturnTypeAnnotation, + resolveAlias: AliasResolver, ): string { const {nullable} = typeAnnotation; - switch (typeAnnotation.type) { + let realTypeAnnotation = typeAnnotation; + if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { + realTypeAnnotation = resolveAlias(realTypeAnnotation.name); + } + + switch (realTypeAnnotation.type) { case 'ReservedFunctionValueTypeAnnotation': - switch (typeAnnotation.name) { + switch (realTypeAnnotation.name) { case 'RootTag': - return 'D'; + return nullable ? 'Ljava/lang/Double;' : 'D'; default: - (typeAnnotation.name: empty); + (realTypeAnnotation.name: empty); throw new Error( - `Invalid ReservedFunctionValueTypeName name, got ${typeAnnotation.name}`, + `Invalid ReservedFunctionValueTypeName name, got ${realTypeAnnotation.name}`, ); } case 'VoidTypeAnnotation': @@ -186,35 +204,39 @@ function translateReturnTypeToJniType( case 'BooleanTypeAnnotation': return nullable ? 'Ljava/lang/Boolean' : 'Z'; case 'NumberTypeAnnotation': + return nullable ? 'Ljava/lang/Double;' : 'D'; case 'DoubleTypeAnnotation': + return nullable ? 'Ljava/lang/Double;' : 'D'; case 'FloatTypeAnnotation': + return nullable ? 'Ljava/lang/Double;' : 'D'; case 'Int32TypeAnnotation': return nullable ? 'Ljava/lang/Double;' : 'D'; - case 'GenericPromiseTypeAnnotation': + case 'PromiseTypeAnnotation': return 'Lcom/facebook/react/bridge/Promise;'; case 'GenericObjectTypeAnnotation': + return 'Lcom/facebook/react/bridge/WritableMap;'; case 'ObjectTypeAnnotation': return 'Lcom/facebook/react/bridge/WritableMap;'; case 'ArrayTypeAnnotation': return 'Lcom/facebook/react/bridge/WritableArray;'; default: + (realTypeAnnotation.type: empty); throw new Error( - `Unknown prop type for method return type, found: ${typeAnnotation.type}"`, + `Unknown prop type for method return type, found: ${realTypeAnnotation.type}"`, ); } } function translateMethodTypeToJniSignature( - property, - aliases: $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}>, + property: NativeModulePropertySchema, + resolveAlias: AliasResolver, ): string { const {name, typeAnnotation} = property; const {returnTypeAnnotation} = typeAnnotation; const params = [...typeAnnotation.params]; let processedReturnTypeAnnotation = returnTypeAnnotation; - const isPromiseReturn = - returnTypeAnnotation.type === 'GenericPromiseTypeAnnotation'; + const isPromiseReturn = returnTypeAnnotation.type === 'PromiseTypeAnnotation'; if (isPromiseReturn) { processedReturnTypeAnnotation = { nullable: false, @@ -223,36 +245,39 @@ function translateMethodTypeToJniSignature( } const argsSignatureParts = params.map(t => { - return translateParamTypeToJniType(t, aliases); + return translateParamTypeToJniType(t, resolveAlias); }); if (isPromiseReturn) { // Additional promise arg for this case. - argsSignatureParts.push(translateReturnTypeToJniType(returnTypeAnnotation)); + argsSignatureParts.push( + translateReturnTypeToJniType(returnTypeAnnotation, resolveAlias), + ); } const argsSignature = argsSignatureParts.join(''); const returnSignature = name === 'getConstants' ? 'Ljava/util/Map;' - : translateReturnTypeToJniType(processedReturnTypeAnnotation); + : translateReturnTypeToJniType( + processedReturnTypeAnnotation, + resolveAlias, + ); return `(${argsSignature})${returnSignature}`; } function translateMethodForImplementation( - property, - aliases: $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}>, + property: NativeModulePropertySchema, + resolveAlias: AliasResolver, ): string { const {returnTypeAnnotation} = property.typeAnnotation; const numberOfParams = property.typeAnnotation.params.length + - (returnTypeAnnotation.type === 'GenericPromiseTypeAnnotation' ? 1 : 0); + (returnTypeAnnotation.type === 'PromiseTypeAnnotation' ? 1 : 0); const translatedArguments = property.typeAnnotation.params .map(param => param.name) .concat( - returnTypeAnnotation.type === 'GenericPromiseTypeAnnotation' - ? ['promise'] - : [], + returnTypeAnnotation.type === 'PromiseTypeAnnotation' ? ['promise'] : [], ) .slice(1) .join(':') @@ -266,7 +291,10 @@ function translateMethodForImplementation( return ''; } return propertyTemplate - .replace(/::_KIND_::/g, translateReturnTypeToKind(returnTypeAnnotation)) + .replace( + /::_KIND_::/g, + translateReturnTypeToKind(returnTypeAnnotation, resolveAlias), + ) .replace(/::_PROPERTY_NAME_::/g, property.name) .replace( /::_ARGS_::/g, @@ -276,7 +304,7 @@ function translateMethodForImplementation( ) .replace( /::_SIGNATURE_::/g, - translateMethodTypeToJniSignature(property, aliases), + translateMethodTypeToJniSignature(property, resolveAlias), ); } @@ -286,25 +314,17 @@ module.exports = { schema: SchemaType, moduleSpecName: string, ): FilesOutput { - const nativeModules: {[name: string]: NativeModuleShape, ...} = Object.keys( - schema.modules, - ) - .map(moduleName => { - const modules = schema.modules[moduleName].nativeModules; - if (modules == null) { - return null; - } - - return modules; - }) - .filter(Boolean) - .reduce((acc, modules) => Object.assign(acc, modules), {}); + const nativeModules = getModules(schema); const modules = Object.keys(nativeModules) .map(name => { const {aliases, properties} = nativeModules[name]; + const resolveAlias = createAliasResolver(aliases); + const translatedMethods = properties - .map(property => translateMethodForImplementation(property, aliases)) + .map(property => + translateMethodForImplementation(property, resolveAlias), + ) .join('\n'); return moduleTemplate .replace(/::_TURBOMODULE_METHOD_INVOKERS_::/g, translatedMethods) diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniH.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniH.js index 1b50545f5f4071..18dde67c9b1297 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniH.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniH.js @@ -14,6 +14,8 @@ import type {SchemaType} from '../../CodegenSchema'; type FilesOutput = Map; +const {getModules} = require('./Utils'); + const moduleTemplate = `/** * JNI C++ class for module '::_MODULE_NAME_::' */ @@ -25,7 +27,7 @@ public: const template = ` /** - * Copyright (c) Facebook, Inc. and its affiliates. + * ${'C'}opyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. @@ -33,10 +35,11 @@ const template = ` * ${'@'}generated by codegen project: GenerateModuleJniH.js */ -#import -#import -#import -#import +#pragma once + +#include +#include +#include namespace facebook { namespace react { @@ -49,25 +52,42 @@ std::shared_ptr ::_LIBRARY_NAME_::_ModuleProvider(const std::string } // namespace facebook `; +const androidMkTemplate = `# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := ::_LIBRARY_NAME_:: + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES := libjsi + +LOCAL_CFLAGS := \\ + -DLOG_TAG=\\"ReactNative\\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) +`; + module.exports = { generate( libraryName: string, schema: SchemaType, moduleSpecName: string, ): FilesOutput { - const nativeModules = Object.keys(schema.modules) - .sort() - .map(moduleName => { - const modules = schema.modules[moduleName].nativeModules; - if (modules == null) { - return null; - } - - return modules; - }) - .filter(Boolean) - .reduce((acc, components) => Object.assign(acc, components), {}); - + const nativeModules = getModules(schema); const modules = Object.keys(nativeModules) .map(name => moduleTemplate.replace(/::_MODULE_NAME_::/g, name)) .join('\n'); @@ -76,6 +96,15 @@ module.exports = { const replacedTemplate = template .replace(/::_MODULES_::/g, modules) .replace(/::_LIBRARY_NAME_::/g, libraryName); - return new Map([[fileName, replacedTemplate]]); + return new Map([ + [fileName, replacedTemplate], + [ + 'Android.mk', + androidMkTemplate.replace( + /::_LIBRARY_NAME_::/g, + `react_codegen_${libraryName.toLowerCase()}`, + ), + ], + ]); }, }; diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleMm.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleMm.js deleted file mode 100644 index 8b60d07e127429..00000000000000 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleMm.js +++ /dev/null @@ -1,320 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict - * @format - */ - -'use strict'; - -import type {SchemaType, NativeModuleShape} from '../../CodegenSchema'; - -const {capitalizeFirstLetter} = require('./ObjCppUtils/GenerateStructs'); -const {flatObjects} = require('./ObjCppUtils/Utils'); -const {getTypeAliasTypeAnnotation} = require('./Utils'); - -type FilesOutput = Map; - -const propertyHeaderTemplate = - ' static facebook::jsi::Value __hostFunction_Native::_MODULE_NAME_::SpecJSI_::_PROPERTY_NAME_::(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {'; - -const propertyCastTemplate = `static_cast(turboModule) - .invokeObjCMethod(rt, ::_KIND_::, "::_PROPERTY_NAME_::", @selector(::_PROPERTY_NAME_::::_ARGS_::), args, count);`; - -const propertyTemplate = ` -${propertyHeaderTemplate} - return ${propertyCastTemplate} - }`; - -const propertyDefTemplate = - ' methodMap_["::_PROPERTY_NAME_::"] = MethodMetadata {::_ARGS_COUNT_::, __hostFunction_Native::_MODULE_NAME_::SpecJSI_::_PROPERTY_NAME_::};'; - -const moduleTemplate = ` - ::_TURBOMODULE_METHOD_INVOKERS_:: - - Native::_MODULE_NAME_::SpecJSI::Native::_MODULE_NAME_::SpecJSI(const ObjCTurboModule::InitParams ¶ms) - : ObjCTurboModule(params) { - ::_PROPERTIES_MAP_::::_CONVERSION_SELECTORS_:: - }`.trim(); - -const getterTemplate = ` -@implementation RCTCxxConvert (Native::_MODULE_NAME_::_::_GETTER_NAME_::) -+ (RCTManagedPointer *)JS_Native::_MODULE_NAME_::_::_GETTER_NAME_:::(id)json -{ - return facebook::react::managedPointer(json); -} -@end -`; - -const argConvertionTemplate = - '\n setMethodArgConversionSelector(@"::_ARG_NAME_::", ::_ARG_NUMBER_::, @"JS_Native::_MODULE_NAME_::_::_SELECTOR_NAME_:::");'; - -const template = ` -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * ${'@'}generated by codegen project: GenerateModuleMm.js - */ - -#include <::_INCLUDE_::> -#import - -::_GETTERS_:: -namespace facebook { - namespace react { -::_MODULES_:: - } // namespace react -} // namespace facebook -`; - -function translateReturnTypeToKind(typeAnnotation): string { - switch (typeAnnotation.type) { - case 'ReservedFunctionValueTypeAnnotation': - switch (typeAnnotation.name) { - case 'RootTag': - return 'NumberKind'; - default: - (typeAnnotation.name: empty); - throw new Error( - `Invalid ReservedFunctionValueTypeName name, got ${typeAnnotation.name}`, - ); - } - case 'VoidTypeAnnotation': - return 'VoidKind'; - case 'StringTypeAnnotation': - return 'StringKind'; - case 'BooleanTypeAnnotation': - return 'BooleanKind'; - case 'NumberTypeAnnotation': - case 'DoubleTypeAnnotation': - case 'FloatTypeAnnotation': - case 'Int32TypeAnnotation': - return 'NumberKind'; - case 'GenericPromiseTypeAnnotation': - return 'PromiseKind'; - case 'GenericObjectTypeAnnotation': - case 'ObjectTypeAnnotation': - return 'ObjectKind'; - case 'ArrayTypeAnnotation': - return 'ArrayKind'; - default: - // TODO (T65847278): Figure out why this does not work. - // (typeAnnotation.type: empty); - throw new Error( - `Unknown prop type for returning value, found: ${typeAnnotation.type}"`, - ); - } -} - -function translateMethodForImplementation(property): string { - const {returnTypeAnnotation} = property.typeAnnotation; - - const numberOfParams = - property.typeAnnotation.params.length + - (returnTypeAnnotation.type === 'GenericPromiseTypeAnnotation' ? 2 : 0); - const translatedArguments = property.typeAnnotation.params - .map(param => param.name) - .concat( - returnTypeAnnotation.type === 'GenericPromiseTypeAnnotation' - ? ['resolve', 'reject'] - : [], - ) - .slice(1) - .join(':') - .concat(':'); - if ( - property.name === 'getConstants' && - returnTypeAnnotation.type === 'ObjectTypeAnnotation' && - returnTypeAnnotation.properties && - returnTypeAnnotation.properties.length === 0 - ) { - return ''; - } - return propertyTemplate - .replace(/::_KIND_::/g, translateReturnTypeToKind(returnTypeAnnotation)) - .replace(/::_PROPERTY_NAME_::/g, property.name) - .replace( - /::_ARGS_::/g, - numberOfParams === 0 - ? '' - : (numberOfParams === 1 ? '' : ':') + translatedArguments, - ); -} - -module.exports = { - generate( - libraryName: string, - schema: SchemaType, - moduleSpecName: string, - ): FilesOutput { - const nativeModules: {[name: string]: NativeModuleShape, ...} = Object.keys( - schema.modules, - ) - .map(moduleName => { - const modules = schema.modules[moduleName].nativeModules; - if (modules == null) { - return null; - } - - return modules; - }) - .filter(Boolean) - .reduce((acc, modules) => Object.assign(acc, modules), {}); - - const gettersImplementations = Object.keys(nativeModules) - .reduce((acc, moduleName: string) => { - const module: NativeModuleShape = nativeModules[moduleName]; - return acc.concat( - flatObjects( - module.properties - .reduce((moduleAcc, property) => { - const {returnTypeAnnotation} = property.typeAnnotation; - if (returnTypeAnnotation.type === 'ObjectTypeAnnotation') { - const {properties} = returnTypeAnnotation; - if (properties) { - moduleAcc.push({ - name: - 'Spec' + - capitalizeFirstLetter(property.name) + - 'ReturnType', - object: { - type: 'ObjectTypeAnnotation', - properties: properties, - }, - }); - } - } - if (property.typeAnnotation.params) { - return moduleAcc.concat( - property.typeAnnotation.params - .map(param => { - if ( - param.typeAnnotation.type === 'ObjectTypeAnnotation' - ) { - const {properties} = param.typeAnnotation; - if (properties) { - return { - name: - 'Spec' + - capitalizeFirstLetter(property.name) + - capitalizeFirstLetter(param.name), - object: { - type: 'ObjectTypeAnnotation', - properties: properties, - }, - }; - } - } - }) - .filter(Boolean), - ); - } - return moduleAcc; - }, []) - .concat( - Object.keys(module.aliases).map(aliasName => { - const alias = getTypeAliasTypeAnnotation( - aliasName, - module.aliases, - ); - return { - name: aliasName, - object: {type: alias.type, properties: alias.properties}, - }; - }), - ), - false, - module.aliases, - ) - .map(object => - getterTemplate - .replace(/::_GETTER_NAME_::/g, object.name) - .replace(/::_MODULE_NAME_::/g, moduleName), - ) - .join('\n'), - ); - }, []) - .join('\n'); - - const modules = Object.keys(nativeModules) - .map(name => { - const {aliases, properties} = nativeModules[name]; - const translatedMethods = properties - .map(property => translateMethodForImplementation(property)) - .join('\n'); - return moduleTemplate - .replace(/::_TURBOMODULE_METHOD_INVOKERS_::/g, translatedMethods) - .replace( - '::_PROPERTIES_MAP_::', - properties - .map( - ({ - name: propertyName, - typeAnnotation: {params, returnTypeAnnotation}, - }) => - propertyName === 'getConstants' && - returnTypeAnnotation.type === 'ObjectTypeAnnotation' && - returnTypeAnnotation.properties && - returnTypeAnnotation.properties.length === 0 - ? '' - : propertyDefTemplate - .replace(/::_PROPERTY_NAME_::/g, propertyName) - .replace(/::_ARGS_COUNT_::/g, params.length.toString()), - ) - .join('\n'), - ) - .replace( - '::_CONVERSION_SELECTORS_::', - properties - .map(({name: propertyName, typeAnnotation: {params}}) => - params - .map((param, index) => { - const typeAnnotation = - param.typeAnnotation.type === 'TypeAliasTypeAnnotation' - ? getTypeAliasTypeAnnotation( - param.typeAnnotation.name, - aliases, - ) - : param.typeAnnotation; - const selectorName = - param.typeAnnotation.type === 'TypeAliasTypeAnnotation' - ? param.typeAnnotation.name - : 'Spec' + - capitalizeFirstLetter(propertyName) + - capitalizeFirstLetter(param.name); - - if ( - typeAnnotation.type === 'ObjectTypeAnnotation' && - typeAnnotation.properties - ) { - return argConvertionTemplate - .replace('::_SELECTOR_NAME_::', selectorName) - .replace('::_ARG_NUMBER_::', index.toString()) - .replace('::_ARG_NAME_::', propertyName); - } - - return ''; - }) - .join(''), - ) - .join(''), - ) - .replace(/::_MODULE_NAME_::/g, name); - }) - .join('\n'); - - const fileName = `${moduleSpecName}-generated.mm`; - const replacedTemplate = template - .replace(/::_GETTERS_::/g, gettersImplementations) - .replace(/::_MODULES_::/g, modules) - .replace(/::_LIBRARY_NAME_::/g, libraryName) - .replace(/::_INCLUDE_::/g, `${moduleSpecName}/${moduleSpecName}.h`); - return new Map([[fileName, replacedTemplate]]); - }, -}; diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js new file mode 100644 index 00000000000000..71855f15f4f864 --- /dev/null +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js @@ -0,0 +1,191 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +'use strict'; + +import type { + Required, + NativeModuleObjectTypeAnnotation, + NativeModuleStringTypeAnnotation, + NativeModuleNumberTypeAnnotation, + NativeModuleInt32TypeAnnotation, + NativeModuleDoubleTypeAnnotation, + NativeModuleFloatTypeAnnotation, + NativeModuleBooleanTypeAnnotation, + NativeModuleGenericObjectTypeAnnotation, + NativeModuleReservedFunctionValueTypeAnnotation, + NativeModuleTypeAliasTypeAnnotation, + NativeModuleArrayTypeAnnotation, + NativeModuleBaseTypeAnnotation, +} from '../../../CodegenSchema'; + +import type {AliasResolver} from '../Utils'; + +const {capitalize} = require('./Utils'); + +type StructContext = 'CONSTANTS' | 'REGULAR'; + +export type RegularStruct = $ReadOnly<{| + context: 'REGULAR', + name: string, + properties: $ReadOnlyArray, +|}>; + +export type ConstantsStruct = $ReadOnly<{| + context: 'CONSTANTS', + name: string, + properties: $ReadOnlyArray, +|}>; + +export type Struct = RegularStruct | ConstantsStruct; + +export type StructProperty = $ReadOnly<{| + name: string, + optional: boolean, + typeAnnotation: StructTypeAnnotation, +|}>; + +export type StructTypeAnnotation = + | NativeModuleStringTypeAnnotation + | NativeModuleNumberTypeAnnotation + | NativeModuleInt32TypeAnnotation + | NativeModuleDoubleTypeAnnotation + | NativeModuleFloatTypeAnnotation + | NativeModuleBooleanTypeAnnotation + | NativeModuleGenericObjectTypeAnnotation + | NativeModuleReservedFunctionValueTypeAnnotation + | NativeModuleTypeAliasTypeAnnotation + | NativeModuleArrayTypeAnnotation; + +class StructCollector { + _structs: Map = new Map(); + + process( + structName: string, + structContext: StructContext, + resolveAlias: AliasResolver, + typeAnnotation: NativeModuleBaseTypeAnnotation, + ): StructTypeAnnotation { + switch (typeAnnotation.type) { + case 'ObjectTypeAnnotation': { + this._insertStruct(structName, structContext, resolveAlias, { + ...typeAnnotation, + // The nullability status of this struct is recorded in the type-alias we create for it below. + nullable: false, + }); + return { + type: 'TypeAliasTypeAnnotation', + name: structName, + nullable: typeAnnotation.nullable, + }; + } + case 'ArrayTypeAnnotation': { + if (typeAnnotation.elementType == null) { + return { + type: 'ArrayTypeAnnotation', + nullable: typeAnnotation.nullable, + }; + } + + return { + type: 'ArrayTypeAnnotation', + nullable: typeAnnotation.nullable, + elementType: this.process( + structName + 'Element', + structContext, + resolveAlias, + typeAnnotation.elementType, + ), + }; + } + case 'TypeAliasTypeAnnotation': { + this._insertAlias(typeAnnotation.name, structContext, resolveAlias); + return typeAnnotation; + } + default: { + return typeAnnotation; + } + } + } + + _insertAlias( + aliasName: string, + structContext: StructContext, + resolveAlias: AliasResolver, + ): void { + const usedStruct = this._structs.get(aliasName); + if (usedStruct == null) { + this._insertStruct( + aliasName, + structContext, + resolveAlias, + resolveAlias(aliasName), + ); + } else if (usedStruct.context !== structContext) { + throw new Error( + `Tried to use alias '${aliasName}' in a getConstants() return type and inside a regular struct.`, + ); + } + } + + _insertStruct( + structName: string, + structContext: StructContext, + resolveAlias: AliasResolver, + objectTypeAnnotation: Required, + ): void { + const properties = objectTypeAnnotation.properties.map(property => { + const {typeAnnotation: propertyTypeAnnotation} = property; + const propertyStructName = structName + capitalize(property.name); + + return { + ...property, + typeAnnotation: this.process( + propertyStructName, + structContext, + resolveAlias, + propertyTypeAnnotation, + ), + }; + }); + + switch (structContext) { + case 'REGULAR': + this._structs.set(structName, { + name: structName, + context: 'REGULAR', + properties: properties, + }); + break; + case 'CONSTANTS': + this._structs.set(structName, { + name: structName, + context: 'CONSTANTS', + properties: properties, + }); + break; + default: + (structContext: empty); + throw new Error(`Detected an invalid struct context: ${structContext}`); + } + } + + getAllStructs(): $ReadOnlyArray { + return [...this._structs.values()]; + } + + getStruct(name: string): ?Struct { + return this._structs.get(name); + } +} + +module.exports = { + StructCollector, +}; diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/Utils.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/Utils.js new file mode 100644 index 00000000000000..0b989b12e03d02 --- /dev/null +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/Utils.js @@ -0,0 +1,36 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +'use strict'; + +import type {StructProperty} from './StructCollector'; + +function capitalize(string: string): string { + return string.charAt(0).toUpperCase() + string.slice(1); +} +function getSafePropertyName(property: StructProperty): string { + if (property.name === 'id') { + return `${property.name}_`; + } + return property.name; +} + +function getNamespacedStructName( + moduleName: string, + structName: string, +): string { + return `JS::Native${moduleName}::${structName}`; +} + +module.exports = { + capitalize, + getSafePropertyName, + getNamespacedStructName, +}; diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js new file mode 100644 index 00000000000000..27a71f43a32cf1 --- /dev/null +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js @@ -0,0 +1,263 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +'use strict'; + +const { + capitalize, + getSafePropertyName, + getNamespacedStructName, +} = require('../Utils'); + +import type {StructTypeAnnotation, ConstantsStruct} from '../StructCollector'; +import type {StructSerilizationOutput} from './serializeStruct'; + +const StructTemplate = ({ + moduleName, + structName, + builderInputProps, +}: $ReadOnly<{| + moduleName: string, + structName: string, + builderInputProps: string, +|}>) => ` +namespace JS { + namespace Native${moduleName} { + struct ${structName} { + + struct Builder { + struct Input { + ${builderInputProps} + }; + + /** Initialize with a set of values */ + Builder(const Input i); + /** Initialize with an existing ${structName} */ + Builder(${structName} i); + /** Builds the object. Generally used only by the infrastructure. */ + NSDictionary *buildUnsafeRawValue() const { return _factory(); }; + private: + NSDictionary *(^_factory)(void); + }; + + static ${structName} fromUnsafeRawValue(NSDictionary *const v) { return {v}; } + NSDictionary *unsafeRawValue() const { return _v; } + private: + ${structName}(NSDictionary *const v) : _v(v) {} + NSDictionary *_v; + }; + } +}`; + +const MethodTemplate = ({ + moduleName, + structName, + properties, +}: $ReadOnly<{| + moduleName: string, + structName: string, + properties: string, +|}>) => ` +inline JS::Native${moduleName}::${structName}::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; +${properties} + return d; +}) {} +inline JS::Native${moduleName}::${structName}::Builder::Builder(${structName} i) : _factory(^{ + return i.unsafeRawValue(); +}) {}`; + +function toObjCType( + moduleName: string, + typeAnnotation: StructTypeAnnotation, + isOptional: boolean = false, +): string { + const isRequired = !typeAnnotation.nullable && !isOptional; + const wrapFollyOptional = (type: string) => { + return isRequired ? type : `folly::Optional<${type}>`; + }; + + switch (typeAnnotation.type) { + case 'ReservedFunctionValueTypeAnnotation': + switch (typeAnnotation.name) { + case 'RootTag': + return wrapFollyOptional('double'); + default: + (typeAnnotation.name: empty); + throw new Error(`Unknown prop type, found: ${typeAnnotation.name}"`); + } + case 'StringTypeAnnotation': + return 'NSString *'; + case 'NumberTypeAnnotation': + return wrapFollyOptional('double'); + case 'FloatTypeAnnotation': + return wrapFollyOptional('double'); + case 'Int32TypeAnnotation': + return wrapFollyOptional('double'); + case 'DoubleTypeAnnotation': + return wrapFollyOptional('double'); + case 'BooleanTypeAnnotation': + return wrapFollyOptional('bool'); + case 'GenericObjectTypeAnnotation': + return isRequired ? 'id ' : 'id _Nullable '; + case 'ArrayTypeAnnotation': + if (typeAnnotation.elementType == null) { + return isRequired ? 'id ' : 'id _Nullable '; + } + + return wrapFollyOptional( + `std::vector<${toObjCType(moduleName, typeAnnotation.elementType)}>`, + ); + case 'TypeAliasTypeAnnotation': + const structName = capitalize(typeAnnotation.name); + const namespacedStructName = getNamespacedStructName( + moduleName, + structName, + ); + return wrapFollyOptional(`${namespacedStructName}::Builder`); + default: + (typeAnnotation.type: empty); + throw new Error( + `Couldn't convert into ObjC type: ${typeAnnotation.type}"`, + ); + } +} + +function toObjCValue( + moduleName: string, + typeAnnotation: StructTypeAnnotation, + value: string, + depth: number, + isOptional: boolean = false, +): string { + const isRequired = !isOptional && !typeAnnotation.nullable; + + function wrapPrimitive(type: string) { + return !isRequired + ? `${value}.hasValue() ? @((${type})${value}.value()) : nil` + : `@(${value})`; + } + + switch (typeAnnotation.type) { + case 'ReservedFunctionValueTypeAnnotation': + switch (typeAnnotation.name) { + case 'RootTag': + return wrapPrimitive('double'); + default: + (typeAnnotation.name: empty); + throw new Error( + `Couldn't convert into ObjC type: ${typeAnnotation.type}"`, + ); + } + case 'StringTypeAnnotation': + return value; + case 'NumberTypeAnnotation': + return wrapPrimitive('double'); + case 'FloatTypeAnnotation': + return wrapPrimitive('double'); + case 'Int32TypeAnnotation': + return wrapPrimitive('double'); + case 'DoubleTypeAnnotation': + return wrapPrimitive('double'); + case 'BooleanTypeAnnotation': + return wrapPrimitive('BOOL'); + case 'GenericObjectTypeAnnotation': + return value; + case 'ArrayTypeAnnotation': + const {elementType} = typeAnnotation; + if (elementType == null) { + return value; + } + + const localVarName = `el${'_'.repeat(depth + 1)}`; + const elementObjCType = toObjCType(moduleName, elementType); + const elementObjCValue = toObjCValue( + moduleName, + elementType, + localVarName, + depth + 1, + ); + + const RCTConvertVecToArray = transformer => { + return `RCTConvert${ + !isRequired ? 'Optional' : '' + }VecToArray(${value}, ${transformer})`; + }; + + return RCTConvertVecToArray( + `^id(${elementObjCType} ${localVarName}) { return ${elementObjCValue}; }`, + ); + case 'TypeAliasTypeAnnotation': + return !isRequired + ? `${value}.hasValue() ? ${value}.value().buildUnsafeRawValue() : nil` + : `${value}.buildUnsafeRawValue()`; + default: + (typeAnnotation.type: empty); + throw new Error( + `Couldn't convert into ObjC value: ${typeAnnotation.type}"`, + ); + } +} + +function serializeConstantsStruct( + moduleName: string, + struct: ConstantsStruct, +): StructSerilizationOutput { + const declaration = StructTemplate({ + moduleName, + structName: struct.name, + builderInputProps: struct.properties + .map(property => { + const {typeAnnotation, optional} = property; + const propName = getSafePropertyName(property); + const objCType = toObjCType(moduleName, typeAnnotation, optional); + + if (!optional) { + return `RCTRequired<${objCType}> ${propName};`; + } + + const space = ' '.repeat(objCType.endsWith('*') ? 0 : 1); + return `${objCType}${space}${propName};`; + }) + .join('\n '), + }); + + const methods = MethodTemplate({ + moduleName, + structName: struct.name, + properties: struct.properties + .map(property => { + const {typeAnnotation, optional} = property; + const propName = getSafePropertyName(property); + const objCValue = toObjCValue( + moduleName, + typeAnnotation, + propName, + 0, + optional, + ); + + let varDecl = `auto ${propName} = i.${propName}`; + if (!optional) { + varDecl += '.get()'; + } + + const assignment = `d[@"${propName}"] = ` + objCValue; + return ` ${varDecl};\n ${assignment};`; + }) + .join('\n'), + }); + + return {declaration, methods}; +} + +module.exports = { + serializeConstantsStruct, +}; diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js new file mode 100644 index 00000000000000..ce7b5f12d4f5bb --- /dev/null +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js @@ -0,0 +1,251 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +'use strict'; + +const { + capitalize, + getSafePropertyName, + getNamespacedStructName, +} = require('../Utils'); + +import type {StructTypeAnnotation, RegularStruct} from '../StructCollector'; +import type {StructSerilizationOutput} from './serializeStruct'; + +const StructTemplate = ({ + moduleName, + structName, + structProperties, +}: $ReadOnly<{| + moduleName: string, + structName: string, + structProperties: string, +|}>) => ` +namespace JS { + namespace Native${moduleName} { + struct ${structName} { + ${structProperties} + + ${structName}(NSDictionary *const v) : _v(v) {} + private: + NSDictionary *_v; + }; + } +} + +@interface RCTCxxConvert (Native${moduleName}_${structName}) ++ (RCTManagedPointer *)JS_Native${moduleName}_${structName}:(id)json; +@end +`; + +const MethodTemplate = ({ + returnType, + returnValue, + moduleName, + structName, + propertyName, +}: $ReadOnly<{| + returnType: string, + returnValue: string, + moduleName: string, + structName: string, + propertyName: string, +|}>) => ` +inline ${returnType}JS::Native${moduleName}::${structName}::${propertyName}() const +{ + id const p = _v[@"${propertyName}"]; + return ${returnValue}; +} +`; + +function toObjCType( + moduleName: string, + typeAnnotation: StructTypeAnnotation, + isOptional: boolean = false, +): string { + const isRequired = !typeAnnotation.nullable && !isOptional; + const wrapFollyOptional = (type: string) => { + return isRequired ? type : `folly::Optional<${type}>`; + }; + + switch (typeAnnotation.type) { + case 'ReservedFunctionValueTypeAnnotation': + switch (typeAnnotation.name) { + case 'RootTag': + return wrapFollyOptional('double'); + default: + (typeAnnotation.name: empty); + throw new Error(`Unknown prop type, found: ${typeAnnotation.name}"`); + } + case 'StringTypeAnnotation': + return 'NSString *'; + case 'NumberTypeAnnotation': + return wrapFollyOptional('double'); + case 'FloatTypeAnnotation': + return wrapFollyOptional('double'); + case 'Int32TypeAnnotation': + return wrapFollyOptional('double'); + case 'DoubleTypeAnnotation': + return wrapFollyOptional('double'); + case 'BooleanTypeAnnotation': + return wrapFollyOptional('bool'); + case 'GenericObjectTypeAnnotation': + return isRequired ? 'id ' : 'id _Nullable'; + case 'ArrayTypeAnnotation': + if (typeAnnotation.elementType == null) { + return isRequired ? 'id ' : 'id _Nullable'; + } + return wrapFollyOptional( + `facebook::react::LazyVector<${toObjCType( + moduleName, + typeAnnotation.elementType, + )}>`, + ); + case 'TypeAliasTypeAnnotation': + const structName = capitalize(typeAnnotation.name); + const namespacedStructName = getNamespacedStructName( + moduleName, + structName, + ); + return wrapFollyOptional(namespacedStructName); + default: + (typeAnnotation.type: empty); + throw new Error( + `Couldn't convert into ObjC type: ${typeAnnotation.type}"`, + ); + } +} + +function toObjCValue( + moduleName: string, + typeAnnotation: StructTypeAnnotation, + value: string, + depth: number, + isOptional: boolean = false, +): string { + const isRequired = !typeAnnotation.nullable && !isOptional; + const RCTBridgingTo = (type: string, arg?: string) => { + const args = [value, arg].filter(Boolean).join(', '); + return isRequired + ? `RCTBridgingTo${type}(${args})` + : `RCTBridgingToOptional${type}(${args})`; + }; + + switch (typeAnnotation.type) { + case 'ReservedFunctionValueTypeAnnotation': + switch (typeAnnotation.name) { + case 'RootTag': + return RCTBridgingTo('Double'); + default: + (typeAnnotation.name: empty); + throw new Error( + `Couldn't convert into ObjC type: ${typeAnnotation.type}"`, + ); + } + case 'StringTypeAnnotation': + return RCTBridgingTo('String'); + case 'NumberTypeAnnotation': + return RCTBridgingTo('Double'); + case 'FloatTypeAnnotation': + return RCTBridgingTo('Double'); + case 'Int32TypeAnnotation': + return RCTBridgingTo('Double'); + case 'DoubleTypeAnnotation': + return RCTBridgingTo('Double'); + case 'BooleanTypeAnnotation': + return RCTBridgingTo('Bool'); + case 'GenericObjectTypeAnnotation': + return value; + case 'ArrayTypeAnnotation': + const {elementType} = typeAnnotation; + if (elementType == null) { + return value; + } + + const localVarName = `itemValue_${depth}`; + const elementObjCType = toObjCType(moduleName, elementType); + const elementObjCValue = toObjCValue( + moduleName, + elementType, + localVarName, + depth + 1, + ); + + return RCTBridgingTo( + 'Vec', + `^${elementObjCType}(id ${localVarName}) { return ${elementObjCValue}; }`, + ); + case 'TypeAliasTypeAnnotation': + const structName = capitalize(typeAnnotation.name); + const namespacedStructName = getNamespacedStructName( + moduleName, + structName, + ); + + return !isRequired + ? `(p == nil ? folly::none : folly::make_optional(${namespacedStructName}(p)))` + : `${namespacedStructName}(p)`; + default: + (typeAnnotation.type: empty); + throw new Error( + `Couldn't convert into ObjC value: ${typeAnnotation.type}"`, + ); + } +} + +function serializeRegularStruct( + moduleName: string, + struct: RegularStruct, +): StructSerilizationOutput { + const declaration = StructTemplate({ + moduleName: moduleName, + structName: struct.name, + structProperties: struct.properties + .map(property => { + const {typeAnnotation, optional} = property; + const propName = getSafePropertyName(property); + const returnType = toObjCType(moduleName, typeAnnotation, optional); + + const padding = ' '.repeat(returnType.endsWith('*') ? 0 : 1); + return `${returnType}${padding}${propName}() const;`; + }) + .join('\n '), + }); + + const methods = struct.properties + .map(property => { + const {typeAnnotation, optional} = property; + const propName = getSafePropertyName(property); + const returnType = toObjCType(moduleName, typeAnnotation, optional); + const returnValue = toObjCValue( + moduleName, + typeAnnotation, + 'p', + 0, + optional, + ); + + const padding = ' '.repeat(returnType.endsWith('*') ? 0 : 1); + return MethodTemplate({ + moduleName, + structName: struct.name, + returnType: returnType + padding, + returnValue: returnValue, + propertyName: propName, + }); + }) + .join('\n'); + + return {methods, declaration}; +} + +module.exports = { + serializeRegularStruct, +}; diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeStruct.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeStruct.js new file mode 100644 index 00000000000000..fe2355333b3a83 --- /dev/null +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeStruct.js @@ -0,0 +1,35 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +'use strict'; + +import type {Struct} from '../StructCollector'; + +const {serializeConstantsStruct} = require('./serializeConstantsStruct'); +const {serializeRegularStruct} = require('./serializeRegularStruct'); + +export type StructSerilizationOutput = $ReadOnly<{| + methods: string, + declaration: string, +|}>; + +function serializeStruct( + moduleName: string, + struct: Struct, +): StructSerilizationOutput { + if (struct.context === 'REGULAR') { + return serializeRegularStruct(moduleName, struct); + } + return serializeConstantsStruct(moduleName, struct); +} + +module.exports = { + serializeStruct, +}; diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/index.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/index.js new file mode 100644 index 00000000000000..ee2d5e1e42d375 --- /dev/null +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/index.js @@ -0,0 +1,210 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +'use strict'; + +import type {SchemaType} from '../../../CodegenSchema'; +import type {MethodSerializationOutput} from './serializeMethod'; + +const {createAliasResolver, getModules} = require('../Utils'); + +const {StructCollector} = require('./StructCollector'); +const {serializeStruct} = require('./header/serializeStruct'); +const {serializeMethod} = require('./serializeMethod'); +const {serializeModuleSource} = require('./source/serializeModule'); + +type FilesOutput = Map; + +const ModuleDeclarationTemplate = ({ + moduleName, + structDeclarations, + protocolMethods, +}: $ReadOnly<{| + moduleName: string, + structDeclarations: string, + protocolMethods: string, +|}>) => ` +${structDeclarations} +@protocol Native${moduleName}Spec + +${protocolMethods} + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module '${moduleName}' + */ + class JSI_EXPORT Native${moduleName}SpecJSI : public ObjCTurboModule { + public: + Native${moduleName}SpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook +`; + +const HeaderFileTemplate = ({ + moduleDeclarations, + structInlineMethods, +}: $ReadOnly<{| + moduleDeclarations: string, + structInlineMethods: string, +|}>) => ` +/** + * ${'C'}opyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * ${'@'}generated by codegen project: GenerateModuleHObjCpp.js + */ + +#ifndef __cplusplus +#error This file must be compiled as Obj-C++. If you are importing it, you must change your file extension to .mm. +#endif + +#import + +#import + +#import + +#import +#import +#import + +#import +#import +#import + +#import + +${moduleDeclarations} + +${structInlineMethods} +`; + +const SourceFileTemplate = ({ + headerFileName, + moduleImplementations, +}: $ReadOnly<{| + headerFileName: string, + moduleImplementations: string, +|}>) => ` +/** + * ${'C'}opyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * ${'@'}generated by an internal genrule from Flow types. + * + * We create an umbrella header (and corresponding implementation) here since + * Cxx compilation in BUCK has a limitation: source-code producing genrule()s + * must have a single output. More files => more genrule()s => slower builds. + */ + +#import "${headerFileName}" + +${moduleImplementations} +`; + +module.exports = { + generate( + libraryName: string, + schema: SchemaType, + moduleSpecName: string, + ): FilesOutput { + const nativeModules = getModules(schema); + + const moduleDeclarations: Array = []; + const structInlineMethods: Array = []; + const moduleImplementations: Array = []; + + const moduleNames: Array = Object.keys(nativeModules).sort(); + for (const moduleName of moduleNames) { + const {aliases, properties} = nativeModules[moduleName]; + const resolveAlias = createAliasResolver(aliases); + const structCollector = new StructCollector(); + + const methodSerializations: Array = []; + const serializeProperty = property => { + methodSerializations.push( + ...serializeMethod( + moduleName, + property, + structCollector, + resolveAlias, + ), + ); + }; + + /** + * Note: As we serialize NativeModule methods, we insert structs into + * StructCollector, as we encounter them. + */ + properties + .filter(property => property.name !== 'getConstants') + .forEach(serializeProperty); + properties + .filter(property => property.name === 'getConstants') + .forEach(serializeProperty); + + const generatedStructs = structCollector.getAllStructs(); + const structStrs = []; + const methodStrs = []; + + for (const struct of generatedStructs) { + const {methods, declaration} = serializeStruct(moduleName, struct); + structStrs.push(declaration); + methodStrs.push(methods); + } + + moduleDeclarations.push( + ModuleDeclarationTemplate({ + moduleName: moduleName, + structDeclarations: structStrs.join('\n'), + protocolMethods: methodSerializations + .map(({protocolMethod}) => protocolMethod) + .join('\n'), + }), + ); + + structInlineMethods.push(methodStrs.join('\n')); + + moduleImplementations.push( + serializeModuleSource( + moduleName, + generatedStructs, + methodSerializations.filter( + ({selector}) => selector !== '@selector(constantsToExport)', + ), + ), + ); + } + + const headerFileName = `${moduleSpecName}.h`; + const headerFile = HeaderFileTemplate({ + moduleDeclarations: moduleDeclarations.join('\n'), + structInlineMethods: structInlineMethods.join('\n'), + }); + + const sourceFileName = `${moduleSpecName}-generated.mm`; + const sourceFile = SourceFileTemplate({ + headerFileName, + moduleImplementations: moduleImplementations.join('\n'), + }); + + return new Map([ + [headerFileName, headerFile], + [sourceFileName, sourceFile], + ]); + }, +}; diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js new file mode 100644 index 00000000000000..bec84f59d6de76 --- /dev/null +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js @@ -0,0 +1,427 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +'use strict'; + +import type { + NativeModuleMethodParamSchema, + NativeModuleReturnTypeAnnotation, + NativeModulePropertySchema, +} from '../../../CodegenSchema'; + +import type {AliasResolver} from '../Utils'; + +const invariant = require('invariant'); +const {StructCollector} = require('./StructCollector'); +const {capitalize, getNamespacedStructName} = require('./Utils'); + +const ProtocolMethodTemplate = ({ + returnObjCType, + methodName, + params, +}: $ReadOnly<{| + returnObjCType: string, + methodName: string, + params: string, +|}>) => `- (${returnObjCType})${methodName}${params};`; + +export type StructParameterRecord = $ReadOnly<{| + paramIndex: number, + structName: string, +|}>; + +type ReturnJSType = + | 'VoidKind' + | 'PromiseKind' + | 'ObjectKind' + | 'ArrayKind' + | 'NumberKind' + | 'StringKind'; + +export type MethodSerializationOutput = $ReadOnly<{| + methodName: string, + protocolMethod: string, + selector: string, + structParamRecords: $ReadOnlyArray, + returnJSType: ReturnJSType, + argCount: number, +|}>; + +function serializeMethod( + moduleName: string, + property: NativeModulePropertySchema, + structCollector: StructCollector, + resolveAlias: AliasResolver, +): $ReadOnlyArray { + const { + name: methodName, + typeAnnotation: {params, returnTypeAnnotation}, + } = property; + + if (methodName === 'getConstants') { + return serializeConstantsProtocolMethods( + moduleName, + property, + structCollector, + resolveAlias, + ); + } + + const methodParams: Array<{|paramName: string, objCType: string|}> = []; + const structParamRecords: Array = []; + + params.forEach((param, index) => { + const structName = getParamStructName(methodName, param); + const {objCType, isStruct} = getParamObjCType( + moduleName, + methodName, + param, + structName, + structCollector, + resolveAlias, + ); + + methodParams.push({paramName: param.name, objCType}); + + if (isStruct) { + structParamRecords.push({paramIndex: index, structName}); + } + }); + + if (returnTypeAnnotation.type === 'PromiseTypeAnnotation') { + methodParams.push( + {paramName: 'resolve', objCType: 'RCTPromiseResolveBlock'}, + {paramName: 'reject', objCType: 'RCTPromiseRejectBlock'}, + ); + } + + /** + * Build Protocol Method + **/ + const returnObjCType = getReturnObjCType(methodName, returnTypeAnnotation); + const paddingMax = `- (${returnObjCType})${methodName}`.length; + + const objCParams = methodParams.reduce( + ($objCParams, {objCType, paramName}, i) => { + const rhs = `(${objCType})${paramName}`; + const padding = ' '.repeat(Math.max(0, paddingMax - paramName.length)); + return i === 0 + ? `:${rhs}` + : `${$objCParams}\n${padding}${paramName}:${rhs}`; + }, + '', + ); + + const protocolMethod = ProtocolMethodTemplate({ + methodName, + returnObjCType, + params: objCParams, + }); + + /** + * Build ObjC Selector + */ + const selector = methodParams + .map(({paramName}) => paramName) + .reduce(($selector, paramName, i) => { + return i === 0 ? `${$selector}:` : `${$selector}${paramName}:`; + }, methodName); + + /** + * Build JS Return type + */ + const returnJSType = getReturnJSType(methodName, returnTypeAnnotation); + + return [ + { + methodName, + protocolMethod, + selector: `@selector(${selector})`, + structParamRecords, + returnJSType, + argCount: params.length, + }, + ]; +} + +function getParamStructName( + methodName: string, + param: NativeModuleMethodParamSchema, +): string { + if (param.typeAnnotation.type === 'TypeAliasTypeAnnotation') { + return param.typeAnnotation.name; + } + + return `Spec${capitalize(methodName)}${capitalize(param.name)}`; +} + +function getParamObjCType( + moduleName: string, + methodName: string, + param: NativeModuleMethodParamSchema, + structName: string, + structCollector: StructCollector, + resolveAlias: AliasResolver, +): $ReadOnly<{|objCType: string, isStruct: boolean|}> { + const {name: paramName, typeAnnotation} = param; + const notRequired = param.optional || typeAnnotation.nullable; + + function wrapIntoNullableIfNeeded(generatedType: string) { + return typeAnnotation.nullable + ? `${generatedType} _Nullable` + : generatedType; + } + + const isStruct = (objCType: string) => ({ + isStruct: true, + objCType, + }); + + const notStruct = (objCType: string) => ({ + isStruct: false, + objCType, + }); + + // Handle types that can only be in parameters + switch (typeAnnotation.type) { + case 'FunctionTypeAnnotation': { + return notStruct('RCTResponseSenderBlock'); + } + case 'ArrayTypeAnnotation': { + /** + * Array in params always codegen NSArray * + * + * TODO(T73933406): Support codegen for Arrays of structs and primitives + * + * For example: + * Array => NSArray + * type Animal = {||}; + * Array => NSArray, etc. + */ + return notStruct(wrapIntoNullableIfNeeded('NSArray *')); + } + } + + const structTypeAnnotation = structCollector.process( + structName, + 'REGULAR', + resolveAlias, + typeAnnotation, + ); + + invariant( + structTypeAnnotation.type !== 'ArrayTypeAnnotation', + 'ArrayTypeAnnotations should have been processed earlier', + ); + + switch (structTypeAnnotation.type) { + case 'TypeAliasTypeAnnotation': { + /** + * TODO(T73943261): Support nullable object literals and aliases? + */ + return isStruct( + getNamespacedStructName(moduleName, structTypeAnnotation.name) + ' &', + ); + } + case 'ReservedFunctionValueTypeAnnotation': + switch (structTypeAnnotation.name) { + case 'RootTag': + return notStruct(notRequired ? 'NSNumber *' : 'double'); + default: + (structTypeAnnotation.name: empty); + throw new Error( + `Unsupported type for param "${paramName}" in ${methodName}. Found: ${structTypeAnnotation.type}`, + ); + } + case 'StringTypeAnnotation': + return notStruct(wrapIntoNullableIfNeeded('NSString *')); + case 'NumberTypeAnnotation': + return notStruct(notRequired ? 'NSNumber *' : 'double'); + case 'FloatTypeAnnotation': + return notStruct(notRequired ? 'NSNumber *' : 'double'); + case 'DoubleTypeAnnotation': + return notStruct(notRequired ? 'NSNumber *' : 'double'); + case 'Int32TypeAnnotation': + return notStruct(notRequired ? 'NSNumber *' : 'double'); + case 'BooleanTypeAnnotation': + return notStruct(notRequired ? 'NSNumber *' : 'BOOL'); + case 'GenericObjectTypeAnnotation': + return notStruct(wrapIntoNullableIfNeeded('NSDictionary *')); + default: + (structTypeAnnotation.type: empty); + throw new Error( + `Unsupported type for param "${paramName}" in ${methodName}. Found: ${typeAnnotation.type}`, + ); + } +} + +function getReturnObjCType( + methodName: string, + typeAnnotation: NativeModuleReturnTypeAnnotation, +) { + function wrapIntoNullableIfNeeded(generatedType: string) { + return typeAnnotation.nullable + ? `${generatedType} _Nullable` + : generatedType; + } + + switch (typeAnnotation.type) { + case 'VoidTypeAnnotation': + return 'void'; + case 'PromiseTypeAnnotation': + return 'void'; + case 'ObjectTypeAnnotation': + return wrapIntoNullableIfNeeded('NSDictionary *'); + case 'TypeAliasTypeAnnotation': + return wrapIntoNullableIfNeeded('NSDictionary *'); + case 'ArrayTypeAnnotation': + if (typeAnnotation.elementType == null) { + return wrapIntoNullableIfNeeded('NSArray> *'); + } + + return wrapIntoNullableIfNeeded( + `NSArray<${getReturnObjCType( + methodName, + typeAnnotation.elementType, + )}> *`, + ); + case 'ReservedFunctionValueTypeAnnotation': + switch (typeAnnotation.name) { + case 'RootTag': + return wrapIntoNullableIfNeeded('NSNumber *'); + default: + (typeAnnotation.name: empty); + throw new Error( + `Unsupported return type for ${methodName}. Found: ${typeAnnotation.name}`, + ); + } + case 'StringTypeAnnotation': + // TODO: Can NSString * returns not be _Nullable? + // In the legacy codegen, we don't surround NSSTring * with _Nullable + return wrapIntoNullableIfNeeded('NSString *'); + case 'NumberTypeAnnotation': + return wrapIntoNullableIfNeeded('NSNumber *'); + case 'FloatTypeAnnotation': + return wrapIntoNullableIfNeeded('NSNumber *'); + case 'DoubleTypeAnnotation': + return wrapIntoNullableIfNeeded('NSNumber *'); + case 'Int32TypeAnnotation': + return wrapIntoNullableIfNeeded('NSNumber *'); + case 'BooleanTypeAnnotation': + return wrapIntoNullableIfNeeded('NSNumber *'); + case 'GenericObjectTypeAnnotation': + return wrapIntoNullableIfNeeded('NSDictionary *'); + default: + (typeAnnotation.type: empty); + throw new Error( + `Unsupported return type for ${methodName}. Found: ${typeAnnotation.type}`, + ); + } +} + +function getReturnJSType( + methodName: string, + typeAnnotation: NativeModuleReturnTypeAnnotation, +): ReturnJSType { + switch (typeAnnotation.type) { + case 'VoidTypeAnnotation': + return 'VoidKind'; + case 'PromiseTypeAnnotation': + return 'PromiseKind'; + case 'ObjectTypeAnnotation': + return 'ObjectKind'; + case 'TypeAliasTypeAnnotation': + return 'ObjectKind'; + case 'ArrayTypeAnnotation': + return 'ArrayKind'; + case 'ReservedFunctionValueTypeAnnotation': + return 'NumberKind'; + case 'StringTypeAnnotation': + return 'StringKind'; + case 'NumberTypeAnnotation': + return 'NumberKind'; + case 'FloatTypeAnnotation': + return 'NumberKind'; + case 'DoubleTypeAnnotation': + return 'NumberKind'; + case 'Int32TypeAnnotation': + return 'NumberKind'; + case 'BooleanTypeAnnotation': + return 'NumberKind'; + case 'GenericObjectTypeAnnotation': + return 'ObjectKind'; + default: + (typeAnnotation.type: empty); + throw new Error( + `Unsupported return type for ${methodName}. Found: ${typeAnnotation.type}`, + ); + } +} + +function serializeConstantsProtocolMethods( + moduleName: string, + property: NativeModulePropertySchema, + structCollector: StructCollector, + resolveAlias: AliasResolver, +): $ReadOnlyArray { + if (property.typeAnnotation.params.length !== 0) { + throw new Error( + `${moduleName}.getConstants() may only accept 0 arguments.`, + ); + } + + const {returnTypeAnnotation} = property.typeAnnotation; + if (returnTypeAnnotation.type !== 'ObjectTypeAnnotation') { + throw new Error( + `${moduleName}.getConstants() may only return an object literal: {|...|}.`, + ); + } + + if (returnTypeAnnotation.properties.length === 0) { + return []; + } + + const realTypeAnnotation = structCollector.process( + 'Constants', + 'CONSTANTS', + resolveAlias, + returnTypeAnnotation, + ); + + invariant( + realTypeAnnotation.type === 'TypeAliasTypeAnnotation', + "Unable to generate C++ struct from module's getConstants() method return type.", + ); + + const returnObjCType = `facebook::react::ModuleConstants`; + + return ['constantsToExport', 'getConstants'].map( + methodName => { + const protocolMethod = ProtocolMethodTemplate({ + methodName, + returnObjCType, + params: '', + }); + + return { + methodName, + protocolMethod, + returnJSType: 'ObjectKind', + selector: `@selector(${methodName})`, + structParamRecords: [], + argCount: 0, + }; + }, + ); +} + +module.exports = { + serializeMethod, +}; diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/source/serializeModule.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/source/serializeModule.js new file mode 100644 index 00000000000000..72013475009fbe --- /dev/null +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/source/serializeModule.js @@ -0,0 +1,127 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +'use strict'; + +import type {Struct} from '../StructCollector'; +import type { + MethodSerializationOutput, + StructParameterRecord, +} from '../serializeMethod'; + +const ModuleTemplate = ({ + moduleName, + structs, + methodSerializationOutputs, +}: $ReadOnly<{| + moduleName: string, + structs: $ReadOnlyArray, + methodSerializationOutputs: $ReadOnlyArray, +|}>) => ` +${structs + .map(struct => + RCTCxxConvertCategoryTemplate({moduleName, structName: struct.name}), + ) + .join('\n')} +namespace facebook { + namespace react { + ${methodSerializationOutputs + .map(serializedMethodParts => + InlineHostFunctionTemplate({ + moduleName, + methodName: serializedMethodParts.methodName, + returnJSType: serializedMethodParts.returnJSType, + selector: serializedMethodParts.selector, + }), + ) + .join('\n')} + + Native${moduleName}SpecJSI::Native${moduleName}SpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + ${methodSerializationOutputs + .map(({methodName, structParamRecords, argCount}) => + MethodMapEntryTemplate({ + moduleName, + methodName, + structParamRecords, + argCount, + }), + ) + .join('\n' + ' '.repeat(8))} + } + } // namespace react +} // namespace facebook +`; + +const RCTCxxConvertCategoryTemplate = ({ + moduleName, + structName, +}: $ReadOnly<{| + moduleName: string, + structName: string, +|}>) => ` +@implementation RCTCxxConvert (Native${moduleName}_${structName}) ++ (RCTManagedPointer *)JS_Native${moduleName}_${structName}:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +`; + +const InlineHostFunctionTemplate = ({ + moduleName, + methodName, + returnJSType, + selector, +}: $ReadOnly<{| + moduleName: string, + methodName: string, + returnJSType: string, + selector: string, +|}>) => ` + static facebook::jsi::Value __hostFunction_Native${moduleName}SpecJSI_${methodName}(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ${returnJSType}, "${methodName}", ${selector}, args, count); + } +`; + +const MethodMapEntryTemplate = ({ + moduleName, + methodName, + structParamRecords, + argCount, +}: $ReadOnly<{| + moduleName: string, + methodName: string, + structParamRecords: $ReadOnlyArray, + argCount: number, +|}>) => ` + methodMap_["${methodName}"] = MethodMetadata {${argCount}, __hostFunction_Native${moduleName}SpecJSI_${methodName}}; + ${structParamRecords + .map(({paramIndex, structName}) => { + return `setMethodArgConversionSelector(@"${methodName}", ${paramIndex}, @"JS_Native${moduleName}_${structName}:");`; + }) + .join('\n' + ' '.repeat(8))} +`; + +function serializeModuleSource( + moduleName: string, + structs: $ReadOnlyArray, + methodSerializationOutputs: $ReadOnlyArray, +): string { + return ModuleTemplate({ + moduleName, + structs: structs.filter(({context}) => context !== 'CONSTANTS'), + methodSerializationOutputs, + }); +} + +module.exports = { + serializeModuleSource, +}; diff --git a/packages/react-native-codegen/src/generators/modules/ObjCppUtils/GenerateStructs.js b/packages/react-native-codegen/src/generators/modules/ObjCppUtils/GenerateStructs.js deleted file mode 100644 index c9e1d817a296d1..00000000000000 --- a/packages/react-native-codegen/src/generators/modules/ObjCppUtils/GenerateStructs.js +++ /dev/null @@ -1,452 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict - * @format - */ - -'use strict'; - -import type { - ObjectParamTypeAnnotation, - ObjectTypeAliasTypeShape, -} from '../../../CodegenSchema'; -const { - flatObjects, - capitalizeFirstLetter, - getSafePropertyName, -} = require('./Utils'); -const {getTypeAliasTypeAnnotation} = require('../Utils'); -const {generateStructsForConstants} = require('./GenerateStructsForConstants'); - -const template = ` -::_CONSTANTS_::::_STRUCTS_::::_INLINES_:: -`; - -const structTemplate = ` -namespace JS { - namespace Native::_MODULE_NAME_:: { - struct ::_STRUCT_NAME_:: { - ::_STRUCT_PROPERTIES_:: - - ::_STRUCT_NAME_::(NSDictionary *const v) : _v(v) {} - private: - NSDictionary *_v; - }; - } -} - -@interface RCTCxxConvert (Native::_MODULE_NAME_::_::_STRUCT_NAME_::) -+ (RCTManagedPointer *)JS_Native::_MODULE_NAME_::_::_STRUCT_NAME_:::(id)json; -@end -`; - -const inlineTemplate = ` -inline ::_RETURN_TYPE_::JS::Native::_MODULE_NAME_::::::_STRUCT_NAME_::::::_PROPERTY_NAME_::() const -{ - id const p = _v[@"::_PROPERTY_NAME_::"]; - return ::_RETURN_VALUE_::; -} -`; - -function getNamespacedStructName(structName: string): string { - return `JS::Native::_MODULE_NAME_::::${structName}`; -} - -function getElementTypeForArray( - property: ObjectParamTypeAnnotation, - name: string, - moduleName: string, - aliases: $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}>, -): string { - const {typeAnnotation} = property; - - // TODO(T67898313): Workaround for NativeLinking's use of union type. This check may be removed once typeAnnotation is non-optional. - if (!typeAnnotation) { - throw new Error( - `Cannot get array element type, property ${property.name} does not contain a type annotation`, - ); - } - - if (typeAnnotation.type !== 'ArrayTypeAnnotation') { - throw new Error( - `Cannot get array element type for non-array type ${typeAnnotation.type}`, - ); - } - - if (!typeAnnotation.elementType) { - return 'id'; - } - - const type = - typeAnnotation.elementType.type === 'TypeAliasTypeAnnotation' - ? getTypeAliasTypeAnnotation(typeAnnotation.elementType.name, aliases) - .type - : typeAnnotation.elementType.type; - switch (type) { - case 'StringTypeAnnotation': - return 'NSString *'; - case 'DoubleTypeAnnotation': - case 'NumberTypeAnnotation': - case 'FloatTypeAnnotation': - case 'Int32TypeAnnotation': - return 'double'; - case 'ObjectTypeAnnotation': - const structName = - typeAnnotation.elementType.type === 'TypeAliasTypeAnnotation' - ? typeAnnotation.elementType.name - : `${property.name}Element`; - return getNamespacedStructName(structName); - case 'GenericObjectTypeAnnotation': - // TODO(T67565166): Generic objects are not type safe and should be disallowed in the schema. This case should throw an error once it is disallowed in schema. - console.error( - `Warning: Generic objects are not type safe and should be avoided whenever possible (see '${property.name}' in ${moduleName}'s ${name})`, - ); - return 'id'; - case 'BooleanTypeAnnotation': - case 'AnyObjectTypeAnnotation': - case 'AnyTypeAnnotation': - case 'ArrayTypeAnnotation': - case 'FunctionTypeAnnotation': - case 'ReservedFunctionValueTypeAnnotation': - case 'ReservedPropTypeAnnotation': - case 'StringEnumTypeAnnotation': - throw new Error(`Unsupported array element type, found: ${type}"`); - default: - (type: empty); - throw new Error(`Unknown array element type, found: ${type}"`); - } -} - -function getInlineMethodSignature( - property: ObjectParamTypeAnnotation, - name: string, - moduleName: string, - aliases: $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}>, -): string { - const {typeAnnotation} = property; - function markOptionalTypeIfNecessary(type: string) { - if (property.optional) { - return `folly::Optional<${type}>`; - } - return type; - } - - // TODO(T67672788): Workaround for values key in NativeLinking which lacks a typeAnnotation. id is not type safe! - if (!typeAnnotation) { - console.error( - `Warning: Unsafe type found (see '${property.name}' in ${moduleName}'s ${name})`, - ); - return `id ${getSafePropertyName(property)}() const;`; - } - - const realTypeAnnotation = - typeAnnotation.type === 'TypeAliasTypeAnnotation' - ? getTypeAliasTypeAnnotation(typeAnnotation.name, aliases) - : typeAnnotation; - - const variableName = - typeAnnotation.type === 'TypeAliasTypeAnnotation' - ? `${capitalizeFirstLetter(typeAnnotation.name)}` - : `${capitalizeFirstLetter(name)}${capitalizeFirstLetter( - getSafePropertyName(property), - )}`; - - switch (realTypeAnnotation.type) { - case 'ReservedFunctionValueTypeAnnotation': - switch (realTypeAnnotation.name) { - case 'RootTag': - return `double ${getSafePropertyName(property)}() const;`; - default: - (realTypeAnnotation.name: empty); - throw new Error( - `Unknown prop type, found: ${realTypeAnnotation.name}"`, - ); - } - case 'StringTypeAnnotation': - return `NSString *${getSafePropertyName(property)}() const;`; - case 'NumberTypeAnnotation': - case 'FloatTypeAnnotation': - case 'Int32TypeAnnotation': - return `${markOptionalTypeIfNecessary('double')} ${getSafePropertyName( - property, - )}() const;`; - case 'BooleanTypeAnnotation': - return `${markOptionalTypeIfNecessary('bool')} ${getSafePropertyName( - property, - )}() const;`; - case 'ObjectTypeAnnotation': - return `${markOptionalTypeIfNecessary( - getNamespacedStructName(variableName), - )} ${getSafePropertyName(property)}() const;`; - case 'GenericObjectTypeAnnotation': - case 'AnyTypeAnnotation': - return `id ${ - property.optional ? '_Nullable ' : ' ' - }${getSafePropertyName(property)}() const;`; - case 'ArrayTypeAnnotation': - return `${markOptionalTypeIfNecessary( - `facebook::react::LazyVector<${getElementTypeForArray( - property, - name, - moduleName, - aliases, - )}>`, - )} ${getSafePropertyName(property)}() const;`; - case 'FunctionTypeAnnotation': - default: - throw new Error(`Unknown prop type, found: ${realTypeAnnotation.type}"`); - } -} - -function getInlineMethodImplementation( - property: ObjectParamTypeAnnotation, - name: string, - moduleName: string, - aliases: $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}>, -): string { - const {typeAnnotation} = property; - function markOptionalTypeIfNecessary(type: string): string { - if (property.optional) { - return `folly::Optional<${type}> `; - } - return `${type} `; - } - function markOptionalValueIfNecessary(value: string): string { - if (property.optional) { - return `RCTBridgingToOptional${capitalizeFirstLetter(value)}`; - } - return `RCTBridgingTo${capitalizeFirstLetter(value)}`; - } - function bridgeArrayElementValueIfNecessary(element: string): string { - // TODO(T67898313): Workaround for NativeLinking's use of union type - if (!typeAnnotation) { - throw new Error( - `Cannot get array element type, property ${property.name} does not contain a type annotation`, - ); - } - - if (typeAnnotation.type !== 'ArrayTypeAnnotation') { - throw new Error( - `Cannot get array element type for non-array type ${typeAnnotation.type}`, - ); - } - - if (!typeAnnotation.elementType) { - throw new Error(`Cannot get array element type for ${name}`); - } - - const type = - typeAnnotation.elementType.type === 'TypeAliasTypeAnnotation' - ? getTypeAliasTypeAnnotation(typeAnnotation.elementType.name, aliases) - .type - : typeAnnotation.elementType.type; - - switch (type) { - case 'StringTypeAnnotation': - return `RCTBridgingToString(${element})`; - case 'DoubleTypeAnnotation': - case 'NumberTypeAnnotation': - case 'FloatTypeAnnotation': - case 'Int32TypeAnnotation': - return `RCTBridgingToDouble(${element})`; - case 'BooleanTypeAnnotation': - return `RCTBridgingToBool(${element})`; - case 'ObjectTypeAnnotation': - const structName = - typeAnnotation.elementType.type === 'TypeAliasTypeAnnotation' - ? `${typeAnnotation.elementType.name}(${element})` - : `${getSafePropertyName(property)}Element(${element})`; - return getNamespacedStructName(structName); - case 'GenericObjectTypeAnnotation': - return element; - case 'AnyObjectTypeAnnotation': - case 'AnyTypeAnnotation': - case 'ArrayTypeAnnotation': - case 'FunctionTypeAnnotation': - case 'ReservedFunctionValueTypeAnnotation': - case 'ReservedPropTypeAnnotation': - case 'StringEnumTypeAnnotation': - case 'TupleTypeAnnotation': - throw new Error(`Unsupported array element type, found: ${type}"`); - default: - (type: empty); - throw new Error(`Unknown array element type, found: ${type}"`); - } - } - - // TODO(T67672788): Workaround for values key in NativeLinking which lacks a typeAnnotation. id is not type safe! - if (!typeAnnotation) { - console.error( - `Warning: Unsafe type found (see '${property.name}' in ${moduleName}'s ${name})`, - ); - return inlineTemplate - .replace( - /::_RETURN_TYPE_::/, - property.optional ? 'id _Nullable ' : 'id ', - ) - .replace(/::_RETURN_VALUE_::/, 'p'); - } - - const realTypeAnnotation = - typeAnnotation.type === 'TypeAliasTypeAnnotation' - ? getTypeAliasTypeAnnotation(typeAnnotation.name, aliases) - : typeAnnotation; - - switch (realTypeAnnotation.type) { - case 'ReservedFunctionValueTypeAnnotation': - switch (realTypeAnnotation.name) { - case 'RootTag': - return inlineTemplate - .replace(/::_RETURN_TYPE_::/, 'double ') - .replace(/::_RETURN_VALUE_::/, 'RCTBridgingToDouble(p)'); - default: - (realTypeAnnotation.name: empty); - throw new Error( - `Unknown prop type, found: ${realTypeAnnotation.name}"`, - ); - } - case 'StringTypeAnnotation': - return inlineTemplate - .replace(/::_RETURN_TYPE_::/, 'NSString *') - .replace(/::_RETURN_VALUE_::/, 'RCTBridgingToString(p)'); - case 'NumberTypeAnnotation': - case 'FloatTypeAnnotation': - case 'Int32TypeAnnotation': - return inlineTemplate - .replace(/::_RETURN_TYPE_::/, markOptionalTypeIfNecessary('double')) - .replace( - /::_RETURN_VALUE_::/, - `${markOptionalValueIfNecessary('double')}(p)`, - ); - case 'BooleanTypeAnnotation': - return inlineTemplate - .replace(/::_RETURN_TYPE_::/, markOptionalTypeIfNecessary('bool')) - .replace( - /::_RETURN_VALUE_::/, - `${markOptionalValueIfNecessary('bool')}(p)`, - ); - case 'GenericObjectTypeAnnotation': - case 'AnyTypeAnnotation': - return inlineTemplate - .replace( - /::_RETURN_TYPE_::/, - property.optional ? 'id _Nullable ' : 'id ', - ) - .replace(/::_RETURN_VALUE_::/, 'p'); - case 'ObjectTypeAnnotation': - const structName = - typeAnnotation.type === 'TypeAliasTypeAnnotation' - ? `${capitalizeFirstLetter(typeAnnotation.name)}` - : `${name}${capitalizeFirstLetter(getSafePropertyName(property))}`; - const namespacedStructName = getNamespacedStructName(structName); - return inlineTemplate - .replace( - /::_RETURN_TYPE_::/, - markOptionalTypeIfNecessary(namespacedStructName), - ) - .replace( - /::_RETURN_VALUE_::/, - property.optional - ? `(p == nil ? folly::none : folly::make_optional(${namespacedStructName}(p)))` - : `${namespacedStructName}(p)`, - ); - case 'ArrayTypeAnnotation': - return inlineTemplate - .replace( - /::_RETURN_TYPE_::/, - markOptionalTypeIfNecessary( - `facebook::react::LazyVector<${getElementTypeForArray( - property, - name, - moduleName, - aliases, - )}>`, - ), - ) - .replace( - /::_RETURN_VALUE_::/, - `${markOptionalValueIfNecessary('vec')}(p, ^${getElementTypeForArray( - property, - name, - moduleName, - aliases, - )}(id itemValue_0) { return ${bridgeArrayElementValueIfNecessary( - 'itemValue_0', - )}; })`, - ); - case 'FunctionTypeAnnotation': - default: - throw new Error(`Unknown prop type, found: ${realTypeAnnotation.type}"`); - } -} - -function translateObjectsForStructs( - annotations: $ReadOnlyArray< - $ReadOnly<{| - name: string, - object: $ReadOnly<{| - type: 'ObjectTypeAnnotation', - properties: $ReadOnlyArray, - |}>, - |}>, - >, - moduleName: string, - aliases: $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}>, -): string { - const flattenObjects = flatObjects(annotations, false, aliases); - - const translatedInlineMethods = flattenObjects - .reduce( - (acc, object) => - acc.concat( - object.properties.map(property => - getInlineMethodImplementation( - property, - object.name, - moduleName, - aliases, - ) - .replace(/::_PROPERTY_NAME_::/g, getSafePropertyName(property)) - .replace(/::_STRUCT_NAME_::/g, object.name), - ), - ), - [], - ) - .join('\n'); - - const translatedStructs = flattenObjects - .map(object => { - return structTemplate - .replace( - /::_STRUCT_PROPERTIES_::/g, - object.properties - .map(property => - getInlineMethodSignature( - property, - object.name, - moduleName, - aliases, - ), - ) - .join('\n '), - ) - .replace(/::_STRUCT_NAME_::/g, object.name); - }) - .reverse() - .join('\n'); - const translatedConstants = generateStructsForConstants(annotations, aliases); - - return template - .replace(/::_STRUCTS_::/, translatedStructs) - .replace(/::_INLINES_::/, translatedInlineMethods) - .replace(/::_CONSTANTS_::/, translatedConstants); -} -module.exports = { - translateObjectsForStructs, - capitalizeFirstLetter, - getNamespacedStructName, -}; diff --git a/packages/react-native-codegen/src/generators/modules/ObjCppUtils/GenerateStructsForConstants.js b/packages/react-native-codegen/src/generators/modules/ObjCppUtils/GenerateStructsForConstants.js deleted file mode 100644 index 0433152f7f627d..00000000000000 --- a/packages/react-native-codegen/src/generators/modules/ObjCppUtils/GenerateStructsForConstants.js +++ /dev/null @@ -1,275 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict - * @format - */ - -'use strict'; - -import type { - ObjectParamTypeAnnotation, - ObjectTypeAliasTypeShape, -} from '../../../CodegenSchema'; -const {flatObjects, capitalizeFirstLetter} = require('./Utils'); -const {getTypeAliasTypeAnnotation} = require('../Utils'); - -const structTemplate = ` -namespace JS { - namespace Native::_MODULE_NAME_:: { - struct ::_STRUCT_NAME_:: { - - struct Builder { - struct Input { - ::_INPUT_:: - }; - - /** Initialize with a set of values */ - Builder(const Input i); - /** Initialize with an existing ::_STRUCT_NAME_:: */ - Builder(::_STRUCT_NAME_:: i); - /** Builds the object. Generally used only by the infrastructure. */ - NSDictionary *buildUnsafeRawValue() const { return _factory(); }; - private: - NSDictionary *(^_factory)(void); - }; - - static ::_STRUCT_NAME_:: fromUnsafeRawValue(NSDictionary *const v) { return {v}; } - NSDictionary *unsafeRawValue() const { return _v; } - private: - ::_STRUCT_NAME_::(NSDictionary *const v) : _v(v) {} - NSDictionary *_v; - }; - } -} - -inline JS::Native::_MODULE_NAME_::::::_STRUCT_NAME_::::Builder::Builder(const Input i) : _factory(^{ - NSMutableDictionary *d = [NSMutableDictionary new]; - ::_PROPERTIES_:: - return d; -}) {} -inline JS::Native::_MODULE_NAME_::::::_STRUCT_NAME_::::Builder::Builder(::_STRUCT_NAME_:: i) : _factory(^{ - return i.unsafeRawValue(); -}) {}`; - -function getBuilderInputFieldDeclaration( - property: ObjectParamTypeAnnotation, - name: string, - aliases: $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}>, -): string { - function markRequiredIfNecessary(annotation) { - if (!property.optional) { - return 'RCTRequired<' + annotation + '> ' + property.name + ';'; - } - return 'folly::Optional<' + annotation + '> ' + property.name + ';'; - } - const {typeAnnotation} = property; - - // TODO(T67898313): Workaround for NativeLinking's use of union type. This check may be removed once typeAnnotation is non-optional. - if (!typeAnnotation) { - throw new Error( - `Cannot get array element type, property ${property.name} does not contain a type annotation`, - ); - } - - const realTypeAnnotation = - typeAnnotation.type === 'TypeAliasTypeAnnotation' - ? getTypeAliasTypeAnnotation(typeAnnotation.name, aliases) - : typeAnnotation; - - const variableName = - typeAnnotation.type === 'TypeAliasTypeAnnotation' - ? typeAnnotation.name - : `${name}${capitalizeFirstLetter(property.name)}`; - - switch (realTypeAnnotation.type) { - case 'ReservedFunctionValueTypeAnnotation': - switch (realTypeAnnotation.name) { - case 'RootTag': - return markRequiredIfNecessary('double'); - default: - (realTypeAnnotation.name: empty); - throw new Error( - `Unknown prop type, found: ${realTypeAnnotation.name}"`, - ); - } - case 'StringTypeAnnotation': - if (property.optional) { - return 'NSString *' + property.name + ';'; - } - return markRequiredIfNecessary('NSString *'); - case 'NumberTypeAnnotation': - case 'FloatTypeAnnotation': - case 'Int32TypeAnnotation': - return markRequiredIfNecessary('double'); - case 'BooleanTypeAnnotation': - return markRequiredIfNecessary('bool'); - case 'ObjectTypeAnnotation': - return markRequiredIfNecessary( - `JS::Native::_MODULE_NAME_::::${variableName}::Builder`, - ); - case 'GenericObjectTypeAnnotation': - case 'AnyTypeAnnotation': - if (property.optional) { - return 'id _Nullable ' + property.name + ';'; - } - return markRequiredIfNecessary('id'); - case 'ArrayTypeAnnotation': - return markRequiredIfNecessary('std::vector>'); - case 'FunctionTypeAnnotation': - default: - throw new Error(`Unknown prop type, found: ${realTypeAnnotation.type}"`); - } -} - -function safeGetter(name: string, optional: boolean) { - return ` - auto ${name} = i.${name}${optional ? '' : '.get()'}; - d[@"${name}"] = ${name}; - `.trim(); -} - -function arrayGetter(name: string, optional: boolean) { - return ` - auto ${name} = i.${name}${optional ? '' : '.get()'}; - d[@"${name}"] = RCTConvert${ - optional ? 'Optional' : '' - }VecToArray(${name}, ^id(id el_) { return el_; }); - `.trim(); -} - -function boolGetter(name: string, optional: boolean) { - return ` - auto ${name} = i.${name}${optional ? '' : '.get()'}; - d[@"${name}"] = ${ - optional - ? `${name}.hasValue() ? @((BOOL)${name}.value()) : nil` - : `@(${name})` - }; - `.trim(); -} - -function numberGetter(name: string, optional: boolean) { - return ` - auto ${name} = i.${name}${optional ? '' : '.get()'}; - d[@"${name}"] = ${ - optional - ? `${name}.hasValue() ? @((double)${name}.value()) : nil` - : `@(${name})` - }; - `.trim(); -} - -function unsafeGetter(name: string, optional: boolean) { - return ` - auto ${name} = i.${name}${optional ? '' : '.get()'}; - d[@"${name}"] = ${ - optional - ? `${name}.hasValue() ? ${name}.value().buildUnsafeRawValue() : nil` - : `${name}.buildUnsafeRawValue()` - }; - `.trim(); -} - -function getObjectProperty( - property: ObjectParamTypeAnnotation, - aliases: $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}>, -): string { - const {typeAnnotation} = property; - - // TODO(T67898313): Workaround for NativeLinking's use of union type. This check may be removed once typeAnnotation is non-optional. - if (!typeAnnotation) { - throw new Error( - `Cannot get array element type, property ${property.name} does not contain a type annotation`, - ); - } - - const type = - typeAnnotation.type === 'TypeAliasTypeAnnotation' - ? getTypeAliasTypeAnnotation(typeAnnotation.name, aliases).type - : typeAnnotation.type; - - switch (type) { - case 'ReservedFunctionValueTypeAnnotation': - if (typeAnnotation.name == null) { - throw new Error(`Prop type ${type} has no name.`); - } - switch (typeAnnotation.name) { - case 'RootTag': - return numberGetter(property.name, property.optional); - default: - // TODO (T65847278): Figure out why this does not work. - // (typeAnnotation.name: empty); - throw new Error(`Unknown prop type, found: ${typeAnnotation.name}"`); - } - case 'NumberTypeAnnotation': - case 'FloatTypeAnnotation': - case 'Int32TypeAnnotation': - return numberGetter(property.name, property.optional); - case 'BooleanTypeAnnotation': - return boolGetter(property.name, property.optional); - case 'StringTypeAnnotation': - case 'GenericObjectTypeAnnotation': - case 'AnyTypeAnnotation': - return safeGetter(property.name, property.optional); - case 'ObjectTypeAnnotation': - return unsafeGetter(property.name, property.optional); - case 'ArrayTypeAnnotation': - return arrayGetter(property.name, property.optional); - case 'FunctionTypeAnnotation': - default: - throw new Error(`Unknown prop type, found: ${type}"`); - } -} - -function generateStructsForConstants( - annotations: $ReadOnlyArray< - $ReadOnly<{| - name: string, - object: $ReadOnly<{| - type: 'ObjectTypeAnnotation', - properties: $ReadOnlyArray, - |}>, - |}>, - >, - aliases: $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}>, -): string { - return flatObjects(annotations, true, aliases) - .reduce( - (acc, object) => - acc.concat( - structTemplate - .replace( - /::_INPUT_::/g, - object.properties - .map(property => - getBuilderInputFieldDeclaration( - property, - object.name, - aliases, - ), - ) - .join('\n '), - ) - .replace( - /::_PROPERTIES_::/g, - object.properties - .map(property => getObjectProperty(property, aliases)) - .join('\n'), - ) - .replace(/::_STRUCT_NAME_::/g, object.name), - ), - [], - ) - .reverse() - .join('\n') - .replace(/SpecGetConstantsReturnType/g, 'Constants') - .replace(/GetConstantsReturnType/g, 'Constants'); -} -module.exports = { - generateStructsForConstants, - capitalizeFirstLetter, -}; diff --git a/packages/react-native-codegen/src/generators/modules/ObjCppUtils/Utils.js b/packages/react-native-codegen/src/generators/modules/ObjCppUtils/Utils.js deleted file mode 100644 index fd4dca38468dad..00000000000000 --- a/packages/react-native-codegen/src/generators/modules/ObjCppUtils/Utils.js +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict - * @format - */ - -'use strict'; - -import type { - ObjectParamTypeAnnotation, - ObjectTypeAliasTypeShape, -} from '../../../CodegenSchema'; -const {getTypeAliasTypeAnnotation} = require('../Utils'); - -function capitalizeFirstLetter(string: string): string { - return string.charAt(0).toUpperCase() + string.slice(1); -} - -function flatObjects( - annotations: $ReadOnlyArray< - $ReadOnly<{| - name: string, - object: $ReadOnly<{| - type: 'ObjectTypeAnnotation', - properties: $ReadOnlyArray, - |}>, - |}>, - >, - forConstants: boolean = false, - aliases: $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}>, -): $ReadOnlyArray< - $ReadOnly<{| - name: string, - properties: $ReadOnlyArray, - |}>, -> { - let objectTypesToFlatten: Array<{| - properties: $ReadOnlyArray, - name: string, - |}> = annotations - .map(annotation => { - if (annotation.object.type === 'TypeAliasTypeAnnotation') { - const alias = getTypeAliasTypeAnnotation(annotation.name, aliases); - return {name: annotation.name, properties: alias.properties}; - } - return { - name: annotation.name, - properties: annotation.object.properties, - }; - }) - .filter( - annotation => - (annotation.name === 'SpecGetConstantsReturnType') === forConstants, - ) - .filter( - annotation => - annotation.name !== 'SpecGetConstantsReturnType' || - annotation.properties.length > 0, - ); - - let flattenObjects: Array<{| - properties: $ReadOnlyArray, - name: string, - |}> = []; - - while (objectTypesToFlatten.length !== 0) { - const oldObjectTypesToFlatten = objectTypesToFlatten; - objectTypesToFlatten = []; - flattenObjects = flattenObjects.concat( - oldObjectTypesToFlatten.map(object => { - const {properties} = object; - if (properties !== undefined) { - objectTypesToFlatten = objectTypesToFlatten.concat( - properties.reduce((acc, curr) => { - if ( - curr.typeAnnotation && - curr.typeAnnotation.type === 'ObjectTypeAnnotation' && - curr.typeAnnotation.properties - ) { - return acc.concat({ - properties: curr.typeAnnotation.properties, - name: object.name + capitalizeFirstLetter(curr.name), - }); - } - return acc; - }, []), - ); - } - return object; - }), - ); - } - - return flattenObjects; -} - -function getSafePropertyName(property: ObjectParamTypeAnnotation): string { - if (property.name === 'id') { - return `${property.name}_`; - } - return property.name; -} - -module.exports = { - flatObjects, - capitalizeFirstLetter, - getSafePropertyName, -}; diff --git a/packages/react-native-codegen/src/generators/modules/Utils.js b/packages/react-native-codegen/src/generators/modules/Utils.js index 8ce5c95416f610..6985b861c7989e 100644 --- a/packages/react-native-codegen/src/generators/modules/Utils.js +++ b/packages/react-native-codegen/src/generators/modules/Utils.js @@ -10,35 +10,43 @@ 'use strict'; -import type {ObjectTypeAliasTypeShape} from '../../CodegenSchema'; +import type { + SchemaType, + NativeModuleAliasMap, + Required, + NativeModuleObjectTypeAnnotation, + NativeModuleSchema, +} from '../../CodegenSchema'; -function getTypeAliasTypeAnnotation( - name: string, - aliases: $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}>, -): $ReadOnly { - const typeAnnotation = aliases[name]; - if (!typeAnnotation) { - throw Error(`No type annotation found for "${name}" in schema`); - } - if (typeAnnotation.type === 'ObjectTypeAnnotation') { - if (typeAnnotation.properties) { - return typeAnnotation; - } +const invariant = require('invariant'); - throw new Error( - `Unsupported type for "${name}". Please provide properties.`, - ); - } - // $FlowFixMe[incompatible-type] - if (typeAnnotation.type === 'TypeAliasTypeAnnotation') { - return getTypeAliasTypeAnnotation(typeAnnotation.name, aliases); - } +export type AliasResolver = ( + aliasName: string, +) => Required; + +function createAliasResolver(aliasMap: NativeModuleAliasMap): AliasResolver { + return (aliasName: string) => { + const alias = aliasMap[aliasName]; + invariant(alias != null, `Unable to resolve type alias '${aliasName}'.`); + return alias; + }; +} - throw Error( - `Unsupported type annotation in alias "${name}", found: ${typeAnnotation.type}`, - ); +function getModules( + schema: SchemaType, +): $ReadOnly<{|[moduleName: string]: NativeModuleSchema|}> { + return Object.keys(schema.modules) + .map( + moduleName => schema.modules[moduleName].nativeModules, + ) + .filter(Boolean) + .reduce<{+[string]: NativeModuleSchema}>( + (acc, modules) => ({...acc, ...modules}), + {}, + ); } module.exports = { - getTypeAliasTypeAnnotation, + createAliasResolver, + getModules, }; diff --git a/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js b/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js index cea663c8b1b425..92a7dc3489823e 100644 --- a/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js @@ -34,6 +34,7 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { properties: [ { name: 'getConstants', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -45,6 +46,7 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { name: 'const1', typeAnnotation: { type: 'BooleanTypeAnnotation', + nullable: false, }, }, { @@ -52,6 +54,7 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { name: 'const2', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -59,16 +62,18 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { name: 'const3', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, ], }, params: [], - optional: false, + nullable: false, }, }, { name: 'voidFunc', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -76,11 +81,12 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { type: 'VoidTypeAnnotation', }, params: [], - optional: false, + nullable: false, }, }, { name: 'getBool', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -89,18 +95,20 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { }, params: [ { - nullable: false, + optional: false, name: 'arg', typeAnnotation: { type: 'BooleanTypeAnnotation', + nullable: false, }, }, ], - optional: false, + nullable: false, }, }, { name: 'getNumber', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -109,18 +117,20 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { }, params: [ { - nullable: false, + optional: false, name: 'arg', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, ], - optional: false, + nullable: false, }, }, { name: 'getString', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -129,44 +139,50 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { }, params: [ { - nullable: false, + optional: false, name: 'arg', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, ], - optional: false, + nullable: false, }, }, { name: 'getArray', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { nullable: false, type: 'ArrayTypeAnnotation', elementType: { - type: 'AnyTypeAnnotation', + type: 'GenericObjectTypeAnnotation', + nullable: false, }, }, params: [ { name: 'arg', - nullable: false, + optional: false, typeAnnotation: { type: 'ArrayTypeAnnotation', + nullable: false, elementType: { - type: 'AnyTypeAnnotation', + type: 'GenericObjectTypeAnnotation', + nullable: false, }, }, }, ], - optional: false, + nullable: false, }, }, { name: 'getObject', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -175,18 +191,20 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { }, params: [ { - nullable: false, + optional: false, name: 'arg', typeAnnotation: { type: 'GenericObjectTypeAnnotation', + nullable: false, }, }, ], - optional: false, + nullable: false, }, }, { name: 'getRootTag', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -196,19 +214,21 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { }, params: [ { - nullable: false, + optional: false, name: 'arg', typeAnnotation: { type: 'ReservedFunctionValueTypeAnnotation', name: 'RootTag', + nullable: false, }, }, ], - optional: false, + nullable: false, }, }, { name: 'getValue', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -217,32 +237,36 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { }, params: [ { - nullable: false, + optional: false, name: 'x', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { - nullable: false, + optional: false, name: 'y', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { - nullable: false, + optional: false, name: 'z', typeAnnotation: { type: 'GenericObjectTypeAnnotation', + nullable: false, }, }, ], - optional: false, + nullable: false, }, }, { name: 'getValueWithCallback', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -252,33 +276,41 @@ const SIMPLE_NATIVE_MODULES: SchemaType = { params: [ { name: 'callback', - nullable: false, + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', + params: [], + returnTypeAnnotation: { + type: 'VoidTypeAnnotation', + nullable: false, + }, + nullable: false, }, }, ], - optional: false, + nullable: false, }, }, { name: 'getValueWithPromise', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { nullable: false, - type: 'GenericPromiseTypeAnnotation', + type: 'PromiseTypeAnnotation', }, params: [ { - nullable: false, + optional: false, name: 'error', typeAnnotation: { type: 'BooleanTypeAnnotation', + nullable: false, }, }, ], - optional: false, + nullable: false, }, }, ], @@ -297,6 +329,7 @@ const TWO_MODULES_SAME_FILE: SchemaType = { properties: [ { name: 'voidFunc', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -304,7 +337,7 @@ const TWO_MODULES_SAME_FILE: SchemaType = { type: 'VoidTypeAnnotation', }, params: [], - optional: false, + nullable: false, }, }, ], @@ -314,6 +347,7 @@ const TWO_MODULES_SAME_FILE: SchemaType = { properties: [ { name: 'voidFunc', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -321,7 +355,7 @@ const TWO_MODULES_SAME_FILE: SchemaType = { type: 'VoidTypeAnnotation', }, params: [], - optional: false, + nullable: false, }, }, ], @@ -340,6 +374,7 @@ const TWO_MODULES_DIFFERENT_FILES: SchemaType = { properties: [ { name: 'voidFunc', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -347,7 +382,7 @@ const TWO_MODULES_DIFFERENT_FILES: SchemaType = { type: 'VoidTypeAnnotation', }, params: [], - optional: false, + nullable: false, }, }, ], @@ -361,6 +396,7 @@ const TWO_MODULES_DIFFERENT_FILES: SchemaType = { properties: [ { name: 'getConstants', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -369,11 +405,12 @@ const TWO_MODULES_DIFFERENT_FILES: SchemaType = { properties: [], }, params: [], - optional: false, + nullable: false, }, }, { name: 'voidFunc', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -381,7 +418,7 @@ const TWO_MODULES_DIFFERENT_FILES: SchemaType = { type: 'VoidTypeAnnotation', }, params: [], - optional: false, + nullable: false, }, }, ], @@ -400,6 +437,7 @@ const COMPLEX_OBJECTS: SchemaType = { properties: [ { name: 'difficult', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -411,6 +449,7 @@ const COMPLEX_OBJECTS: SchemaType = { name: 'D', typeAnnotation: { type: 'BooleanTypeAnnotation', + nullable: false, }, }, { @@ -418,6 +457,7 @@ const COMPLEX_OBJECTS: SchemaType = { name: 'E', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -425,22 +465,25 @@ const COMPLEX_OBJECTS: SchemaType = { name: 'F', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, ], }, params: [ { - nullable: false, + optional: false, name: 'A', typeAnnotation: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, name: 'D', typeAnnotation: { type: 'BooleanTypeAnnotation', + nullable: false, }, }, { @@ -448,12 +491,14 @@ const COMPLEX_OBJECTS: SchemaType = { name: 'E', typeAnnotation: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, name: 'D', typeAnnotation: { type: 'BooleanTypeAnnotation', + nullable: false, }, }, { @@ -461,6 +506,7 @@ const COMPLEX_OBJECTS: SchemaType = { name: 'E', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -468,6 +514,7 @@ const COMPLEX_OBJECTS: SchemaType = { name: 'F', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { @@ -475,6 +522,7 @@ const COMPLEX_OBJECTS: SchemaType = { name: 'id', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, ], @@ -485,17 +533,19 @@ const COMPLEX_OBJECTS: SchemaType = { name: 'F', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, ], }, }, ], - optional: false, + nullable: false, }, }, { name: 'optionals', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -504,16 +554,18 @@ const COMPLEX_OBJECTS: SchemaType = { }, params: [ { - nullable: false, + optional: false, name: 'A', typeAnnotation: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: true, name: 'optionalNumberProperty', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -521,8 +573,10 @@ const COMPLEX_OBJECTS: SchemaType = { name: 'optionalArrayProperty', typeAnnotation: { type: 'ArrayTypeAnnotation', + nullable: false, elementType: { type: 'NumberTypeAnnotation', + nullable: false, }, }, }, @@ -531,12 +585,14 @@ const COMPLEX_OBJECTS: SchemaType = { name: 'optionalObjectProperty', typeAnnotation: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, name: 'x', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -544,6 +600,7 @@ const COMPLEX_OBJECTS: SchemaType = { name: 'y', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, ], @@ -554,6 +611,7 @@ const COMPLEX_OBJECTS: SchemaType = { name: 'optionalGenericObjectProperty', typeAnnotation: { type: 'GenericObjectTypeAnnotation', + nullable: false, }, }, { @@ -561,17 +619,19 @@ const COMPLEX_OBJECTS: SchemaType = { name: 'optionalBooleanTypeProperty', typeAnnotation: { type: 'BooleanTypeAnnotation', + nullable: false, }, }, ], }, }, ], - optional: false, + nullable: false, }, }, { name: 'optionalMethod', + optional: true, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -580,48 +640,63 @@ const COMPLEX_OBJECTS: SchemaType = { }, params: [ { - nullable: false, + optional: false, name: 'options', typeAnnotation: { type: 'GenericObjectTypeAnnotation', + nullable: false, }, }, { name: 'callback', - nullable: false, + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', + params: [], + returnTypeAnnotation: { + type: 'VoidTypeAnnotation', + nullable: false, + }, + nullable: false, }, }, { name: 'extras', - nullable: true, + optional: true, typeAnnotation: { type: 'ArrayTypeAnnotation', + nullable: false, elementType: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, name: 'key', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { optional: false, name: 'value', + typeAnnotation: { + type: 'GenericObjectTypeAnnotation', + nullable: false, + }, }, ], }, }, }, ], - optional: true, + nullable: true, }, }, { name: 'getArrays', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -630,18 +705,21 @@ const COMPLEX_OBJECTS: SchemaType = { }, params: [ { - nullable: false, + optional: false, name: 'options', typeAnnotation: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, name: 'arrayOfNumbers', typeAnnotation: { type: 'ArrayTypeAnnotation', + nullable: false, elementType: { type: 'NumberTypeAnnotation', + nullable: false, }, }, }, @@ -650,8 +728,10 @@ const COMPLEX_OBJECTS: SchemaType = { name: 'optionalArrayOfNumbers', typeAnnotation: { type: 'ArrayTypeAnnotation', + nullable: false, elementType: { type: 'NumberTypeAnnotation', + nullable: false, }, }, }, @@ -660,8 +740,10 @@ const COMPLEX_OBJECTS: SchemaType = { name: 'arrayOfStrings', typeAnnotation: { type: 'ArrayTypeAnnotation', + nullable: false, elementType: { type: 'StringTypeAnnotation', + nullable: false, }, }, }, @@ -670,8 +752,10 @@ const COMPLEX_OBJECTS: SchemaType = { name: 'optionalArrayOfStrings', typeAnnotation: { type: 'ArrayTypeAnnotation', + nullable: false, elementType: { type: 'StringTypeAnnotation', + nullable: false, }, }, }, @@ -680,14 +764,17 @@ const COMPLEX_OBJECTS: SchemaType = { name: 'arrayOfObjects', typeAnnotation: { type: 'ArrayTypeAnnotation', + nullable: false, elementType: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, name: 'numberProperty', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, ], @@ -698,7 +785,7 @@ const COMPLEX_OBJECTS: SchemaType = { }, }, ], - optional: false, + nullable: false, }, }, ], @@ -715,18 +802,22 @@ const NATIVE_MODULES_WITH_TYPE_ALIASES: SchemaType = { AliasTurboModule: { aliases: { Options: { + type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, name: 'offset', typeAnnotation: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, name: 'x', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -734,6 +825,7 @@ const NATIVE_MODULES_WITH_TYPE_ALIASES: SchemaType = { name: 'y', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, ], @@ -744,12 +836,14 @@ const NATIVE_MODULES_WITH_TYPE_ALIASES: SchemaType = { name: 'size', typeAnnotation: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, name: 'width', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -757,6 +851,7 @@ const NATIVE_MODULES_WITH_TYPE_ALIASES: SchemaType = { name: 'height', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, ], @@ -767,12 +862,14 @@ const NATIVE_MODULES_WITH_TYPE_ALIASES: SchemaType = { name: 'displaySize', typeAnnotation: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, name: 'width', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -780,6 +877,7 @@ const NATIVE_MODULES_WITH_TYPE_ALIASES: SchemaType = { name: 'height', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, ], @@ -790,6 +888,7 @@ const NATIVE_MODULES_WITH_TYPE_ALIASES: SchemaType = { name: 'resizeMode', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { @@ -797,15 +896,16 @@ const NATIVE_MODULES_WITH_TYPE_ALIASES: SchemaType = { name: 'allowExternalStorage', typeAnnotation: { type: 'BooleanTypeAnnotation', + nullable: false, }, }, ], - type: 'ObjectTypeAnnotation', }, }, properties: [ { name: 'getConstants', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -814,11 +914,12 @@ const NATIVE_MODULES_WITH_TYPE_ALIASES: SchemaType = { properties: [], }, params: [], - optional: false, + nullable: false, }, }, { name: 'cropImage', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -827,15 +928,16 @@ const NATIVE_MODULES_WITH_TYPE_ALIASES: SchemaType = { }, params: [ { - nullable: false, + optional: false, name: 'cropData', typeAnnotation: { type: 'TypeAliasTypeAnnotation', name: 'Options', + nullable: false, }, }, ], - optional: false, + nullable: false, }, }, ], @@ -853,12 +955,14 @@ const REAL_MODULE_EXAMPLE: SchemaType = { aliases: { PhotoIdentifierImage: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, name: 'uri', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { @@ -866,6 +970,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'playableDuration', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -873,6 +978,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'width', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -880,6 +986,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'height', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -887,6 +994,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'isStored', typeAnnotation: { type: 'BooleanTypeAnnotation', + nullable: false, }, }, { @@ -894,18 +1002,21 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'filename', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, ], }, PhotoIdentifier: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, name: 'node', typeAnnotation: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, @@ -913,6 +1024,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { typeAnnotation: { type: 'TypeAliasTypeAnnotation', name: 'PhotoIdentifierImage', + nullable: false, }, }, { @@ -920,6 +1032,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'type', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { @@ -927,6 +1040,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'group_name', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { @@ -934,6 +1048,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'timestamp', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -941,12 +1056,14 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'location', typeAnnotation: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, name: 'longitude', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -954,6 +1071,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'latitude', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -961,6 +1079,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'altitude', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -968,6 +1087,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'heading', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -975,6 +1095,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'speed', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, ], @@ -987,15 +1108,18 @@ const REAL_MODULE_EXAMPLE: SchemaType = { }, PhotoIdentifiersPage: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, name: 'edges', typeAnnotation: { type: 'ArrayTypeAnnotation', + nullable: false, elementType: { type: 'TypeAliasTypeAnnotation', name: 'PhotoIdentifier', + nullable: false, }, }, }, @@ -1004,12 +1128,14 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'page_info', typeAnnotation: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, name: 'has_next_page', typeAnnotation: { type: 'BooleanTypeAnnotation', + nullable: false, }, }, { @@ -1017,6 +1143,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'start_cursor', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { @@ -1024,6 +1151,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'end_cursor', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, ], @@ -1033,12 +1161,14 @@ const REAL_MODULE_EXAMPLE: SchemaType = { }, GetPhotosParams: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, name: 'first', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -1046,6 +1176,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'after', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { @@ -1053,6 +1184,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'groupName', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { @@ -1060,6 +1192,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'groupTypes', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { @@ -1067,6 +1200,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'assetType', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { @@ -1074,6 +1208,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'maxSize', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -1081,8 +1216,10 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'mimeTypes', typeAnnotation: { type: 'ArrayTypeAnnotation', + nullable: false, elementType: { type: 'StringTypeAnnotation', + nullable: false, }, }, }, @@ -1092,6 +1229,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { properties: [ { name: 'getConstants', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -1100,78 +1238,86 @@ const REAL_MODULE_EXAMPLE: SchemaType = { properties: [], }, params: [], - optional: false, + nullable: false, }, }, { name: 'getPhotos', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { - type: 'GenericPromiseTypeAnnotation', + type: 'PromiseTypeAnnotation', nullable: false, }, params: [ { - nullable: false, + optional: false, name: 'params', typeAnnotation: { type: 'TypeAliasTypeAnnotation', name: 'GetPhotosParams', + nullable: false, }, }, ], - optional: false, + nullable: false, }, }, { name: 'saveToCameraRoll', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { - type: 'GenericPromiseTypeAnnotation', + type: 'PromiseTypeAnnotation', nullable: false, }, params: [ { - nullable: false, + optional: false, name: 'uri', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { - nullable: false, + optional: false, name: 'type', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, ], - optional: false, + nullable: false, }, }, { name: 'deletePhotos', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { - type: 'GenericPromiseTypeAnnotation', + type: 'PromiseTypeAnnotation', nullable: false, }, params: [ { name: 'assets', - nullable: false, + optional: false, typeAnnotation: { type: 'ArrayTypeAnnotation', + nullable: false, elementType: { type: 'StringTypeAnnotation', + nullable: false, }, }, }, ], - optional: false, + nullable: false, }, }, ], @@ -1185,6 +1331,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { properties: [ { name: 'openCameraDialog', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -1193,16 +1340,18 @@ const REAL_MODULE_EXAMPLE: SchemaType = { }, params: [ { - nullable: false, + optional: false, name: 'config', typeAnnotation: { type: 'ObjectTypeAnnotation', + nullable: false, properties: [ { optional: false, name: 'unmirrorFrontFacingCamera', typeAnnotation: { type: 'BooleanTypeAnnotation', + nullable: false, }, }, { @@ -1210,6 +1359,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'videoMode', typeAnnotation: { type: 'BooleanTypeAnnotation', + nullable: false, }, }, ], @@ -1217,20 +1367,32 @@ const REAL_MODULE_EXAMPLE: SchemaType = { }, { name: 'successCallback', - nullable: false, + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', + params: [], + returnTypeAnnotation: { + nullable: false, + type: 'VoidTypeAnnotation', + }, + nullable: false, }, }, { name: 'cancelCallback', - nullable: false, + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', + params: [], + returnTypeAnnotation: { + nullable: false, + type: 'VoidTypeAnnotation', + }, + nullable: false, }, }, ], - optional: false, + nullable: false, }, }, ], @@ -1248,6 +1410,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'column', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -1255,6 +1418,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'file', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { @@ -1262,6 +1426,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'lineNumber', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -1269,6 +1434,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'methodName', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { @@ -1276,10 +1442,12 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'collapse', typeAnnotation: { type: 'BooleanTypeAnnotation', + nullable: false, }, }, ], type: 'ObjectTypeAnnotation', + nullable: false, }, ExceptionData: { properties: [ @@ -1288,6 +1456,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'message', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { @@ -1295,6 +1464,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'originalMessage', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { @@ -1302,6 +1472,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'name', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { @@ -1309,6 +1480,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'componentStack', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { @@ -1316,9 +1488,11 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'stack', typeAnnotation: { type: 'ArrayTypeAnnotation', + nullable: false, elementType: { type: 'TypeAliasTypeAnnotation', name: 'StackFrame', + nullable: false, }, }, }, @@ -1327,6 +1501,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'id', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, { @@ -1334,6 +1509,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'isFatal', typeAnnotation: { type: 'BooleanTypeAnnotation', + nullable: false, }, }, { @@ -1341,15 +1517,18 @@ const REAL_MODULE_EXAMPLE: SchemaType = { name: 'extraData', typeAnnotation: { type: 'GenericObjectTypeAnnotation', + nullable: false, }, }, ], type: 'ObjectTypeAnnotation', + nullable: false, }, }, properties: [ { name: 'reportFatalException', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -1358,36 +1537,41 @@ const REAL_MODULE_EXAMPLE: SchemaType = { }, params: [ { - nullable: false, + optional: false, name: 'message', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { name: 'stack', - nullable: false, + optional: false, typeAnnotation: { type: 'ArrayTypeAnnotation', + nullable: false, elementType: { type: 'TypeAliasTypeAnnotation', name: 'StackFrame', + nullable: false, }, }, }, { - nullable: false, + optional: false, name: 'exceptionId', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, ], - optional: false, + nullable: false, }, }, { name: 'reportSoftException', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -1396,36 +1580,41 @@ const REAL_MODULE_EXAMPLE: SchemaType = { }, params: [ { - nullable: false, + optional: false, name: 'message', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { name: 'stack', - nullable: false, + optional: false, typeAnnotation: { type: 'ArrayTypeAnnotation', + nullable: false, elementType: { type: 'TypeAliasTypeAnnotation', name: 'StackFrame', + nullable: false, }, }, }, { - nullable: false, + optional: false, name: 'exceptionId', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, ], - optional: false, + nullable: false, }, }, { name: 'reportException', + optional: true, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -1434,19 +1623,21 @@ const REAL_MODULE_EXAMPLE: SchemaType = { }, params: [ { - nullable: false, + optional: false, name: 'data', typeAnnotation: { type: 'TypeAliasTypeAnnotation', name: 'ExceptionData', + nullable: false, }, }, ], - optional: true, + nullable: true, }, }, { name: 'updateExceptionMessage', + optional: false, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -1455,36 +1646,41 @@ const REAL_MODULE_EXAMPLE: SchemaType = { }, params: [ { - nullable: false, + optional: false, name: 'message', typeAnnotation: { type: 'StringTypeAnnotation', + nullable: false, }, }, { name: 'stack', - nullable: false, + optional: false, typeAnnotation: { type: 'ArrayTypeAnnotation', + nullable: false, elementType: { type: 'TypeAliasTypeAnnotation', name: 'StackFrame', + nullable: false, }, }, }, { - nullable: false, + optional: false, name: 'exceptionId', typeAnnotation: { type: 'NumberTypeAnnotation', + nullable: false, }, }, ], - optional: false, + nullable: false, }, }, { name: 'dismissRedbox', + optional: true, typeAnnotation: { type: 'FunctionTypeAnnotation', returnTypeAnnotation: { @@ -1492,7 +1688,7 @@ const REAL_MODULE_EXAMPLE: SchemaType = { type: 'VoidTypeAnnotation', }, params: [], - optional: true, + nullable: true, }, }, ], diff --git a/packages/react-native-codegen/src/generators/modules/__test_fixtures__/structFixtures.js b/packages/react-native-codegen/src/generators/modules/__test_fixtures__/structFixtures.js deleted file mode 100644 index 2de452b87c2c39..00000000000000 --- a/packages/react-native-codegen/src/generators/modules/__test_fixtures__/structFixtures.js +++ /dev/null @@ -1,218 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict-local - * @format - */ - -'use strict'; - -import type {ObjectParamTypeAnnotation} from '../../../CodegenSchema.js'; -const SIMPLE_STRUCT: $ReadOnlyArray< - $ReadOnly<{| - name: string, - object: $ReadOnly<{| - type: 'ObjectTypeAnnotation', - properties: $ReadOnlyArray, - |}>, - |}>, -> = [ - { - name: 'SpecSampleFuncReturnType', - object: { - type: 'ObjectTypeAnnotation', - properties: [ - { - optional: false, - name: 'a', - typeAnnotation: { - type: 'BooleanTypeAnnotation', - }, - }, - { - optional: false, - name: 'b', - typeAnnotation: { - type: 'NumberTypeAnnotation', - }, - }, - { - optional: false, - name: 'c', - typeAnnotation: { - type: 'StringTypeAnnotation', - }, - }, - { - optional: false, - name: 'd', - typeAnnotation: { - type: 'ObjectTypeAnnotation', - properties: [ - { - optional: false, - name: 'e', - typeAnnotation: { - type: 'BooleanTypeAnnotation', - }, - }, - { - optional: false, - name: 'f', - typeAnnotation: { - type: 'NumberTypeAnnotation', - }, - }, - { - optional: false, - name: 'g', - typeAnnotation: { - type: 'ObjectTypeAnnotation', - properties: [ - { - optional: false, - name: 'h', - typeAnnotation: { - type: 'BooleanTypeAnnotation', - }, - }, - { - optional: false, - name: 'i', - typeAnnotation: { - type: 'NumberTypeAnnotation', - }, - }, - { - optional: false, - name: 'j', - typeAnnotation: { - type: 'StringTypeAnnotation', - }, - }, - ], - }, - }, - ], - }, - }, - { - optional: false, - name: 'k', - typeAnnotation: { - type: 'ReservedFunctionValueTypeAnnotation', - name: 'RootTag', - }, - }, - ], - }, - }, -]; - -const SIMPLE_CONSTANTS: $ReadOnlyArray< - $ReadOnly<{| - name: string, - object: $ReadOnly<{| - type: 'ObjectTypeAnnotation', - properties: $ReadOnlyArray, - |}>, - |}>, -> = [ - { - name: 'SpecGetConstantsReturnType', - object: { - type: 'ObjectTypeAnnotation', - properties: [ - { - optional: false, - name: 'a', - typeAnnotation: { - type: 'BooleanTypeAnnotation', - }, - }, - { - optional: false, - name: 'b', - typeAnnotation: { - type: 'NumberTypeAnnotation', - }, - }, - { - optional: false, - name: 'c', - typeAnnotation: { - type: 'StringTypeAnnotation', - }, - }, - { - optional: false, - name: 'd', - typeAnnotation: { - type: 'ObjectTypeAnnotation', - properties: [ - { - optional: false, - name: 'e', - typeAnnotation: { - type: 'BooleanTypeAnnotation', - }, - }, - { - optional: false, - name: 'f', - typeAnnotation: { - type: 'NumberTypeAnnotation', - }, - }, - { - optional: false, - name: 'g', - typeAnnotation: { - type: 'ObjectTypeAnnotation', - properties: [ - { - optional: false, - name: 'h', - typeAnnotation: { - type: 'BooleanTypeAnnotation', - }, - }, - { - optional: false, - name: 'i', - typeAnnotation: { - type: 'NumberTypeAnnotation', - }, - }, - { - optional: false, - name: 'j', - typeAnnotation: { - type: 'StringTypeAnnotation', - }, - }, - ], - }, - }, - ], - }, - }, - { - optional: false, - name: 'k', - typeAnnotation: { - type: 'ReservedFunctionValueTypeAnnotation', - name: 'RootTag', - }, - }, - ], - }, - }, -]; -module.exports = { - SIMPLE_STRUCT, - SIMPLE_CONSTANTS, -}; diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleHObjCpp-test.js b/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleHObjCpp-test.js index b41441cb2daf7c..3c4943971857b0 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleHObjCpp-test.js +++ b/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleHObjCpp-test.js @@ -12,7 +12,7 @@ 'use strict'; const fixtures = require('../__test_fixtures__/fixtures.js'); -const generator = require('../GenerateModuleHObjCpp.js'); +const generator = require('../GenerateModuleObjCpp'); describe('GenerateModuleHObjCpp', () => { Object.keys(fixtures) @@ -21,8 +21,9 @@ describe('GenerateModuleHObjCpp', () => { const fixture = fixtures[fixtureName]; it(`can generate fixture ${fixtureName}`, () => { + const output = generator.generate(fixtureName, fixture, 'SampleSpec'); expect( - generator.generate(fixtureName, fixture, 'SampleSpec'), + new Map([['SampleSpec.h', output.get('SampleSpec.h')]]), ).toMatchSnapshot(); }); }); diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleMm-test.js b/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleMm-test.js index 5bfd8c79e07682..ec8f4842a106bf 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleMm-test.js +++ b/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleMm-test.js @@ -12,7 +12,7 @@ 'use strict'; const fixtures = require('../__test_fixtures__/fixtures.js'); -const generator = require('../GenerateModuleMm.js'); +const generator = require('../GenerateModuleObjCpp'); describe('GenerateModuleMm', () => { Object.keys(fixtures) @@ -21,8 +21,11 @@ describe('GenerateModuleMm', () => { const fixture = fixtures[fixtureName]; it(`can generate fixture ${fixtureName}`, () => { + const output = generator.generate(fixtureName, fixture, 'SampleSpec'); expect( - generator.generate(fixtureName, fixture, 'SampleSpec'), + new Map([ + ['SampleSpec-generated.mm', output.get('SampleSpec-generated.mm')], + ]), ).toMatchSnapshot(); }); }); diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/GenerateStructs-test.js b/packages/react-native-codegen/src/generators/modules/__tests__/GenerateStructs-test.js deleted file mode 100644 index a66b5ff7e4c28c..00000000000000 --- a/packages/react-native-codegen/src/generators/modules/__tests__/GenerateStructs-test.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @emails oncall+react_native - * @flow strict-local - * @format - */ - -'use strict'; - -const fixtures = require('../__test_fixtures__/structFixtures.js'); -const generator = require('../ObjCppUtils/GenerateStructs.js'); - -describe('GenerateStructs', () => { - Object.keys(fixtures) - .sort() - .forEach(fixtureName => { - const fixture = fixtures[fixtureName]; - - it(`can generate fixture ${fixtureName}`, () => { - expect( - generator - .translateObjectsForStructs(fixture, fixtureName, {}) - .replace(/::_MODULE_NAME_::/g, 'SampleTurboModule'), - ).toMatchSnapshot(); - }); - }); -}); diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap index fbdcb15bae2c68..4e55a6343121fe 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap @@ -2,7 +2,8 @@ exports[`GenerateModuleH can generate fixture COMPLEX_OBJECTS 1`] = ` Map { - "NativeModules.h" => "/** + "NativeModules.h" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the @@ -38,7 +39,8 @@ virtual void getArrays(jsi::Runtime &rt, const jsi::Object &options) = 0; exports[`GenerateModuleH can generate fixture EMPTY_NATIVE_MODULES 1`] = ` Map { - "NativeModules.h" => "/** + "NativeModules.h" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the @@ -71,7 +73,8 @@ public: exports[`GenerateModuleH can generate fixture NATIVE_MODULES_WITH_TYPE_ALIASES 1`] = ` Map { - "NativeModules.h" => "/** + "NativeModules.h" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the @@ -105,7 +108,8 @@ virtual void cropImage(jsi::Runtime &rt, const jsi::Object &cropData) = 0; exports[`GenerateModuleH can generate fixture REAL_MODULE_EXAMPLE 1`] = ` Map { - "NativeModules.h" => "/** + "NativeModules.h" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the @@ -163,7 +167,8 @@ virtual void dismissRedbox(jsi::Runtime &rt) = 0; exports[`GenerateModuleH can generate fixture SIMPLE_NATIVE_MODULES 1`] = ` Map { - "NativeModules.h" => "/** + "NativeModules.h" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the @@ -206,7 +211,8 @@ virtual jsi::Value getValueWithPromise(jsi::Runtime &rt, bool error) = 0; exports[`GenerateModuleH can generate fixture TWO_MODULES_DIFFERENT_FILES 1`] = ` Map { - "NativeModules.h" => "/** + "NativeModules.h" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the @@ -249,7 +255,8 @@ virtual void voidFunc(jsi::Runtime &rt) = 0; exports[`GenerateModuleH can generate fixture TWO_MODULES_SAME_FILE 1`] = ` Map { - "NativeModules.h" => "/** + "NativeModules.h" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleHObjCpp-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleHObjCpp-test.js.snap index 939b9bcea8d2f2..2d7f078c5257b9 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleHObjCpp-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleHObjCpp-test.js.snap @@ -34,24 +34,6 @@ Map { -namespace JS { - namespace NativeSampleTurboModule { - struct SpecOptionalsAOptionalObjectProperty { - double x() const; - double y() const; - - SpecOptionalsAOptionalObjectProperty(NSDictionary *const v) : _v(v) {} - private: - NSDictionary *_v; - }; - } -} - -@interface RCTCxxConvert (NativeSampleTurboModule_SpecOptionalsAOptionalObjectProperty) -+ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecOptionalsAOptionalObjectProperty:(id)json; -@end - - namespace JS { namespace NativeSampleTurboModule { struct SpecDifficultAE { @@ -74,57 +56,38 @@ namespace JS { namespace JS { namespace NativeSampleTurboModule { - struct SpecGetArraysOptionsArrayOfObjectsElement { - double numberProperty() const; - - SpecGetArraysOptionsArrayOfObjectsElement(NSDictionary *const v) : _v(v) {} - private: - NSDictionary *_v; - }; - } -} - -@interface RCTCxxConvert (NativeSampleTurboModule_SpecGetArraysOptionsArrayOfObjectsElement) -+ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecGetArraysOptionsArrayOfObjectsElement:(id)json; -@end - - -namespace JS { - namespace NativeSampleTurboModule { - struct SpecGetArraysOptions { - facebook::react::LazyVector arrayOfNumbers() const; - folly::Optional> optionalArrayOfNumbers() const; - facebook::react::LazyVector arrayOfStrings() const; - folly::Optional> optionalArrayOfStrings() const; - facebook::react::LazyVector arrayOfObjects() const; + struct SpecDifficultA { + bool D() const; + JS::NativeSampleTurboModule::SpecDifficultAE E() const; + NSString *F() const; - SpecGetArraysOptions(NSDictionary *const v) : _v(v) {} + SpecDifficultA(NSDictionary *const v) : _v(v) {} private: NSDictionary *_v; }; } } -@interface RCTCxxConvert (NativeSampleTurboModule_SpecGetArraysOptions) -+ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecGetArraysOptions:(id)json; +@interface RCTCxxConvert (NativeSampleTurboModule_SpecDifficultA) ++ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecDifficultA:(id)json; @end namespace JS { namespace NativeSampleTurboModule { - struct SpecOptionalMethodExtrasElement { - NSString *key() const; - id value() const; + struct SpecOptionalsAOptionalObjectProperty { + double x() const; + double y() const; - SpecOptionalMethodExtrasElement(NSDictionary *const v) : _v(v) {} + SpecOptionalsAOptionalObjectProperty(NSDictionary *const v) : _v(v) {} private: NSDictionary *_v; }; } } -@interface RCTCxxConvert (NativeSampleTurboModule_SpecOptionalMethodExtrasElement) -+ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecOptionalMethodExtrasElement:(id)json; +@interface RCTCxxConvert (NativeSampleTurboModule_SpecOptionalsAOptionalObjectProperty) ++ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecOptionalsAOptionalObjectProperty:(id)json; @end @@ -151,83 +114,128 @@ namespace JS { namespace JS { namespace NativeSampleTurboModule { - struct SpecDifficultReturnType { - bool D() const; - double E() const; - NSString *F() const; + struct SpecGetArraysOptionsArrayOfObjectsElement { + double numberProperty() const; - SpecDifficultReturnType(NSDictionary *const v) : _v(v) {} + SpecGetArraysOptionsArrayOfObjectsElement(NSDictionary *const v) : _v(v) {} private: NSDictionary *_v; }; } } -@interface RCTCxxConvert (NativeSampleTurboModule_SpecDifficultReturnType) -+ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecDifficultReturnType:(id)json; +@interface RCTCxxConvert (NativeSampleTurboModule_SpecGetArraysOptionsArrayOfObjectsElement) ++ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecGetArraysOptionsArrayOfObjectsElement:(id)json; @end namespace JS { namespace NativeSampleTurboModule { - struct SpecDifficultA { - bool D() const; - JS::NativeSampleTurboModule::SpecDifficultAE E() const; - NSString *F() const; + struct SpecGetArraysOptions { + facebook::react::LazyVector arrayOfNumbers() const; + folly::Optional> optionalArrayOfNumbers() const; + facebook::react::LazyVector arrayOfStrings() const; + folly::Optional> optionalArrayOfStrings() const; + facebook::react::LazyVector arrayOfObjects() const; - SpecDifficultA(NSDictionary *const v) : _v(v) {} + SpecGetArraysOptions(NSDictionary *const v) : _v(v) {} private: NSDictionary *_v; }; } } -@interface RCTCxxConvert (NativeSampleTurboModule_SpecDifficultA) -+ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecDifficultA:(id)json; +@interface RCTCxxConvert (NativeSampleTurboModule_SpecGetArraysOptions) ++ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecGetArraysOptions:(id)json; @end -inline bool JS::NativeSampleTurboModule::SpecDifficultA::D() const +@protocol NativeSampleTurboModuleSpec + +- (NSDictionary *)difficult:(JS::NativeSampleTurboModule::SpecDifficultA &)A; +- (void)optionals:(JS::NativeSampleTurboModule::SpecOptionalsA &)A; +- (void)optionalMethod:(NSDictionary *)options + callback:(RCTResponseSenderBlock)callback + extras:(NSArray *)extras; +- (void)getArrays:(JS::NativeSampleTurboModule::SpecGetArraysOptions &)options; + +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'SampleTurboModule' + */ + class JSI_EXPORT NativeSampleTurboModuleSpecJSI : public ObjCTurboModule { + public: + NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + + + +inline bool JS::NativeSampleTurboModule::SpecDifficultAE::D() const { id const p = _v[@\\"D\\"]; return RCTBridgingToBool(p); } -inline JS::NativeSampleTurboModule::SpecDifficultAE JS::NativeSampleTurboModule::SpecDifficultA::E() const +inline double JS::NativeSampleTurboModule::SpecDifficultAE::E() const { id const p = _v[@\\"E\\"]; - return JS::NativeSampleTurboModule::SpecDifficultAE(p); + return RCTBridgingToDouble(p); } -inline NSString *JS::NativeSampleTurboModule::SpecDifficultA::F() const +inline NSString *JS::NativeSampleTurboModule::SpecDifficultAE::F() const { id const p = _v[@\\"F\\"]; return RCTBridgingToString(p); } -inline bool JS::NativeSampleTurboModule::SpecDifficultReturnType::D() const +inline double JS::NativeSampleTurboModule::SpecDifficultAE::id_() const +{ + id const p = _v[@\\"id_\\"]; + return RCTBridgingToDouble(p); +} + + +inline bool JS::NativeSampleTurboModule::SpecDifficultA::D() const { id const p = _v[@\\"D\\"]; return RCTBridgingToBool(p); } -inline double JS::NativeSampleTurboModule::SpecDifficultReturnType::E() const +inline JS::NativeSampleTurboModule::SpecDifficultAE JS::NativeSampleTurboModule::SpecDifficultA::E() const { id const p = _v[@\\"E\\"]; - return RCTBridgingToDouble(p); + return JS::NativeSampleTurboModule::SpecDifficultAE(p); } -inline NSString *JS::NativeSampleTurboModule::SpecDifficultReturnType::F() const +inline NSString *JS::NativeSampleTurboModule::SpecDifficultA::F() const { id const p = _v[@\\"F\\"]; return RCTBridgingToString(p); } +inline double JS::NativeSampleTurboModule::SpecOptionalsAOptionalObjectProperty::x() const +{ + id const p = _v[@\\"x\\"]; + return RCTBridgingToDouble(p); +} + + +inline double JS::NativeSampleTurboModule::SpecOptionalsAOptionalObjectProperty::y() const +{ + id const p = _v[@\\"y\\"]; + return RCTBridgingToDouble(p); +} + + inline folly::Optional JS::NativeSampleTurboModule::SpecOptionalsA::optionalNumberProperty() const { id const p = _v[@\\"optionalNumberProperty\\"]; @@ -263,17 +271,10 @@ inline folly::Optional JS::NativeSampleTurboModule::SpecOptionalsA::option } -inline NSString *JS::NativeSampleTurboModule::SpecOptionalMethodExtrasElement::key() const -{ - id const p = _v[@\\"key\\"]; - return RCTBridgingToString(p); -} - - -inline id JS::NativeSampleTurboModule::SpecOptionalMethodExtrasElement::value() const +inline double JS::NativeSampleTurboModule::SpecGetArraysOptionsArrayOfObjectsElement::numberProperty() const { - id const p = _v[@\\"value\\"]; - return p; + id const p = _v[@\\"numberProperty\\"]; + return RCTBridgingToDouble(p); } @@ -305,84 +306,12 @@ inline folly::Optional> JS::NativeSample } -inline facebook::react::LazyVector JS::NativeSampleTurboModule::SpecGetArraysOptions::arrayOfObjects() const +inline facebook::react::LazyVector JS::NativeSampleTurboModule::SpecGetArraysOptions::arrayOfObjects() const { id const p = _v[@\\"arrayOfObjects\\"]; - return RCTBridgingToVec(p, ^JS::NativeSampleTurboModule::arrayOfObjectsElement(id itemValue_0) { return JS::NativeSampleTurboModule::arrayOfObjectsElement(itemValue_0); }); -} - - -inline double JS::NativeSampleTurboModule::SpecGetArraysOptionsArrayOfObjectsElement::numberProperty() const -{ - id const p = _v[@\\"numberProperty\\"]; - return RCTBridgingToDouble(p); -} - - -inline bool JS::NativeSampleTurboModule::SpecDifficultAE::D() const -{ - id const p = _v[@\\"D\\"]; - return RCTBridgingToBool(p); -} - - -inline double JS::NativeSampleTurboModule::SpecDifficultAE::E() const -{ - id const p = _v[@\\"E\\"]; - return RCTBridgingToDouble(p); -} - - -inline NSString *JS::NativeSampleTurboModule::SpecDifficultAE::F() const -{ - id const p = _v[@\\"F\\"]; - return RCTBridgingToString(p); -} - - -inline double JS::NativeSampleTurboModule::SpecDifficultAE::id_() const -{ - id const p = _v[@\\"id_\\"]; - return RCTBridgingToDouble(p); -} - - -inline double JS::NativeSampleTurboModule::SpecOptionalsAOptionalObjectProperty::x() const -{ - id const p = _v[@\\"x\\"]; - return RCTBridgingToDouble(p); -} - - -inline double JS::NativeSampleTurboModule::SpecOptionalsAOptionalObjectProperty::y() const -{ - id const p = _v[@\\"y\\"]; - return RCTBridgingToDouble(p); + return RCTBridgingToVec(p, ^JS::NativeSampleTurboModule::SpecGetArraysOptionsArrayOfObjectsElement(id itemValue_0) { return JS::NativeSampleTurboModule::SpecGetArraysOptionsArrayOfObjectsElement(p); }); } - - -@protocol NativeSampleTurboModuleSpec -- (NSDictionary *) difficult:(JS::NativeSampleTurboModule::SpecDifficultA &)A; -- (void) optionals:(JS::NativeSampleTurboModule::SpecOptionalsA &)A; -- (void) optionalMethod:(NSDictionary *)options - callback:(RCTResponseSenderBlock)callback - extras:(NSArray * _Nullable)extras; -- (void) getArrays:(JS::NativeSampleTurboModule::SpecGetArraysOptions &)options; -@end - - -namespace facebook { - namespace react { - /** - * ObjC++ class for module 'SampleTurboModule' - */ - class JSI_EXPORT NativeSampleTurboModuleSpecJSI : public ObjCTurboModule { - public: - NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); - }; - } // namespace react -} // namespace facebook ", } `; @@ -421,24 +350,25 @@ Map { - - @protocol NativeSampleTurboModuleSpec -@end +@end namespace facebook { namespace react { /** - * ObjC++ class for module 'SampleTurboModule' - */ + * ObjC++ class for module 'SampleTurboModule' + */ class JSI_EXPORT NativeSampleTurboModuleSpecJSI : public ObjCTurboModule { public: NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); }; } // namespace react } // namespace facebook + + + ", } `; @@ -479,19 +409,19 @@ Map { namespace JS { namespace NativeAliasTurboModule { - struct OptionsDisplaySize { - double width() const; - double height() const; + struct OptionsOffset { + double x() const; + double y() const; - OptionsDisplaySize(NSDictionary *const v) : _v(v) {} + OptionsOffset(NSDictionary *const v) : _v(v) {} private: NSDictionary *_v; }; } } -@interface RCTCxxConvert (NativeAliasTurboModule_OptionsDisplaySize) -+ (RCTManagedPointer *)JS_NativeAliasTurboModule_OptionsDisplaySize:(id)json; +@interface RCTCxxConvert (NativeAliasTurboModule_OptionsOffset) ++ (RCTManagedPointer *)JS_NativeAliasTurboModule_OptionsOffset:(id)json; @end @@ -515,19 +445,19 @@ namespace JS { namespace JS { namespace NativeAliasTurboModule { - struct OptionsOffset { - double x() const; - double y() const; + struct OptionsDisplaySize { + double width() const; + double height() const; - OptionsOffset(NSDictionary *const v) : _v(v) {} + OptionsDisplaySize(NSDictionary *const v) : _v(v) {} private: NSDictionary *_v; }; } } -@interface RCTCxxConvert (NativeAliasTurboModule_OptionsOffset) -+ (RCTManagedPointer *)JS_NativeAliasTurboModule_OptionsOffset:(id)json; +@interface RCTCxxConvert (NativeAliasTurboModule_OptionsDisplaySize) ++ (RCTManagedPointer *)JS_NativeAliasTurboModule_OptionsDisplaySize:(id)json; @end @@ -551,40 +481,24 @@ namespace JS { + (RCTManagedPointer *)JS_NativeAliasTurboModule_Options:(id)json; @end -inline JS::NativeAliasTurboModule::OptionsOffset JS::NativeAliasTurboModule::Options::offset() const -{ - id const p = _v[@\\"offset\\"]; - return JS::NativeAliasTurboModule::OptionsOffset(p); -} +@protocol NativeAliasTurboModuleSpec +- (void)cropImage:(JS::NativeAliasTurboModule::Options &)cropData; -inline JS::NativeAliasTurboModule::OptionsSize JS::NativeAliasTurboModule::Options::size() const -{ - id const p = _v[@\\"size\\"]; - return JS::NativeAliasTurboModule::OptionsSize(p); -} +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'AliasTurboModule' + */ + class JSI_EXPORT NativeAliasTurboModuleSpecJSI : public ObjCTurboModule { + public: + NativeAliasTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook -inline folly::Optional JS::NativeAliasTurboModule::Options::displaySize() const -{ - id const p = _v[@\\"displaySize\\"]; - return (p == nil ? folly::none : folly::make_optional(JS::NativeAliasTurboModule::OptionsDisplaySize(p))); -} - - -inline NSString *JS::NativeAliasTurboModule::Options::resizeMode() const -{ - id const p = _v[@\\"resizeMode\\"]; - return RCTBridgingToString(p); -} - - -inline folly::Optional JS::NativeAliasTurboModule::Options::allowExternalStorage() const -{ - id const p = _v[@\\"allowExternalStorage\\"]; - return RCTBridgingToOptionalBool(p); -} - inline double JS::NativeAliasTurboModule::OptionsOffset::x() const { @@ -628,24 +542,40 @@ inline double JS::NativeAliasTurboModule::OptionsDisplaySize::height() const } +inline JS::NativeAliasTurboModule::OptionsOffset JS::NativeAliasTurboModule::Options::offset() const +{ + id const p = _v[@\\"offset\\"]; + return JS::NativeAliasTurboModule::OptionsOffset(p); +} -@protocol NativeAliasTurboModuleSpec -- (void) cropImage:(JS::NativeAliasTurboModule::Options &)cropData; -@end +inline JS::NativeAliasTurboModule::OptionsSize JS::NativeAliasTurboModule::Options::size() const +{ + id const p = _v[@\\"size\\"]; + return JS::NativeAliasTurboModule::OptionsSize(p); +} -namespace facebook { - namespace react { - /** - * ObjC++ class for module 'AliasTurboModule' - */ - class JSI_EXPORT NativeAliasTurboModuleSpecJSI : public ObjCTurboModule { - public: - NativeAliasTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); - }; - } // namespace react -} // namespace facebook +inline folly::Optional JS::NativeAliasTurboModule::Options::displaySize() const +{ + id const p = _v[@\\"displaySize\\"]; + return (p == nil ? folly::none : folly::make_optional(JS::NativeAliasTurboModule::OptionsDisplaySize(p))); +} + + +inline NSString *JS::NativeAliasTurboModule::Options::resizeMode() const +{ + id const p = _v[@\\"resizeMode\\"]; + return RCTBridgingToOptionalString(p); +} + + +inline folly::Optional JS::NativeAliasTurboModule::Options::allowExternalStorage() const +{ + id const p = _v[@\\"allowExternalStorage\\"]; + return RCTBridgingToOptionalBool(p); +} + ", } `; @@ -686,143 +616,164 @@ Map { namespace JS { namespace NativeCameraRollManager { - struct PhotoIdentifierNodeLocation { - double longitude() const; - double latitude() const; - folly::Optional altitude() const; - folly::Optional heading() const; - folly::Optional speed() const; - - PhotoIdentifierNodeLocation(NSDictionary *const v) : _v(v) {} + struct GetPhotosParams { + double first() const; + NSString *after() const; + NSString *groupName() const; + NSString *groupTypes() const; + NSString *assetType() const; + folly::Optional maxSize() const; + folly::Optional> mimeTypes() const; + + GetPhotosParams(NSDictionary *const v) : _v(v) {} private: NSDictionary *_v; }; } } -@interface RCTCxxConvert (NativeCameraRollManager_PhotoIdentifierNodeLocation) -+ (RCTManagedPointer *)JS_NativeCameraRollManager_PhotoIdentifierNodeLocation:(id)json; +@interface RCTCxxConvert (NativeCameraRollManager_GetPhotosParams) ++ (RCTManagedPointer *)JS_NativeCameraRollManager_GetPhotosParams:(id)json; @end +@protocol NativeCameraRollManagerSpec -namespace JS { - namespace NativeCameraRollManager { - struct PhotoIdentifierNode { - JS::NativeCameraRollManager::PhotoIdentifierImage image() const; - NSString *type() const; - NSString *group_name() const; - double timestamp() const; - JS::NativeCameraRollManager::PhotoIdentifierNodeLocation location() const; - - PhotoIdentifierNode(NSDictionary *const v) : _v(v) {} - private: - NSDictionary *_v; - }; - } -} +- (void)getPhotos:(JS::NativeCameraRollManager::GetPhotosParams &)params + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)saveToCameraRoll:(NSString *)uri + type:(NSString *)type + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; +- (void)deletePhotos:(NSArray *)assets + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; -@interface RCTCxxConvert (NativeCameraRollManager_PhotoIdentifierNode) -+ (RCTManagedPointer *)JS_NativeCameraRollManager_PhotoIdentifierNode:(id)json; @end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'CameraRollManager' + */ + class JSI_EXPORT NativeCameraRollManagerSpecJSI : public ObjCTurboModule { + public: + NativeCameraRollManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook + namespace JS { - namespace NativeCameraRollManager { - struct PhotoIdentifiersPagePage_info { - bool has_next_page() const; - NSString *start_cursor() const; - NSString *end_cursor() const; + namespace NativeExceptionsManager { + struct StackFrame { + folly::Optional column() const; + NSString *file() const; + folly::Optional lineNumber() const; + NSString *methodName() const; + folly::Optional collapse() const; - PhotoIdentifiersPagePage_info(NSDictionary *const v) : _v(v) {} + StackFrame(NSDictionary *const v) : _v(v) {} private: NSDictionary *_v; }; } } -@interface RCTCxxConvert (NativeCameraRollManager_PhotoIdentifiersPagePage_info) -+ (RCTManagedPointer *)JS_NativeCameraRollManager_PhotoIdentifiersPagePage_info:(id)json; +@interface RCTCxxConvert (NativeExceptionsManager_StackFrame) ++ (RCTManagedPointer *)JS_NativeExceptionsManager_StackFrame:(id)json; @end namespace JS { - namespace NativeCameraRollManager { - struct PhotoIdentifierImage { - NSString *uri() const; - double playableDuration() const; - double width() const; - double height() const; - folly::Optional isStored() const; - NSString *filename() const; + namespace NativeExceptionsManager { + struct ExceptionData { + NSString *message() const; + NSString *originalMessage() const; + NSString *name() const; + NSString *componentStack() const; + facebook::react::LazyVector stack() const; + double id_() const; + bool isFatal() const; + id _Nullable extraData() const; - PhotoIdentifierImage(NSDictionary *const v) : _v(v) {} + ExceptionData(NSDictionary *const v) : _v(v) {} private: NSDictionary *_v; }; } } -@interface RCTCxxConvert (NativeCameraRollManager_PhotoIdentifierImage) -+ (RCTManagedPointer *)JS_NativeCameraRollManager_PhotoIdentifierImage:(id)json; +@interface RCTCxxConvert (NativeExceptionsManager_ExceptionData) ++ (RCTManagedPointer *)JS_NativeExceptionsManager_ExceptionData:(id)json; @end +@protocol NativeExceptionsManagerSpec -namespace JS { - namespace NativeCameraRollManager { - struct PhotoIdentifier { - JS::NativeCameraRollManager::PhotoIdentifierNode node() const; +- (void)reportFatalException:(NSString *)message + stack:(NSArray *)stack + exceptionId:(double)exceptionId; +- (void)reportSoftException:(NSString *)message + stack:(NSArray *)stack + exceptionId:(double)exceptionId; +- (void)reportException:(JS::NativeExceptionsManager::ExceptionData &)data; +- (void)updateExceptionMessage:(NSString *)message + stack:(NSArray *)stack + exceptionId:(double)exceptionId; +- (void)dismissRedbox; - PhotoIdentifier(NSDictionary *const v) : _v(v) {} - private: - NSDictionary *_v; +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'ExceptionsManager' + */ + class JSI_EXPORT NativeExceptionsManagerSpecJSI : public ObjCTurboModule { + public: + NativeExceptionsManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms); }; - } -} + } // namespace react +} // namespace facebook -@interface RCTCxxConvert (NativeCameraRollManager_PhotoIdentifier) -+ (RCTManagedPointer *)JS_NativeCameraRollManager_PhotoIdentifier:(id)json; -@end namespace JS { - namespace NativeCameraRollManager { - struct PhotoIdentifiersPage { - facebook::react::LazyVector edges() const; - JS::NativeCameraRollManager::PhotoIdentifiersPagePage_info page_info() const; + namespace NativeImagePickerIOS { + struct SpecOpenCameraDialogConfig { + bool unmirrorFrontFacingCamera() const; + bool videoMode() const; - PhotoIdentifiersPage(NSDictionary *const v) : _v(v) {} + SpecOpenCameraDialogConfig(NSDictionary *const v) : _v(v) {} private: NSDictionary *_v; }; } } -@interface RCTCxxConvert (NativeCameraRollManager_PhotoIdentifiersPage) -+ (RCTManagedPointer *)JS_NativeCameraRollManager_PhotoIdentifiersPage:(id)json; +@interface RCTCxxConvert (NativeImagePickerIOS_SpecOpenCameraDialogConfig) ++ (RCTManagedPointer *)JS_NativeImagePickerIOS_SpecOpenCameraDialogConfig:(id)json; @end +@protocol NativeImagePickerIOSSpec -namespace JS { - namespace NativeCameraRollManager { - struct GetPhotosParams { - double first() const; - NSString *after() const; - NSString *groupName() const; - NSString *groupTypes() const; - NSString *assetType() const; - folly::Optional maxSize() const; - folly::Optional> mimeTypes() const; +- (void)openCameraDialog:(JS::NativeImagePickerIOS::SpecOpenCameraDialogConfig &)config + successCallback:(RCTResponseSenderBlock)successCallback + cancelCallback:(RCTResponseSenderBlock)cancelCallback; - GetPhotosParams(NSDictionary *const v) : _v(v) {} - private: - NSDictionary *_v; +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'ImagePickerIOS' + */ + class JSI_EXPORT NativeImagePickerIOSSpecJSI : public ObjCTurboModule { + public: + NativeImagePickerIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms); }; - } -} + } // namespace react +} // namespace facebook + -@interface RCTCxxConvert (NativeCameraRollManager_GetPhotosParams) -+ (RCTManagedPointer *)JS_NativeCameraRollManager_GetPhotosParams:(id)json; -@end inline double JS::NativeCameraRollManager::GetPhotosParams::first() const { @@ -834,28 +785,28 @@ inline double JS::NativeCameraRollManager::GetPhotosParams::first() const inline NSString *JS::NativeCameraRollManager::GetPhotosParams::after() const { id const p = _v[@\\"after\\"]; - return RCTBridgingToString(p); + return RCTBridgingToOptionalString(p); } inline NSString *JS::NativeCameraRollManager::GetPhotosParams::groupName() const { id const p = _v[@\\"groupName\\"]; - return RCTBridgingToString(p); + return RCTBridgingToOptionalString(p); } inline NSString *JS::NativeCameraRollManager::GetPhotosParams::groupTypes() const { id const p = _v[@\\"groupTypes\\"]; - return RCTBridgingToString(p); + return RCTBridgingToOptionalString(p); } inline NSString *JS::NativeCameraRollManager::GetPhotosParams::assetType() const { id const p = _v[@\\"assetType\\"]; - return RCTBridgingToString(p); + return RCTBridgingToOptionalString(p); } @@ -873,220 +824,40 @@ inline folly::Optional> JS::NativeCamera } -inline facebook::react::LazyVector JS::NativeCameraRollManager::PhotoIdentifiersPage::edges() const -{ - id const p = _v[@\\"edges\\"]; - return RCTBridgingToVec(p, ^JS::NativeCameraRollManager::PhotoIdentifier(id itemValue_0) { return JS::NativeCameraRollManager::PhotoIdentifier(itemValue_0); }); -} - - -inline JS::NativeCameraRollManager::PhotoIdentifiersPagePage_info JS::NativeCameraRollManager::PhotoIdentifiersPage::page_info() const -{ - id const p = _v[@\\"page_info\\"]; - return JS::NativeCameraRollManager::PhotoIdentifiersPagePage_info(p); -} - - -inline JS::NativeCameraRollManager::PhotoIdentifierNode JS::NativeCameraRollManager::PhotoIdentifier::node() const -{ - id const p = _v[@\\"node\\"]; - return JS::NativeCameraRollManager::PhotoIdentifierNode(p); -} - - -inline NSString *JS::NativeCameraRollManager::PhotoIdentifierImage::uri() const -{ - id const p = _v[@\\"uri\\"]; - return RCTBridgingToString(p); -} - - -inline double JS::NativeCameraRollManager::PhotoIdentifierImage::playableDuration() const -{ - id const p = _v[@\\"playableDuration\\"]; - return RCTBridgingToDouble(p); -} - - -inline double JS::NativeCameraRollManager::PhotoIdentifierImage::width() const -{ - id const p = _v[@\\"width\\"]; - return RCTBridgingToDouble(p); -} - - -inline double JS::NativeCameraRollManager::PhotoIdentifierImage::height() const -{ - id const p = _v[@\\"height\\"]; - return RCTBridgingToDouble(p); -} - - -inline folly::Optional JS::NativeCameraRollManager::PhotoIdentifierImage::isStored() const -{ - id const p = _v[@\\"isStored\\"]; - return RCTBridgingToOptionalBool(p); -} - - -inline NSString *JS::NativeCameraRollManager::PhotoIdentifierImage::filename() const -{ - id const p = _v[@\\"filename\\"]; - return RCTBridgingToString(p); -} - - -inline bool JS::NativeCameraRollManager::PhotoIdentifiersPagePage_info::has_next_page() const -{ - id const p = _v[@\\"has_next_page\\"]; - return RCTBridgingToBool(p); -} - - -inline NSString *JS::NativeCameraRollManager::PhotoIdentifiersPagePage_info::start_cursor() const -{ - id const p = _v[@\\"start_cursor\\"]; - return RCTBridgingToString(p); -} - - -inline NSString *JS::NativeCameraRollManager::PhotoIdentifiersPagePage_info::end_cursor() const -{ - id const p = _v[@\\"end_cursor\\"]; - return RCTBridgingToString(p); -} - - -inline JS::NativeCameraRollManager::PhotoIdentifierImage JS::NativeCameraRollManager::PhotoIdentifierNode::image() const -{ - id const p = _v[@\\"image\\"]; - return JS::NativeCameraRollManager::PhotoIdentifierImage(p); -} - - -inline NSString *JS::NativeCameraRollManager::PhotoIdentifierNode::type() const +inline folly::Optional JS::NativeExceptionsManager::StackFrame::column() const { - id const p = _v[@\\"type\\"]; - return RCTBridgingToString(p); + id const p = _v[@\\"column\\"]; + return RCTBridgingToOptionalDouble(p); } -inline NSString *JS::NativeCameraRollManager::PhotoIdentifierNode::group_name() const +inline NSString *JS::NativeExceptionsManager::StackFrame::file() const { - id const p = _v[@\\"group_name\\"]; + id const p = _v[@\\"file\\"]; return RCTBridgingToString(p); } -inline double JS::NativeCameraRollManager::PhotoIdentifierNode::timestamp() const -{ - id const p = _v[@\\"timestamp\\"]; - return RCTBridgingToDouble(p); -} - - -inline JS::NativeCameraRollManager::PhotoIdentifierNodeLocation JS::NativeCameraRollManager::PhotoIdentifierNode::location() const -{ - id const p = _v[@\\"location\\"]; - return JS::NativeCameraRollManager::PhotoIdentifierNodeLocation(p); -} - - -inline double JS::NativeCameraRollManager::PhotoIdentifierNodeLocation::longitude() const -{ - id const p = _v[@\\"longitude\\"]; - return RCTBridgingToDouble(p); -} - - -inline double JS::NativeCameraRollManager::PhotoIdentifierNodeLocation::latitude() const -{ - id const p = _v[@\\"latitude\\"]; - return RCTBridgingToDouble(p); -} - - -inline folly::Optional JS::NativeCameraRollManager::PhotoIdentifierNodeLocation::altitude() const +inline folly::Optional JS::NativeExceptionsManager::StackFrame::lineNumber() const { - id const p = _v[@\\"altitude\\"]; + id const p = _v[@\\"lineNumber\\"]; return RCTBridgingToOptionalDouble(p); } -inline folly::Optional JS::NativeCameraRollManager::PhotoIdentifierNodeLocation::heading() const +inline NSString *JS::NativeExceptionsManager::StackFrame::methodName() const { - id const p = _v[@\\"heading\\"]; - return RCTBridgingToOptionalDouble(p); + id const p = _v[@\\"methodName\\"]; + return RCTBridgingToString(p); } -inline folly::Optional JS::NativeCameraRollManager::PhotoIdentifierNodeLocation::speed() const +inline folly::Optional JS::NativeExceptionsManager::StackFrame::collapse() const { - id const p = _v[@\\"speed\\"]; - return RCTBridgingToOptionalDouble(p); -} - - - -@protocol NativeCameraRollManagerSpec - -- (void) getPhotos:(JS::NativeCameraRollManager::GetPhotosParams &)params - resolve:(RCTPromiseResolveBlock)resolve - reject:(RCTPromiseRejectBlock)reject; -- (void) saveToCameraRoll:(NSString *)uri - type:(NSString *)type - resolve:(RCTPromiseResolveBlock)resolve - reject:(RCTPromiseRejectBlock)reject; -- (void) deletePhotos:(NSArray *)assets - resolve:(RCTPromiseResolveBlock)resolve - reject:(RCTPromiseRejectBlock)reject; -@end - - - -namespace JS { - namespace NativeExceptionsManager { - struct StackFrame { - folly::Optional column() const; - NSString *file() const; - folly::Optional lineNumber() const; - NSString *methodName() const; - folly::Optional collapse() const; - - StackFrame(NSDictionary *const v) : _v(v) {} - private: - NSDictionary *_v; - }; - } -} - -@interface RCTCxxConvert (NativeExceptionsManager_StackFrame) -+ (RCTManagedPointer *)JS_NativeExceptionsManager_StackFrame:(id)json; -@end - - -namespace JS { - namespace NativeExceptionsManager { - struct ExceptionData { - NSString *message() const; - NSString *originalMessage() const; - NSString *name() const; - NSString *componentStack() const; - facebook::react::LazyVector stack() const; - double id_() const; - bool isFatal() const; - id _Nullable extraData() const; - - ExceptionData(NSDictionary *const v) : _v(v) {} - private: - NSDictionary *_v; - }; - } + id const p = _v[@\\"collapse\\"]; + return RCTBridgingToOptionalBool(p); } -@interface RCTCxxConvert (NativeExceptionsManager_ExceptionData) -+ (RCTManagedPointer *)JS_NativeExceptionsManager_ExceptionData:(id)json; -@end inline NSString *JS::NativeExceptionsManager::ExceptionData::message() const { @@ -1119,7 +890,7 @@ inline NSString *JS::NativeExceptionsManager::ExceptionData::componentStack() co inline facebook::react::LazyVector JS::NativeExceptionsManager::ExceptionData::stack() const { id const p = _v[@\\"stack\\"]; - return RCTBridgingToVec(p, ^JS::NativeExceptionsManager::StackFrame(id itemValue_0) { return JS::NativeExceptionsManager::StackFrame(itemValue_0); }); + return RCTBridgingToVec(p, ^JS::NativeExceptionsManager::StackFrame(id itemValue_0) { return JS::NativeExceptionsManager::StackFrame(p); }); } @@ -1144,75 +915,6 @@ inline id _Nullable JS::NativeExceptionsManager::ExceptionData::extraD } -inline folly::Optional JS::NativeExceptionsManager::StackFrame::column() const -{ - id const p = _v[@\\"column\\"]; - return RCTBridgingToOptionalDouble(p); -} - - -inline NSString *JS::NativeExceptionsManager::StackFrame::file() const -{ - id const p = _v[@\\"file\\"]; - return RCTBridgingToString(p); -} - - -inline folly::Optional JS::NativeExceptionsManager::StackFrame::lineNumber() const -{ - id const p = _v[@\\"lineNumber\\"]; - return RCTBridgingToOptionalDouble(p); -} - - -inline NSString *JS::NativeExceptionsManager::StackFrame::methodName() const -{ - id const p = _v[@\\"methodName\\"]; - return RCTBridgingToString(p); -} - - -inline folly::Optional JS::NativeExceptionsManager::StackFrame::collapse() const -{ - id const p = _v[@\\"collapse\\"]; - return RCTBridgingToOptionalBool(p); -} - - - -@protocol NativeExceptionsManagerSpec -- (void) reportFatalException:(NSString *)message - stack:(NSArray *)stack - exceptionId:(double)exceptionId; -- (void) reportSoftException:(NSString *)message - stack:(NSArray *)stack - exceptionId:(double)exceptionId; -- (void) reportException:(JS::NativeExceptionsManager::ExceptionData &)data; -- (void) updateExceptionMessage:(NSString *)message - stack:(NSArray *)stack - exceptionId:(double)exceptionId; -- (void) dismissRedbox; -@end - - - -namespace JS { - namespace NativeImagePickerIOS { - struct SpecOpenCameraDialogConfig { - bool unmirrorFrontFacingCamera() const; - bool videoMode() const; - - SpecOpenCameraDialogConfig(NSDictionary *const v) : _v(v) {} - private: - NSDictionary *_v; - }; - } -} - -@interface RCTCxxConvert (NativeImagePickerIOS_SpecOpenCameraDialogConfig) -+ (RCTManagedPointer *)JS_NativeImagePickerIOS_SpecOpenCameraDialogConfig:(id)json; -@end - inline bool JS::NativeImagePickerIOS::SpecOpenCameraDialogConfig::unmirrorFrontFacingCamera() const { id const p = _v[@\\"unmirrorFrontFacingCamera\\"]; @@ -1226,40 +928,6 @@ inline bool JS::NativeImagePickerIOS::SpecOpenCameraDialogConfig::videoMode() co return RCTBridgingToBool(p); } - - -@protocol NativeImagePickerIOSSpec -- (void) openCameraDialog:(JS::NativeImagePickerIOS::SpecOpenCameraDialogConfig &)config - successCallback:(RCTResponseSenderBlock)successCallback - cancelCallback:(RCTResponseSenderBlock)cancelCallback; -@end - - -namespace facebook { - namespace react { - /** - * ObjC++ class for module 'CameraRollManager' - */ - class JSI_EXPORT NativeCameraRollManagerSpecJSI : public ObjCTurboModule { - public: - NativeCameraRollManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms); - }; - /** - * ObjC++ class for module 'ExceptionsManager' - */ - class JSI_EXPORT NativeExceptionsManagerSpecJSI : public ObjCTurboModule { - public: - NativeExceptionsManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms); - }; - /** - * ObjC++ class for module 'ImagePickerIOS' - */ - class JSI_EXPORT NativeImagePickerIOSSpecJSI : public ObjCTurboModule { - public: - NativeImagePickerIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms); - }; - } // namespace react -} // namespace facebook ", } `; @@ -1327,53 +995,53 @@ namespace JS { }; } } - -inline JS::NativeSampleTurboModule::Constants::Builder::Builder(const Input i) : _factory(^{ - NSMutableDictionary *d = [NSMutableDictionary new]; - auto const1 = i.const1.get(); - d[@\\"const1\\"] = @(const1); -auto const2 = i.const2.get(); - d[@\\"const2\\"] = @(const2); -auto const3 = i.const3.get(); - d[@\\"const3\\"] = const3; - return d; -}) {} -inline JS::NativeSampleTurboModule::Constants::Builder::Builder(Constants i) : _factory(^{ - return i.unsafeRawValue(); -}) {} - - @protocol NativeSampleTurboModuleSpec + +- (void)voidFunc; +- (NSNumber *)getBool:(BOOL)arg; +- (NSNumber *)getNumber:(double)arg; +- (NSString *)getString:(NSString *)arg; +- (NSArray *)getArray:(NSArray *)arg; +- (NSDictionary *)getObject:(NSDictionary *)arg; +- (NSNumber *)getRootTag:(double)arg; +- (NSDictionary *)getValue:(double)x + y:(NSString *)y + z:(NSDictionary *)z; +- (void)getValueWithCallback:(RCTResponseSenderBlock)callback; +- (void)getValueWithPromise:(BOOL)error + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; - (facebook::react::ModuleConstants)constantsToExport; - (facebook::react::ModuleConstants)getConstants; -- (void) voidFunc; -- (BOOL) getBool:(BOOL)arg; -- (NSNumber *) getNumber:(double)arg; -- (NSString *) getString:(NSString *)arg; -- (NSArray> *) getArray:(NSArray *)arg; -- (NSDictionary *) getObject:(NSDictionary *)arg; -- (NSNumber *) getRootTag:(double)arg; -- (NSDictionary *) getValue:(double)x - y:(NSString *)y - z:(NSDictionary *)z; -- (void) getValueWithCallback:(RCTResponseSenderBlock)callback; -- (void) getValueWithPromise:(BOOL)error - resolve:(RCTPromiseResolveBlock)resolve - reject:(RCTPromiseRejectBlock)reject; -@end - +@end namespace facebook { namespace react { /** - * ObjC++ class for module 'SampleTurboModule' - */ + * ObjC++ class for module 'SampleTurboModule' + */ class JSI_EXPORT NativeSampleTurboModuleSpecJSI : public ObjCTurboModule { public: NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); }; } // namespace react } // namespace facebook + + + +inline JS::NativeSampleTurboModule::Constants::Builder::Builder(const Input i) : _factory(^{ + NSMutableDictionary *d = [NSMutableDictionary new]; + auto const1 = i.const1.get(); + d[@\\"const1\\"] = @(const1); + auto const2 = i.const2.get(); + d[@\\"const2\\"] = @(const2); + auto const3 = i.const3.get(); + d[@\\"const3\\"] = const3; + return d; +}) {} +inline JS::NativeSampleTurboModule::Constants::Builder::Builder(Constants i) : _factory(^{ + return i.unsafeRawValue(); +}) {} ", } `; @@ -1412,40 +1080,45 @@ Map { - - @protocol NativeSample2TurboModuleSpec -- (void) voidFunc; -@end - +- (void)voidFunc; +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'Sample2TurboModule' + */ + class JSI_EXPORT NativeSample2TurboModuleSpecJSI : public ObjCTurboModule { + public: + NativeSample2TurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook @protocol NativeSampleTurboModuleSpec -- (void) voidFunc; -@end +- (void)voidFunc; +@end namespace facebook { namespace react { /** - * ObjC++ class for module 'SampleTurboModule' - */ + * ObjC++ class for module 'SampleTurboModule' + */ class JSI_EXPORT NativeSampleTurboModuleSpecJSI : public ObjCTurboModule { public: NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); }; - /** - * ObjC++ class for module 'Sample2TurboModule' - */ - class JSI_EXPORT NativeSample2TurboModuleSpecJSI : public ObjCTurboModule { - public: - NativeSample2TurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); - }; } // namespace react } // namespace facebook + + + + ", } `; @@ -1484,39 +1157,45 @@ Map { - - @protocol NativeSample2TurboModuleSpec -- (void) voidFunc; -@end +- (void)voidFunc; +@end +namespace facebook { + namespace react { + /** + * ObjC++ class for module 'Sample2TurboModule' + */ + class JSI_EXPORT NativeSample2TurboModuleSpecJSI : public ObjCTurboModule { + public: + NativeSample2TurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); + }; + } // namespace react +} // namespace facebook @protocol NativeSampleTurboModuleSpec -- (void) voidFunc; -@end +- (void)voidFunc; +@end namespace facebook { namespace react { /** - * ObjC++ class for module 'SampleTurboModule' - */ + * ObjC++ class for module 'SampleTurboModule' + */ class JSI_EXPORT NativeSampleTurboModuleSpecJSI : public ObjCTurboModule { public: NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); }; - /** - * ObjC++ class for module 'Sample2TurboModule' - */ - class JSI_EXPORT NativeSample2TurboModuleSpecJSI : public ObjCTurboModule { - public: - NativeSample2TurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms); - }; } // namespace react } // namespace facebook + + + + ", } `; diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJavaSpec-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJavaSpec-test.js.snap index 13eeb45cc922e6..c9a415a2064dee 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJavaSpec-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJavaSpec-test.js.snap @@ -2,7 +2,8 @@ exports[`GenerateModuleJavaSpec can generate fixture COMPLEX_OBJECTS 1`] = ` Map { - "NativeSampleTurboModuleSpec.java" => "/** + "NativeSampleTurboModuleSpec.java" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the LICENSE file in the root @@ -48,7 +49,8 @@ public abstract class NativeSampleTurboModuleSpec extends ReactContextBaseJavaMo exports[`GenerateModuleJavaSpec can generate fixture EMPTY_NATIVE_MODULES 1`] = ` Map { - "NativeSampleTurboModuleSpec.java" => "/** + "NativeSampleTurboModuleSpec.java" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the LICENSE file in the root @@ -80,7 +82,8 @@ public abstract class NativeSampleTurboModuleSpec extends ReactContextBaseJavaMo exports[`GenerateModuleJavaSpec can generate fixture NATIVE_MODULES_WITH_TYPE_ALIASES 1`] = ` Map { - "NativeAliasTurboModuleSpec.java" => "/** + "NativeAliasTurboModuleSpec.java" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the LICENSE file in the root @@ -114,7 +117,8 @@ public abstract class NativeAliasTurboModuleSpec extends ReactContextBaseJavaMod exports[`GenerateModuleJavaSpec can generate fixture REAL_MODULE_EXAMPLE 1`] = ` Map { - "NativeCameraRollManagerSpec.java" => "/** + "NativeCameraRollManagerSpec.java" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the LICENSE file in the root @@ -151,7 +155,8 @@ public abstract class NativeCameraRollManagerSpec extends ReactContextBaseJavaMo public abstract void deletePhotos(ReadableArray assets, Promise promise); } ", - "NativeImagePickerIOSSpec.java" => "/** + "NativeImagePickerIOSSpec.java" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the LICENSE file in the root @@ -181,7 +186,8 @@ public abstract class NativeImagePickerIOSSpec extends ReactContextBaseJavaModul public abstract void openCameraDialog(ReadableMap config, Callback successCallback, Callback cancelCallback); } ", - "NativeExceptionsManagerSpec.java" => "/** + "NativeExceptionsManagerSpec.java" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the LICENSE file in the root @@ -228,7 +234,8 @@ public abstract class NativeExceptionsManagerSpec extends ReactContextBaseJavaMo exports[`GenerateModuleJavaSpec can generate fixture SIMPLE_NATIVE_MODULES 1`] = ` Map { - "NativeSampleTurboModuleSpec.java" => "/** + "NativeSampleTurboModuleSpec.java" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the LICENSE file in the root @@ -327,7 +334,8 @@ public abstract class NativeSampleTurboModuleSpec extends ReactContextBaseJavaMo exports[`GenerateModuleJavaSpec can generate fixture TWO_MODULES_DIFFERENT_FILES 1`] = ` Map { - "NativeSampleTurboModuleSpec.java" => "/** + "NativeSampleTurboModuleSpec.java" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the LICENSE file in the root @@ -355,7 +363,8 @@ public abstract class NativeSampleTurboModuleSpec extends ReactContextBaseJavaMo public abstract void voidFunc(); } ", - "NativeSample2TurboModuleSpec.java" => "/** + "NativeSample2TurboModuleSpec.java" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the LICENSE file in the root @@ -388,7 +397,8 @@ public abstract class NativeSample2TurboModuleSpec extends ReactContextBaseJavaM exports[`GenerateModuleJavaSpec can generate fixture TWO_MODULES_SAME_FILE 1`] = ` Map { - "NativeSampleTurboModuleSpec.java" => "/** + "NativeSampleTurboModuleSpec.java" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the LICENSE file in the root @@ -416,7 +426,8 @@ public abstract class NativeSampleTurboModuleSpec extends ReactContextBaseJavaMo public abstract void voidFunc(); } ", - "NativeSample2TurboModuleSpec.java" => "/** + "NativeSample2TurboModuleSpec.java" => " +/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the LICENSE file in the root diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniCpp-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniCpp-test.js.snap index d12482b2b57f7a..47f3a3168ef989 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniCpp-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniCpp-test.js.snap @@ -46,6 +46,7 @@ std::shared_ptr COMPLEX_OBJECTS_ModuleProvider(const std::string mo if (moduleName == \\"SampleTurboModule\\") { return std::make_shared(params); } + return nullptr; } } // namespace react @@ -82,6 +83,7 @@ std::shared_ptr EMPTY_NATIVE_MODULES_ModuleProvider(const std::stri if (moduleName == \\"SampleTurboModule\\") { return std::make_shared(params); } + return nullptr; } } // namespace react @@ -123,6 +125,7 @@ std::shared_ptr NATIVE_MODULES_WITH_TYPE_ALIASES_ModuleProvider(con if (moduleName == \\"AliasTurboModule\\") { return std::make_shared(params); } + return nullptr; } } // namespace react @@ -218,6 +221,7 @@ std::shared_ptr REAL_MODULE_EXAMPLE_ModuleProvider(const std::strin if (moduleName == \\"ExceptionsManager\\") { return std::make_shared(params); } + return nullptr; } } // namespace react @@ -307,6 +311,7 @@ std::shared_ptr SIMPLE_NATIVE_MODULES_ModuleProvider(const std::str if (moduleName == \\"SampleTurboModule\\") { return std::make_shared(params); } + return nullptr; } } // namespace react @@ -360,6 +365,7 @@ std::shared_ptr TWO_MODULES_DIFFERENT_FILES_ModuleProvider(const st if (moduleName == \\"Sample2TurboModule\\") { return std::make_shared(params); } + return nullptr; } } // namespace react @@ -411,6 +417,7 @@ std::shared_ptr TWO_MODULES_SAME_FILE_ModuleProvider(const std::str if (moduleName == \\"Sample2TurboModule\\") { return std::make_shared(params); } + return nullptr; } } // namespace react diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniH-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniH-test.js.snap index afb55a73886ba9..64cbc779b30315 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniH-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniH-test.js.snap @@ -12,10 +12,11 @@ Map { * @generated by codegen project: GenerateModuleJniH.js */ -#import -#import -#import -#import +#pragma once + +#include +#include +#include namespace facebook { namespace react { @@ -33,6 +34,34 @@ std::shared_ptr COMPLEX_OBJECTS_ModuleProvider(const std::string mo } // namespace react } // namespace facebook +", + "Android.mk" => "# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_codegen_complex_objects + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES := libjsi + +LOCAL_CFLAGS := \\\\ + -DLOG_TAG=\\\\\\"ReactNative\\\\\\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) ", } `; @@ -49,10 +78,11 @@ Map { * @generated by codegen project: GenerateModuleJniH.js */ -#import -#import -#import -#import +#pragma once + +#include +#include +#include namespace facebook { namespace react { @@ -70,6 +100,34 @@ std::shared_ptr EMPTY_NATIVE_MODULES_ModuleProvider(const std::stri } // namespace react } // namespace facebook +", + "Android.mk" => "# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_codegen_empty_native_modules + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES := libjsi + +LOCAL_CFLAGS := \\\\ + -DLOG_TAG=\\\\\\"ReactNative\\\\\\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) ", } `; @@ -86,10 +144,11 @@ Map { * @generated by codegen project: GenerateModuleJniH.js */ -#import -#import -#import -#import +#pragma once + +#include +#include +#include namespace facebook { namespace react { @@ -107,6 +166,34 @@ std::shared_ptr NATIVE_MODULES_WITH_TYPE_ALIASES_ModuleProvider(con } // namespace react } // namespace facebook +", + "Android.mk" => "# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_codegen_native_modules_with_type_aliases + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES := libjsi + +LOCAL_CFLAGS := \\\\ + -DLOG_TAG=\\\\\\"ReactNative\\\\\\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) ", } `; @@ -123,10 +210,11 @@ Map { * @generated by codegen project: GenerateModuleJniH.js */ -#import -#import -#import -#import +#pragma once + +#include +#include +#include namespace facebook { namespace react { @@ -140,19 +228,19 @@ public: }; /** - * JNI C++ class for module 'ExceptionsManager' + * JNI C++ class for module 'ImagePickerIOS' */ -class JSI_EXPORT NativeExceptionsManagerSpecJSI : public JavaTurboModule { +class JSI_EXPORT NativeImagePickerIOSSpecJSI : public JavaTurboModule { public: - NativeExceptionsManagerSpecJSI(const JavaTurboModule::InitParams ¶ms); + NativeImagePickerIOSSpecJSI(const JavaTurboModule::InitParams ¶ms); }; /** - * JNI C++ class for module 'ImagePickerIOS' + * JNI C++ class for module 'ExceptionsManager' */ -class JSI_EXPORT NativeImagePickerIOSSpecJSI : public JavaTurboModule { +class JSI_EXPORT NativeExceptionsManagerSpecJSI : public JavaTurboModule { public: - NativeImagePickerIOSSpecJSI(const JavaTurboModule::InitParams ¶ms); + NativeExceptionsManagerSpecJSI(const JavaTurboModule::InitParams ¶ms); }; @@ -160,6 +248,34 @@ std::shared_ptr REAL_MODULE_EXAMPLE_ModuleProvider(const std::strin } // namespace react } // namespace facebook +", + "Android.mk" => "# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_codegen_real_module_example + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES := libjsi + +LOCAL_CFLAGS := \\\\ + -DLOG_TAG=\\\\\\"ReactNative\\\\\\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) ", } `; @@ -176,10 +292,11 @@ Map { * @generated by codegen project: GenerateModuleJniH.js */ -#import -#import -#import -#import +#pragma once + +#include +#include +#include namespace facebook { namespace react { @@ -197,6 +314,34 @@ std::shared_ptr SIMPLE_NATIVE_MODULES_ModuleProvider(const std::str } // namespace react } // namespace facebook +", + "Android.mk" => "# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_codegen_simple_native_modules + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES := libjsi + +LOCAL_CFLAGS := \\\\ + -DLOG_TAG=\\\\\\"ReactNative\\\\\\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) ", } `; @@ -213,10 +358,11 @@ Map { * @generated by codegen project: GenerateModuleJniH.js */ -#import -#import -#import -#import +#pragma once + +#include +#include +#include namespace facebook { namespace react { @@ -242,6 +388,34 @@ std::shared_ptr TWO_MODULES_DIFFERENT_FILES_ModuleProvider(const st } // namespace react } // namespace facebook +", + "Android.mk" => "# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_codegen_two_modules_different_files + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES := libjsi + +LOCAL_CFLAGS := \\\\ + -DLOG_TAG=\\\\\\"ReactNative\\\\\\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) ", } `; @@ -258,10 +432,11 @@ Map { * @generated by codegen project: GenerateModuleJniH.js */ -#import -#import -#import -#import +#pragma once + +#include +#include +#include namespace facebook { namespace react { @@ -287,6 +462,34 @@ std::shared_ptr TWO_MODULES_SAME_FILE_ModuleProvider(const std::str } // namespace react } // namespace facebook +", + "Android.mk" => "# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := react_codegen_two_modules_same_file + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SHARED_LIBRARIES := libreact_nativemodule_core + +LOCAL_STATIC_LIBRARIES := libjsi + +LOCAL_CFLAGS := \\\\ + -DLOG_TAG=\\\\\\"ReactNative\\\\\\" + +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall + +include $(BUILD_SHARED_LIBRARY) ", } `; diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleMm-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleMm-test.js.snap index 192e19235d1cf1..53e98c9fdf939a 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleMm-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleMm-test.js.snap @@ -9,17 +9,21 @@ Map { * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated by codegen project: GenerateModuleMm.js + * @generated by an internal genrule from Flow types. + * + * We create an umbrella header (and corresponding implementation) here since + * Cxx compilation in BUCK has a limitation: source-code producing genrule()s + * must have a single output. More files => more genrule()s => slower builds. */ -#include -#import +#import \\"SampleSpec.h\\" + -@implementation RCTCxxConvert (NativeSampleTurboModule_SpecDifficultReturnType) -+ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecDifficultReturnType:(id)json +@implementation RCTCxxConvert (NativeSampleTurboModule_SpecDifficultAE) ++ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecDifficultAE:(id)json { - return facebook::react::managedPointer(json); + return facebook::react::managedPointer(json); } @end @@ -32,72 +36,82 @@ Map { @end -@implementation RCTCxxConvert (NativeSampleTurboModule_SpecOptionalsA) -+ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecOptionalsA:(id)json +@implementation RCTCxxConvert (NativeSampleTurboModule_SpecOptionalsAOptionalObjectProperty) ++ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecOptionalsAOptionalObjectProperty:(id)json { - return facebook::react::managedPointer(json); + return facebook::react::managedPointer(json); } @end -@implementation RCTCxxConvert (NativeSampleTurboModule_SpecGetArraysOptions) -+ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecGetArraysOptions:(id)json +@implementation RCTCxxConvert (NativeSampleTurboModule_SpecOptionalsA) ++ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecOptionalsA:(id)json { - return facebook::react::managedPointer(json); + return facebook::react::managedPointer(json); } @end -@implementation RCTCxxConvert (NativeSampleTurboModule_SpecDifficultAE) -+ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecDifficultAE:(id)json +@implementation RCTCxxConvert (NativeSampleTurboModule_SpecGetArraysOptionsArrayOfObjectsElement) ++ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecGetArraysOptionsArrayOfObjectsElement:(id)json { - return facebook::react::managedPointer(json); + return facebook::react::managedPointer(json); } @end -@implementation RCTCxxConvert (NativeSampleTurboModule_SpecOptionalsAOptionalObjectProperty) -+ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecOptionalsAOptionalObjectProperty:(id)json +@implementation RCTCxxConvert (NativeSampleTurboModule_SpecGetArraysOptions) ++ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecGetArraysOptions:(id)json { - return facebook::react::managedPointer(json); + return facebook::react::managedPointer(json); } @end namespace facebook { namespace react { - + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_difficult(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, ObjectKind, \\"difficult\\", @selector(difficult:), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"difficult\\", @selector(difficult:), args, count); } + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_optionals(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, VoidKind, \\"optionals\\", @selector(optionals:), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"optionals\\", @selector(optionals:), args, count); } + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_optionalMethod(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, VoidKind, \\"optionalMethod\\", @selector(optionalMethod:callback:extras:), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"optionalMethod\\", @selector(optionalMethod:callback:extras:), args, count); } + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getArrays(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, VoidKind, \\"getArrays\\", @selector(getArrays:), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"getArrays\\", @selector(getArrays:), args, count); } + NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) : ObjCTurboModule(params) { - methodMap_[\\"difficult\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_difficult}; - methodMap_[\\"optionals\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_optionals}; - methodMap_[\\"optionalMethod\\"] = MethodMetadata {3, __hostFunction_NativeSampleTurboModuleSpecJSI_optionalMethod}; - methodMap_[\\"getArrays\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getArrays}; - setMethodArgConversionSelector(@\\"difficult\\", 0, @\\"JS_NativeSampleTurboModule_SpecDifficultA:\\"); - setMethodArgConversionSelector(@\\"optionals\\", 0, @\\"JS_NativeSampleTurboModule_SpecOptionalsA:\\"); - setMethodArgConversionSelector(@\\"getArrays\\", 0, @\\"JS_NativeSampleTurboModule_SpecGetArraysOptions:\\"); + + methodMap_[\\"difficult\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_difficult}; + setMethodArgConversionSelector(@\\"difficult\\", 0, @\\"JS_NativeSampleTurboModule_SpecDifficultA:\\"); + + + methodMap_[\\"optionals\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_optionals}; + setMethodArgConversionSelector(@\\"optionals\\", 0, @\\"JS_NativeSampleTurboModule_SpecOptionalsA:\\"); + + + methodMap_[\\"optionalMethod\\"] = MethodMetadata {3, __hostFunction_NativeSampleTurboModuleSpecJSI_optionalMethod}; + + + + methodMap_[\\"getArrays\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getArrays}; + setMethodArgConversionSelector(@\\"getArrays\\", 0, @\\"JS_NativeSampleTurboModule_SpecGetArraysOptions:\\"); + } } // namespace react } // namespace facebook + ", } `; @@ -111,23 +125,28 @@ Map { * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated by codegen project: GenerateModuleMm.js + * @generated by an internal genrule from Flow types. + * + * We create an umbrella header (and corresponding implementation) here since + * Cxx compilation in BUCK has a limitation: source-code producing genrule()s + * must have a single output. More files => more genrule()s => slower builds. */ -#include -#import +#import \\"SampleSpec.h\\" + namespace facebook { namespace react { - + NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) : ObjCTurboModule(params) { - + } } // namespace react } // namespace facebook + ", } `; @@ -141,19 +160,15 @@ Map { * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated by codegen project: GenerateModuleMm.js + * @generated by an internal genrule from Flow types. + * + * We create an umbrella header (and corresponding implementation) here since + * Cxx compilation in BUCK has a limitation: source-code producing genrule()s + * must have a single output. More files => more genrule()s => slower builds. */ -#include -#import - +#import \\"SampleSpec.h\\" -@implementation RCTCxxConvert (NativeAliasTurboModule_Options) -+ (RCTManagedPointer *)JS_NativeAliasTurboModule_Options:(id)json -{ - return facebook::react::managedPointer(json); -} -@end @implementation RCTCxxConvert (NativeAliasTurboModule_OptionsOffset) @@ -179,23 +194,32 @@ Map { } @end -namespace facebook { - namespace react { +@implementation RCTCxxConvert (NativeAliasTurboModule_Options) ++ (RCTManagedPointer *)JS_NativeAliasTurboModule_Options:(id)json +{ + return facebook::react::managedPointer(json); +} +@end +namespace facebook { + namespace react { + static facebook::jsi::Value __hostFunction_NativeAliasTurboModuleSpecJSI_cropImage(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, VoidKind, \\"cropImage\\", @selector(cropImage:), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"cropImage\\", @selector(cropImage:), args, count); } + NativeAliasTurboModuleSpecJSI::NativeAliasTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) : ObjCTurboModule(params) { - - methodMap_[\\"cropImage\\"] = MethodMetadata {1, __hostFunction_NativeAliasTurboModuleSpecJSI_cropImage}; - setMethodArgConversionSelector(@\\"cropImage\\", 0, @\\"JS_NativeAliasTurboModule_Options:\\"); + + methodMap_[\\"cropImage\\"] = MethodMetadata {1, __hostFunction_NativeAliasTurboModuleSpecJSI_cropImage}; + setMethodArgConversionSelector(@\\"cropImage\\", 0, @\\"JS_NativeAliasTurboModule_Options:\\"); + } } // namespace react } // namespace facebook + ", } `; @@ -209,35 +233,15 @@ Map { * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated by codegen project: GenerateModuleMm.js + * @generated by an internal genrule from Flow types. + * + * We create an umbrella header (and corresponding implementation) here since + * Cxx compilation in BUCK has a limitation: source-code producing genrule()s + * must have a single output. More files => more genrule()s => slower builds. */ -#include -#import - - -@implementation RCTCxxConvert (NativeCameraRollManager_PhotoIdentifierImage) -+ (RCTManagedPointer *)JS_NativeCameraRollManager_PhotoIdentifierImage:(id)json -{ - return facebook::react::managedPointer(json); -} -@end - - -@implementation RCTCxxConvert (NativeCameraRollManager_PhotoIdentifier) -+ (RCTManagedPointer *)JS_NativeCameraRollManager_PhotoIdentifier:(id)json -{ - return facebook::react::managedPointer(json); -} -@end - +#import \\"SampleSpec.h\\" -@implementation RCTCxxConvert (NativeCameraRollManager_PhotoIdentifiersPage) -+ (RCTManagedPointer *)JS_NativeCameraRollManager_PhotoIdentifiersPage:(id)json -{ - return facebook::react::managedPointer(json); -} -@end @implementation RCTCxxConvert (NativeCameraRollManager_GetPhotosParams) @@ -247,37 +251,42 @@ Map { } @end +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeCameraRollManagerSpecJSI_getPhotos(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, \\"getPhotos\\", @selector(getPhotos:resolve:reject:), args, count); + } -@implementation RCTCxxConvert (NativeCameraRollManager_PhotoIdentifierNode) -+ (RCTManagedPointer *)JS_NativeCameraRollManager_PhotoIdentifierNode:(id)json -{ - return facebook::react::managedPointer(json); -} -@end + + static facebook::jsi::Value __hostFunction_NativeCameraRollManagerSpecJSI_saveToCameraRoll(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, \\"saveToCameraRoll\\", @selector(saveToCameraRoll:type:resolve:reject:), args, count); + } -@implementation RCTCxxConvert (NativeCameraRollManager_PhotoIdentifiersPagePage_info) -+ (RCTManagedPointer *)JS_NativeCameraRollManager_PhotoIdentifiersPagePage_info:(id)json -{ - return facebook::react::managedPointer(json); -} -@end + static facebook::jsi::Value __hostFunction_NativeCameraRollManagerSpecJSI_deletePhotos(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, \\"deletePhotos\\", @selector(deletePhotos:resolve:reject:), args, count); + } -@implementation RCTCxxConvert (NativeCameraRollManager_PhotoIdentifierNodeLocation) -+ (RCTManagedPointer *)JS_NativeCameraRollManager_PhotoIdentifierNodeLocation:(id)json -{ - return facebook::react::managedPointer(json); -} -@end + NativeCameraRollManagerSpecJSI::NativeCameraRollManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms) + : ObjCTurboModule(params) { + + methodMap_[\\"getPhotos\\"] = MethodMetadata {1, __hostFunction_NativeCameraRollManagerSpecJSI_getPhotos}; + setMethodArgConversionSelector(@\\"getPhotos\\", 0, @\\"JS_NativeCameraRollManager_GetPhotosParams:\\"); + + methodMap_[\\"saveToCameraRoll\\"] = MethodMetadata {2, __hostFunction_NativeCameraRollManagerSpecJSI_saveToCameraRoll}; + + + + methodMap_[\\"deletePhotos\\"] = MethodMetadata {1, __hostFunction_NativeCameraRollManagerSpecJSI_deletePhotos}; + + + } + } // namespace react +} // namespace facebook -@implementation RCTCxxConvert (NativeImagePickerIOS_SpecOpenCameraDialogConfig) -+ (RCTManagedPointer *)JS_NativeImagePickerIOS_SpecOpenCameraDialogConfig:(id)json -{ - return facebook::react::managedPointer(json); -} -@end @implementation RCTCxxConvert (NativeExceptionsManager_StackFrame) @@ -297,79 +306,85 @@ Map { namespace facebook { namespace react { + + static facebook::jsi::Value __hostFunction_NativeExceptionsManagerSpecJSI_reportFatalException(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"reportFatalException\\", @selector(reportFatalException:stack:exceptionId:), args, count); + } - static facebook::jsi::Value __hostFunction_NativeCameraRollManagerSpecJSI_getPhotos(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, PromiseKind, \\"getPhotos\\", @selector(getPhotos:resolve:reject:), args, count); + static facebook::jsi::Value __hostFunction_NativeExceptionsManagerSpecJSI_reportSoftException(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"reportSoftException\\", @selector(reportSoftException:stack:exceptionId:), args, count); } - static facebook::jsi::Value __hostFunction_NativeCameraRollManagerSpecJSI_saveToCameraRoll(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, PromiseKind, \\"saveToCameraRoll\\", @selector(saveToCameraRoll:type:resolve:reject:), args, count); - } - static facebook::jsi::Value __hostFunction_NativeCameraRollManagerSpecJSI_deletePhotos(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, PromiseKind, \\"deletePhotos\\", @selector(deletePhotos:resolve:reject:), args, count); + static facebook::jsi::Value __hostFunction_NativeExceptionsManagerSpecJSI_reportException(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"reportException\\", @selector(reportException:), args, count); } - NativeCameraRollManagerSpecJSI::NativeCameraRollManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms) - : ObjCTurboModule(params) { - - methodMap_[\\"getPhotos\\"] = MethodMetadata {1, __hostFunction_NativeCameraRollManagerSpecJSI_getPhotos}; - methodMap_[\\"saveToCameraRoll\\"] = MethodMetadata {2, __hostFunction_NativeCameraRollManagerSpecJSI_saveToCameraRoll}; - methodMap_[\\"deletePhotos\\"] = MethodMetadata {1, __hostFunction_NativeCameraRollManagerSpecJSI_deletePhotos}; - setMethodArgConversionSelector(@\\"getPhotos\\", 0, @\\"JS_NativeCameraRollManager_GetPhotosParams:\\"); + + static facebook::jsi::Value __hostFunction_NativeExceptionsManagerSpecJSI_updateExceptionMessage(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"updateExceptionMessage\\", @selector(updateExceptionMessage:stack:exceptionId:), args, count); } - static facebook::jsi::Value __hostFunction_NativeImagePickerIOSSpecJSI_openCameraDialog(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, VoidKind, \\"openCameraDialog\\", @selector(openCameraDialog:successCallback:cancelCallback:), args, count); + + static facebook::jsi::Value __hostFunction_NativeExceptionsManagerSpecJSI_dismissRedbox(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"dismissRedbox\\", @selector(dismissRedbox), args, count); } - NativeImagePickerIOSSpecJSI::NativeImagePickerIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms) + + NativeExceptionsManagerSpecJSI::NativeExceptionsManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms) : ObjCTurboModule(params) { - methodMap_[\\"openCameraDialog\\"] = MethodMetadata {3, __hostFunction_NativeImagePickerIOSSpecJSI_openCameraDialog}; - setMethodArgConversionSelector(@\\"openCameraDialog\\", 0, @\\"JS_NativeImagePickerIOS_SpecOpenCameraDialogConfig:\\"); - } + + methodMap_[\\"reportFatalException\\"] = MethodMetadata {3, __hostFunction_NativeExceptionsManagerSpecJSI_reportFatalException}; + - static facebook::jsi::Value __hostFunction_NativeExceptionsManagerSpecJSI_reportFatalException(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, VoidKind, \\"reportFatalException\\", @selector(reportFatalException:stack:exceptionId:), args, count); - } + + methodMap_[\\"reportSoftException\\"] = MethodMetadata {3, __hostFunction_NativeExceptionsManagerSpecJSI_reportSoftException}; + - static facebook::jsi::Value __hostFunction_NativeExceptionsManagerSpecJSI_reportSoftException(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, VoidKind, \\"reportSoftException\\", @selector(reportSoftException:stack:exceptionId:), args, count); - } + + methodMap_[\\"reportException\\"] = MethodMetadata {1, __hostFunction_NativeExceptionsManagerSpecJSI_reportException}; + setMethodArgConversionSelector(@\\"reportException\\", 0, @\\"JS_NativeExceptionsManager_ExceptionData:\\"); - static facebook::jsi::Value __hostFunction_NativeExceptionsManagerSpecJSI_reportException(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, VoidKind, \\"reportException\\", @selector(reportException:), args, count); - } + + methodMap_[\\"updateExceptionMessage\\"] = MethodMetadata {3, __hostFunction_NativeExceptionsManagerSpecJSI_updateExceptionMessage}; + + + + methodMap_[\\"dismissRedbox\\"] = MethodMetadata {0, __hostFunction_NativeExceptionsManagerSpecJSI_dismissRedbox}; + - static facebook::jsi::Value __hostFunction_NativeExceptionsManagerSpecJSI_updateExceptionMessage(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, VoidKind, \\"updateExceptionMessage\\", @selector(updateExceptionMessage:stack:exceptionId:), args, count); } + } // namespace react +} // namespace facebook - static facebook::jsi::Value __hostFunction_NativeExceptionsManagerSpecJSI_dismissRedbox(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, VoidKind, \\"dismissRedbox\\", @selector(dismissRedbox), args, count); + + +@implementation RCTCxxConvert (NativeImagePickerIOS_SpecOpenCameraDialogConfig) ++ (RCTManagedPointer *)JS_NativeImagePickerIOS_SpecOpenCameraDialogConfig:(id)json +{ + return facebook::react::managedPointer(json); +} +@end + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeImagePickerIOSSpecJSI_openCameraDialog(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"openCameraDialog\\", @selector(openCameraDialog:successCallback:cancelCallback:), args, count); } - NativeExceptionsManagerSpecJSI::NativeExceptionsManagerSpecJSI(const ObjCTurboModule::InitParams ¶ms) + + NativeImagePickerIOSSpecJSI::NativeImagePickerIOSSpecJSI(const ObjCTurboModule::InitParams ¶ms) : ObjCTurboModule(params) { - methodMap_[\\"reportFatalException\\"] = MethodMetadata {3, __hostFunction_NativeExceptionsManagerSpecJSI_reportFatalException}; - methodMap_[\\"reportSoftException\\"] = MethodMetadata {3, __hostFunction_NativeExceptionsManagerSpecJSI_reportSoftException}; - methodMap_[\\"reportException\\"] = MethodMetadata {1, __hostFunction_NativeExceptionsManagerSpecJSI_reportException}; - methodMap_[\\"updateExceptionMessage\\"] = MethodMetadata {3, __hostFunction_NativeExceptionsManagerSpecJSI_updateExceptionMessage}; - methodMap_[\\"dismissRedbox\\"] = MethodMetadata {0, __hostFunction_NativeExceptionsManagerSpecJSI_dismissRedbox}; - setMethodArgConversionSelector(@\\"reportException\\", 0, @\\"JS_NativeExceptionsManager_ExceptionData:\\"); + + methodMap_[\\"openCameraDialog\\"] = MethodMetadata {3, __hostFunction_NativeImagePickerIOSSpecJSI_openCameraDialog}; + setMethodArgConversionSelector(@\\"openCameraDialog\\", 0, @\\"JS_NativeImagePickerIOS_SpecOpenCameraDialogConfig:\\"); + } } // namespace react } // namespace facebook + ", } `; @@ -383,87 +398,125 @@ Map { * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated by codegen project: GenerateModuleMm.js + * @generated by an internal genrule from Flow types. + * + * We create an umbrella header (and corresponding implementation) here since + * Cxx compilation in BUCK has a limitation: source-code producing genrule()s + * must have a single output. More files => more genrule()s => slower builds. */ -#include -#import +#import \\"SampleSpec.h\\" + namespace facebook { namespace react { - - static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, ObjectKind, \\"getConstants\\", @selector(getConstants), args, count); - } - + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); } + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getBool(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, BooleanKind, \\"getBool\\", @selector(getBool:), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getBool\\", @selector(getBool:), args, count); } + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getNumber(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, NumberKind, \\"getNumber\\", @selector(getNumber:), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getNumber\\", @selector(getNumber:), args, count); } + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getString(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, StringKind, \\"getString\\", @selector(getString:), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, StringKind, \\"getString\\", @selector(getString:), args, count); } + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getArray(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, ArrayKind, \\"getArray\\", @selector(getArray:), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, ArrayKind, \\"getArray\\", @selector(getArray:), args, count); } + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getObject(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, ObjectKind, \\"getObject\\", @selector(getObject:), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getObject\\", @selector(getObject:), args, count); } + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getRootTag(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, NumberKind, \\"getRootTag\\", @selector(getRootTag:), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, NumberKind, \\"getRootTag\\", @selector(getRootTag:), args, count); } + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, ObjectKind, \\"getValue\\", @selector(getValue:y:z:), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getValue\\", @selector(getValue:y:z:), args, count); } + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithCallback(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, VoidKind, \\"getValueWithCallback\\", @selector(getValueWithCallback:), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"getValueWithCallback\\", @selector(getValueWithCallback:), args, count); } + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithPromise(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, PromiseKind, \\"getValueWithPromise\\", @selector(getValueWithPromise:resolve:reject:), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, PromiseKind, \\"getValueWithPromise\\", @selector(getValueWithPromise:resolve:reject:), args, count); } + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, ObjectKind, \\"getConstants\\", @selector(getConstants), args, count); + } + + NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) : ObjCTurboModule(params) { - methodMap_[\\"getConstants\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleSpecJSI_getConstants}; - methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc}; - methodMap_[\\"getBool\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getBool}; - methodMap_[\\"getNumber\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getNumber}; - methodMap_[\\"getString\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getString}; - methodMap_[\\"getArray\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getArray}; - methodMap_[\\"getObject\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getObject}; - methodMap_[\\"getRootTag\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getRootTag}; - methodMap_[\\"getValue\\"] = MethodMetadata {3, __hostFunction_NativeSampleTurboModuleSpecJSI_getValue}; - methodMap_[\\"getValueWithCallback\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithCallback}; - methodMap_[\\"getValueWithPromise\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithPromise}; + + methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc}; + + + + methodMap_[\\"getBool\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getBool}; + + + + methodMap_[\\"getNumber\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getNumber}; + + + + methodMap_[\\"getString\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getString}; + + + + methodMap_[\\"getArray\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getArray}; + + + + methodMap_[\\"getObject\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getObject}; + + + + methodMap_[\\"getRootTag\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getRootTag}; + + + + methodMap_[\\"getValue\\"] = MethodMetadata {3, __hostFunction_NativeSampleTurboModuleSpecJSI_getValue}; + + + + methodMap_[\\"getValueWithCallback\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithCallback}; + + + + methodMap_[\\"getValueWithPromise\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithPromise}; + + + + methodMap_[\\"getConstants\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleSpecJSI_getConstants}; + + } } // namespace react } // namespace facebook + ", } `; @@ -477,40 +530,55 @@ Map { * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated by codegen project: GenerateModuleMm.js + * @generated by an internal genrule from Flow types. + * + * We create an umbrella header (and corresponding implementation) here since + * Cxx compilation in BUCK has a limitation: source-code producing genrule()s + * must have a single output. More files => more genrule()s => slower builds. */ -#include -#import +#import \\"SampleSpec.h\\" namespace facebook { namespace react { - - static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); + + static facebook::jsi::Value __hostFunction_NativeSample2TurboModuleSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); } - NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + + NativeSample2TurboModuleSpecJSI::NativeSample2TurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) : ObjCTurboModule(params) { - methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc}; + + methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSample2TurboModuleSpecJSI_voidFunc}; + + } + } // namespace react +} // namespace facebook - static facebook::jsi::Value __hostFunction_NativeSample2TurboModuleSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); } - NativeSample2TurboModuleSpecJSI::NativeSample2TurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + + NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) : ObjCTurboModule(params) { - - methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSample2TurboModuleSpecJSI_voidFunc}; + + methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc}; + + } } // namespace react } // namespace facebook + ", } `; @@ -524,38 +592,55 @@ Map { * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated by codegen project: GenerateModuleMm.js + * @generated by an internal genrule from Flow types. + * + * We create an umbrella header (and corresponding implementation) here since + * Cxx compilation in BUCK has a limitation: source-code producing genrule()s + * must have a single output. More files => more genrule()s => slower builds. */ -#include -#import +#import \\"SampleSpec.h\\" namespace facebook { namespace react { - - static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); + + static facebook::jsi::Value __hostFunction_NativeSample2TurboModuleSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); } - NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + + NativeSample2TurboModuleSpecJSI::NativeSample2TurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) : ObjCTurboModule(params) { - methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc}; + + methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSample2TurboModuleSpecJSI_voidFunc}; + + } + } // namespace react +} // namespace facebook - static facebook::jsi::Value __hostFunction_NativeSample2TurboModuleSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule) - .invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); + + +namespace facebook { + namespace react { + + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); } - NativeSample2TurboModuleSpecJSI::NativeSample2TurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) + + NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) : ObjCTurboModule(params) { - methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSample2TurboModuleSpecJSI_voidFunc}; + + methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc}; + + } } // namespace react } // namespace facebook + ", } `; diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateStructs-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateStructs-test.js.snap deleted file mode 100644 index 75bb7bfe5dbde1..00000000000000 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateStructs-test.js.snap +++ /dev/null @@ -1,284 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`GenerateStructs can generate fixture SIMPLE_CONSTANTS 1`] = ` -" - -namespace JS { - namespace NativeSampleTurboModule { - struct ConstantsDG { - - struct Builder { - struct Input { - RCTRequired h; - RCTRequired i; - RCTRequired j; - }; - - /** Initialize with a set of values */ - Builder(const Input i); - /** Initialize with an existing ConstantsDG */ - Builder(ConstantsDG i); - /** Builds the object. Generally used only by the infrastructure. */ - NSDictionary *buildUnsafeRawValue() const { return _factory(); }; - private: - NSDictionary *(^_factory)(void); - }; - - static ConstantsDG fromUnsafeRawValue(NSDictionary *const v) { return {v}; } - NSDictionary *unsafeRawValue() const { return _v; } - private: - ConstantsDG(NSDictionary *const v) : _v(v) {} - NSDictionary *_v; - }; - } -} - -inline JS::NativeSampleTurboModule::ConstantsDG::Builder::Builder(const Input i) : _factory(^{ - NSMutableDictionary *d = [NSMutableDictionary new]; - auto h = i.h.get(); - d[@\\"h\\"] = @(h); -auto i = i.i.get(); - d[@\\"i\\"] = @(i); -auto j = i.j.get(); - d[@\\"j\\"] = j; - return d; -}) {} -inline JS::NativeSampleTurboModule::ConstantsDG::Builder::Builder(ConstantsDG i) : _factory(^{ - return i.unsafeRawValue(); -}) {} - -namespace JS { - namespace NativeSampleTurboModule { - struct ConstantsD { - - struct Builder { - struct Input { - RCTRequired e; - RCTRequired f; - RCTRequired g; - }; - - /** Initialize with a set of values */ - Builder(const Input i); - /** Initialize with an existing ConstantsD */ - Builder(ConstantsD i); - /** Builds the object. Generally used only by the infrastructure. */ - NSDictionary *buildUnsafeRawValue() const { return _factory(); }; - private: - NSDictionary *(^_factory)(void); - }; - - static ConstantsD fromUnsafeRawValue(NSDictionary *const v) { return {v}; } - NSDictionary *unsafeRawValue() const { return _v; } - private: - ConstantsD(NSDictionary *const v) : _v(v) {} - NSDictionary *_v; - }; - } -} - -inline JS::NativeSampleTurboModule::ConstantsD::Builder::Builder(const Input i) : _factory(^{ - NSMutableDictionary *d = [NSMutableDictionary new]; - auto e = i.e.get(); - d[@\\"e\\"] = @(e); -auto f = i.f.get(); - d[@\\"f\\"] = @(f); -auto g = i.g.get(); - d[@\\"g\\"] = g.buildUnsafeRawValue(); - return d; -}) {} -inline JS::NativeSampleTurboModule::ConstantsD::Builder::Builder(ConstantsD i) : _factory(^{ - return i.unsafeRawValue(); -}) {} - -namespace JS { - namespace NativeSampleTurboModule { - struct Constants { - - struct Builder { - struct Input { - RCTRequired a; - RCTRequired b; - RCTRequired c; - RCTRequired d; - RCTRequired k; - }; - - /** Initialize with a set of values */ - Builder(const Input i); - /** Initialize with an existing Constants */ - Builder(Constants i); - /** Builds the object. Generally used only by the infrastructure. */ - NSDictionary *buildUnsafeRawValue() const { return _factory(); }; - private: - NSDictionary *(^_factory)(void); - }; - - static Constants fromUnsafeRawValue(NSDictionary *const v) { return {v}; } - NSDictionary *unsafeRawValue() const { return _v; } - private: - Constants(NSDictionary *const v) : _v(v) {} - NSDictionary *_v; - }; - } -} - -inline JS::NativeSampleTurboModule::Constants::Builder::Builder(const Input i) : _factory(^{ - NSMutableDictionary *d = [NSMutableDictionary new]; - auto a = i.a.get(); - d[@\\"a\\"] = @(a); -auto b = i.b.get(); - d[@\\"b\\"] = @(b); -auto c = i.c.get(); - d[@\\"c\\"] = c; -auto d = i.d.get(); - d[@\\"d\\"] = d.buildUnsafeRawValue(); -auto k = i.k.get(); - d[@\\"k\\"] = @(k); - return d; -}) {} -inline JS::NativeSampleTurboModule::Constants::Builder::Builder(Constants i) : _factory(^{ - return i.unsafeRawValue(); -}) {} -" -`; - -exports[`GenerateStructs can generate fixture SIMPLE_STRUCT 1`] = ` -" - -namespace JS { - namespace NativeSampleTurboModule { - struct SpecSampleFuncReturnTypeDG { - bool h() const; - double i() const; - NSString *j() const; - - SpecSampleFuncReturnTypeDG(NSDictionary *const v) : _v(v) {} - private: - NSDictionary *_v; - }; - } -} - -@interface RCTCxxConvert (NativeSampleTurboModule_SpecSampleFuncReturnTypeDG) -+ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecSampleFuncReturnTypeDG:(id)json; -@end - - -namespace JS { - namespace NativeSampleTurboModule { - struct SpecSampleFuncReturnTypeD { - bool e() const; - double f() const; - JS::NativeSampleTurboModule::SpecSampleFuncReturnTypeDG g() const; - - SpecSampleFuncReturnTypeD(NSDictionary *const v) : _v(v) {} - private: - NSDictionary *_v; - }; - } -} - -@interface RCTCxxConvert (NativeSampleTurboModule_SpecSampleFuncReturnTypeD) -+ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecSampleFuncReturnTypeD:(id)json; -@end - - -namespace JS { - namespace NativeSampleTurboModule { - struct SpecSampleFuncReturnType { - bool a() const; - double b() const; - NSString *c() const; - JS::NativeSampleTurboModule::SpecSampleFuncReturnTypeD d() const; - double k() const; - - SpecSampleFuncReturnType(NSDictionary *const v) : _v(v) {} - private: - NSDictionary *_v; - }; - } -} - -@interface RCTCxxConvert (NativeSampleTurboModule_SpecSampleFuncReturnType) -+ (RCTManagedPointer *)JS_NativeSampleTurboModule_SpecSampleFuncReturnType:(id)json; -@end - -inline bool JS::NativeSampleTurboModule::SpecSampleFuncReturnType::a() const -{ - id const p = _v[@\\"a\\"]; - return RCTBridgingToBool(p); -} - - -inline double JS::NativeSampleTurboModule::SpecSampleFuncReturnType::b() const -{ - id const p = _v[@\\"b\\"]; - return RCTBridgingToDouble(p); -} - - -inline NSString *JS::NativeSampleTurboModule::SpecSampleFuncReturnType::c() const -{ - id const p = _v[@\\"c\\"]; - return RCTBridgingToString(p); -} - - -inline JS::NativeSampleTurboModule::SpecSampleFuncReturnTypeD JS::NativeSampleTurboModule::SpecSampleFuncReturnType::d() const -{ - id const p = _v[@\\"d\\"]; - return JS::NativeSampleTurboModule::SpecSampleFuncReturnTypeD(p); -} - - -inline double JS::NativeSampleTurboModule::SpecSampleFuncReturnType::k() const -{ - id const p = _v[@\\"k\\"]; - return RCTBridgingToDouble(p); -} - - -inline bool JS::NativeSampleTurboModule::SpecSampleFuncReturnTypeD::e() const -{ - id const p = _v[@\\"e\\"]; - return RCTBridgingToBool(p); -} - - -inline double JS::NativeSampleTurboModule::SpecSampleFuncReturnTypeD::f() const -{ - id const p = _v[@\\"f\\"]; - return RCTBridgingToDouble(p); -} - - -inline JS::NativeSampleTurboModule::SpecSampleFuncReturnTypeDG JS::NativeSampleTurboModule::SpecSampleFuncReturnTypeD::g() const -{ - id const p = _v[@\\"g\\"]; - return JS::NativeSampleTurboModule::SpecSampleFuncReturnTypeDG(p); -} - - -inline bool JS::NativeSampleTurboModule::SpecSampleFuncReturnTypeDG::h() const -{ - id const p = _v[@\\"h\\"]; - return RCTBridgingToBool(p); -} - - -inline double JS::NativeSampleTurboModule::SpecSampleFuncReturnTypeDG::i() const -{ - id const p = _v[@\\"i\\"]; - return RCTBridgingToDouble(p); -} - - -inline NSString *JS::NativeSampleTurboModule::SpecSampleFuncReturnTypeDG::j() const -{ - id const p = _v[@\\"j\\"]; - return RCTBridgingToString(p); -} - -" -`; diff --git a/packages/react-native-codegen/src/parsers/flow/components/commands.js b/packages/react-native-codegen/src/parsers/flow/components/commands.js index 769039be7a074d..92aef0a2ad0b18 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/commands.js +++ b/packages/react-native-codegen/src/parsers/flow/components/commands.js @@ -11,13 +11,13 @@ 'use strict'; import type {CommandTypeShape} from '../../../CodegenSchema.js'; -import type {TypeMap} from '../utils.js'; +import type {TypeDeclarationMap} from '../utils.js'; const {getValueFromTypes} = require('../utils.js'); type EventTypeAST = Object; -function buildCommandSchema(property, types: TypeMap) { +function buildCommandSchema(property, types: TypeDeclarationMap) { const name = property.key.name; const optional = property.optional; const value = getValueFromTypes(property.value, types); @@ -98,7 +98,7 @@ function buildCommandSchema(property, types: TypeMap) { function getCommands( commandTypeAST: $ReadOnlyArray, - types: TypeMap, + types: TypeDeclarationMap, ): $ReadOnlyArray { return commandTypeAST .filter(property => property.type === 'ObjectTypeProperty') diff --git a/packages/react-native-codegen/src/parsers/flow/components/extends.js b/packages/react-native-codegen/src/parsers/flow/components/extends.js index 77081974a30a69..7d5e899b80828b 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/extends.js +++ b/packages/react-native-codegen/src/parsers/flow/components/extends.js @@ -11,9 +11,9 @@ 'use strict'; import type {ExtendsPropsShape} from '../../../CodegenSchema.js'; -import type {TypeMap} from '../utils.js'; +import type {TypeDeclarationMap} from '../utils.js'; -function extendsForProp(prop: PropsAST, types: TypeMap) { +function extendsForProp(prop: PropsAST, types: TypeDeclarationMap) { if (!prop.argument) { console.log('null', prop); } @@ -38,7 +38,7 @@ function extendsForProp(prop: PropsAST, types: TypeMap) { function removeKnownExtends( typeDefinition: $ReadOnlyArray, - types: TypeMap, + types: TypeDeclarationMap, ): $ReadOnlyArray { return typeDefinition.filter( prop => @@ -52,7 +52,7 @@ type PropsAST = Object; function getExtendsProps( typeDefinition: $ReadOnlyArray, - types: TypeMap, + types: TypeDeclarationMap, ): $ReadOnlyArray { return typeDefinition .filter(prop => prop.type === 'ObjectTypeSpreadProperty') diff --git a/packages/react-native-codegen/src/parsers/flow/components/index.js b/packages/react-native-codegen/src/parsers/flow/components/index.js index eb8867401d5372..f3b9f4770fb87b 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/index.js +++ b/packages/react-native-codegen/src/parsers/flow/components/index.js @@ -167,7 +167,7 @@ function getCommandProperties(commandTypeName, types, commandOptions) { } // $FlowFixMe there's no flowtype for AST -function processComponent(ast, types): ComponentSchemaBuilderConfig { +function buildComponentSchema(ast, types): ComponentSchemaBuilderConfig { const { componentName, propsTypeName, @@ -205,5 +205,5 @@ function processComponent(ast, types): ComponentSchemaBuilderConfig { } module.exports = { - processComponent, + buildComponentSchema, }; diff --git a/packages/react-native-codegen/src/parsers/flow/components/props.js b/packages/react-native-codegen/src/parsers/flow/components/props.js index 9b549ac2337acd..50e887bcd93b9e 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/props.js +++ b/packages/react-native-codegen/src/parsers/flow/components/props.js @@ -13,9 +13,12 @@ const {getValueFromTypes} = require('../utils.js'); import type {PropTypeShape} from '../../../CodegenSchema.js'; -import type {TypeMap} from '../utils.js'; +import type {TypeDeclarationMap} from '../utils.js'; -function getPropProperties(propsTypeName: string, types: TypeMap): $FlowFixMe { +function getPropProperties( + propsTypeName: string, + types: TypeDeclarationMap, +): $FlowFixMe { const typeAlias = types[propsTypeName]; try { return typeAlias.right.typeParameters.params[0].properties; @@ -321,7 +324,7 @@ function getTypeAnnotation( } } -function buildPropSchema(property, types: TypeMap): ?PropTypeShape { +function buildPropSchema(property, types: TypeDeclarationMap): ?PropTypeShape { const name = property.key.name; const value = getValueFromTypes(property.value, types); @@ -426,7 +429,7 @@ function verifyPropNotAlreadyDefined( function flattenProperties( typeDefinition: $ReadOnlyArray, - types: TypeMap, + types: TypeDeclarationMap, ) { return typeDefinition .map(property => { @@ -456,7 +459,7 @@ function flattenProperties( function getProps( typeDefinition: $ReadOnlyArray, - types: TypeMap, + types: TypeDeclarationMap, ): $ReadOnlyArray { return flattenProperties(typeDefinition, types) .map(property => buildPropSchema(property, types)) diff --git a/packages/react-native-codegen/src/parsers/flow/components/schema.js b/packages/react-native-codegen/src/parsers/flow/components/schema.js index f55e77f162753a..c561f9e0b182fe 100644 --- a/packages/react-native-codegen/src/parsers/flow/components/schema.js +++ b/packages/react-native-codegen/src/parsers/flow/components/schema.js @@ -29,7 +29,7 @@ export type ComponentSchemaBuilderConfig = $ReadOnly<{| options?: ?OptionsShape, |}>; -function buildComponentSchema({ +function wrapComponentSchema({ filename, componentName, extendsProps, @@ -56,5 +56,5 @@ function buildComponentSchema({ } module.exports = { - buildComponentSchema, + wrapComponentSchema, }; diff --git a/packages/react-native-codegen/src/parsers/flow/index.js b/packages/react-native-codegen/src/parsers/flow/index.js index 4de02b0094393d..6f0669f998de7e 100644 --- a/packages/react-native-codegen/src/parsers/flow/index.js +++ b/packages/react-native-codegen/src/parsers/flow/index.js @@ -15,15 +15,20 @@ import type {SchemaType} from '../../CodegenSchema.js'; const flowParser = require('flow-parser'); const fs = require('fs'); const path = require('path'); -const {buildModuleSchema} = require('./modules/schema'); -const {buildComponentSchema} = require('./components/schema'); -const {processComponent} = require('./components'); -const {processModule} = require('./modules'); +const {buildComponentSchema} = require('./components'); +const {wrapComponentSchema} = require('./components/schema'); +const {buildModuleSchema} = require('./modules'); +const {wrapModuleSchema} = require('./modules/schema'); -function getTypes(ast) { +import type {TypeDeclarationMap} from './utils'; + +function getTypes(ast): TypeDeclarationMap { return ast.body.reduce((types, node) => { - if (node.type === 'ExportNamedDeclaration') { - if (node.declaration && node.declaration.type !== 'VariableDeclaration') { + if (node.type === 'ExportNamedDeclaration' && node.exportKind === 'type') { + if ( + node.declaration.type === 'TypeAlias' || + node.declaration.type === 'InterfaceDeclaration' + ) { types[node.declaration.id.name] = node.declaration; } } else if ( @@ -36,63 +41,84 @@ function getTypes(ast) { }, {}); } -function getConfigType(ast, types): 'module' | 'component' { +function isComponent(ast) { const defaultExports = ast.body.filter( node => node.type === 'ExportDefaultDeclaration', ); - let isComponent = false; + if (defaultExports.length === 0) { + return false; + } - if (defaultExports.length > 0) { - let declaration = defaultExports[0].declaration; - // codegenNativeComponent can be nested inside a cast - // expression so we need to go one level deeper - if (declaration.type === 'TypeCastExpression') { - declaration = declaration.expression; - } + let declaration = defaultExports[0].declaration; + // codegenNativeComponent can be nested inside a cast + // expression so we need to go one level deeper + if (declaration.type === 'TypeCastExpression') { + declaration = declaration.expression; + } - isComponent = - declaration && - declaration.callee && - declaration.callee.name === 'codegenNativeComponent'; + if (declaration.type !== 'CallExpression') { + return false; } - const typesExtendingTurboModule = Object.keys(types) - .map(typeName => types[typeName]) - .filter( - type => - type.extends && - type.extends[0] && - type.extends[0].id.name === 'TurboModule', - ); + return ( + declaration.callee.type === 'Identifier' && + declaration.callee.name === 'codegenNativeComponent' + ); +} - if (typesExtendingTurboModule.length > 1) { +function isModule(types: TypeDeclarationMap) { + const declaredModuleNames: Array = Object.keys(types).filter( + (typeName: string) => { + const declaration = types[typeName]; + return ( + declaration.type === 'InterfaceDeclaration' && + declaration.extends.length === 1 && + declaration.extends[0].type === 'InterfaceExtends' && + declaration.extends[0].id.name === 'TurboModule' + ); + }, + ); + + if (declaredModuleNames.length === 0) { + return false; + } + + if (declaredModuleNames.length > 1) { throw new Error( - 'Found two types extending "TurboModule" is one file. Split them into separated files.', + 'File contains declarations of more than one module: ' + + declaredModuleNames.join(', ') + + '. Please declare exactly one module in this file.', ); } - const isModule = typesExtendingTurboModule.length === 1; + return true; +} + +function getConfigType(ast, types: TypeDeclarationMap): 'module' | 'component' { + const isConfigAComponent = isComponent(ast); + const isConfigAModule = isModule(types); - if (isModule && isComponent) { + if (isConfigAModule && isConfigAComponent) { throw new Error( 'Found type extending "TurboModule" and exported "codegenNativeComponent" declaration in one file. Split them into separated files.', ); } - if (isModule) { + if (isConfigAModule) { return 'module'; - } else if (isComponent) { + } else if (isConfigAComponent) { return 'component'; } else { throw new Error( - `Default export for module specified incorrectly. It should containts - either type extending "TurboModule" or "codegenNativeComponent".`, + 'File neither contains a module declaration, nor a component declaration. ' + + 'For module declarations, please make sure your file has an InterfaceDeclaration extending TurboModule. ' + + 'For component declarations, please make sure your file has a default export calling the codegenNativeComponent(...) macro.', ); } } -function buildSchema(contents: string, filename: ?string): ?SchemaType { +function buildSchema(contents: string, filename: ?string): SchemaType { const ast = flowParser.parse(contents); const types = getTypes(ast); @@ -100,30 +126,30 @@ function buildSchema(contents: string, filename: ?string): ?SchemaType { const configType = getConfigType(ast, types); if (configType === 'component') { - return buildComponentSchema(processComponent(ast, types)); + return wrapComponentSchema(buildComponentSchema(ast, types)); } else { if (filename === undefined || filename === null) { throw new Error('Filepath expected while parasing a module'); } const moduleName = path.basename(filename).slice(6, -3); - return buildModuleSchema(processModule(types), moduleName); + return wrapModuleSchema(buildModuleSchema(moduleName, types), moduleName); } } -function parseFile(filename: string): ?SchemaType { +function parseFile(filename: string): SchemaType { const contents = fs.readFileSync(filename, 'utf8'); return buildSchema(contents, filename); } -function parseModuleFixture(filename: string): ?SchemaType { +function parseModuleFixture(filename: string): SchemaType { const contents = fs.readFileSync(filename, 'utf8'); return buildSchema(contents, 'path/NativeSampleTurboModule.js'); } -function parseString(contents: string): ?SchemaType { - return buildSchema(contents); +function parseString(contents: string, filename: ?string): SchemaType { + return buildSchema(contents, filename); } module.exports = { diff --git a/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js b/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js index a326be96accd23..7a9b29a3d868c9 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js @@ -200,7 +200,7 @@ type Foo = {| export interface Spec extends TurboModule { // Exported methods. - foo1: (x: Foo) => void; + foo1: (x: Foo) => Foo; foo2: (x: Foo) => void; } @@ -367,6 +367,7 @@ export interface Spec extends TurboModule { export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); `; + const NATIVE_MODULE_WITH_ARRAY_WITH_UNION_AND_TOUPLE = ` /** * Copyright (c) Facebook, Inc. and its affiliates. @@ -434,7 +435,7 @@ import type {TurboModule} from '../RCTExport'; import * as TurboModuleRegistry from '../TurboModuleRegistry'; export interface Spec extends TurboModule { - +getArray: (arg: Array>>>>) => Array>>; + +getArray: (arg: Array>>>>) => Array>>; } export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); diff --git a/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-test.js.snap b/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap similarity index 81% rename from packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-test.js.snap rename to packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap index 0aeb307d1b2201..6c35735fee2055 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-test.js.snap +++ b/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap @@ -1,23 +1,20 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT 1`] = `"Unsupported return type for getString: expected to find annotation for type of array contents"`; +exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT 1`] = `"Module SampleTurboModule: Detected a type of Array, without type parameters."`; -exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM 1`] = `"Unsupported type for getString, param: \\"arg\\": expected to find annotation for type of array contents"`; +exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_ARRAY_WITH_NO_TYPE_FOR_CONTENT_AS_PARAM 1`] = `"Module SampleTurboModule: Detected a type of Array, without type parameters."`; exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_NOT_ONLY_METHODS 1`] = `"Only methods are supported as module properties. Found BooleanTypeAnnotation in sampleBool"`; -exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE 1`] = `"Unsupported return promise type for getBool: expected to find annotation for type of promise content"`; +exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_PROMISE_WITHOUT_TYPE 1`] = `"Module SampleTurboModule: Detected a type of Promise, without type parameters."`; -exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_READ_ONLY_OBJECT_NO_TYPE_FOR_CONTENT 1`] = `"Unsupported param for method \\"getString\\", param \\"arg\\". No type specified for $ReadOnly"`; +exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_READ_ONLY_OBJECT_NO_TYPE_FOR_CONTENT 1`] = `"Module SampleTurboModule: Detected a type of $ReadOnly, with 0 type parameters specified. Expected exactly 1."`; -exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_UNNAMED_PARAMS 1`] = `"Unsupported type for getBool. Please provide a name for every parameter."`; +exports[`RN Codegen Flow Parser Fails with error message NATIVE_MODULES_WITH_UNNAMED_PARAMS 1`] = `"Module SampleTurboModule: Detected a FunctionTypeAnnotation with an unnamed param. Please name all params."`; -exports[`RN Codegen Flow Parser Fails with error message TWO_NATIVE_EXTENDING_TURBO_MODULE 1`] = `"Found two types extending \\"TurboModule\\" is one file. Split them into separated files."`; +exports[`RN Codegen Flow Parser Fails with error message TWO_NATIVE_EXTENDING_TURBO_MODULE 1`] = `"File contains declarations of more than one module: Spec, Spec2. Please declare exactly one module in this file."`; -exports[`RN Codegen Flow Parser Fails with error message TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT 1`] = ` -"Default export for module specified incorrectly. It should containts - either type extending \\"TurboModule\\" or \\"codegenNativeComponent\\"." -`; +exports[`RN Codegen Flow Parser Fails with error message TWO_NATIVE_MODULES_EXPORTED_WITH_DEFAULT 1`] = `"File neither contains a module declaration, nor a component declaration. For module declarations, please make sure your file has an InterfaceDeclaration extending TurboModule. For component declarations, please make sure your file has a default export calling the codegenNativeComponent(...) macro."`; exports[`RN Codegen Flow Parser can generate fixture EMPTY_NATIVE_MODULE 1`] = ` Object { @@ -42,11 +39,13 @@ Object { "SampleTurboModule": Object { "aliases": Object { "ObjectAlias": Object { + "nullable": false, "properties": Array [ Object { "name": "x", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "NumberTypeAnnotation", }, }, @@ -54,6 +53,7 @@ Object { "name": "y", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "NumberTypeAnnotation", }, }, @@ -61,6 +61,7 @@ Object { "name": "label", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "StringTypeAnnotation", }, }, @@ -68,6 +69,7 @@ Object { "name": "truthy", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "BooleanTypeAnnotation", }, }, @@ -78,13 +80,15 @@ Object { "properties": Array [ Object { "name": "getNumber", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "arg", - "nullable": false, + "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "NumberTypeAnnotation", }, }, @@ -98,8 +102,9 @@ Object { }, Object { "name": "getVoid", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [], "returnTypeAnnotation": Object { "nullable": false, @@ -110,16 +115,19 @@ Object { }, Object { "name": "getArray", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "a", - "nullable": false, + "optional": false, "typeAnnotation": Object { "elementType": Object { + "nullable": false, "type": "NumberTypeAnnotation", }, + "nullable": false, "type": "ArrayTypeAnnotation", }, }, @@ -131,6 +139,7 @@ Object { "name": "a", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "NumberTypeAnnotation", }, }, @@ -142,14 +151,16 @@ Object { }, Object { "name": "getStringFromAlias", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "a", - "nullable": false, + "optional": false, "typeAnnotation": Object { "name": "ObjectAlias", + "nullable": false, "type": "TypeAliasTypeAnnotation", }, }, @@ -179,22 +190,26 @@ Object { "properties": Array [ Object { "name": "getArray", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "arg", - "nullable": false, + "optional": false, "typeAnnotation": Object { "elementType": Object { + "nullable": false, "type": "StringTypeAnnotation", }, + "nullable": false, "type": "ArrayTypeAnnotation", }, }, ], "returnTypeAnnotation": Object { "elementType": Object { + "nullable": false, "type": "StringTypeAnnotation", }, "nullable": false, @@ -221,20 +236,20 @@ Object { "properties": Array [ Object { "name": "getArray", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "arg", - "nullable": false, + "optional": false, "typeAnnotation": Object { - "elementType": undefined, + "nullable": false, "type": "ArrayTypeAnnotation", }, }, ], "returnTypeAnnotation": Object { - "elementType": undefined, "nullable": false, "type": "ArrayTypeAnnotation", }, @@ -259,22 +274,26 @@ Object { "properties": Array [ Object { "name": "getArray", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "arg", - "nullable": false, + "optional": false, "typeAnnotation": Object { "elementType": Object { + "nullable": false, "type": "StringTypeAnnotation", }, + "nullable": false, "type": "ArrayTypeAnnotation", }, }, ], "returnTypeAnnotation": Object { "elementType": Object { + "nullable": false, "type": "StringTypeAnnotation", }, "nullable": false, @@ -285,22 +304,26 @@ Object { }, Object { "name": "getArray", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "arg", - "nullable": false, + "optional": false, "typeAnnotation": Object { "elementType": Object { + "nullable": false, "type": "StringTypeAnnotation", }, + "nullable": false, "type": "ArrayTypeAnnotation", }, }, ], "returnTypeAnnotation": Object { "elementType": Object { + "nullable": false, "type": "StringTypeAnnotation", }, "nullable": false, @@ -327,13 +350,15 @@ Object { "properties": Array [ Object { "name": "passBool", + "optional": true, "typeAnnotation": Object { - "optional": true, + "nullable": false, "params": Array [ Object { "name": "arg", - "nullable": false, + "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "BooleanTypeAnnotation", }, }, @@ -347,13 +372,15 @@ Object { }, Object { "name": "passNumber", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "arg", - "nullable": false, + "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "NumberTypeAnnotation", }, }, @@ -367,13 +394,15 @@ Object { }, Object { "name": "passString", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "arg", - "nullable": false, + "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "StringTypeAnnotation", }, }, @@ -387,13 +416,15 @@ Object { }, Object { "name": "passStringish", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "arg", - "nullable": false, + "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "StringTypeAnnotation", }, }, @@ -423,13 +454,45 @@ Object { "properties": Array [ Object { "name": "getValueWithCallback", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "callback", - "nullable": false, + "optional": false, "typeAnnotation": Object { + "nullable": false, + "params": Array [ + Object { + "name": "value", + "optional": false, + "typeAnnotation": Object { + "nullable": false, + "type": "StringTypeAnnotation", + }, + }, + Object { + "name": "arr", + "optional": false, + "typeAnnotation": Object { + "elementType": Object { + "elementType": Object { + "nullable": false, + "type": "StringTypeAnnotation", + }, + "nullable": false, + "type": "ArrayTypeAnnotation", + }, + "nullable": false, + "type": "ArrayTypeAnnotation", + }, + }, + ], + "returnTypeAnnotation": Object { + "nullable": false, + "type": "VoidTypeAnnotation", + }, "type": "FunctionTypeAnnotation", }, }, @@ -459,28 +522,35 @@ Object { "properties": Array [ Object { "name": "getArray", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "arg", - "nullable": false, + "optional": false, "typeAnnotation": Object { "elementType": Object { "elementType": Object { "elementType": Object { "elementType": Object { "elementType": Object { - "type": "AnyTypeAnnotation", + "nullable": false, + "type": "StringTypeAnnotation", }, + "nullable": false, "type": "ArrayTypeAnnotation", }, + "nullable": false, "type": "ArrayTypeAnnotation", }, + "nullable": false, "type": "ArrayTypeAnnotation", }, + "nullable": false, "type": "ArrayTypeAnnotation", }, + "nullable": false, "type": "ArrayTypeAnnotation", }, }, @@ -489,10 +559,13 @@ Object { "elementType": Object { "elementType": Object { "elementType": Object { + "nullable": false, "type": "StringTypeAnnotation", }, + "nullable": false, "type": "ArrayTypeAnnotation", }, + "nullable": false, "type": "ArrayTypeAnnotation", }, "nullable": false, @@ -519,23 +592,27 @@ Object { "properties": Array [ Object { "name": "getObject", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "arg", - "nullable": false, + "optional": false, "typeAnnotation": Object { + "nullable": false, "properties": Array [ Object { "name": "const1", "optional": false, "typeAnnotation": Object { + "nullable": false, "properties": Array [ Object { "name": "const1", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "BooleanTypeAnnotation", }, }, @@ -555,11 +632,13 @@ Object { "name": "const1", "optional": false, "typeAnnotation": Object { + "nullable": false, "properties": Array [ Object { "name": "const1", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "BooleanTypeAnnotation", }, }, @@ -575,23 +654,27 @@ Object { }, Object { "name": "getReadOnlyObject", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "arg", - "nullable": false, + "optional": false, "typeAnnotation": Object { + "nullable": false, "properties": Array [ Object { "name": "const1", "optional": false, "typeAnnotation": Object { + "nullable": false, "properties": Array [ Object { "name": "const1", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "BooleanTypeAnnotation", }, }, @@ -611,11 +694,13 @@ Object { "name": "const1", "optional": false, "typeAnnotation": Object { + "nullable": false, "properties": Array [ Object { "name": "const1", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "BooleanTypeAnnotation", }, }, @@ -631,18 +716,21 @@ Object { }, Object { "name": "getObject2", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "arg", - "nullable": false, + "optional": false, "typeAnnotation": Object { + "nullable": false, "properties": Array [ Object { "name": "a", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "StringTypeAnnotation", }, }, @@ -660,23 +748,27 @@ Object { }, Object { "name": "getObjectInArray", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "arg", - "nullable": false, + "optional": false, "typeAnnotation": Object { + "nullable": false, "properties": Array [ Object { "name": "const1", "optional": false, "typeAnnotation": Object { + "nullable": false, "properties": Array [ Object { "name": "const1", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "BooleanTypeAnnotation", }, }, @@ -691,16 +783,19 @@ Object { ], "returnTypeAnnotation": Object { "elementType": Object { + "nullable": false, "properties": Array [ Object { "name": "const1", "optional": false, "typeAnnotation": Object { + "nullable": false, "properties": Array [ Object { "name": "const1", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "BooleanTypeAnnotation", }, }, @@ -735,8 +830,9 @@ Object { "properties": Array [ Object { "name": "getConstants", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [], "returnTypeAnnotation": Object { "nullable": false, @@ -745,6 +841,7 @@ Object { "name": "isTesting", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "BooleanTypeAnnotation", }, }, @@ -752,11 +849,13 @@ Object { "name": "reactNativeVersion", "optional": false, "typeAnnotation": Object { + "nullable": false, "properties": Array [ Object { "name": "major", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "NumberTypeAnnotation", }, }, @@ -764,6 +863,7 @@ Object { "name": "minor", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "NumberTypeAnnotation", }, }, @@ -771,13 +871,15 @@ Object { "name": "patch", "optional": true, "typeAnnotation": Object { + "nullable": false, "type": "NumberTypeAnnotation", }, }, Object { "name": "prerelease", - "optional": true, + "optional": false, "typeAnnotation": Object { + "nullable": true, "type": "NumberTypeAnnotation", }, }, @@ -789,6 +891,7 @@ Object { "name": "forceTouchAvailable", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "BooleanTypeAnnotation", }, }, @@ -796,6 +899,7 @@ Object { "name": "osVersion", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "StringTypeAnnotation", }, }, @@ -803,6 +907,7 @@ Object { "name": "systemName", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "StringTypeAnnotation", }, }, @@ -810,6 +915,7 @@ Object { "name": "interfaceIdiom", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "StringTypeAnnotation", }, }, @@ -837,13 +943,15 @@ Object { "properties": Array [ Object { "name": "getInt", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "arg", - "nullable": false, + "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "Int32TypeAnnotation", }, }, @@ -857,13 +965,15 @@ Object { }, Object { "name": "getFloat", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "arg", - "nullable": false, + "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "FloatTypeAnnotation", }, }, @@ -891,11 +1001,13 @@ Object { "SampleTurboModule": Object { "aliases": Object { "Bar": Object { + "nullable": false, "properties": Array [ Object { "name": "z", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "NumberTypeAnnotation", }, }, @@ -903,12 +1015,14 @@ Object { "type": "ObjectTypeAnnotation", }, "Foo": Object { + "nullable": false, "properties": Array [ Object { "name": "bar1", "optional": false, "typeAnnotation": Object { "name": "Bar", + "nullable": false, "type": "TypeAliasTypeAnnotation", }, }, @@ -917,6 +1031,7 @@ Object { "optional": false, "typeAnnotation": Object { "name": "Bar", + "nullable": false, "type": "TypeAliasTypeAnnotation", }, }, @@ -927,35 +1042,40 @@ Object { "properties": Array [ Object { "name": "foo1", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "x", - "nullable": false, + "optional": false, "typeAnnotation": Object { "name": "Foo", + "nullable": false, "type": "TypeAliasTypeAnnotation", }, }, ], "returnTypeAnnotation": Object { + "name": "Foo", "nullable": false, - "type": "VoidTypeAnnotation", + "type": "TypeAliasTypeAnnotation", }, "type": "FunctionTypeAnnotation", }, }, Object { "name": "foo2", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "x", - "nullable": false, + "optional": false, "typeAnnotation": Object { "name": "Foo", + "nullable": false, "type": "TypeAliasTypeAnnotation", }, }, @@ -985,13 +1105,15 @@ Object { "properties": Array [ Object { "name": "voidFunc", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "arg", - "nullable": true, + "optional": false, "typeAnnotation": Object { + "nullable": true, "type": "StringTypeAnnotation", }, }, @@ -1019,11 +1141,13 @@ Object { "SampleTurboModule": Object { "aliases": Object { "DisplayMetricsAndroid": Object { + "nullable": false, "properties": Array [ Object { "name": "width", "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "NumberTypeAnnotation", }, }, @@ -1034,8 +1158,9 @@ Object { "properties": Array [ Object { "name": "getConstants", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [], "returnTypeAnnotation": Object { "nullable": false, @@ -1044,12 +1169,14 @@ Object { "name": "Dimensions", "optional": false, "typeAnnotation": Object { + "nullable": false, "properties": Array [ Object { "name": "windowPhysicalPixels", "optional": false, "typeAnnotation": Object { "name": "DisplayMetricsAndroid", + "nullable": false, "type": "TypeAliasTypeAnnotation", }, }, @@ -1065,8 +1192,9 @@ Object { }, Object { "name": "getConstants2", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [], "returnTypeAnnotation": Object { "nullable": false, @@ -1075,12 +1203,14 @@ Object { "name": "Dimensions", "optional": false, "typeAnnotation": Object { + "nullable": false, "properties": Array [ Object { "name": "windowPhysicalPixels", "optional": false, "typeAnnotation": Object { "name": "DisplayMetricsAndroid", + "nullable": false, "type": "TypeAliasTypeAnnotation", }, }, @@ -1108,53 +1238,43 @@ Object { "NativeSampleTurboModule": Object { "nativeModules": Object { "SampleTurboModule": Object { - "aliases": Object { - "SomeObj": Object { - "properties": Array [ - Object { - "name": "a", - "optional": false, - "typeAnnotation": Object { - "type": "StringTypeAnnotation", - }, - }, - ], - "type": "ObjectTypeAnnotation", - }, - }, + "aliases": Object {}, "properties": Array [ Object { "name": "getValueWithPromise", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [], "returnTypeAnnotation": Object { "nullable": false, - "type": "GenericPromiseTypeAnnotation", + "type": "PromiseTypeAnnotation", }, "type": "FunctionTypeAnnotation", }, }, Object { "name": "getValueWithPromiseDefinedSomewhereElse", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [], "returnTypeAnnotation": Object { "nullable": false, - "type": "GenericPromiseTypeAnnotation", + "type": "PromiseTypeAnnotation", }, "type": "FunctionTypeAnnotation", }, }, Object { "name": "getValueWithPromiseObjDefinedSomewhereElse", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [], "returnTypeAnnotation": Object { "nullable": false, - "type": "GenericPromiseTypeAnnotation", + "type": "PromiseTypeAnnotation", }, "type": "FunctionTypeAnnotation", }, @@ -1177,14 +1297,16 @@ Object { "properties": Array [ Object { "name": "getRootTag", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "rootTag", - "nullable": false, + "optional": false, "typeAnnotation": Object { "name": "RootTag", + "nullable": false, "type": "ReservedFunctionValueTypeAnnotation", }, }, @@ -1215,13 +1337,15 @@ Object { "properties": Array [ Object { "name": "getObject", + "optional": false, "typeAnnotation": Object { - "optional": false, + "nullable": false, "params": Array [ Object { "name": "o", - "nullable": false, + "optional": false, "typeAnnotation": Object { + "nullable": false, "type": "GenericObjectTypeAnnotation", }, }, diff --git a/packages/react-native-codegen/src/parsers/flow/modules/__tests__/module-parser-e2e-test.js b/packages/react-native-codegen/src/parsers/flow/modules/__tests__/module-parser-e2e-test.js new file mode 100644 index 00000000000000..b48f155accf153 --- /dev/null +++ b/packages/react-native-codegen/src/parsers/flow/modules/__tests__/module-parser-e2e-test.js @@ -0,0 +1,1186 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +import type { + ReservedFunctionValueTypeName, + NativeModuleSchema, +} from '../../../../CodegenSchema'; +const {parseString} = require('../../index.js'); +const { + FlowGenericNotTypeParameterizedParserError, + UnrecognizedFlowTypeAnnotationParserError, + UnrecognizedFlowGenericParserError, + UnnamedFunctionTypeAnnotationParamError, +} = require('../errors.js'); +const invariant = require('invariant'); + +type PrimitiveTypeAnnotationType = + | 'StringTypeAnnotation' + | 'NumberTypeAnnotation' + | 'Int32TypeAnnotation' + | 'DoubleTypeAnnotation' + | 'FloatTypeAnnotation' + | 'BooleanTypeAnnotation'; + +const PRIMITIVES: $ReadOnlyArray<[string, PrimitiveTypeAnnotationType]> = [ + ['string', 'StringTypeAnnotation'], + ['number', 'NumberTypeAnnotation'], + ['Int32', 'Int32TypeAnnotation'], + ['Double', 'DoubleTypeAnnotation'], + ['Float', 'FloatTypeAnnotation'], + ['boolean', 'BooleanTypeAnnotation'], +]; + +const RESERVED_FUNCTION_VALUE_TYPE_NAME: $ReadOnlyArray = [ + 'RootTag', +]; + +const MODULE_NAME = 'Foo'; + +const TYPE_ALIAS_DECLARATIONS = ` +type Animal = {| + name: string, +|}; + +type AnimalPointer = Animal; +`; + +function expectAnimalTypeAliasToExist(module: NativeModuleSchema) { + const animalAlias = module.aliases.Animal; + + expect(animalAlias).not.toBe(null); + invariant(animalAlias != null, ''); + expect(animalAlias.type).toBe('ObjectTypeAnnotation'); + expect(animalAlias.nullable).toBe(false); + expect(animalAlias.properties.length).toBe(1); + expect(animalAlias.properties[0].name).toBe('name'); + expect(animalAlias.properties[0].optional).toBe(false); + expect(animalAlias.properties[0].typeAnnotation.type).toBe( + 'StringTypeAnnotation', + ); + expect(animalAlias.properties[0].typeAnnotation.nullable).toBe(false); +} + +describe('Flow Module Parser', () => { + describe('Parameter Parsing', () => { + it("should fail parsing when a method has an parameter of type 'any'", () => { + const parser = () => + parseModule(` + import type {TurboModule} from 'RCTExport'; + import * as TurboModuleRegistry from 'TurboModuleRegistry'; + export interface Spec extends TurboModule { + +useArg(arg: any): void; + } + export default TurboModuleRegistry.get('Foo'); + `); + + expect(parser).toThrow( + new UnrecognizedFlowTypeAnnotationParserError( + MODULE_NAME, + 'AnyTypeAnnotation', + ), + ); + }); + + it('should fail parsing when a function param type is unamed', () => { + const parser = () => + parseModule(` + import type {TurboModule} from 'RCTExport'; + import * as TurboModuleRegistry from 'TurboModuleRegistry'; + export interface Spec extends TurboModule { + +useArg(boolean): void; + } + export default TurboModuleRegistry.get('Foo'); + `); + + expect(parser).toThrow( + new UnnamedFunctionTypeAnnotationParamError(MODULE_NAME), + ); + }); + + [ + {nullable: false, optional: false}, + {nullable: false, optional: true}, + {nullable: true, optional: false}, + {nullable: true, optional: true}, + ].forEach(({nullable, optional}) => { + const PARAM_TYPE_DESCRIPTION = + nullable && optional + ? 'a nullable and optional' + : nullable + ? 'a nullable' + : optional + ? 'an optional' + : 'a required'; + + function annotateArg(paramName: string, paramType: string) { + if (nullable && optional) { + return `${paramName}?: ?${paramType}`; + } + if (nullable) { + return `${paramName}: ?${paramType}`; + } + if (optional) { + return `${paramName}?: ${paramType}`; + } + return `${paramName}: ${paramType}`; + } + + function parseParamType(paramName: string, paramType: string) { + const module = parseModule(` + import type {TurboModule} from 'RCTExport'; + import * as TurboModuleRegistry from 'TurboModuleRegistry'; + + ${TYPE_ALIAS_DECLARATIONS} + + export interface Spec extends TurboModule { + +useArg(${annotateArg(paramName, paramType)}): void; + } + export default TurboModuleRegistry.get('Foo'); + `); + + expect(module.properties[0]).not.toBe(null); + const param = module.properties[0].typeAnnotation.params[0]; + expect(param).not.toBe(null); + expect(param.name).toBe(paramName); + expect(param.optional).toBe(optional); + expect(param.typeAnnotation.nullable).toBe(nullable); + + return [param, module]; + } + + describe( + (nullable && optional + ? 'Nullable and Optional' + : nullable + ? 'Nullable' + : optional + ? 'Optional' + : 'Required') + ' Parameter', + () => { + it(`should not parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter of type 'Function'`, () => { + expect(() => parseParamType('arg', 'Function')).toThrow( + new UnrecognizedFlowGenericParserError(MODULE_NAME, 'Function'), + ); + }); + + describe('Primitive types', () => { + PRIMITIVES.forEach(([FLOW_TYPE, PARSED_TYPE_NAME]) => { + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} primitive parameter of type '${FLOW_TYPE}'`, () => { + const [param] = parseParamType('arg', FLOW_TYPE); + expect(param.typeAnnotation.type).toBe(PARSED_TYPE_NAME); + }); + }); + }); + + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter of type 'Object'`, () => { + const [param] = parseParamType('arg', 'Object'); + expect(param.typeAnnotation.type).toBe( + 'GenericObjectTypeAnnotation', + ); + }); + + describe('Reserved Types', () => { + RESERVED_FUNCTION_VALUE_TYPE_NAME.forEach(FLOW_TYPE => { + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter of reserved type '${FLOW_TYPE}'`, () => { + const [param] = parseParamType('arg', FLOW_TYPE); + + expect(param.typeAnnotation.type).toBe( + 'ReservedFunctionValueTypeAnnotation', + ); + invariant( + param.typeAnnotation.type === + 'ReservedFunctionValueTypeAnnotation', + 'Param must be a Reserved type', + ); + + expect(param.typeAnnotation.name).toBe(FLOW_TYPE); + }); + }); + }); + + describe('Array Types', () => { + it(`should not parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter of type 'Array'`, () => { + expect(() => parseParamType('arg', 'Array')).toThrow( + new FlowGenericNotTypeParameterizedParserError( + MODULE_NAME, + 'Array', + ), + ); + }); + + function parseParamArrayElementType( + paramName: string, + paramType: string, + ) { + const [param, module] = parseParamType( + paramName, + `Array<${paramType}>`, + ); + + expect(param.typeAnnotation.type).toBe('ArrayTypeAnnotation'); + invariant( + param.typeAnnotation.type === 'ArrayTypeAnnotation', + '', + ); + + expect(param.typeAnnotation.elementType).not.toBe(null); + invariant(param.typeAnnotation.elementType != null, ''); + return [param.typeAnnotation.elementType, module]; + } + + // TODO: Do we support nullable element types? + + describe('Primitive Element Types', () => { + PRIMITIVES.forEach(([FLOW_TYPE, PARSED_TYPE_NAME]) => { + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter of type 'Array<${FLOW_TYPE}>'`, () => { + const [elementType] = parseParamArrayElementType( + 'arg', + FLOW_TYPE, + ); + expect(elementType.type).toBe(PARSED_TYPE_NAME); + }); + }); + }); + + describe('Reserved Element Types', () => { + RESERVED_FUNCTION_VALUE_TYPE_NAME.forEach(FLOW_TYPE => { + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter of type 'Array<${FLOW_TYPE}>'`, () => { + const [elementType] = parseParamArrayElementType( + 'arg', + FLOW_TYPE, + ); + expect(elementType.type).toBe( + 'ReservedFunctionValueTypeAnnotation', + ); + invariant( + elementType.type === 'ReservedFunctionValueTypeAnnotation', + '', + ); + + expect(elementType.name).toBe(FLOW_TYPE); + }); + }); + }); + + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter of type 'Array'`, () => { + const [elementType] = parseParamArrayElementType('arg', 'Object'); + expect(elementType.type).toBe('GenericObjectTypeAnnotation'); + }); + + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter type of some array of an alias`, () => { + const [elementType, module] = parseParamArrayElementType( + 'arg', + 'Animal', + ); + expect(elementType.type).toBe('TypeAliasTypeAnnotation'); + invariant(elementType.type === 'TypeAliasTypeAnnotation', ''); + + expect(elementType.name).toBe('Animal'); + expectAnimalTypeAliasToExist(module); + }); + + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter of type 'Array<{|foo: ?string|}>'`, () => { + const [elementType] = parseParamArrayElementType( + 'arg', + '{|foo: ?string|}', + ); + expect(elementType).not.toBe(null); + + expect(elementType.type).toBe('ObjectTypeAnnotation'); + invariant(elementType.type === 'ObjectTypeAnnotation', ''); + + const {properties} = elementType; + invariant(properties != null, ''); + + expect(properties).not.toBe(null); + expect(properties[0]).not.toBe(null); + expect(properties[0].name).toBe('foo'); + expect(properties[0].typeAnnotation).not.toBe(null); + expect(properties[0].typeAnnotation?.type).toBe( + 'StringTypeAnnotation', + ); + expect(properties[0].typeAnnotation?.nullable).toBe(true); + expect(properties[0].optional).toBe(false); + }); + }); + + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter type of some type alias`, () => { + const [param, module] = parseParamType('arg', 'Animal'); + expect(param.typeAnnotation.type).toBe('TypeAliasTypeAnnotation'); + invariant( + param.typeAnnotation.type === 'TypeAliasTypeAnnotation', + '', + ); + + expect(param.typeAnnotation.name).toBe('Animal'); + expectAnimalTypeAliasToExist(module); + }); + + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter type of some type alias that points to another type alias`, () => { + const [param, module] = parseParamType('arg', 'AnimalPointer'); + expect(param.typeAnnotation.type).toBe('TypeAliasTypeAnnotation'); + invariant( + param.typeAnnotation.type === 'TypeAliasTypeAnnotation', + '', + ); + + expect(param.typeAnnotation.name).toBe('Animal'); + expectAnimalTypeAliasToExist(module); + }); + + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter type of some type alias that points to another nullable type alias`, () => { + const module = parseModule(` + import type {TurboModule} from 'RCTExport'; + import * as TurboModuleRegistry from 'TurboModuleRegistry'; + + type Animal = ?{| + name: string, + |}; + + type AnimalPointer = Animal; + + export interface Spec extends TurboModule { + +useArg(${annotateArg('arg', 'AnimalPointer')}): void; + } + export default TurboModuleRegistry.get('Foo'); + `); + + expect(module.properties[0]).not.toBe(null); + const param = module.properties[0].typeAnnotation.params[0]; + expect(param.name).toBe('arg'); + expect(param.optional).toBe(optional); + + // The TypeAliasAnnotation is called Animal, and is nullable + expect(param.typeAnnotation.type).toBe('TypeAliasTypeAnnotation'); + invariant( + param.typeAnnotation.type === 'TypeAliasTypeAnnotation', + '', + ); + expect(param.typeAnnotation.name).toBe('Animal'); + expect(param.typeAnnotation.nullable).toBe(true); + + // The Animal type alias RHS is valid, and non-null + expectAnimalTypeAliasToExist(module); + }); + + [ + {nullable: false, optional: false}, + {nullable: false, optional: true}, + {nullable: true, optional: false}, + {nullable: true, optional: true}, + ].forEach(({nullable: isPropNullable, optional: isPropOptional}) => { + const PROP_TYPE_DESCRIPTION = + isPropNullable && isPropOptional + ? 'a nullable and optional' + : isPropNullable + ? 'a nullable' + : isPropOptional + ? 'an optional' + : 'a required'; + + function annotateProp(propName: string, propType: string) { + if (isPropNullable && isPropOptional) { + return `${propName}?: ?${propType}`; + } + if (isPropNullable) { + return `${propName}: ?${propType}`; + } + if (isPropOptional) { + return `${propName}?: ${propType}`; + } + return `${propName}: ${propType}`; + } + + function parseParamTypeObjectLiteralProp( + propName: string, + propType: string, + ) { + const [param, module] = parseParamType( + 'arg', + `{|${annotateProp(propName, propType)}|}`, + ); + + expect(param.typeAnnotation.type).toBe('ObjectTypeAnnotation'); + invariant( + param.typeAnnotation.type === 'ObjectTypeAnnotation', + '', + ); + + const {properties} = param.typeAnnotation; + + expect(properties).not.toBe(null); + invariant(properties != null, ''); + + expect(properties.length).toBe(1); + expect(properties[0].name).toBe(propName); + expect(properties[0].optional).toBe(isPropOptional); + expect(properties[0].typeAnnotation).not.toBe(null); + expect(properties[0].typeAnnotation.nullable).toBe( + isPropNullable, + ); + invariant(properties[0].typeAnnotation != null, ''); + + return [ + { + ...properties[0], + typeAnnotation: properties[0].typeAnnotation, + }, + module, + ]; + } + + describe( + (isPropNullable && isPropOptional + ? 'Nullable and Optional' + : isPropNullable + ? 'Nullable' + : isPropOptional + ? 'Optional' + : 'Required') + ' Property', + () => { + describe('Props with Primitive Types', () => { + PRIMITIVES.forEach(([FLOW_TYPE, PARSED_TYPE_NAME]) => { + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of primitive type '${FLOW_TYPE}'`, () => { + const [prop] = parseParamTypeObjectLiteralProp( + 'prop', + FLOW_TYPE, + ); + expect(prop.typeAnnotation.type).toBe(PARSED_TYPE_NAME); + }); + }); + }); + + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of type 'Object'`, () => { + const [prop] = parseParamTypeObjectLiteralProp( + 'prop', + 'Object', + ); + expect(prop.typeAnnotation.type).toBe( + 'GenericObjectTypeAnnotation', + ); + }); + + describe('Props with Reserved Types', () => { + RESERVED_FUNCTION_VALUE_TYPE_NAME.forEach(FLOW_TYPE => { + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of reserved type '${FLOW_TYPE}'`, () => { + const [prop] = parseParamTypeObjectLiteralProp( + 'prop', + FLOW_TYPE, + ); + expect(prop.typeAnnotation.type).toBe( + 'ReservedFunctionValueTypeAnnotation', + ); + invariant( + prop.typeAnnotation.type === + 'ReservedFunctionValueTypeAnnotation', + '', + ); + + expect(prop.typeAnnotation.name).toBe(FLOW_TYPE); + }); + }); + }); + + describe('Props with Array Types', () => { + it(`should not parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of type 'Array`, () => { + expect(() => + parseParamTypeObjectLiteralProp('prop', 'Array'), + ).toThrow( + new FlowGenericNotTypeParameterizedParserError( + MODULE_NAME, + 'Array', + ), + ); + }); + + function parseArrayElementType( + propName: string, + arrayElementType: string, + ) { + const [property, module] = parseParamTypeObjectLiteralProp( + 'propName', + `Array<${arrayElementType}>`, + ); + expect(property.typeAnnotation.type).toBe( + 'ArrayTypeAnnotation', + ); + invariant( + property.typeAnnotation.type === 'ArrayTypeAnnotation', + '', + ); + + const {elementType} = property.typeAnnotation; + expect(elementType).not.toBe(null); + invariant(elementType != null, ''); + return [elementType, module]; + } + + PRIMITIVES.forEach(([FLOW_TYPE, PARSED_TYPE_NAME]) => { + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of type 'Array<${FLOW_TYPE}>'`, () => { + const [elementType] = parseArrayElementType( + 'prop', + FLOW_TYPE, + ); + + expect(elementType.type).toBe(PARSED_TYPE_NAME); + }); + }); + + RESERVED_FUNCTION_VALUE_TYPE_NAME.forEach(FLOW_TYPE => { + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of type 'Array<${FLOW_TYPE}>'`, () => { + const [elementType] = parseArrayElementType( + 'prop', + FLOW_TYPE, + ); + + expect(elementType.type).toBe( + 'ReservedFunctionValueTypeAnnotation', + ); + invariant( + elementType.type === + 'ReservedFunctionValueTypeAnnotation', + '', + ); + expect(elementType.name).toBe(FLOW_TYPE); + }); + }); + + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of type 'Array'`, () => { + const [elementType] = parseArrayElementType( + 'prop', + 'Object', + ); + expect(elementType.type).toBe( + 'GenericObjectTypeAnnotation', + ); + }); + + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of type of some array of an alias`, () => { + const [elementType, module] = parseArrayElementType( + 'prop', + 'Animal', + ); + + expect(elementType.type).toBe('TypeAliasTypeAnnotation'); + invariant( + elementType.type === 'TypeAliasTypeAnnotation', + '', + ); + + expect(elementType.name).toBe('Animal'); + expectAnimalTypeAliasToExist(module); + }); + + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of 'Array<{|foo: ?string|}>'`, () => { + const [elementType] = parseArrayElementType( + 'prop', + '{|foo: ?string|}', + ); + + expect(elementType.type).toBe('ObjectTypeAnnotation'); + invariant(elementType.type === 'ObjectTypeAnnotation', ''); + + const {properties} = elementType; + expect(properties).not.toBe(null); + invariant(properties != null, ''); + + expect(properties[0]).not.toBe(null); + expect(properties[0].name).toBe('foo'); + expect(properties[0].typeAnnotation).not.toBe(null); + expect(properties[0].typeAnnotation?.type).toBe( + 'StringTypeAnnotation', + ); + expect(properties[0].typeAnnotation?.nullable).toBe(true); + expect(properties[0].optional).toBe(false); + }); + }); + + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of type '{|foo: ?string|}'`, () => { + const [property] = parseParamTypeObjectLiteralProp( + 'prop', + '{|foo: ?string|}', + ); + + expect(property.typeAnnotation.type).toBe( + 'ObjectTypeAnnotation', + ); + invariant( + property.typeAnnotation.type === 'ObjectTypeAnnotation', + '', + ); + + const {properties} = property.typeAnnotation; + expect(properties).not.toBe(null); + invariant(properties != null, ''); + + expect(properties[0]).not.toBe(null); + expect(properties[0].name).toBe('foo'); + expect(properties[0].typeAnnotation).not.toBe(null); + expect(properties[0].typeAnnotation?.type).toBe( + 'StringTypeAnnotation', + ); + expect(properties[0].typeAnnotation?.nullable).toBe(true); + expect(properties[0].optional).toBe(false); + }); + + it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of some type alias`, () => { + const [property, module] = parseParamTypeObjectLiteralProp( + 'prop', + 'Animal', + ); + + expect(property.typeAnnotation.type).toBe( + 'TypeAliasTypeAnnotation', + ); + invariant( + property.typeAnnotation.type === 'TypeAliasTypeAnnotation', + '', + ); + + expect(property.typeAnnotation.name).toBe('Animal'); + expectAnimalTypeAliasToExist(module); + }); + }, + ); + }); + }, + ); + }); + }); + + describe('Return Parsing', () => { + it('should parse methods that have a return type of void', () => { + const module = parseModule(` + import type {TurboModule} from 'RCTExport'; + import * as TurboModuleRegistry from 'TurboModuleRegistry'; + export interface Spec extends TurboModule { + +useArg(): void; + } + export default TurboModuleRegistry.get('Foo'); + `); + + expect(module.properties[0]).not.toBe(null); + const {returnTypeAnnotation} = module.properties[0].typeAnnotation; + expect(returnTypeAnnotation).not.toBe(null); + expect(returnTypeAnnotation.type).toBe('VoidTypeAnnotation'); + expect(returnTypeAnnotation.nullable).toBe(false); + }); + + [true, false].forEach(IS_RETURN_TYPE_NULLABLE => { + const RETURN_TYPE_DESCRIPTION = IS_RETURN_TYPE_NULLABLE + ? 'a nullable' + : 'a non-nullable'; + const annotateRet = retType => + IS_RETURN_TYPE_NULLABLE ? `?${retType}` : retType; + + function parseReturnType(flowType: string) { + const module = parseModule(` + import type {TurboModule} from 'RCTExport'; + import * as TurboModuleRegistry from 'TurboModuleRegistry'; + + ${TYPE_ALIAS_DECLARATIONS} + + export interface Spec extends TurboModule { + +useArg(): ${annotateRet(flowType)}; + } + export default TurboModuleRegistry.get('Foo'); + `); + + expect(module.properties[0]).not.toBe(null); + const {returnTypeAnnotation} = module.properties[0].typeAnnotation; + expect(returnTypeAnnotation).not.toBe(null); + expect(returnTypeAnnotation.nullable).toBe(IS_RETURN_TYPE_NULLABLE); + return [returnTypeAnnotation, module]; + } + + describe( + IS_RETURN_TYPE_NULLABLE ? 'Nullable Returns' : 'Non-Nullable Returns', + () => { + ['Promise', 'Promise<{||}>', 'Promise<*>'].forEach( + promiseFlowType => { + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return of type '${promiseFlowType}'`, () => { + const [returnTypeAnnotation] = parseReturnType(promiseFlowType); + expect(returnTypeAnnotation.type).toBe('PromiseTypeAnnotation'); + }); + }, + ); + + describe('Primitive Types', () => { + PRIMITIVES.forEach(([FLOW_TYPE, PARSED_TYPE_NAME]) => { + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} primitive return of type '${FLOW_TYPE}'`, () => { + const [returnTypeAnnotation] = parseReturnType(FLOW_TYPE); + expect(returnTypeAnnotation.type).toBe(PARSED_TYPE_NAME); + }); + }); + }); + + describe('Reserved Types', () => { + RESERVED_FUNCTION_VALUE_TYPE_NAME.forEach(FLOW_TYPE => { + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} reserved return of type '${FLOW_TYPE}'`, () => { + const [returnTypeAnnotation] = parseReturnType(FLOW_TYPE); + expect(returnTypeAnnotation.type).toBe( + 'ReservedFunctionValueTypeAnnotation', + ); + invariant( + returnTypeAnnotation.type === + 'ReservedFunctionValueTypeAnnotation', + '', + ); + expect(returnTypeAnnotation.name).toBe(FLOW_TYPE); + }); + }); + }); + + describe('Array Types', () => { + it(`should not parse methods that have ${RETURN_TYPE_DESCRIPTION} return of type 'Array'`, () => { + expect(() => parseReturnType('Array')).toThrow( + new FlowGenericNotTypeParameterizedParserError( + MODULE_NAME, + 'Array', + ), + ); + }); + + function parseArrayElementReturnType(flowType: string) { + const [returnTypeAnnotation, module] = parseReturnType( + 'Array' + (flowType != null ? `<${flowType}>` : ''), + ); + expect(returnTypeAnnotation.type).toBe('ArrayTypeAnnotation'); + invariant( + returnTypeAnnotation.type === 'ArrayTypeAnnotation', + '', + ); + + const {elementType} = returnTypeAnnotation; + expect(elementType).not.toBe(null); + invariant(elementType != null, ''); + return [elementType, module]; + } + + // TODO: Do we support nullable element types? + + describe('Primitive Element Types', () => { + PRIMITIVES.forEach(([FLOW_TYPE, PARSED_TYPE_NAME]) => { + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return of type 'Array<${FLOW_TYPE}>'`, () => { + const [elementType, module] = parseArrayElementReturnType( + FLOW_TYPE, + ); + expect(elementType.type).toBe(PARSED_TYPE_NAME); + }); + }); + }); + + describe('Reserved Element Types', () => { + RESERVED_FUNCTION_VALUE_TYPE_NAME.forEach(FLOW_TYPE => { + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return of type 'Array<${FLOW_TYPE}>'`, () => { + const [elementType] = parseArrayElementReturnType(FLOW_TYPE); + expect(elementType.type).toBe( + 'ReservedFunctionValueTypeAnnotation', + ); + invariant( + elementType.type === 'ReservedFunctionValueTypeAnnotation', + '', + ); + + expect(elementType.name).toBe(FLOW_TYPE); + }); + }); + }); + + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return of type 'Array'`, () => { + const [elementType] = parseArrayElementReturnType('Object'); + expect(elementType.type).toBe('GenericObjectTypeAnnotation'); + }); + + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return type of some array of an alias`, () => { + const [elementType, module] = parseArrayElementReturnType( + 'Animal', + ); + expect(elementType.type).toBe('TypeAliasTypeAnnotation'); + invariant(elementType.type === 'TypeAliasTypeAnnotation', ''); + expect(elementType.name).toBe('Animal'); + expectAnimalTypeAliasToExist(module); + }); + + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return of type 'Array<{|foo: ?string|}>'`, () => { + const [elementType] = parseArrayElementReturnType( + '{|foo: ?string|}', + ); + expect(elementType.type).toBe('ObjectTypeAnnotation'); + invariant(elementType.type === 'ObjectTypeAnnotation', ''); + + const {properties} = elementType; + expect(properties).not.toBe(null); + invariant(properties != null, ''); + + expect(properties[0]).not.toBe(null); + expect(properties[0].name).toBe('foo'); + expect(properties[0].typeAnnotation).not.toBe(null); + expect(properties[0].typeAnnotation?.type).toBe( + 'StringTypeAnnotation', + ); + expect(properties[0].typeAnnotation?.nullable).toBe(true); + expect(properties[0].optional).toBe(false); + }); + }); + + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return type of some type alias`, () => { + const [returnTypeAnnotation, module] = parseReturnType('Animal'); + expect(returnTypeAnnotation.type).toBe('TypeAliasTypeAnnotation'); + invariant( + returnTypeAnnotation.type === 'TypeAliasTypeAnnotation', + '', + ); + expect(returnTypeAnnotation.name).toBe('Animal'); + expectAnimalTypeAliasToExist(module); + }); + + it(`should not parse methods that have ${RETURN_TYPE_DESCRIPTION} return of type 'Function'`, () => { + expect(() => parseReturnType('Function')).toThrow( + new UnrecognizedFlowGenericParserError(MODULE_NAME, 'Function'), + ); + }); + + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return of type 'Object'`, () => { + const [returnTypeAnnotation] = parseReturnType('Object'); + expect(returnTypeAnnotation.type).toBe( + 'GenericObjectTypeAnnotation', + ); + }); + + describe('Object Literals Types', () => { + // TODO: Inexact vs exact object literals? + + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return type of an empty object literal`, () => { + const [returnTypeAnnotation] = parseReturnType('{||}'); + expect(returnTypeAnnotation.type).toBe('ObjectTypeAnnotation'); + invariant( + returnTypeAnnotation.type === 'ObjectTypeAnnotation', + '', + ); + + // Validate properties of object literal + expect(returnTypeAnnotation.properties).not.toBe(null); + expect(returnTypeAnnotation.properties?.length).toBe(0); + }); + + [ + {nullable: false, optional: false}, + {nullable: false, optional: true}, + {nullable: true, optional: false}, + {nullable: true, optional: true}, + ].forEach(({nullable, optional}) => { + const PROP_TYPE_DESCRIPTION = + nullable && optional + ? 'a nullable and optional' + : nullable + ? 'a nullable' + : optional + ? 'an optional' + : 'a required'; + + function annotateProp(propName, propType) { + if (nullable && optional) { + return `${propName}?: ?${propType}`; + } + if (nullable) { + return `${propName}: ?${propType}`; + } + if (optional) { + return `${propName}?: ${propType}`; + } + return `${propName}: ${propType}`; + } + + function parseObjectLiteralReturnTypeProp( + propName: string, + propType: string, + ) { + const [returnTypeAnnotation, module] = parseReturnType( + `{|${annotateProp(propName, propType)}|}`, + ); + expect(returnTypeAnnotation.type).toBe('ObjectTypeAnnotation'); + invariant( + returnTypeAnnotation.type === 'ObjectTypeAnnotation', + '', + ); + + const properties = returnTypeAnnotation.properties; + expect(properties).not.toBe(null); + invariant(properties != null, ''); + + expect(properties.length).toBe(1); + + // Validate property + const property = properties[0]; + expect(property.name).toBe(propName); + expect(property.optional).toBe(optional); + expect(property.typeAnnotation).not.toBe(null); + expect(property.typeAnnotation?.nullable).toBe(nullable); + invariant(property.typeAnnotation != null, ''); + return [ + { + ...property, + typeAnnotation: property.typeAnnotation, + }, + module, + ]; + } + + describe( + (nullable && optional + ? 'Nullable and Optional' + : nullable + ? 'Nullable' + : optional + ? 'Optional' + : 'Required') + ' Property', + () => { + /** + * TODO: Fill out props in promise + */ + + describe('Props with Primitive Types', () => { + PRIMITIVES.forEach(([FLOW_TYPE, PARSED_TYPE_NAME]) => { + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of primitive type '${FLOW_TYPE}'`, () => { + const [property] = parseObjectLiteralReturnTypeProp( + 'prop', + FLOW_TYPE, + ); + expect(property.typeAnnotation.type).toBe( + PARSED_TYPE_NAME, + ); + }); + }); + }); + + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of type 'Object'`, () => { + const [property] = parseObjectLiteralReturnTypeProp( + 'prop', + 'Object', + ); + + expect(property.typeAnnotation.type).toBe( + 'GenericObjectTypeAnnotation', + ); + }); + + describe('Props with Reserved Types', () => { + RESERVED_FUNCTION_VALUE_TYPE_NAME.forEach(FLOW_TYPE => { + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of reserved type '${FLOW_TYPE}'`, () => { + const [property] = parseObjectLiteralReturnTypeProp( + 'prop', + FLOW_TYPE, + ); + + expect(property.typeAnnotation.type).toBe( + 'ReservedFunctionValueTypeAnnotation', + ); + invariant( + property.typeAnnotation.type === + 'ReservedFunctionValueTypeAnnotation', + '', + ); + + expect(property.typeAnnotation.name).toBe(FLOW_TYPE); + }); + }); + }); + + describe('Props with Array Types', () => { + it(`should not parse methods that have ${RETURN_TYPE_DESCRIPTION} return type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of type 'Array`, () => { + expect(() => + parseObjectLiteralReturnTypeProp('prop', 'Array'), + ).toThrow( + new FlowGenericNotTypeParameterizedParserError( + MODULE_NAME, + 'Array', + ), + ); + }); + + function parseArrayElementType( + propName: string, + arrayElementType: string, + ) { + const [ + property, + module, + ] = parseObjectLiteralReturnTypeProp( + propName, + `Array<${arrayElementType}>`, + ); + expect(property.name).toBe(propName); + expect(property.typeAnnotation.type).toBe( + 'ArrayTypeAnnotation', + ); + invariant( + property.typeAnnotation.type === 'ArrayTypeAnnotation', + '', + ); + + const {elementType} = property.typeAnnotation; + expect(elementType).not.toBe(null); + invariant(elementType != null, ''); + return [elementType, module]; + } + + PRIMITIVES.forEach(([FLOW_TYPE, PARSED_TYPE_NAME]) => { + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of type 'Array<${FLOW_TYPE}>'`, () => { + const [elementType] = parseArrayElementType( + 'prop', + FLOW_TYPE, + ); + expect(elementType.type).toBe(PARSED_TYPE_NAME); + }); + }); + + RESERVED_FUNCTION_VALUE_TYPE_NAME.forEach(FLOW_TYPE => { + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of type 'Array<${FLOW_TYPE}>'`, () => { + const [elementType] = parseArrayElementType( + 'prop', + FLOW_TYPE, + ); + expect(elementType.type).toBe( + 'ReservedFunctionValueTypeAnnotation', + ); + invariant( + elementType.type === + 'ReservedFunctionValueTypeAnnotation', + '', + ); + + expect(elementType.name).toBe(FLOW_TYPE); + }); + }); + + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of type 'Array'`, () => { + const [elementType] = parseArrayElementType( + 'prop', + 'Object', + ); + expect(elementType).not.toBe(null); + expect(elementType.type).toBe( + 'GenericObjectTypeAnnotation', + ); + }); + + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of type of some array of an aliase`, () => { + const [elementType, module] = parseArrayElementType( + 'prop', + 'Animal', + ); + expect(elementType.type).toBe('TypeAliasTypeAnnotation'); + invariant( + elementType.type === 'TypeAliasTypeAnnotation', + '', + ); + expect(elementType.name).toBe('Animal'); + expectAnimalTypeAliasToExist(module); + }); + + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of type 'Array<{|foo: ?string|}>'`, () => { + const [elementType] = parseArrayElementType( + 'prop', + '{|foo: ?string|}', + ); + expect(elementType.type).toBe('ObjectTypeAnnotation'); + invariant( + elementType.type === 'ObjectTypeAnnotation', + '', + ); + + const {properties} = elementType; + invariant(properties != null, ''); + expect(properties).not.toBe(null); + expect(properties[0]).not.toBe(null); + expect(properties[0].name).toBe('foo'); + expect(properties[0].optional).toBe(false); + + expect(properties[0].typeAnnotation).not.toBe(null); + invariant(properties[0].typeAnnotation != null, ''); + + expect(properties[0].typeAnnotation.type).toBe( + 'StringTypeAnnotation', + ); + expect(properties[0].typeAnnotation.nullable).toBe(true); + }); + }); + + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of '{|foo: ?string|}'`, () => { + const [property] = parseObjectLiteralReturnTypeProp( + 'prop', + '{|foo: ?string|}', + ); + + expect(property.typeAnnotation.type).toBe( + 'ObjectTypeAnnotation', + ); + invariant( + property.typeAnnotation.type === 'ObjectTypeAnnotation', + '', + ); + + const {properties} = property.typeAnnotation; + + expect(properties).not.toBe(null); + invariant(properties != null, ''); + + expect(properties[0]).not.toBe(null); + expect(properties[0].name).toBe('foo'); + expect(properties[0].optional).toBe(false); + + expect(properties[0].typeAnnotation).not.toBe(null); + invariant(properties[0].typeAnnotation != null, ''); + + expect(properties[0].typeAnnotation.type).toBe( + 'StringTypeAnnotation', + ); + expect(properties[0].typeAnnotation.nullable).toBe(true); + }); + + it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of some type alias`, () => { + const [property, module] = parseObjectLiteralReturnTypeProp( + 'prop', + 'Animal', + ); + + expect(property.typeAnnotation.type).toBe( + 'TypeAliasTypeAnnotation', + ); + invariant( + property.typeAnnotation.type === + 'TypeAliasTypeAnnotation', + '', + ); + + expect(property.typeAnnotation.name).toBe('Animal'); + expectAnimalTypeAliasToExist(module); + }); + }, + ); + }); + }); + }, + ); + }); + }); +}); + +function parseModule(source) { + const schema = parseString(source, `Native${MODULE_NAME}.js`); + const {nativeModules} = schema.modules.NativeFoo; + invariant( + nativeModules, + "'nativeModules' in Spec NativeFoo shouldn't be null", + ); + return nativeModules.Foo; +} diff --git a/packages/react-native-codegen/src/parsers/flow/modules/__tests__/module-parser-test.js b/packages/react-native-codegen/src/parsers/flow/modules/__tests__/module-parser-snapshot-test.js similarity index 100% rename from packages/react-native-codegen/src/parsers/flow/modules/__tests__/module-parser-test.js rename to packages/react-native-codegen/src/parsers/flow/modules/__tests__/module-parser-snapshot-test.js diff --git a/packages/react-native-codegen/src/parsers/flow/modules/aliases.js b/packages/react-native-codegen/src/parsers/flow/modules/aliases.js deleted file mode 100644 index 7c55e4d9ff61a6..00000000000000 --- a/packages/react-native-codegen/src/parsers/flow/modules/aliases.js +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict-local - * @format - */ - -'use strict'; - -import type {ObjectTypeAliasTypeShape} from '../../../CodegenSchema.js'; - -import type {TypeMap} from '../utils.js'; - -const {getObjectProperties} = require('./properties'); - -// $FlowFixMe there's no flowtype for ASTs -type MethodAST = Object; - -function getAliases( - typeDefinition: $ReadOnlyArray, - types: TypeMap, -): $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}> { - const aliases = {}; - typeDefinition.map(moduleAlias => { - const aliasName = Object.keys(moduleAlias)[0]; - const typeAnnotation = moduleAlias[Object.keys(moduleAlias)[0]]; - - switch (typeAnnotation.type) { - case 'ObjectTypeAnnotation': - aliases[aliasName] = { - type: 'ObjectTypeAnnotation', - ...(typeAnnotation.properties && { - properties: getObjectProperties( - aliasName, - {properties: typeAnnotation.properties}, - aliasName, - types, - ), - }), - }; - return; - case 'GenericTypeAnnotation': - if (typeAnnotation.id.name && typeAnnotation.id.name !== '') { - aliases[aliasName] = { - type: 'TypeAliasTypeAnnotation', - name: typeAnnotation.id.name, - }; - return; - } else { - throw new Error( - `Cannot use "${typeAnnotation.type}" type annotation for "${aliasName}": must specify a type alias name`, - ); - } - default: - // TODO (T65847278): Figure out why this does not work. - // (typeAnnotation.type: empty); - throw new Error( - `Unknown prop type, found "${typeAnnotation.type}" in "${aliasName}"`, - ); - } - }); - return aliases; -} - -module.exports = { - getAliases, -}; diff --git a/packages/react-native-codegen/src/parsers/flow/modules/errors.js b/packages/react-native-codegen/src/parsers/flow/modules/errors.js new file mode 100644 index 00000000000000..537e4b1aedb78f --- /dev/null +++ b/packages/react-native-codegen/src/parsers/flow/modules/errors.js @@ -0,0 +1,112 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +'use strict'; + +const invariant = require('invariant'); + +class UnrecognizedFlowTypeAnnotationParserError extends Error { + +typeAnnotationType: string; + constructor(moduleName: string, typeAnnotationType: string) { + super( + `Module ${moduleName}: Detected unsupported type annotation of type '${typeAnnotationType}'`, + ); + + // assign the error class name in your custom error (as a shortcut) + this.name = this.constructor.name; + + // capturing the stack trace keeps the reference to your error class + Error.captureStackTrace(this, this.constructor); + this.typeAnnotationType = typeAnnotationType; + } +} + +class UnrecognizedFlowGenericParserError extends Error { + +genericName: string; + constructor(moduleName: string, genericName: string) { + super( + `Module ${moduleName}: Detected unsupported generic type '${genericName}'`, + ); + + // assign the error class name in your custom error (as a shortcut) + this.name = this.constructor.name; + + // capturing the stack trace keeps the reference to your error class + Error.captureStackTrace(this, this.constructor); + this.genericName = genericName; + } +} + +class FlowGenericNotTypeParameterizedParserError extends Error { + +genericName: string; + constructor(moduleName: string, genericName: string) { + super( + `Module ${moduleName}: Detected a type of ${genericName}, without type parameters.`, + ); + + // assign the error class name in your custom error (as a shortcut) + this.name = this.constructor.name; + + // capturing the stack trace keeps the reference to your error class + Error.captureStackTrace(this, this.constructor); + this.genericName = genericName; + } +} + +class FlowGenericTypeParameterCountMismatchParserError extends Error { + +genericName: string; + +numTypeParameters: number; + + constructor( + moduleName: string, + genericName: string, + numTypeParameters: number, + expectedNumTypeParameters: number, + ) { + invariant( + numTypeParameters !== expectedNumTypeParameters, + `FlowGenericNotTypeParameterizedWithExactlyOneTypeParserError can only be created with numTypeParameters != ${expectedNumTypeParameters}`, + ); + + super( + `Module ${moduleName}: Detected a type of ${genericName}, with ${numTypeParameters} type parameters specified. Expected exactly ${expectedNumTypeParameters}.`, + ); + + // assign the error class name in your custom error (as a shortcut) + this.name = this.constructor.name; + + // capturing the stack trace keeps the reference to your error class + Error.captureStackTrace(this, this.constructor); + this.genericName = genericName; + this.numTypeParameters = numTypeParameters; + } +} + +class UnnamedFunctionTypeAnnotationParamError extends Error { + constructor(moduleName: string) { + super( + `Module ${moduleName}: Detected a FunctionTypeAnnotation with an unnamed param. Please name all params.`, + ); + + // assign the error class name in your custom error (as a shortcut) + this.name = this.constructor.name; + + // capturing the stack trace keeps the reference to your error class + Error.captureStackTrace(this, this.constructor); + } +} + +module.exports = { + FlowGenericNotTypeParameterizedParserError, + FlowGenericTypeParameterCountMismatchParserError, + UnrecognizedFlowTypeAnnotationParserError, + UnrecognizedFlowGenericParserError, + UnnamedFunctionTypeAnnotationParamError, +}; diff --git a/packages/react-native-codegen/src/parsers/flow/modules/index.js b/packages/react-native-codegen/src/parsers/flow/modules/index.js index c09c66acf47c51..a1da76184fba9c 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/index.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/index.js @@ -10,67 +10,440 @@ 'use strict'; -import type {NativeModuleSchemaBuilderConfig} from './schema.js'; -const {getAliases} = require('./aliases'); -const {getMethods} = require('./methods'); +import type { + NativeModulePropertySchema, + NativeModuleMethodParamSchema, + NativeModuleAliasMap, + NativeModuleSchema, + NativeModuleFunctionTypeAnnotation, +} from '../../../CodegenSchema.js'; -function getModuleProperties(types, interfaceName) { - if (types[interfaceName] && types[interfaceName].body) { - return types[interfaceName].body.properties; +import type {TypeDeclarationMap} from '../utils.js'; +const {resolveTypeAnnotation} = require('../utils.js'); +const { + FlowGenericNotTypeParameterizedParserError, + FlowGenericTypeParameterCountMismatchParserError, + UnrecognizedFlowTypeAnnotationParserError, + UnrecognizedFlowGenericParserError, + UnnamedFunctionTypeAnnotationParamError, +} = require('./errors.js'); +const invariant = require('invariant'); + +import type {NativeModuleTypeAnnotation} from '../../../CodegenSchema.js'; + +function translateTypeAnnotation( + moduleName: string, + /** + * TODO(T71778680): Flow-type this node. + */ + flowTypeAnnotation: $FlowFixMe, + types: TypeDeclarationMap, + aliasMap: {...NativeModuleAliasMap}, +): NativeModuleTypeAnnotation { + const { + nullable, + typeAnnotation, + typeAliasResolutionStatus, + } = resolveTypeAnnotation(flowTypeAnnotation, types); + + switch (typeAnnotation.type) { + case 'GenericTypeAnnotation': { + switch (typeAnnotation.id.name) { + case 'RootTag': + return { + nullable, + type: 'ReservedFunctionValueTypeAnnotation', + name: 'RootTag', + }; + case 'Promise': { + assertGenericTypeAnnotationHasExactlyOneTypeParameter( + moduleName, + typeAnnotation, + ); + return { + nullable, + type: 'PromiseTypeAnnotation', + }; + } + case 'Array': + case '$ReadOnlyArray': { + assertGenericTypeAnnotationHasExactlyOneTypeParameter( + moduleName, + typeAnnotation, + ); + + try { + /** + * TODO(T72031674): Migrate all our NativeModule specs to not use + * invalid Array ElementTypes. Then, make the elementType a required + * parameter. + */ + const elementType = translateTypeAnnotation( + moduleName, + typeAnnotation.typeParameters.params[0], + types, + aliasMap, + ); + + invariant( + elementType.type !== 'VoidTypeAnnotation', + `${typeAnnotation.id.name} element type cannot be 'void'.`, + ); + invariant( + elementType.type !== 'PromiseTypeAnnotation', + `${typeAnnotation.id.name} element type cannot be 'Promise'.`, + ); + + invariant( + elementType.type !== 'FunctionTypeAnnotation', + `${typeAnnotation.id.name} element type cannot be a function.`, + ); + + return { + nullable, + type: 'ArrayTypeAnnotation', + elementType: elementType, + }; + } catch (ex) { + return { + nullable, + type: 'ArrayTypeAnnotation', + }; + } + } + case '$ReadOnly': { + assertGenericTypeAnnotationHasExactlyOneTypeParameter( + moduleName, + typeAnnotation, + ); + return translateTypeAnnotation( + moduleName, + typeAnnotation.typeParameters.params[0], + types, + aliasMap, + ); + } + case 'Stringish': { + return { + nullable, + type: 'StringTypeAnnotation', + }; + } + case 'Int32': { + return { + nullable, + type: 'Int32TypeAnnotation', + }; + } + case 'Double': { + return { + nullable, + type: 'DoubleTypeAnnotation', + }; + } + case 'Float': { + return { + nullable, + type: 'FloatTypeAnnotation', + }; + } + case 'Object': { + return { + nullable, + type: 'GenericObjectTypeAnnotation', + }; + } + default: { + throw new UnrecognizedFlowGenericParserError( + moduleName, + typeAnnotation.id.name, + ); + } + } + } + case 'ObjectTypeAnnotation': { + const objectTypeAnnotationPartial = { + type: 'ObjectTypeAnnotation', + properties: typeAnnotation.properties.map(property => { + const {optional} = property; + return { + name: property.key.name, + optional, + typeAnnotation: translateTypeAnnotation( + moduleName, + property.value, + types, + aliasMap, + ), + }; + }), + }; + + if (!typeAliasResolutionStatus.successful) { + return { + nullable, + ...objectTypeAnnotationPartial, + }; + } + + /** + * All aliases RHS are required. + */ + aliasMap[typeAliasResolutionStatus.aliasName] = { + nullable: false, + ...objectTypeAnnotationPartial, + }; + + /** + * Nullability of type aliases is transitive. + * + * Consider this case: + * + * type Animal = ?{| + * name: string, + * |}; + * + * type B = Animal + * + * export interface Spec extends TurboModule { + * +greet: (animal: B) => void; + * } + * + * In this case, we follow B to Animal, and then Animal to ?{|name: string|}. + * + * We: + * 1. Replace `+greet: (animal: B) => void;` with `+greet: (animal: ?Animal) => void;`, + * 2. Pretend that Animal = {|name: string|}. + * + * Why do we do this? + * 1. In ObjC, we need to generate a struct called Animal, not B. + * 2. This design is simpler than managing nullability within both the type alias usage, and the type alias RHS. + * 3. What does it mean for a C++ struct, which is what this type alias RHS will generate, to be nullable? ¯\_(ツ)_/¯ + * Nullability is a concept that only makes sense when talking about instances (i.e: usages) of the C++ structs. + * Hence, it's better to manage nullability within the actual TypeAliasTypeAnnotation nodes, and not the + * associated ObjectTypeAnnotations. + */ + return { + nullable: nullable, + type: 'TypeAliasTypeAnnotation', + name: typeAliasResolutionStatus.aliasName, + }; + } + case 'BooleanTypeAnnotation': { + return { + nullable, + type: 'BooleanTypeAnnotation', + }; + } + case 'NumberTypeAnnotation': { + return { + nullable, + type: 'NumberTypeAnnotation', + }; + } + case 'VoidTypeAnnotation': { + return { + nullable, + type: 'VoidTypeAnnotation', + }; + } + case 'StringTypeAnnotation': { + return { + nullable, + type: 'StringTypeAnnotation', + }; + } + case 'FunctionTypeAnnotation': { + return translateFunctionTypeAnnotation( + moduleName, + typeAnnotation, + types, + nullable, + aliasMap, + ); + } + default: { + throw new UnrecognizedFlowTypeAnnotationParserError( + moduleName, + typeAnnotation.type, + ); + } } - throw new Error( - `Interface properties for "${interfaceName}" has been specified incorrectly.`, - ); } -function findInterfaceName(types) { - return Object.keys(types) - .map(typeName => types[typeName]) - .filter( - type => - type.extends && - type.extends[0] && - type.extends[0].id.name === 'TurboModule', - )[0].id.name; -} +function assertGenericTypeAnnotationHasExactlyOneTypeParameter( + moduleName: string, + /** + * TODO(T71778680): This is a GenericTypeAnnotation. Flow type this node + */ + typeAnnotation: $FlowFixMe, +) { + if (typeAnnotation.typeParameters == null) { + throw new FlowGenericNotTypeParameterizedParserError( + moduleName, + typeAnnotation.id.name, + ); + } + + invariant( + typeAnnotation.typeParameters.type === 'TypeParameterInstantiation', + "assertGenericTypeAnnotationHasExactlyOneTypeParameter: Type parameters must be an AST node of type 'TypeParameterInstantiation'", + ); -function findAliasNames(types) { - return Object.keys(types) - .map(typeName => types[typeName]) - .filter( - type => - type.type && - type.type === 'TypeAlias' && - type.right && - type.right.type === 'ObjectTypeAnnotation', - ) - .map(type => type.id.name); + if (typeAnnotation.typeParameters.params.length !== 1) { + throw new FlowGenericTypeParameterCountMismatchParserError( + moduleName, + typeAnnotation.id.name, + (typeAnnotation.typeParameters.params.length: number), + 1, + ); + } } -function getModuleAliases(types, aliasNames) { - return aliasNames.map(aliasName => { - if (types[aliasName] && types[aliasName].right) { - return {[aliasName]: types[aliasName].right}; +function translateFunctionTypeAnnotation( + moduleName: string, + // TODO(T71778680): This is a FunctionTypeAnnotation. Type this. + flowFunctionTypeAnnotation: $FlowFixMe, + types: TypeDeclarationMap, + nullable: boolean, + aliasMap: {...NativeModuleAliasMap}, +): NativeModuleFunctionTypeAnnotation { + const params: Array = []; + for (const flowParam of (flowFunctionTypeAnnotation.params: $ReadOnlyArray<$FlowFixMe>)) { + if (flowParam.name == null) { + throw new UnnamedFunctionTypeAnnotationParamError(moduleName); } + + const paramName = flowParam.name.name; + const paramTypeAnnotation = translateTypeAnnotation( + moduleName, + flowParam.typeAnnotation, + types, + aliasMap, + ); + + invariant( + paramTypeAnnotation.type !== 'VoidTypeAnnotation', + `Parameter ${paramName} cannot have type 'void'.`, + ); + + invariant( + paramTypeAnnotation.type !== 'PromiseTypeAnnotation', + `Parameter ${paramName} cannot have type 'Promise'.`, + ); + + params.push({ + name: flowParam.name.name, + optional: flowParam.optional, + typeAnnotation: paramTypeAnnotation, + }); + } + + const returnTypeAnnotation = translateTypeAnnotation( + moduleName, + flowFunctionTypeAnnotation.returnType, + types, + aliasMap, + ); + + invariant( + returnTypeAnnotation.type !== 'FunctionTypeAnnotation', + "Return cannot have type 'Promise'.", + ); + + return { + type: 'FunctionTypeAnnotation', + returnTypeAnnotation, + params, + nullable, + }; +} + +function buildPropertySchema( + moduleName: string, + // TODO(T71778680): This is an ObjectTypeProperty containing either: + // - a FunctionTypeAnnotation or GenericTypeAnnotation + // - a NullableTypeAnnoation containing a FunctionTypeAnnotation or GenericTypeAnnotation + // Flow type this node + property: $FlowFixMe, + types: TypeDeclarationMap, + aliasMap: {...NativeModuleAliasMap}, +): NativeModulePropertySchema { + let nullable = false; + let {key, value} = property; + + const methodName: string = key.name; + + ({nullable, typeAnnotation: value} = resolveTypeAnnotation(value, types)); + + if (value.type !== 'FunctionTypeAnnotation') { throw new Error( - `Interface properties for "${aliasName}" has been specified incorrectly.`, + `Only methods are supported as module properties. Found ${value.type} in ${property.key.name}`, ); - }); + } + + return { + name: methodName, + optional: property.optional, + typeAnnotation: translateFunctionTypeAnnotation( + moduleName, + value, + types, + nullable, + aliasMap, + ), + }; } -// $FlowFixMe there's no flowtype for AST -function processModule(types): NativeModuleSchemaBuilderConfig { - const interfaceName = findInterfaceName(types); - const moduleProperties = getModuleProperties(types, interfaceName); - const properties = getMethods(moduleProperties, types); +function buildModuleSchema( + moduleName: string, + types: TypeDeclarationMap, +): NativeModuleSchema { + const moduleInterfaceNames = (Object.keys( + types, + ): $ReadOnlyArray).filter((typeName: string) => { + const declaration = types[typeName]; + return ( + declaration.type === 'InterfaceDeclaration' && + declaration.extends.length === 1 && + declaration.extends[0].type === 'InterfaceExtends' && + declaration.extends[0].id.name === 'TurboModule' + ); + }); + + invariant( + moduleInterfaceNames.length === 1, + 'There must be exactly one module declaration per file.', + ); - const aliasNames = findAliasNames(types); - const moduleAliases = getModuleAliases(types, aliasNames); - const aliases = getAliases(moduleAliases, types); + const [moduleInterfaceName] = moduleInterfaceNames; - return {aliases, properties}; + const declaration = types[moduleInterfaceName]; + return (declaration.body.properties: $ReadOnlyArray<$FlowFixMe>) + .filter(property => property.type === 'ObjectTypeProperty') + .map(property => { + const aliasMap: {...NativeModuleAliasMap} = {}; + return { + aliasMap: aliasMap, + propertySchema: buildPropertySchema( + moduleName, + property, + types, + aliasMap, + ), + }; + }) + .reduce( + (moduleSchema: NativeModuleSchema, {aliasMap, propertySchema}) => { + return { + aliases: {...moduleSchema.aliases, ...aliasMap}, + properties: [...moduleSchema.properties, propertySchema], + }; + }, + {aliases: {}, properties: []}, + ); } module.exports = { - processModule, + buildModuleSchema, }; diff --git a/packages/react-native-codegen/src/parsers/flow/modules/methods.js b/packages/react-native-codegen/src/parsers/flow/modules/methods.js deleted file mode 100644 index c63fc903692d56..00000000000000 --- a/packages/react-native-codegen/src/parsers/flow/modules/methods.js +++ /dev/null @@ -1,352 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict-local - * @format - */ - -'use strict'; - -import type { - NativeModuleMethodTypeShape, - FunctionTypeAnnotationParam, - FunctionTypeAnnotationReturn, -} from '../../../CodegenSchema.js'; - -import type {ASTNode, TypeMap} from '../utils.js'; -const {getValueFromTypes} = require('../utils.js'); -const { - getElementTypeForArrayOrObject, - getObjectProperties, -} = require('./properties'); - -// $FlowFixMe there's no flowtype for ASTs -type MethodAST = Object; - -function getTypeAnnotationForParam( - name: string, - paramAnnotation: ASTNode, - types: TypeMap, -): FunctionTypeAnnotationParam { - let param = paramAnnotation; - if (param.name === null) { - throw new Error( - `Unsupported type for ${name}. Please provide a name for every parameter.`, - ); - } - let paramName = param.name.name; - let nullable = false; - if (param.typeAnnotation.type === 'NullableTypeAnnotation') { - nullable = true; - param = paramAnnotation.typeAnnotation; - } - - const typeAnnotation = getValueFromTypes(param.typeAnnotation, types); - const type = - typeAnnotation.type === 'GenericTypeAnnotation' - ? typeAnnotation.id.name - : typeAnnotation.type; - - switch (type) { - case 'RootTag': - return { - name: paramName, - nullable, - typeAnnotation: { - type: 'ReservedFunctionValueTypeAnnotation', - name: 'RootTag', - }, - }; - case 'Array': - case '$ReadOnlyArray': - if ( - typeAnnotation.typeParameters && - typeAnnotation.typeParameters.params[0] - ) { - return { - name: paramName, - nullable, - typeAnnotation: { - type: 'ArrayTypeAnnotation', - elementType: getElementTypeForArrayOrObject( - name, - typeAnnotation.typeParameters.params[0], - paramName, - types, - ), - }, - }; - } else { - throw new Error( - `Unsupported type for ${name}, param: "${paramName}": expected to find annotation for type of array contents`, - ); - } - case 'ObjectTypeAnnotation': - if (param.typeAnnotation.type === 'GenericTypeAnnotation') { - return { - nullable, - name: paramName, - typeAnnotation: { - type: 'TypeAliasTypeAnnotation', - name: param.typeAnnotation.id.name, - }, - }; - } - return { - nullable, - name: paramName, - typeAnnotation: { - type: 'ObjectTypeAnnotation', - properties: getObjectProperties( - name, - typeAnnotation, - paramName, - types, - ), - }, - }; - case '$ReadOnly': - if ( - typeAnnotation.typeParameters.params && - typeAnnotation.typeParameters.params[0] - ) { - return { - nullable, - name: paramName, - typeAnnotation: { - type: 'ObjectTypeAnnotation', - properties: getObjectProperties( - name, - typeAnnotation.typeParameters.params[0], - paramName, - types, - ), - }, - }; - } else { - throw new Error( - `Unsupported param for method "${name}", param "${paramName}". No type specified for $ReadOnly`, - ); - } - case 'FunctionTypeAnnotation': - return { - name: paramName, - nullable, - typeAnnotation: { - type: 'FunctionTypeAnnotation', - }, - }; - case 'NumberTypeAnnotation': - case 'BooleanTypeAnnotation': - return { - nullable, - name: paramName, - typeAnnotation: { - type, - }, - }; - - case 'StringTypeAnnotation': - case 'Stringish': - return { - nullable, - name: paramName, - typeAnnotation: { - type: 'StringTypeAnnotation', - }, - }; - case 'Int32': - return { - nullable, - name: paramName, - typeAnnotation: { - type: 'Int32TypeAnnotation', - }, - }; - case 'Float': - return { - nullable, - name: paramName, - typeAnnotation: { - type: 'FloatTypeAnnotation', - }, - }; - default: - return { - nullable, - name: paramName, - typeAnnotation: { - type: 'GenericObjectTypeAnnotation', - }, - }; - } -} - -function getReturnTypeAnnotation( - methodName: string, - returnType, - types: TypeMap, -): FunctionTypeAnnotationReturn { - let typeAnnotation = getValueFromTypes(returnType, types); - let nullable = false; - if (typeAnnotation.type === 'NullableTypeAnnotation') { - nullable = true; - typeAnnotation = typeAnnotation.typeAnnotation; - } - let type = - typeAnnotation.type === 'GenericTypeAnnotation' - ? typeAnnotation.id.name - : typeAnnotation.type; - - switch (type) { - case 'RootTag': - return { - nullable, - type: 'ReservedFunctionValueTypeAnnotation', - name: 'RootTag', - }; - case 'Promise': - if ( - typeAnnotation.typeParameters && - typeAnnotation.typeParameters.params[0] - ) { - return { - type: 'GenericPromiseTypeAnnotation', - nullable, - }; - } else { - throw new Error( - `Unsupported return promise type for ${methodName}: expected to find annotation for type of promise content`, - ); - } - case 'Array': - case '$ReadOnlyArray': - if ( - typeAnnotation.typeParameters && - typeAnnotation.typeParameters.params[0] - ) { - return { - nullable, - type: 'ArrayTypeAnnotation', - elementType: getElementTypeForArrayOrObject( - methodName, - typeAnnotation.typeParameters.params[0], - 'returning value', - types, - ), - }; - } else { - throw new Error( - `Unsupported return type for ${methodName}: expected to find annotation for type of array contents`, - ); - } - case 'ObjectTypeAnnotation': - return { - nullable, - type: 'ObjectTypeAnnotation', - properties: getObjectProperties( - methodName, - typeAnnotation, - 'returning value', - types, - ), - }; - case '$ReadOnly': - if ( - typeAnnotation.typeParameters.params && - typeAnnotation.typeParameters.params[0] - ) { - return { - nullable, - type: 'ObjectTypeAnnotation', - properties: getObjectProperties( - methodName, - typeAnnotation.typeParameters.params[0], - 'returning value', - types, - ), - }; - } else { - throw new Error( - `Unsupported return type for method "${methodName}", No type specified for $ReadOnly`, - ); - } - case 'BooleanTypeAnnotation': - case 'NumberTypeAnnotation': - case 'VoidTypeAnnotation': - return { - nullable, - type, - }; - case 'StringTypeAnnotation': - case 'Stringish': - return { - nullable, - type: 'StringTypeAnnotation', - }; - - case 'Int32': - return { - nullable, - type: 'Int32TypeAnnotation', - }; - case 'Float': - return { - nullable, - type: 'FloatTypeAnnotation', - }; - default: - return { - type: 'GenericObjectTypeAnnotation', - nullable, - }; - } -} - -function buildMethodSchema( - property: MethodAST, - types: TypeMap, -): NativeModuleMethodTypeShape { - const name: string = property.key.name; - const value = getValueFromTypes(property.value, types); - if (value.type !== 'FunctionTypeAnnotation') { - throw new Error( - `Only methods are supported as module properties. Found ${value.type} in ${property.key.name}`, - ); - } - const params = value.params.map(param => - getTypeAnnotationForParam(name, param, types), - ); - - const returnTypeAnnotation = getReturnTypeAnnotation( - name, - getValueFromTypes(value.returnType, types), - types, - ); - return { - name, - typeAnnotation: { - type: 'FunctionTypeAnnotation', - returnTypeAnnotation, - params, - optional: property.optional, - }, - }; -} - -function getMethods( - typeDefinition: $ReadOnlyArray, - types: TypeMap, -): $ReadOnlyArray { - return typeDefinition - .filter(property => property.type === 'ObjectTypeProperty') - .map(property => buildMethodSchema(property, types)) - .filter(Boolean); -} - -module.exports = { - getMethods, -}; diff --git a/packages/react-native-codegen/src/parsers/flow/modules/properties.js b/packages/react-native-codegen/src/parsers/flow/modules/properties.js deleted file mode 100644 index f1e8c51e7fe443..00000000000000 --- a/packages/react-native-codegen/src/parsers/flow/modules/properties.js +++ /dev/null @@ -1,159 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict-local - * @format - */ - -'use strict'; - -import type { - FunctionTypeAnnotationParamTypeAnnotation, - ObjectParamTypeAnnotation, - TypeAliasTypeAnnotation, -} from '../../../CodegenSchema.js'; - -import type {ASTNode, TypeMap} from '../utils.js'; -const {getValueFromTypes} = require('../utils.js'); - -function getObjectProperties( - name: string, - objectParam: ASTNode, - paramName: string, - types: TypeMap, -): $ReadOnlyArray { - return objectParam.properties.map(objectTypeProperty => { - let optional = objectTypeProperty.optional; - let value = objectTypeProperty.value; - if (value.type === 'NullableTypeAnnotation') { - if ( - objectTypeProperty.value.typeAnnotation.type !== 'StringTypeAnnotation' - ) { - optional = true; - } - value = objectTypeProperty.value.typeAnnotation; - } - return { - optional, - name: objectTypeProperty.key.name, - typeAnnotation: getElementTypeForArrayOrObject( - name, - value, - paramName, - types, - ), - }; - }); -} - -function getElementTypeForArrayOrObject( - name: string, - arrayParam: ASTNode, - paramName: string, - types: TypeMap, -): - | FunctionTypeAnnotationParamTypeAnnotation - | TypeAliasTypeAnnotation - | typeof undefined { - const typeAnnotation = getValueFromTypes(arrayParam, types); - const type = - typeAnnotation.type === 'GenericTypeAnnotation' - ? typeAnnotation.id.name - : typeAnnotation.type; - - switch (type) { - case 'RootTag': - return { - type: 'ReservedFunctionValueTypeAnnotation', - name: 'RootTag', - }; - case 'Array': - case '$ReadOnlyArray': - if ( - typeAnnotation.typeParameters && - typeAnnotation.typeParameters.params[0] - ) { - return { - type: 'ArrayTypeAnnotation', - elementType: getElementTypeForArrayOrObject( - name, - typeAnnotation.typeParameters.params[0], - 'returning value', - types, - ), - }; - } else { - throw new Error( - `Unsupported type for "${name}", param: "${paramName}": expected to find annotation for type of nested array contents`, - ); - } - case 'ObjectTypeAnnotation': - if (arrayParam.id) { - return { - type: 'TypeAliasTypeAnnotation', - name: arrayParam.id.name, - }; - } - return { - type: 'ObjectTypeAnnotation', - properties: getObjectProperties(name, typeAnnotation, paramName, types), - }; - case '$ReadOnly': - if ( - typeAnnotation.typeParameters.params && - typeAnnotation.typeParameters.params[0] - ) { - return { - type: 'ObjectTypeAnnotation', - properties: getObjectProperties( - name, - typeAnnotation.typeParameters.params[0], - paramName, - types, - ), - }; - } else { - throw new Error( - `Unsupported param for method "${name}", param "${paramName}". No type specified for $ReadOnly`, - ); - } - case 'AnyTypeAnnotation': - return { - type, - }; - case 'NumberTypeAnnotation': - case 'BooleanTypeAnnotation': - return { - type, - }; - case 'StringTypeAnnotation': - case 'Stringish': - return { - type: 'StringTypeAnnotation', - }; - case 'Int32': - return { - type: 'Int32TypeAnnotation', - }; - case 'Float': - return { - type: 'FloatTypeAnnotation', - }; - case 'TupleTypeAnnotation': - case 'UnionTypeAnnotation': - return undefined; - default: - // TODO T67565166: Generic objects are not type safe and should be disallowed in the schema. - return { - type: 'GenericObjectTypeAnnotation', - }; - } -} - -module.exports = { - getElementTypeForArrayOrObject, - getObjectProperties, -}; diff --git a/packages/react-native-codegen/src/parsers/flow/modules/schema.js b/packages/react-native-codegen/src/parsers/flow/modules/schema.js index c1e4c80cfd5fed..f54053a0c86d84 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/schema.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/schema.js @@ -10,19 +10,10 @@ 'use strict'; -import type { - SchemaType, - ObjectTypeAliasTypeShape, - NativeModuleMethodTypeShape, -} from '../../../CodegenSchema.js'; +import type {SchemaType, NativeModuleSchema} from '../../../CodegenSchema.js'; -export type NativeModuleSchemaBuilderConfig = $ReadOnly<{| - aliases: $ReadOnly<{[aliasName: string]: ObjectTypeAliasTypeShape, ...}>, - properties: $ReadOnlyArray, -|}>; - -function buildModuleSchema( - {aliases, properties}: NativeModuleSchemaBuilderConfig, +function wrapModuleSchema( + {aliases, properties}: NativeModuleSchema, moduleName: string, ): SchemaType { return { @@ -40,5 +31,5 @@ function buildModuleSchema( } module.exports = { - buildModuleSchema, + wrapModuleSchema, }; diff --git a/packages/react-native-codegen/src/parsers/flow/utils.js b/packages/react-native-codegen/src/parsers/flow/utils.js index 663b6b90bb941a..15b13c5949975a 100644 --- a/packages/react-native-codegen/src/parsers/flow/utils.js +++ b/packages/react-native-codegen/src/parsers/flow/utils.js @@ -10,13 +10,82 @@ 'use strict'; -// $FlowFixMe there's no flowtype for ASTs -export type TypeMap = $ReadOnly<{|[name: string]: Object|}>; +/** + * This FlowFixMe is supposed to refer to an InterfaceDeclaration or TypeAlias + * declaration type. Unfortunately, we don't have those types, because flow-parser + * generates them, and flow-parser is not type-safe. In the future, we should find + * a way to get these types from our flow parser library. + * + * TODO(T71778680): Flow type AST Nodes + */ +export type TypeDeclarationMap = {|[declarationName: string]: $FlowFixMe|}; // $FlowFixMe there's no flowtype for ASTs export type ASTNode = Object; -function getValueFromTypes(value: ASTNode, types: TypeMap): ASTNode { +const invariant = require('invariant'); + +type TypeAliasResolutionStatus = + | $ReadOnly<{| + successful: true, + aliasName: string, + |}> + | $ReadOnly<{| + successful: false, + |}>; + +function resolveTypeAnnotation( + // TODO(T71778680): This is an Flow TypeAnnotation. Flow-type this + typeAnnotation: $FlowFixMe, + types: TypeDeclarationMap, +): { + nullable: boolean, + typeAnnotation: $FlowFixMe, + typeAliasResolutionStatus: TypeAliasResolutionStatus, +} { + invariant( + typeAnnotation != null, + 'resolveTypeAnnotation(): typeAnnotation cannot be null', + ); + + let node = typeAnnotation; + let nullable = false; + let typeAliasResolutionStatus: TypeAliasResolutionStatus = { + successful: false, + }; + + for (;;) { + if (node.type === 'NullableTypeAnnotation') { + nullable = true; + node = node.typeAnnotation; + } else if (node.type === 'GenericTypeAnnotation') { + typeAliasResolutionStatus = { + successful: true, + aliasName: node.id.name, + }; + const resolvedTypeAnnotation = types[node.id.name]; + if (resolvedTypeAnnotation == null) { + break; + } + + invariant( + resolvedTypeAnnotation.type === 'TypeAlias', + `GenericTypeAnnotation '${node.id.name}' must resolve to a TypeAlias. Instead, it resolved to a '${resolvedTypeAnnotation.type}'`, + ); + node = resolvedTypeAnnotation.right; + } else { + break; + } + } + + return { + nullable: nullable, + typeAnnotation: node, + typeAliasResolutionStatus, + }; +} + +function getValueFromTypes(value: ASTNode, types: TypeDeclarationMap): ASTNode { if (value.type === 'GenericTypeAnnotation' && types[value.id.name]) { return getValueFromTypes(types[value.id.name].right, types); } @@ -25,4 +94,5 @@ function getValueFromTypes(value: ASTNode, types: TypeMap): ASTNode { module.exports = { getValueFromTypes, + resolveTypeAnnotation, }; diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index 229a4f1ff65701..e6bdee1888ba1c 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -506,12 +506,12 @@ EXTERNAL SOURCES: :path: "../../ReactCommon/yoga" SPEC CHECKSUMS: - boost-for-react-native: a110407d9db2642fd2e1bcd7c5a51c81f2521dc9 + boost-for-react-native: d5ad1140010aa8cb622323a781ecbeab4425d19a CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845 CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f - DoubleConversion: 2b45d0f8e156a5b02354c8a4062de64d41ccb4e0 - FBLazyVector: b8a28eee8442e7a9325fe89540ed9c4324289609 - FBReactNativeSpec: 13f1c0dbe36ebcf005e52262c1b54f4c2c912d17 + DoubleConversion: 0ea4559a49682230337df966e735d6cc7760108e + FBLazyVector: 666596dc7c29ee7b2d1a48f535acb7cbb061ea90 + FBReactNativeSpec: 0e9685ad5e72311405176c684617ef5d0dc8292e Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365 Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41 Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3 @@ -519,36 +519,36 @@ SPEC CHECKSUMS: Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7 FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d - glog: 789873d01e4b200777d0a09bc23d548446758699 + glog: b0ffa5c7cbb8a2c455ee70cc920fe61694d43248 OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355 - RCT-Folly: 55d0039b24e192081ec0b2257f7bd9f42e382fb7 - RCTRequired: ec03bb34730cebf218f644a237c678d673867c8e - RCTTypeSafety: 9fadc2d7e38f0d86e6e3e8f7fdd3efddba4176f2 - React: 3cb1cac6f7d5c3f1dc0973a2ad663ecab9c94f01 - React-callinvoker: e4586903afcb806fa895570fe969328059056ccb - React-Core: b5d0b4caca9dbbe6bfa0acc22ee4cbe4a83621d8 - React-CoreModules: b73021daaf7f92f316060679da242afe9a18d6ed - React-cxxreact: 1e05d22ed8dce2063eb8530d861159350cdf0523 - React-jsi: 1206eb5de6f63bfabc990fddefdbbe43d72a7a5d - React-jsiexecutor: dae9e48238516378c62f647adc28dde5dafee2ca - React-jsinspector: 0c241e676c805b3309638058d8b22e5726cccaea - React-perflogger: da0120585df4e83f2cbeda0ebb1d4648511b5a0d - React-RCTActionSheet: 4d67e8699a7863350789897e5111fa2b569fac96 - React-RCTAnimation: bfff6290d1d18a716fb879e5c0bafa9c207b05d8 - React-RCTBlob: 1d809a4d687c95d0a237cfb038c86d8eb0f0269f - React-RCTImage: b6f9bb883c20c67cf4c54a94ff39effd42d91188 - React-RCTLinking: b25f8c005c3ecaf6749525ac158bca5cf2d9db36 - React-RCTNetwork: 4a7c7003a0c2b4117456651fa592348ccffa417c - React-RCTPushNotification: 99bf2d6dfabfeeab92de7f4f828b2172f559a4af - React-RCTSettings: 0fb40a79122cb2a82598e666c1d46eb0c5b4302d - React-RCTTest: a9b79125f4624601242b01c337feabe69b9f6c85 - React-RCTText: c2a4c8e11ea3d8ce36a4c922afe980b4497442b8 - React-RCTVibration: 9e15c22a4957a67536cb69b3fda6e573b8461b8c - React-runtimeexecutor: 3891efb88aec49413bc5bd435b98df41e144cca6 - React-TurboModuleCxx-RNW: 18bb71af41fe34c8b12a56bef60aae7ee32b0817 - React-TurboModuleCxx-WinRTPort: 5ae8c27ca09ffdf37a9d78ed6d41da52f5ef4617 - ReactCommon: 86ff610a6021898ee969befabd3d27ece4a82e04 - Yoga: da3c83261429bf2721c251c009dacab988dd8670 + RCT-Folly: 2f2111690f1e23490285c059ca53be22fe6d6bee + RCTRequired: bc021672176516f9077a9b2f1300d5a04e98dd8e + RCTTypeSafety: dd88e89b2d08df093f650af090bab371a8b7d369 + React: fd0bed4a49748a1114bfae48c93bc55a178b8634 + React-callinvoker: 49976d8b74c72ee82defb81422a331465441692a + React-Core: 138d769f39849681f6a6bc7e5a4db3c621df2c16 + React-CoreModules: c1ab907464352b4ad65753945eced45e2286c17a + React-cxxreact: e7a5eda119759b32a0b085ee983938dd3c2ee83a + React-jsi: 6e01f87cc6fd6ba9a75d1a57d959a8290f6adfea + React-jsiexecutor: f5dc0df6ed8af85b00a8e985ec54c8967a082b5f + React-jsinspector: bdf106e1464bc469ba629a26c488520e6a112b1b + React-perflogger: 1add839c33e8241e6d20c468e4a4ffae59a13f64 + React-RCTActionSheet: e14f85abb409305569e02ccecc7ad49ba571578b + React-RCTAnimation: 070e13c3df0cd154bdc32b52e59e5666fa6e9289 + React-RCTBlob: 3e1bdc2139ea89309ec093b0c19429d30f560409 + React-RCTImage: ad73b0817497213958d0bf36e4f2c27a8e668f3a + React-RCTLinking: 04988e0fe0f9ba3654374863442ec914ec3eb10b + React-RCTNetwork: 3054f8640873825d5e007d0a86e43a781b14f80e + React-RCTPushNotification: f8a39937fa6d734377cf64695293f7ae316d7051 + React-RCTSettings: 6c95ad626d0535501c6a6a34bec2ba7d4de29790 + React-RCTTest: 4341221f53c6524dcb02b035a406ec7cf3b7d964 + React-RCTText: bcce263b99aa6721f2e3b2d62c863748d15debb3 + React-RCTVibration: 751b81dfa647c0a3639047ac3e6adf2fd8fef86a + React-runtimeexecutor: c2c32ed679eb9763095d13960adce6b21a885046 + React-TurboModuleCxx-RNW: 12172bdbaaf052406ec571465243fad4b2eb2702 + React-TurboModuleCxx-WinRTPort: c2d49fa6eda38319bbc454b8b1525d3b007218dc + ReactCommon: bf7db57736b24e57497d1fdd384809345a0a5653 + Yoga: d203d48354fa48e0100655ac7be4444477df794f YogaKit: f782866e155069a2cca2517aafea43200b01fd5a PODFILE CHECKSUM: cb260f8f7765c910b68f8267cbd74709f6ae6e54 diff --git a/packages/rn-tester/RCTTest/React-RCTTest.podspec b/packages/rn-tester/RCTTest/React-RCTTest.podspec index a7a75f81ace121..f2c3f7b7af21e5 100644 --- a/packages/rn-tester/RCTTest/React-RCTTest.podspec +++ b/packages/rn-tester/RCTTest/React-RCTTest.podspec @@ -26,7 +26,7 @@ Pod::Spec.new do |s| s.homepage = "https://reactnative.dev/" s.license = package["license"] s.author = "Facebook, Inc. and its affiliates" - s.platforms = { :ios => "10.0", :tvos => "10.0", :osx => "10.14" } # TODO(macOS GH#214) + s.platforms = { :ios => "10.0", :osx => "10.14" } # TODO(macOS GH#214) s.compiler_flags = folly_compiler_flags + ' -Wno-nullability-completeness' s.source = source s.source_files = "**/*.{h,m,mm}" diff --git a/packages/rn-tester/README.md b/packages/rn-tester/README.md index 4c9733acf95377..e1d52db22883a7 100644 --- a/packages/rn-tester/README.md +++ b/packages/rn-tester/README.md @@ -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. diff --git a/packages/rn-tester/android/app/build.gradle b/packages/rn-tester/android/app/build.gradle index 3bad38b8dc353f..186a325259e28d 100644 --- a/packages/rn-tester/android/app/build.gradle +++ b/packages/rn-tester/android/app/build.gradle @@ -85,6 +85,7 @@ project.ext.react = [ hermesCommand: "$rootDir/node_modules/hermes-engine/%OS-BIN%/hermesc", enableHermesForVariant: { def v -> v.name.contains("hermes") }, jsRootDir: "$rootDir/RNTester", + enableCodegen: (System.getenv('USE_CODEGEN') ?: '0').toBoolean(), enableFabric: (System.getenv('USE_FABRIC') ?: '0').toBoolean(), ] @@ -106,6 +107,11 @@ def enableSeparateBuildPerCPUArchitecture = true */ def enableProguardInReleaseBuilds = true +/** + * Build and enable codegen-related output in RN Tester app. + */ +def enableCodegen = project.ext.react.enableCodegen + /** * Build and enable Fabric in RN Tester app. */ @@ -148,6 +154,7 @@ android { testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' buildConfigField("boolean", "ENABLE_FABRIC", "$enableFabric") + buildConfigField("boolean", "ENABLE_TURBOMODULE", "$enableCodegen") // If using codegen, assume using TurboModule } signingConfigs { release { @@ -177,6 +184,14 @@ android { proguardFile "${rootProject.projectDir}/../node_modules/detox/android/detox/proguard-rules-app.pro" } } + sourceSets.main { + java { + // SampleTurboModule. + srcDirs += [ + "$rootDir/ReactCommon/react/nativemodule/samples/platform/android", + ] + } + } } configurations { @@ -220,8 +235,63 @@ dependencies { } react { - enableCodegen = System.getenv("USE_CODEGEN") ?: false jsRootDir = file("$rootDir/packages/rn-tester") reactNativeRootDir = file("$rootDir") useJavaGenerator = System.getenv("USE_CODEGEN_JAVAPOET") ?: false } + +if (enableCodegen) { + // TODO: Move all this logic to CodegenPlugin.java. + def reactAndroidProjectDir = project(':ReactAndroid').projectDir; + def reactAndroidBuildDir = project(':ReactAndroid').buildDir; + + android { + defaultConfig { + externalNativeBuild { + ndkBuild { + abiFilters "armeabi-v7a", "x86", "x86_64", "arm64-v8a" + arguments "APP_PLATFORM=android-16", + "APP_STL=c++_shared", + "NDK_TOOLCHAIN_VERSION=clang", + // The following paths assume building React Native from source. + "GENERATED_SRC_DIR=$buildDir/generated/source", + "PROJECT_BUILD_DIR=$buildDir", + "REACT_ANDROID_DIR=$reactAndroidProjectDir" + cFlags "-Wall", "-Werror", "-fexceptions", "-frtti", "-DWITH_INSPECTOR=1" + cppFlags "-std=c++1y" + targets "rntester_appmodules" + } + } + } + externalNativeBuild { + ndkBuild { + path "$projectDir/src/main/jni/Android.mk" + } + } + } + + def packageReactNdkLibs = tasks.register("packageReactNdkLibs", Copy) { + // TODO: handle extracting .so from prebuilt :ReactAndroid. + dependsOn(":ReactAndroid:packageReactNdkLibs") + from("$reactAndroidBuildDir/react-ndk/exported") + into("$buildDir/react-ndk/exported") + } + + def cleanProjectNdkBuild = tasks.register("cleanProjectNdkBuild", Exec) { + ignoreExitValue(true) + tasks.forEach { + t -> + if (t.name.startsWith("externalNativeBuildClean")) { + dependsOn(t); + } + } + // This .cxx folder may cause stale ndkBuild configuration. + // See https://stackoverflow.com/a/58288851. + commandLine("rm", "-rf", "$projectDir/.cxx") + } + + afterEvaluate { + preBuild.dependsOn(packageReactNdkLibs) + clean.dependsOn(cleanProjectNdkBuild) + } +} diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.java b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.java index d7a315ca26dae4..4cbb384cded703 100644 --- a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.java +++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterActivity.java @@ -7,8 +7,6 @@ package com.facebook.react.uiapp; -import static com.facebook.react.uiapp.RNTesterApplication.IS_FABRIC_ENABLED; - import android.content.res.Configuration; import android.os.Bundle; import androidx.annotation.Nullable; @@ -31,7 +29,7 @@ public RNTesterActivityDelegate(ReactActivity activity, String mainComponentName @Override protected ReactRootView createRootView() { ReactRootView reactRootView = new ReactRootView(getContext()); - reactRootView.setIsFabric(IS_FABRIC_ENABLED); + reactRootView.setIsFabric(BuildConfig.ENABLE_FABRIC); return reactRootView; } diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java index e9a946ee224f75..710d8e0c7254e5 100644 --- a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java +++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java @@ -7,39 +7,44 @@ package com.facebook.react.uiapp; -import static com.facebook.react.uiapp.BuildConfig.ENABLE_FABRIC; - import android.app.Application; import android.content.Context; import androidx.annotation.Nullable; -import com.facebook.react.BuildConfig; +import com.facebook.fbreact.specs.SampleTurboModule; import com.facebook.react.ReactApplication; import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; +import com.facebook.react.TurboReactPackage; +import com.facebook.react.bridge.JSIModule; import com.facebook.react.bridge.JSIModulePackage; import com.facebook.react.bridge.JSIModuleProvider; import com.facebook.react.bridge.JSIModuleSpec; import com.facebook.react.bridge.JSIModuleType; import com.facebook.react.bridge.JavaScriptContextHolder; +import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.UIManager; +import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.fabric.ComponentFactory; import com.facebook.react.fabric.CoreComponentsRegistry; import com.facebook.react.fabric.FabricJSIModuleProvider; import com.facebook.react.fabric.ReactNativeConfig; +import com.facebook.react.module.model.ReactModuleInfo; +import com.facebook.react.module.model.ReactModuleInfoProvider; import com.facebook.react.shell.MainReactPackage; +import com.facebook.react.turbomodule.core.TurboModuleManager; import com.facebook.react.views.text.ReactFontManager; import com.facebook.soloader.SoLoader; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class RNTesterApplication extends Application implements ReactApplication { - static final boolean IS_FABRIC_ENABLED = ENABLE_FABRIC; - private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override @@ -59,13 +64,53 @@ public boolean getUseDeveloperSupport() { @Override public List getPackages() { - return Arrays.asList(new MainReactPackage()); + return Arrays.asList( + new MainReactPackage(), + new TurboReactPackage() { + public NativeModule getModule( + final String name, final ReactApplicationContext reactContext) { + if (!ReactFeatureFlags.useTurboModules) { + return null; + } + + if (SampleTurboModule.NAME.equals(name)) { + return new SampleTurboModule(reactContext); + } + + return null; + } + + // Note: Specialized annotation processor for @ReactModule isn't configured in OSS + // yet. For now, hardcode this information, though it's not necessary for most + // modules. + public ReactModuleInfoProvider getReactModuleInfoProvider() { + return new ReactModuleInfoProvider() { + public Map getReactModuleInfos() { + final Map moduleInfos = new HashMap<>(); + if (ReactFeatureFlags.useTurboModules) { + moduleInfos.put( + SampleTurboModule.NAME, + new ReactModuleInfo( + SampleTurboModule.NAME, + "SampleTurboModule", + false, // canOverrideExistingModule + false, // needsEagerInit + true, // hasConstants + false, // isCxxModule + true // isTurboModule + )); + } + return moduleInfos; + } + }; + } + }); } @Nullable @Override protected JSIModulePackage getJSIModulePackage() { - if (!IS_FABRIC_ENABLED) { + if (!BuildConfig.ENABLE_FABRIC && !ReactFeatureFlags.useTurboModules) { return null; } @@ -74,45 +119,83 @@ protected JSIModulePackage getJSIModulePackage() { public List getJSIModules( final ReactApplicationContext reactApplicationContext, final JavaScriptContextHolder jsContext) { - List specs = new ArrayList<>(); - specs.add( - new JSIModuleSpec() { - @Override - public JSIModuleType getJSIModuleType() { - return JSIModuleType.UIManager; - } - - @Override - public JSIModuleProvider getJSIModuleProvider() { - ComponentFactory ComponentFactory = new ComponentFactory(); - CoreComponentsRegistry.register(ComponentFactory); - return new FabricJSIModuleProvider( - reactApplicationContext, - ComponentFactory, - // TODO: T71362667 add ReactNativeConfig's support in RNTester - new ReactNativeConfig() { - @Override - public boolean getBool(String s) { - return true; - } - - @Override - public int getInt64(String s) { - return 0; - } - - @Override - public String getString(String s) { - return ""; - } - - @Override - public double getDouble(String s) { - return 0; - } - }); - } - }); + final List specs = new ArrayList<>(); + + // Install the new native module system. + if (ReactFeatureFlags.useTurboModules) { + specs.add( + new JSIModuleSpec() { + @Override + public JSIModuleType getJSIModuleType() { + return JSIModuleType.TurboModuleManager; + } + + @Override + public JSIModuleProvider getJSIModuleProvider() { + return new JSIModuleProvider() { + @Override + public JSIModule get() { + final ReactInstanceManager reactInstanceManager = + getReactInstanceManager(); + final List packages = reactInstanceManager.getPackages(); + + return new TurboModuleManager( + jsContext, + new RNTesterTurboModuleManagerDelegate( + reactApplicationContext, packages), + reactApplicationContext + .getCatalystInstance() + .getJSCallInvokerHolder(), + reactApplicationContext + .getCatalystInstance() + .getNativeCallInvokerHolder()); + } + }; + } + }); + } + + // Install the new renderer. + if (BuildConfig.ENABLE_FABRIC) { + specs.add( + new JSIModuleSpec() { + @Override + public JSIModuleType getJSIModuleType() { + return JSIModuleType.UIManager; + } + + @Override + public JSIModuleProvider getJSIModuleProvider() { + final ComponentFactory ComponentFactory = new ComponentFactory(); + CoreComponentsRegistry.register(ComponentFactory); + return new FabricJSIModuleProvider( + reactApplicationContext, + ComponentFactory, + // TODO: T71362667 add ReactNativeConfig's support in RNTester + new ReactNativeConfig() { + @Override + public boolean getBool(final String s) { + return false; + } + + @Override + public int getInt64(final String s) { + return 0; + } + + @Override + public String getString(final String s) { + return ""; + } + + @Override + public double getDouble(final String s) { + return 0; + } + }); + } + }); + } return specs; } @@ -122,6 +205,8 @@ public double getDouble(String s) { @Override public void onCreate() { + // Set `USE_CODEGEN` env var when building RNTester to enable TurboModule. + ReactFeatureFlags.useTurboModules = BuildConfig.ENABLE_TURBOMODULE; ReactFontManager.getInstance().addCustomFont(this, "Rubik", R.font.rubik); super.onCreate(); SoLoader.init(this, /* native exopackage */ false); @@ -141,24 +226,24 @@ public ReactNativeHost getReactNativeHost() { * @param reactInstanceManager */ private static void initializeFlipper( - Context context, ReactInstanceManager reactInstanceManager) { + final Context context, final ReactInstanceManager reactInstanceManager) { if (BuildConfig.DEBUG) { try { /* We use reflection here to pick up the class that initializes Flipper, since Flipper library is not available in release mode */ - Class aClass = Class.forName("com.facebook.react.uiapp.ReactNativeFlipper"); + final Class aClass = Class.forName("com.facebook.react.uiapp.ReactNativeFlipper"); aClass .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) .invoke(null, context, reactInstanceManager); - } catch (ClassNotFoundException e) { + } catch (final ClassNotFoundException e) { e.printStackTrace(); - } catch (NoSuchMethodException e) { + } catch (final NoSuchMethodException e) { e.printStackTrace(); - } catch (IllegalAccessException e) { + } catch (final IllegalAccessException e) { e.printStackTrace(); - } catch (InvocationTargetException e) { + } catch (final InvocationTargetException e) { e.printStackTrace(); } } diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterTurboModuleManagerDelegate.java b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterTurboModuleManagerDelegate.java new file mode 100644 index 00000000000000..0baf04bc1d7da5 --- /dev/null +++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterTurboModuleManagerDelegate.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) Facebook, Inc. and its 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.uiapp; + +import androidx.annotation.VisibleForTesting; +import com.facebook.jni.HybridData; +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.turbomodule.core.ReactPackageTurboModuleManagerDelegate; +import com.facebook.soloader.SoLoader; +import java.util.List; + +/** This class is responsible for creating all the TurboModules for the RNTester app. */ +public class RNTesterTurboModuleManagerDelegate extends ReactPackageTurboModuleManagerDelegate { + static { + SoLoader.loadLibrary("rntester_appmodules"); + } + + protected native HybridData initHybrid(); + + @VisibleForTesting + native boolean canCreateTurboModule(String moduleName); + + public RNTesterTurboModuleManagerDelegate( + ReactApplicationContext context, List packages) { + super(context, packages); + } +} diff --git a/packages/rn-tester/android/app/src/main/jni/Android.mk b/packages/rn-tester/android/app/src/main/jni/Android.mk new file mode 100644 index 00000000000000..6e003cdb953721 --- /dev/null +++ b/packages/rn-tester/android/app/src/main/jni/Android.mk @@ -0,0 +1,26 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +THIS_DIR := $(call my-dir) + +include $(REACT_ANDROID_DIR)/Android-prebuilt.mk + +# SampleNativeModule +include $(REACT_COMMON_DIR)/react/nativemodule/samples/platform/android/Android.mk + +LOCAL_PATH := $(THIS_DIR) + +include $(CLEAR_VARS) +LOCAL_MODULE := rntester_appmodules +# Note: Build the react-native-codegen output along with other app-specific C++ files. +LOCAL_C_INCLUDES := $(LOCAL_PATH) $(GENERATED_SRC_DIR)/codegen/jni +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) $(wildcard $(GENERATED_SRC_DIR)/codegen/jni/*.cpp) +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) $(GENERATED_SRC_DIR)/codegen/jni +LOCAL_SHARED_LIBRARIES := libfbjni libreact_nativemodule_core libturbomodulejsijni libreact_codegen_reactandroidspec +LOCAL_STATIC_LIBRARIES := libsampleturbomodule +LOCAL_CFLAGS := \ + -DLOG_TAG=\"ReactNative\" +LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall +include $(BUILD_SHARED_LIBRARY) diff --git a/packages/rn-tester/android/app/src/main/jni/OnLoad.cpp b/packages/rn-tester/android/app/src/main/jni/OnLoad.cpp new file mode 100644 index 00000000000000..f4ed589bcbd37d --- /dev/null +++ b/packages/rn-tester/android/app/src/main/jni/OnLoad.cpp @@ -0,0 +1,17 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include "RNTesterTurboModuleManagerDelegate.h" + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { + return facebook::jni::initialize(vm, [] { + // TODO: dvacca ramanpreet unify this with the way "ComponentDescriptorFactory" is defined in Fabric + facebook::react::RNTesterTurboModuleManagerDelegate::registerNatives(); + }); +} diff --git a/packages/rn-tester/android/app/src/main/jni/RNTesterAppModuleProvider.cpp b/packages/rn-tester/android/app/src/main/jni/RNTesterAppModuleProvider.cpp new file mode 100644 index 00000000000000..06c20c981cd016 --- /dev/null +++ b/packages/rn-tester/android/app/src/main/jni/RNTesterAppModuleProvider.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "RNTesterAppModuleProvider.h" + +#include +#include +#include + +namespace facebook { +namespace react { + +std::shared_ptr RNTesterAppModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) { + auto module = PackagesRnTesterAndroidAppSpec_ModuleProvider(moduleName, params); + if (module != nullptr) { + return module; + } + + module = SampleTurboModuleSpec_ModuleProvider(moduleName, params); + if (module != nullptr) { + return module; + } + + // TODO: fix up the ReactAndroidSpec_ModuleProvider() to avoid the Android prefix. + if (moduleName == "DatePicker") { + return std::make_shared(params); + } + if (moduleName == "DialogManager") { + return std::make_shared(params); + } + if (moduleName == "ImageLoader") { + return std::make_shared(params); + } + if (moduleName == "Networking") { + return std::make_shared(params); + } + if (moduleName == "Permissions") { + return std::make_shared(params); + } + if (moduleName == "PlatformConstants") { + return std::make_shared(params); + } + if (moduleName == "StatusBarManager") { + return std::make_shared(params); + } + if (moduleName == "Toast") { + return std::make_shared(params); + } + + // TODO: handle some special case naming. + if (moduleName == "IntentAndroid") { + return std::make_shared(params); + } + + // TODO: Animated module has special cases. + if ("NativeAnimatedModule" == moduleName) { + return std::make_shared(params); + } + if ("NativeAnimatedTurboModule" == moduleName) { + return std::make_shared(params); + } + + // TODO: handle multiple names for one spec. + if ("AsyncLocalStorage" == moduleName) { + return std::make_shared(params); + } + if ("AsyncSQLiteDBStorage" == moduleName) { + return std::make_shared(params); + } + + return ReactAndroidSpec_ModuleProvider(moduleName, params); +} + +} // namespace react +} // namespace facebook diff --git a/packages/rn-tester/android/app/src/main/jni/RNTesterAppModuleProvider.h b/packages/rn-tester/android/app/src/main/jni/RNTesterAppModuleProvider.h new file mode 100644 index 00000000000000..1bdc8477ed5ac9 --- /dev/null +++ b/packages/rn-tester/android/app/src/main/jni/RNTesterAppModuleProvider.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +#include + +namespace facebook { +namespace react { + +std::shared_ptr RNTesterAppModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms); + +} // namespace react +} // namespace facebook diff --git a/packages/rn-tester/android/app/src/main/jni/RNTesterTurboModuleManagerDelegate.cpp b/packages/rn-tester/android/app/src/main/jni/RNTesterTurboModuleManagerDelegate.cpp new file mode 100644 index 00000000000000..e6ca3d0ecd0ac2 --- /dev/null +++ b/packages/rn-tester/android/app/src/main/jni/RNTesterTurboModuleManagerDelegate.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "RNTesterTurboModuleManagerDelegate.h" + +#include "RNTesterAppModuleProvider.h" + +namespace facebook { +namespace react { + +jni::local_ref RNTesterTurboModuleManagerDelegate::initHybrid(jni::alias_ref) { + return makeCxxInstance(); +} + +void RNTesterTurboModuleManagerDelegate::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", RNTesterTurboModuleManagerDelegate::initHybrid), + makeNativeMethod("canCreateTurboModule", RNTesterTurboModuleManagerDelegate::canCreateTurboModule), + }); +} + +std::shared_ptr RNTesterTurboModuleManagerDelegate::getTurboModule(const std::string name, const std::shared_ptr jsInvoker) { + // Not implemented yet: provide pure-C++ NativeModules here. + return nullptr; +} + +std::shared_ptr RNTesterTurboModuleManagerDelegate::getTurboModule(const std::string name, const JavaTurboModule::InitParams ¶ms) { + return RNTesterAppModuleProvider(name, params); +} + +bool RNTesterTurboModuleManagerDelegate::canCreateTurboModule(std::string name) { + return getTurboModule(name, nullptr) != nullptr || getTurboModule(name, {.moduleName = name}) != nullptr; +} + +} // namespace react +} // namespace facebook diff --git a/packages/rn-tester/android/app/src/main/jni/RNTesterTurboModuleManagerDelegate.h b/packages/rn-tester/android/app/src/main/jni/RNTesterTurboModuleManagerDelegate.h new file mode 100644 index 00000000000000..8091b73569c1e1 --- /dev/null +++ b/packages/rn-tester/android/app/src/main/jni/RNTesterTurboModuleManagerDelegate.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +#include +#include + +namespace facebook { +namespace react { + +class RNTesterTurboModuleManagerDelegate : public jni::HybridClass { +public: + static constexpr auto kJavaDescriptor = + "Lcom/facebook/react/uiapp/RNTesterTurboModuleManagerDelegate;"; + + static jni::local_ref initHybrid(jni::alias_ref); + + static void registerNatives(); + + std::shared_ptr getTurboModule(const std::string name, const std::shared_ptr jsInvoker) override; + std::shared_ptr getTurboModule(const std::string name, const JavaTurboModule::InitParams ¶ms) override; + + /** + * Test-only method. Allows user to verify whether a TurboModule can be created + * by instances of this class. + */ + bool canCreateTurboModule(std::string name); + +private: + friend HybridBase; + using HybridBase::HybridBase; + +}; + +} // namespace react +} // namespace facebook diff --git a/packages/rn-tester/e2e/__tests__/Button-test.js b/packages/rn-tester/e2e/__tests__/Button-test.js index 53588b168a525a..1b67f2a5871209 100644 --- a/packages/rn-tester/e2e/__tests__/Button-test.js +++ b/packages/rn-tester/e2e/__tests__/Button-test.js @@ -23,36 +23,75 @@ describe('Button', () => { ); }); - it('Simple button should be tappable', async () => { - await openExampleWithTitle('Simple Button'); - await element(by.id('simple_button')).tap(); - await expect(element(by.text('Simple has been pressed!'))).toBeVisible(); + it('Default Styling button should be tappable', async () => { + await openExampleWithTitle('Default Styling'); + await element(by.id('button_default_styling')).tap(); + await expect( + element(by.text('Your application has been submitted!')), + ).toBeVisible(); await element(by.text('OK')).tap(); }); - it('Adjusted color button should be tappable', async () => { - await openExampleWithTitle('Adjusted color'); - await element(by.id('purple_button')).tap(); - await expect(element(by.text('Purple has been pressed!'))).toBeVisible(); + it('Red color button should be tappable', async () => { + await openExampleWithTitle('Color'); + await element(by.id('cancel_button')).tap(); + await expect( + element(by.text('Your application has been cancelled!')), + ).toBeVisible(); await element(by.text('OK')).tap(); }); it("Two buttons with JustifyContent:'space-between' should be tappable", async () => { - await openExampleWithTitle('Fit to text layout'); - await element(by.id('left_button')).tap(); - await expect(element(by.text('Left has been pressed!'))).toBeVisible(); + await openExampleWithTitle('Two Buttons'); + await element(by.id('two_cancel_button')).tap(); + await expect( + element(by.text('Your application has been cancelled!')), + ).toBeVisible(); + await element(by.text('OK')).tap(); + + await element(by.id('two_submit_button')).tap(); + await expect( + element(by.text('Your application has been submitted!')), + ).toBeVisible(); + await element(by.text('OK')).tap(); + }); + + it("Three buttons with JustifyContent:'space-between' should be tappable", async () => { + await openExampleWithTitle('Three Buttons'); + await element(by.id('three_cancel_button')).tap(); + await expect( + element(by.text('Your application has been cancelled!')), + ).toBeVisible(); await element(by.text('OK')).tap(); - await element(by.id('right_button')).tap(); - await expect(element(by.text('Right has been pressed!'))).toBeVisible(); + await openExampleWithTitle('Three Buttons'); + await element(by.id('three_save_button')).tap(); + await expect( + element(by.text('Your application has been saved!')), + ).toBeVisible(); + await element(by.text('OK')).tap(); + + await element(by.id('three_submit_button')).tap(); + await expect( + element(by.text('Your application has been submitted!')), + ).toBeVisible(); await element(by.text('OK')).tap(); }); it('Disabled button should not interact', async () => { - await openExampleWithTitle('Disabled Button'); + await openExampleWithTitle('Disabled'); await element(by.id('disabled_button')).tap(); await expect( - element(by.text('Disabled has been pressed!')), + element(by.text('Your application has been submitted!')), ).toBeNotVisible(); }); + + it('AccessibilityLabel button should be tappable', async () => { + await openExampleWithTitle('AccessibilityLabel'); + await element(by.id('accessibilityLabel_button')).tap(); + await expect( + element(by.text('Your application has been submitted!')), + ).toBeVisible(); + await element(by.text('OK')).tap(); + }); }); diff --git a/packages/rn-tester/js/RNTesterAppShared.js b/packages/rn-tester/js/RNTesterAppShared.js index 5d82b93e3e5dd7..ee0c73677cfbc5 100644 --- a/packages/rn-tester/js/RNTesterAppShared.js +++ b/packages/rn-tester/js/RNTesterAppShared.js @@ -139,13 +139,22 @@ const RNTesterApp = (): React.Node => { // Setup hardware back button press listener React.useEffect(() => { - BackHandler.addEventListener('hardwareBackPress', () => { + const handleHardwareBackPress = () => { if (openExample) { handleBackPress(); return true; } return false; - }); + }; + + BackHandler.addEventListener('hardwareBackPress', handleHardwareBackPress); + + return () => { + BackHandler.removeEventListener( + 'hardwareBackPress', + handleHardwareBackPress, + ); + }; }, [openExample, handleBackPress]); const handleExampleCardPress = React.useCallback( diff --git a/packages/rn-tester/js/components/RNTesterBookmark.js b/packages/rn-tester/js/components/RNTesterBookmark.js deleted file mode 100644 index 62142bd8ad7026..00000000000000 --- a/packages/rn-tester/js/components/RNTesterBookmark.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict-local - * @format - */ - -'use strict'; - -import * as React from 'react'; -import type {RNTesterExample} from '../types/RNTesterTypes'; - -export type RNTesterBookmark = { - Components: {...}, - Api: {...}, - AddApi: (apiName: string, api: RNTesterExample) => mixed, - AddComponent: (componentName: string, component: RNTesterExample) => mixed, - RemoveApi: (apiName: string) => mixed, - RemoveComponent: (componentName: string) => mixed, - checkBookmark: (title: string, key: string) => mixed, -}; - -export const bookmarks: RNTesterBookmark = { - Components: {}, - Api: {}, - AddComponent: () => {}, - RemoveComponent: () => {}, - AddApi: () => {}, - RemoveApi: () => {}, - checkBookmark: () => {}, -}; - -export const RNTesterBookmarkContext: React.Context = React.createContext( - bookmarks, -); diff --git a/packages/rn-tester/js/components/RNTesterDocumentationURL.js b/packages/rn-tester/js/components/RNTesterDocumentationURL.js index 8d6d8423591e81..0a8b20823fb860 100644 --- a/packages/rn-tester/js/components/RNTesterDocumentationURL.js +++ b/packages/rn-tester/js/components/RNTesterDocumentationURL.js @@ -5,15 +5,20 @@ * LICENSE file in the root directory of this source tree. * * @format + * @flow */ 'use strict'; -import React from 'react'; +import * as React from 'react'; import {Image, StyleSheet, TouchableOpacity} from 'react-native'; import openURLInBrowser from 'react-native/Libraries/Core/Devtools/openURLInBrowser'; -const RNTesterDocumentationURL = ({documentationURL}) => ( +type Props = $ReadOnly<{| + documentationURL: string, +|}>; + +const RNTesterDocumentationURL = ({documentationURL}: Props): React.Node => ( openURLInBrowser(documentationURL)}> @@ -28,7 +33,6 @@ export default RNTesterDocumentationURL; const styles = StyleSheet.create({ container: { - textDecorationLine: 'underline', position: 'absolute', bottom: 0, right: -15, diff --git a/packages/rn-tester/js/components/RNTesterNavbar.js b/packages/rn-tester/js/components/RNTesterNavbar.js index 1be08517c8b69d..24d703c00e4028 100644 --- a/packages/rn-tester/js/components/RNTesterNavbar.js +++ b/packages/rn-tester/js/components/RNTesterNavbar.js @@ -13,6 +13,86 @@ import {Text, View, StyleSheet, Image, Pressable} from 'react-native'; import {RNTesterThemeContext} from './RNTesterTheme'; +const BookmarkTab = ({handleNavBarPress, isBookmarkActive, theme}) => ( + + + + handleNavBarPress({screen: 'bookmarks'})}> + + + + + + +); + +const NavbarButton = ({ + testID, + theme, + isActive, + activeImage, + inactiveImage, + label, + handlePress, + iconStyle, +}) => ( + + + + + {label} + + + +); + +const ComponentTab = ({isComponentActive, handleNavBarPress, theme}) => ( + handleNavBarPress({screen: 'components'})} + activeImage={require('./../assets/bottom-nav-components-icon-active.png')} + inactiveImage={require('./../assets/bottom-nav-components-icon-inactive.png')} + isActive={isComponentActive} + theme={theme} + iconStyle={styles.componentIcon} + /> +); + +const APITab = ({isAPIActive, handleNavBarPress, theme}) => ( + handleNavBarPress({screen: 'apis'})} + activeImage={require('./../assets/bottom-nav-apis-icon-active.png')} + inactiveImage={require('./../assets/bottom-nav-apis-icon-inactive.png')} + isActive={isAPIActive} + theme={theme} + iconStyle={styles.apiIcon} + /> +); + type Props = $ReadOnly<{| handleNavBarPress: (data: {screen: string}) => void, screen: string, @@ -33,84 +113,21 @@ const RNTesterNavbar = ({ return ( - handleNavBarPress({screen: 'components'})} - style={[styles.navButton, {backgroundColor: theme.BackgroundColor}]}> - - - - Components - - - - - - - - - handleNavBarPress({screen: 'bookmarks'})}> - - - - - - - - handleNavBarPress({screen: 'apis'})} - style={[styles.navButton, {backgroundColor: theme.BackgroundColor}]}> - - - - APIs - - - + + + ); diff --git a/packages/rn-tester/js/components/RNTesterTheme.js b/packages/rn-tester/js/components/RNTesterTheme.js index 5727f64da5ce80..4a9371296321a8 100644 --- a/packages/rn-tester/js/components/RNTesterTheme.js +++ b/packages/rn-tester/js/components/RNTesterTheme.js @@ -33,7 +33,8 @@ export type RNTesterTheme = { SeparatorColor: ColorValue, OpaqueSeparatorColor: ColorValue, LinkColor: ColorValue, - SystemPurpleColor: ColorValue, + SystemRedColor: ColorValue, + SystemGreenColor: ColorValue, ToolbarColor: ColorValue, BackgroundColor: ColorValue, BorderColor: ColorValue, @@ -59,7 +60,8 @@ export const RNTesterLightTheme = { SeparatorColor: '#3c3c4349', OpaqueSeparatorColor: '#c6c6c8ff', LinkColor: '#007affff', - SystemPurpleColor: '#af52deff', + SystemRedColor: '#ff3b30ff', + SystemGreenColor: '#34c759ff', ToolbarColor: '#e9eaedff', BackgroundColor: '#f3f8ffff', BorderColor: '#005dffff', @@ -84,7 +86,8 @@ export const RNTesterDarkTheme = { SeparatorColor: '#54545899', OpaqueSeparatorColor: '#38383aff', LinkColor: '#0984ffff', - SystemPurpleColor: '#bf5af2ff', + SystemRedColor: '#ff375fff', + SystemGreenColor: '#30d158ff', ToolbarColor: '#3c3c43ff', BackgroundColor: '#0c0700ff', BorderColor: '#005dffff', diff --git a/packages/rn-tester/js/examples/Button/ButtonExample.js b/packages/rn-tester/js/examples/Button/ButtonExample.js index 00e8110f6dc2fe..c15a451589539d 100644 --- a/packages/rn-tester/js/examples/Button/ButtonExample.js +++ b/packages/rn-tester/js/examples/Button/ButtonExample.js @@ -13,10 +13,10 @@ const React = require('react'); const {Alert, Button, View, StyleSheet} = require('react-native'); -import {RNTesterThemeContext} from '../../components/RNTesterTheme'; +const {RNTesterThemeContext} = require('../../components/RNTesterTheme'); function onButtonPress(buttonName) { - Alert.alert(`${buttonName} has been pressed!`); + Alert.alert(`Your application has been ${buttonName}!`); } exports.displayName = 'ButtonExample'; @@ -28,21 +28,33 @@ exports.description = 'Simple React Native button component.'; exports.examples = [ { - title: 'Simple Button', - description: ('The title and onPress handler are required. It is ' + - 'recommended to set accessibilityLabel to help make your app usable by ' + - 'everyone.': string), + title: 'Button with default styling', + render: function(): React.Node { + return ( +