diff --git a/Libraries/TurboModule/samples/NativeSampleTurboModule.js b/Libraries/TurboModule/samples/NativeSampleTurboModule.js index f9c4c6ef53e..d240c4ad3ab 100644 --- a/Libraries/TurboModule/samples/NativeSampleTurboModule.js +++ b/Libraries/TurboModule/samples/NativeSampleTurboModule.js @@ -10,6 +10,7 @@ 'use strict'; +import type {UnsafeObject} from '../../Types/CodegenTypes'; import type {RootTag, TurboModule} from '../RCTExport'; import * as TurboModuleRegistry from '../TurboModuleRegistry'; @@ -26,6 +27,7 @@ export interface Spec extends TurboModule { +getString: (arg: string) => string; +getArray: (arg: Array) => Array; +getObject: (arg: Object) => Object; + +getUnsafeObject: (arg: UnsafeObject) => UnsafeObject; +getRootTag: (arg: RootTag) => RootTag; +getValue: (x: number, y: string, z: Object) => Object; +getValueWithCallback: (callback: (value: string) => void) => void; diff --git a/Libraries/Types/CodegenTypes.js b/Libraries/Types/CodegenTypes.js index 9e33919fdd8..457ef2f3902 100644 --- a/Libraries/Types/CodegenTypes.js +++ b/Libraries/Types/CodegenTypes.js @@ -28,6 +28,7 @@ export type DirectEventHandler< export type Double = number; export type Float = number; export type Int32 = number; +export type UnsafeObject = $FlowFixMe; // Object is forbidden in strict mode type DefaultTypes = number | boolean | string | $ReadOnlyArray; // Default handling, ignore the unused value diff --git a/ReactCommon/react/nativemodule/samples/platform/android/NativeSampleTurboModuleSpec.java b/ReactCommon/react/nativemodule/samples/platform/android/NativeSampleTurboModuleSpec.java index 8475fbc4711..f992e5d6ae1 100644 --- a/ReactCommon/react/nativemodule/samples/platform/android/NativeSampleTurboModuleSpec.java +++ b/ReactCommon/react/nativemodule/samples/platform/android/NativeSampleTurboModuleSpec.java @@ -42,6 +42,9 @@ public abstract class NativeSampleTurboModuleSpec extends ReactContextBaseJavaMo @ReactMethod(isBlockingSynchronousMethod = true) public abstract WritableMap getObject(ReadableMap arg); + @ReactMethod(isBlockingSynchronousMethod = true) + public abstract WritableMap getUnsafeObject(ReadableMap arg); + @ReactMethod public abstract void voidFunc(); diff --git a/ReactCommon/react/nativemodule/samples/platform/android/SampleTurboModule.java b/ReactCommon/react/nativemodule/samples/platform/android/SampleTurboModule.java index bc357d2baa3..f919eb5bd2d 100644 --- a/ReactCommon/react/nativemodule/samples/platform/android/SampleTurboModule.java +++ b/ReactCommon/react/nativemodule/samples/platform/android/SampleTurboModule.java @@ -109,6 +109,16 @@ public class SampleTurboModule extends NativeSampleTurboModuleSpec { return map; } + @DoNotStrip + @Override + @SuppressWarnings("unused") + public WritableMap getUnsafeObject(ReadableMap arg) { + WritableNativeMap map = new WritableNativeMap(); + map.merge(arg); + log("getUnsafeObject", arg, map); + return map; + } + @DoNotStrip @SuppressWarnings("unused") @Override diff --git a/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.h b/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.h index 4738cff8d10..b14289e1cd1 100644 --- a/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.h +++ b/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.h @@ -26,6 +26,7 @@ - (NSString *)getString:(NSString *)arg; - (NSArray> *)getArray:(NSArray *)arg; - (NSDictionary *)getObject:(NSDictionary *)arg; +- (NSDictionary *)getUnsafeObject:(NSDictionary *)arg; - (NSNumber *)getRootTag:(double)arg; - (NSDictionary *)getValue:(double)x y:(NSString *)y z:(NSDictionary *)z; - (void)getValueWithCallback:(RCTResponseSenderBlock)callback; diff --git a/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm b/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm index 70f69b107ba..7337f63d518 100644 --- a/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm +++ b/ReactCommon/react/nativemodule/samples/platform/ios/RCTNativeSampleTurboModuleSpec.mm @@ -70,6 +70,16 @@ static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getObj .invokeObjCMethod(rt, ObjectKind, "getObject", @selector(getObject:), args, count); } +static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getUnsafeObject( + facebook::jsi::Runtime &rt, + TurboModule &turboModule, + const facebook::jsi::Value *args, + size_t count) +{ + return static_cast(turboModule) + .invokeObjCMethod(rt, ObjectKind, "getUnsafeObject", @selector(getUnsafeObject:), args, count); +} + static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getRootTag( facebook::jsi::Runtime &rt, TurboModule &turboModule, @@ -130,6 +140,7 @@ NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const ObjCTurboMo methodMap_["getString"] = MethodMetadata{1, __hostFunction_NativeSampleTurboModuleSpecJSI_getString}; methodMap_["getArray"] = MethodMetadata{1, __hostFunction_NativeSampleTurboModuleSpecJSI_getArray}; methodMap_["getObject"] = MethodMetadata{1, __hostFunction_NativeSampleTurboModuleSpecJSI_getObject}; + methodMap_["getUnsafeObject"] = MethodMetadata{1, __hostFunction_NativeSampleTurboModuleSpecJSI_getUnsafeObject}; methodMap_["getRootTag"] = MethodMetadata{1, __hostFunction_NativeSampleTurboModuleSpecJSI_getRootTag}; methodMap_["getValue"] = MethodMetadata{3, __hostFunction_NativeSampleTurboModuleSpecJSI_getValue}; methodMap_["getValueWithCallback"] = diff --git a/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboModule.mm b/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboModule.mm index 3e019c791ea..ac1d236ed7d 100644 --- a/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboModule.mm +++ b/ReactCommon/react/nativemodule/samples/platform/ios/RCTSampleTurboModule.mm @@ -96,6 +96,11 @@ RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSDictionary *, getObject : (NSDictionary *) return arg; } +RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSDictionary *, getUnsafeObject : (NSDictionary *)arg) +{ + return arg; +} + RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSNumber *, getRootTag : (double)arg) { return @(arg); diff --git a/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.cpp b/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.cpp index 5eea42c3d80..a66b01ef2c1 100644 --- a/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.cpp +++ b/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.cpp @@ -69,6 +69,12 @@ std::vector SampleTurboCxxModuleLegacyImpl::getMethods() { return getObject(xplat::jsArgAsObject(args, 0)); }, CxxModule::SyncTag), + CxxModule::Method( + "getUnsafeObject", + [this](folly::dynamic args) { + return getUnsafeObject(xplat::jsArgAsObject(args, 0)); + }, + CxxModule::SyncTag), CxxModule::Method( "getRootTag", [this](folly::dynamic args) { @@ -126,6 +132,11 @@ folly::dynamic SampleTurboCxxModuleLegacyImpl::getObject( return arg; } +folly::dynamic SampleTurboCxxModuleLegacyImpl::getUnsafeObject( + const folly::dynamic &arg) { + return arg; +} + double SampleTurboCxxModuleLegacyImpl::getRootTag(double arg) { return arg; } diff --git a/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.h b/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.h index 72d0e3b4adb..a3aa44062f5 100644 --- a/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.h +++ b/ReactCommon/react/nativemodule/samples/platform/ios/SampleTurboCxxModuleLegacyImpl.h @@ -31,6 +31,7 @@ class SampleTurboCxxModuleLegacyImpl std::string getString(const std::string &arg); folly::dynamic getArray(const folly::dynamic &arg); folly::dynamic getObject(const folly::dynamic &arg); + folly::dynamic getUnsafeObject(const folly::dynamic &arg); double getRootTag(double arg); folly::dynamic getValue(double x, const std::string &y, const folly::dynamic &z); diff --git a/packages/eslint-plugin-codegen/__tests__/react-native-modules-test.js b/packages/eslint-plugin-codegen/__tests__/react-native-modules-test.js index 18880a57eac..7591ffb86a8 100644 --- a/packages/eslint-plugin-codegen/__tests__/react-native-modules-test.js +++ b/packages/eslint-plugin-codegen/__tests__/react-native-modules-test.js @@ -18,7 +18,20 @@ const NATIVE_MODULES_DIR = __dirname; const eslintTester = new ESLintTester(); -const VALID_SPECS = []; +const VALID_SPECS = [ + { + code: ` +import {TurboModuleRegistry, type TurboModule} from 'react-native'; +import type {UnsafeObject} from 'react-native/Libraries/Types/CodegenTypes'; + +export interface Spec extends TurboModule { + func1(a: string): UnsafeObject, +} +export default TurboModuleRegistry.get('XYZ'); + `, + filename: `${NATIVE_MODULES_DIR}/NativeXYZ.js`, + }, +]; const INVALID_SPECS = [ // Untyped NativeModule require 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 9ef58d4d097..e05e185232c 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 @@ -257,6 +257,31 @@ export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); `; +const NATIVE_MODULE_WITH_UNSAFE_OBJECT = ` +/** + * 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 '../RCTExport'; +import * as TurboModuleRegistry from '../TurboModuleRegistry'; +import type {UnsafeObject} from 'react-native/Libraries/Types/CodegenTypes'; + +export interface Spec extends TurboModule { + +getUnsafeObject: (o: UnsafeObject) => UnsafeObject, +} + +export default TurboModuleRegistry.getEnforcing('SampleTurboModule'); + +`; + const NATIVE_MODULE_WITH_ROOT_TAG = ` /** * Copyright (c) Facebook, Inc. and its affiliates. @@ -555,6 +580,7 @@ module.exports = { NATIVE_MODULE_WITH_COMPLEX_OBJECTS, NATIVE_MODULE_WITH_COMPLEX_OBJECTS_WITH_NULLABLE_KEY, NATIVE_MODULE_WITH_SIMPLE_OBJECT, + NATIVE_MODULE_WITH_UNSAFE_OBJECT, NATIVE_MODULE_WITH_ROOT_TAG, NATIVE_MODULE_WITH_NULLABLE_PARAM, NATIVE_MODULE_WITH_BASIC_ARRAY, diff --git a/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap b/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap index 1ce7e4797dd..2527921cea9 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap +++ b/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap @@ -1297,3 +1297,40 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_SIMPLE_O } }" `; + +exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_UNSAFE_OBJECT 1`] = ` +"{ + 'modules': { + 'NativeSampleTurboModule': { + 'type': 'NativeModule', + 'aliases': {}, + 'spec': { + 'properties': [ + { + 'name': 'getUnsafeObject', + 'optional': false, + 'typeAnnotation': { + 'type': 'FunctionTypeAnnotation', + 'returnTypeAnnotation': { + 'type': 'GenericObjectTypeAnnotation' + }, + 'params': [ + { + 'name': 'o', + 'optional': false, + 'typeAnnotation': { + 'type': 'GenericObjectTypeAnnotation' + } + } + ] + } + } + ] + }, + 'moduleNames': [ + 'SampleTurboModule' + ] + } + } +}" +`; 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 955f8358d5a..e00d157743a 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/index.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/index.js @@ -197,6 +197,7 @@ function translateTypeAnnotation( type: 'FloatTypeAnnotation', }); } + case 'UnsafeObject': case 'Object': { return wrapNullable(nullable, { type: 'GenericObjectTypeAnnotation', diff --git a/packages/rn-tester/js/examples/TurboModule/SampleTurboModuleExample.js b/packages/rn-tester/js/examples/TurboModule/SampleTurboModuleExample.js index bc227cdc94c..09fdd06f9af 100644 --- a/packages/rn-tester/js/examples/TurboModule/SampleTurboModuleExample.js +++ b/packages/rn-tester/js/examples/TurboModule/SampleTurboModuleExample.js @@ -68,6 +68,8 @@ class SampleTurboModuleExample extends React.Component<{||}, State> { ]), getObject: () => NativeSampleTurboModule.getObject({a: 1, b: 'foo', c: null}), + getUnsafeObject: () => + NativeSampleTurboModule.getObject({a: 1, b: 'foo', c: null}), getRootTag: () => NativeSampleTurboModule.getRootTag(this.context), getValue: () => NativeSampleTurboModule.getValue(5, 'test', {a: 1, b: 'foo'}),