diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js index 397516dc5c22..19ce4f8d7903 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js @@ -59,6 +59,7 @@ export type MethodSerializationOutput = Readonly<{ methodName: string, protocolMethod: string, selector: string, + rctArrayBufferSelector: ?string, structParamRecords: ReadonlyArray, returnJSType: ReturnJSType, argCount: number, @@ -122,6 +123,10 @@ function serializeMethod( ); } + const hasDirectArrayBuffer = + params.some(param => isArrayBufferType(param.typeAnnotation)) || + isArrayBufferType(propertyTypeAnnotation.returnTypeAnnotation); + /** * Build Protocol Method **/ @@ -129,34 +134,91 @@ function serializeMethod( methodName, propertyTypeAnnotation.returnTypeAnnotation, ); - const paddingMax = `- (${returnObjCType})${methodName}`.length; - - const objCParams = methodParams.reduce( - ($objCParams, {objCType, paramName}, i) => { + const buildObjCParams = ( + paramsToSerialize: ReadonlyArray<{paramName: string, objCType: string}>, + returnType: string, + selectorMethodName: string, + ) => { + const paddingMax = `- (${returnType})${selectorMethodName}`.length; + return paramsToSerialize.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 buildProtocolMethod = ( + returnType: string, + selectorMethodName: string, + paramsToSerialize: ReadonlyArray<{paramName: string, objCType: string}>, + ) => + ProtocolMethodTemplate({ + methodName: selectorMethodName, + returnObjCType: returnType, + params: buildObjCParams( + paramsToSerialize, + returnType, + selectorMethodName, + ), + }); + + const rctArrayBufferMethodName = `${methodName}WithRCTArrayBuffer`; + + let protocolMethod = buildProtocolMethod( + returnObjCType, + methodName, + methodParams, ); - const protocolMethod = ProtocolMethodTemplate({ - methodName, - returnObjCType, - params: objCParams, - }); + if (hasDirectArrayBuffer) { + const legacyMethodParams = methodParams.map((methodParam, index) => { + if ( + index >= params.length || + !isArrayBufferType(params[index].typeAnnotation) + ) { + return methodParam; + } + + const [, nullable] = unwrapNullable(params[index].typeAnnotation); + return { + ...methodParam, + objCType: wrapOptional('NSData *', !nullable), + }; + }); + const legacyReturnObjCType = isArrayBufferType( + propertyTypeAnnotation.returnTypeAnnotation, + ) + ? wrapOptional( + 'NSMutableData *', + !unwrapNullable(propertyTypeAnnotation.returnTypeAnnotation)[1], + ) + : returnObjCType; + + protocolMethod = `${buildProtocolMethod( + legacyReturnObjCType, + methodName, + legacyMethodParams, + )}\n@optional\n${buildProtocolMethod( + returnObjCType, + rctArrayBufferMethodName, + methodParams, + )}\n@required`; + } /** * Build ObjC Selector */ // $FlowFixMe[missing-type-arg] - const selector = methodParams - .map(({paramName}) => paramName) - .reduce(($selector, paramName, i) => { - return i === 0 ? `${$selector}:` : `${$selector}${paramName}:`; - }, methodName); + const buildSelector = (selectorMethodName: string) => + methodParams + .map(({paramName}) => paramName) + .reduce(($selector, paramName, i) => { + return i === 0 ? `${$selector}:` : `${$selector}${paramName}:`; + }, selectorMethodName); + + const selector = buildSelector(methodName); /** * Build JS Return type @@ -168,6 +230,9 @@ function serializeMethod( methodName, protocolMethod, selector: `@selector(${selector})`, + rctArrayBufferSelector: hasDirectArrayBuffer + ? `@selector(${buildSelector(rctArrayBufferMethodName)})` + : null, structParamRecords, returnJSType, argCount: params.length, @@ -407,6 +472,15 @@ function getReturnObjCType( } } +function isArrayBufferType( + nullableTypeAnnotation: Nullable< + NativeModuleParamTypeAnnotation | NativeModuleReturnTypeAnnotation, + >, +): boolean { + const [typeAnnotation] = unwrapNullable(nullableTypeAnnotation); + return typeAnnotation.type === 'ArrayBufferTypeAnnotation'; +} + function getReturnJSType( methodName: string, nullableTypeAnnotation: Nullable, @@ -543,6 +617,7 @@ function serializeConstantsProtocolMethods( protocolMethod, returnJSType: 'ObjectKind', selector: `@selector(${methodName})`, + rctArrayBufferSelector: null, structParamRecords: [], argCount: 0, }; 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 index 371f640fb82b..36733517135d 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/source/serializeModule.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/source/serializeModule.js @@ -58,6 +58,7 @@ namespace facebook::react { methodName: serializedMethodParts.methodName, returnJSType: serializedMethodParts.returnJSType, selector: serializedMethodParts.selector, + rctArrayBufferSelector: serializedMethodParts.rctArrayBufferSelector, }), ) .join('\n')} @@ -118,14 +119,20 @@ const InlineHostFunctionTemplate = ({ methodName, returnJSType, selector, + rctArrayBufferSelector, }: Readonly<{ hasteModuleName: string, methodName: string, returnJSType: string, selector: string, + rctArrayBufferSelector: ?string, }>) => ` static facebook::jsi::Value __hostFunction_${hasteModuleName}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); + return static_cast(turboModule).invokeObjCMethod(rt, ${returnJSType}, "${methodName}", ${ + rctArrayBufferSelector == null + ? selector + : `${rctArrayBufferSelector}, ${selector}` + }, args, count); }`; const MethodMapEntryTemplate = ({ 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 b1ce7953d5f3..9a374a595154 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 @@ -104,9 +104,18 @@ Map { @protocol NativeSampleTurboModuleSpec -- (RCTArrayBuffer *)getArrayBuffer; -- (void)voidArrayBuffer:(RCTArrayBuffer *)arg; -- (void)voidNullableArrayBuffer:(RCTArrayBuffer * _Nullable)arg; +- (NSMutableData *)getArrayBuffer; +@optional +- (RCTArrayBuffer *)getArrayBufferWithRCTArrayBuffer; +@required +- (void)voidArrayBuffer:(NSData *)arg; +@optional +- (void)voidArrayBufferWithRCTArrayBuffer:(RCTArrayBuffer *)arg; +@required +- (void)voidNullableArrayBuffer:(NSData * _Nullable)arg; +@optional +- (void)voidNullableArrayBufferWithRCTArrayBuffer:(RCTArrayBuffer * _Nullable)arg; +@required @end 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 0395bb0b6548..46b5bcded8a6 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 @@ -71,15 +71,15 @@ Map { namespace facebook::react { static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getArrayBuffer(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule).invokeObjCMethod(rt, ArrayBufferKind, \\"getArrayBuffer\\", @selector(getArrayBuffer), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, ArrayBufferKind, \\"getArrayBuffer\\", @selector(getArrayBufferWithRCTArrayBuffer), @selector(getArrayBuffer), args, count); } static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_voidArrayBuffer(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"voidArrayBuffer\\", @selector(voidArrayBuffer:), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"voidArrayBuffer\\", @selector(voidArrayBufferWithRCTArrayBuffer:), @selector(voidArrayBuffer:), args, count); } static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_voidNullableArrayBuffer(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { - return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"voidNullableArrayBuffer\\", @selector(voidNullableArrayBuffer:), args, count); + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, \\"voidNullableArrayBuffer\\", @selector(voidNullableArrayBufferWithRCTArrayBuffer:), @selector(voidNullableArrayBuffer:), args, count); } NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams ¶ms) diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTInteropTurboModule.h b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTInteropTurboModule.h index 62a82b7e61f0..8767fc46ccb2 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTInteropTurboModule.h +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTInteropTurboModule.h @@ -68,6 +68,15 @@ class JSI_EXPORT ObjCInteropTurboModule : public ObjCTurboModule { * RCT_EXPORT_METHOD macros, which we want to remove long term. But, Legacy native modules rely heavily on RCTConvert * for argument conversion. */ + void setInvocationArg( + jsi::Runtime &runtime, + const char *methodName, + const std::string &objCArgType, + const jsi::Value &arg, + size_t i, + NSInvocation *inv, + NSMutableArray *retainedObjectsForInvocation) override; + void setInvocationArg( jsi::Runtime &runtime, const char *methodName, diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTInteropTurboModule.mm b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTInteropTurboModule.mm index c18326ae45d8..821f6ec5dbf7 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTInteropTurboModule.mm +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTInteropTurboModule.mm @@ -363,8 +363,7 @@ T RCTConvertTo(SEL selector, id json) const jsi::Value &jsiArg, size_t index, NSInvocation *inv, - NSMutableArray *retainedObjectsForInvocation, - [[maybe_unused]] bool mustCopyBytes) + NSMutableArray *retainedObjectsForInvocation) { NSString *methodName = @(methodNameCStr); std::string methodJsSignature = name_ + "." + methodNameCStr + "()"; @@ -597,6 +596,19 @@ T RCTConvertTo(SEL selector, id json) } } +void ObjCInteropTurboModule::setInvocationArg( + jsi::Runtime &runtime, + const char *methodNameCStr, + const std::string &objCArgType, + const jsi::Value &jsiArg, + size_t index, + NSInvocation *inv, + NSMutableArray *retainedObjectsForInvocation, + [[maybe_unused]] bool mustCopyBytes) +{ + setInvocationArg(runtime, methodNameCStr, objCArgType, jsiArg, index, inv, retainedObjectsForInvocation); +} + jsi::Value ObjCInteropTurboModule::convertReturnIdToJSIValue( jsi::Runtime &runtime, const char *methodNameCStr, diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.h b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.h index fcd3231c3833..1ff03d024e96 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.h +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.h @@ -35,8 +35,13 @@ id convertJSIValueToObjCObject( jsi::Runtime &runtime, const jsi::Value &value, const std::shared_ptr &jsInvoker, - BOOL useNSNull = NO, - BOOL mustCopyBytes = YES); + BOOL useNSNull = NO); +id convertJSIValueToObjCObject( + jsi::Runtime &runtime, + const jsi::Value &value, + const std::shared_ptr &jsInvoker, + BOOL useNSNull, + BOOL mustCopyBytes); } // namespace TurboModuleConvertUtils template <> @@ -71,6 +76,15 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule { const jsi::Value *args, size_t count); + jsi::Value invokeObjCMethod( + jsi::Runtime &runtime, + TurboModuleMethodValueKind returnType, + const std::string &methodName, + SEL rctArrayBufferSelector, + SEL legacySelector, + const jsi::Value *args, + size_t count); + id instance_; std::shared_ptr nativeMethodCallInvoker_; @@ -115,9 +129,16 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule { * values. ObjCTurboModule tries to minimize reliance on RCTConvert: RCTConvert uses the RCT_EXPORT_METHOD macros, * which we want to remove long term from React Native. * - * mustCopyBytes says whether the invocation may outlive the JS call, in which case ArrayBuffer arguments must be - * copied rather than aliased. */ + virtual void setInvocationArg( + jsi::Runtime &runtime, + const char *methodName, + const std::string &objCArgType, + const jsi::Value &arg, + size_t i, + NSInvocation *inv, + NSMutableArray *retainedObjectsForInvocation); + virtual void setInvocationArg( jsi::Runtime &runtime, const char *methodName, @@ -148,12 +169,23 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule { NSInvocation *createMethodInvocation( jsi::Runtime &runtime, bool isSync, + bool useRCTArrayBuffer, bool mustCopyBytes, const char *methodName, SEL selector, const jsi::Value *args, size_t count, NSMutableArray *retainedObjectsForInvocation); + void setInvocationArgImpl( + jsi::Runtime &runtime, + const char *methodName, + const std::string &objCArgType, + const jsi::Value &arg, + size_t i, + NSInvocation *inv, + NSMutableArray *retainedObjectsForInvocation, + bool useRCTArrayBuffer, + bool mustCopyBytes); id performMethodInvocation( jsi::Runtime &runtime, bool isSync, diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm index b8b5f2db3cd9..1bb1e1033f79 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm @@ -129,6 +129,21 @@ size_t size() const override return {runtime, std::make_shared(value)}; } +static jsi::ArrayBuffer convertNSMutableDataToJSIArrayBuffer(jsi::Runtime &runtime, NSMutableData *value) +{ + if (value == nil) { + RCTLogWarn(@"convertNSMutableDataToJSIArrayBuffer: received nil NSMutableData; returning empty ArrayBuffer"); + value = [NSMutableData data]; + } + + RCTArrayBuffer *arrayBuffer = [RCTArrayBuffer arrayBufferWithOwnedBytes:value.mutableBytes + length:value.length + cleanup:^{ + (void)value; + }]; + return convertRCTArrayBufferToJSIArrayBuffer(runtime, arrayBuffer); +} + jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value) { if ([value isKindOfClass:[NSString class]]) { @@ -144,6 +159,8 @@ size_t size() const override return convertNSArrayToJSIArray(runtime, (NSArray *)value); } else if ([value isKindOfClass:[RCTArrayBuffer class]]) { return convertRCTArrayBufferToJSIArrayBuffer(runtime, (RCTArrayBuffer *)value); + } else if ([value isKindOfClass:[NSMutableData class]]) { + return convertNSMutableDataToJSIArrayBuffer(runtime, (NSMutableData *)value); } else if (value == (id)kCFNull) { return jsi::Value::null(); } @@ -157,17 +174,30 @@ size_t size() const override return result != nil ? result : @""; } +enum class ArrayBufferConversionMode { LegacyNSData, RCTArrayBuffer }; + +static id convertJSIValueToObjCObjectImpl( + jsi::Runtime &runtime, + const jsi::Value &value, + const std::shared_ptr &jsInvoker, + BOOL useNSNull, + ArrayBufferConversionMode arrayBufferConversionMode, + BOOL mustCopyBytes); + static NSArray *convertJSIArrayToNSArray( jsi::Runtime &runtime, const jsi::Array &value, const std::shared_ptr &jsInvoker, - BOOL useNSNull) + BOOL useNSNull, + ArrayBufferConversionMode arrayBufferConversionMode, + BOOL mustCopyBytes) { size_t size = value.size(runtime); NSMutableArray *result = [NSMutableArray new]; for (size_t i = 0; i < size; i++) { // Insert kCFNull when it's `undefined` value to preserve the indices. - id convertedObject = convertJSIValueToObjCObject(runtime, value.getValueAtIndex(runtime, i), jsInvoker, useNSNull); + id convertedObject = convertJSIValueToObjCObjectImpl( + runtime, value.getValueAtIndex(runtime, i), jsInvoker, useNSNull, arrayBufferConversionMode, mustCopyBytes); [result addObject:(convertedObject != nullptr) ? convertedObject : (id)kCFNull]; } return result; @@ -177,7 +207,9 @@ size_t size() const override jsi::Runtime &runtime, const jsi::Object &value, const std::shared_ptr &jsInvoker, - BOOL useNSNull) + BOOL useNSNull, + ArrayBufferConversionMode arrayBufferConversionMode, + BOOL mustCopyBytes) { jsi::Array propertyNames = value.getPropertyNames(runtime); size_t size = propertyNames.size(runtime); @@ -185,7 +217,8 @@ size_t size() const override for (size_t i = 0; i < size; i++) { jsi::String name = propertyNames.getValueAtIndex(runtime, i).getString(runtime); NSString *k = convertJSIStringToNSString(runtime, name); - id v = convertJSIValueToObjCObject(runtime, value.getProperty(runtime, name), jsInvoker, useNSNull); + id v = convertJSIValueToObjCObjectImpl( + runtime, value.getProperty(runtime, name), jsInvoker, useNSNull, arrayBufferConversionMode, mustCopyBytes); if (v != nullptr) { result[k] = v; } @@ -230,20 +263,29 @@ size_t size() const override void *bytes = arrayBuffer.data(rt); size_t size = arrayBuffer.size(rt); - // The bytes belong to the JS heap -> async call. if (mustCopyBytes) { return [RCTArrayBuffer arrayBufferWithCopiedBytes:bytes length:size]; } - // The bytes belong to the JS heap -> sync call. return [RCTArrayBuffer arrayBufferWithUnownedBytes:bytes length:size]; } -id convertJSIValueToObjCObject( +static NSData *convertJSIArrayBufferToNSData(jsi::Runtime &rt, const jsi::ArrayBuffer &arrayBuffer) +{ + RCTArrayBuffer *buffer = convertJSIArrayBufferToRCTArrayBuffer(rt, arrayBuffer, YES); + return [NSData dataWithBytesNoCopy:buffer.mutableBytes + length:buffer.length + deallocator:^(__unused void *bytes, __unused NSUInteger length) { + (void)buffer; + }]; +} + +static id convertJSIValueToObjCObjectImpl( jsi::Runtime &runtime, const jsi::Value &value, const std::shared_ptr &jsInvoker, BOOL useNSNull, + ArrayBufferConversionMode arrayBufferConversionMode, BOOL mustCopyBytes) { if (value.isUndefined() || (value.isNull() && !useNSNull)) { @@ -264,20 +306,44 @@ id convertJSIValueToObjCObject( if (value.isObject()) { jsi::Object o = value.getObject(runtime); if (o.isArray(runtime)) { - return convertJSIArrayToNSArray(runtime, o.getArray(runtime), jsInvoker, useNSNull); + return convertJSIArrayToNSArray( + runtime, o.getArray(runtime), jsInvoker, useNSNull, arrayBufferConversionMode, mustCopyBytes); } if (o.isFunction(runtime)) { return convertJSIFunctionToCallback(runtime, o.getFunction(runtime), jsInvoker); } if (o.isArrayBuffer(runtime)) { - return convertJSIArrayBufferToRCTArrayBuffer(runtime, o.getArrayBuffer(runtime), mustCopyBytes); + return arrayBufferConversionMode == ArrayBufferConversionMode::RCTArrayBuffer + ? convertJSIArrayBufferToRCTArrayBuffer(runtime, o.getArrayBuffer(runtime), mustCopyBytes) + : convertJSIArrayBufferToNSData(runtime, o.getArrayBuffer(runtime)); } - return convertJSIObjectToNSDictionary(runtime, o, jsInvoker, useNSNull); + return convertJSIObjectToNSDictionary(runtime, o, jsInvoker, useNSNull, arrayBufferConversionMode, mustCopyBytes); } throw jsi::JSError(runtime, "Unsupported jsi::Value kind"); } +id convertJSIValueToObjCObject( + jsi::Runtime &runtime, + const jsi::Value &value, + const std::shared_ptr &jsInvoker, + BOOL useNSNull) +{ + return convertJSIValueToObjCObjectImpl( + runtime, value, jsInvoker, useNSNull, ArrayBufferConversionMode::LegacyNSData, YES); +} + +id convertJSIValueToObjCObject( + jsi::Runtime &runtime, + const jsi::Value &value, + const std::shared_ptr &jsInvoker, + BOOL useNSNull, + BOOL mustCopyBytes) +{ + return convertJSIValueToObjCObjectImpl( + runtime, value, jsInvoker, useNSNull, ArrayBufferConversionMode::RCTArrayBuffer, mustCopyBytes); +} + static jsi::Value createJSRuntimeError(jsi::Runtime &runtime, const std::string &message) { return runtime.global().getPropertyAsFunction(runtime, "Error").call(runtime, message); @@ -629,11 +695,16 @@ TraceSection s( break; } case ArrayBufferKind: { - if (result != nil && ![result isKindOfClass:[RCTArrayBuffer class]]) { - RCTLogError(@"convertReturnIdToJSIValue: expected RCTArrayBuffer for ArrayBufferKind, got %@", [result class]); + if ([result isKindOfClass:[RCTArrayBuffer class]]) { + returnValue = convertRCTArrayBufferToJSIArrayBuffer(runtime, (RCTArrayBuffer *)result); + } else if ([result isKindOfClass:[NSMutableData class]]) { + returnValue = convertNSMutableDataToJSIArrayBuffer(runtime, (NSMutableData *)result); + } else { + RCTLogError( + @"convertReturnIdToJSIValue: expected RCTArrayBuffer or NSMutableData for ArrayBufferKind, got %@", + [result class]); break; } - returnValue = convertRCTArrayBufferToJSIArrayBuffer(runtime, (RCTArrayBuffer *)result); break; } case FunctionKind: @@ -703,6 +774,18 @@ TraceSection s( return nil; } +void ObjCTurboModule::setInvocationArg( + jsi::Runtime &runtime, + const char *methodName, + const std::string &objCArgType, + const jsi::Value &arg, + size_t i, + NSInvocation *inv, + NSMutableArray *retainedObjectsForInvocation) +{ + setInvocationArgImpl(runtime, methodName, objCArgType, arg, i, inv, retainedObjectsForInvocation, false, true); +} + void ObjCTurboModule::setInvocationArg( jsi::Runtime &runtime, const char *methodName, @@ -712,6 +795,21 @@ TraceSection s( NSInvocation *inv, NSMutableArray *retainedObjectsForInvocation, bool mustCopyBytes) +{ + setInvocationArgImpl( + runtime, methodName, objCArgType, arg, i, inv, retainedObjectsForInvocation, true, mustCopyBytes); +} + +void ObjCTurboModule::setInvocationArgImpl( + jsi::Runtime &runtime, + const char *methodName, + const std::string &objCArgType, + const jsi::Value &arg, + size_t i, + NSInvocation *inv, + NSMutableArray *retainedObjectsForInvocation, + bool useRCTArrayBuffer, + bool mustCopyBytes) { if (arg.isBool()) { bool v = arg.getBool(); @@ -754,8 +852,9 @@ TraceSection s( * Convert arg to ObjC objects. */ BOOL enableModuleArgumentNSNullConversionIOS = ReactNativeFeatureFlags::enableModuleArgumentNSNullConversionIOS(); - id objCArg = - convertJSIValueToObjCObject(runtime, arg, jsInvoker_, enableModuleArgumentNSNullConversionIOS, mustCopyBytes); + id objCArg = useRCTArrayBuffer + ? convertJSIValueToObjCObject(runtime, arg, jsInvoker_, enableModuleArgumentNSNullConversionIOS, mustCopyBytes) + : convertJSIValueToObjCObject(runtime, arg, jsInvoker_, enableModuleArgumentNSNullConversionIOS); // A JS `null` in argument position must reach ObjC as `nil`; only nulls nested inside arrays and // dictionaries are preserved as `kCFNull`. Skipping `setArgument:` leaves the slot zeroed. @@ -823,6 +922,7 @@ TraceSection s( NSInvocation *ObjCTurboModule::createMethodInvocation( jsi::Runtime &runtime, bool isSync, + bool useRCTArrayBuffer, bool mustCopyBytes, const char *methodName, SEL selector, @@ -852,7 +952,11 @@ TraceSection s( for (size_t i = 0; i < count; i++) { const jsi::Value &arg = args[i]; const std::string objCArgType = [methodSignature getArgumentTypeAtIndex:i + 2]; - setInvocationArg(runtime, methodName, objCArgType, arg, i, inv, retainedObjectsForInvocation, mustCopyBytes); + if (useRCTArrayBuffer) { + setInvocationArg(runtime, methodName, objCArgType, arg, i, inv, retainedObjectsForInvocation, mustCopyBytes); + } else { + setInvocationArg(runtime, methodName, objCArgType, arg, i, inv, retainedObjectsForInvocation); + } } if (isSync) { @@ -894,12 +998,27 @@ TraceSection s( SEL selector, const jsi::Value *args, size_t count) +{ + return invokeObjCMethod(runtime, returnType, methodNameStr, selector, nullptr, args, count); +} + +jsi::Value ObjCTurboModule::invokeObjCMethod( + jsi::Runtime &runtime, + TurboModuleMethodValueKind returnType, + const std::string &methodNameStr, + SEL rctArrayBufferSelector, + SEL legacySelector, + const jsi::Value *args, + size_t count) { const char *moduleName = name_.c_str(); const char *methodName = methodNameStr.c_str(); bool isSyncInvocation = isMethodSync(returnType); - bool mustCopyBytes = mustCopyJSHeapArrayBufferBytes(returnType); + bool useRCTArrayBuffer = legacySelector != nullptr && [instance_ respondsToSelector:rctArrayBufferSelector]; + bool mustCopyBytes = !useRCTArrayBuffer || mustCopyJSHeapArrayBufferBytes(returnType); + SEL selector = useRCTArrayBuffer ? rctArrayBufferSelector + : (legacySelector != nullptr ? legacySelector : rctArrayBufferSelector); if (isSyncInvocation) { TurboModulePerfLogger::syncMethodCallStart(moduleName, methodName); @@ -909,7 +1028,15 @@ TraceSection s( NSMutableArray *retainedObjectsForInvocation = [NSMutableArray arrayWithCapacity:count + 2]; NSInvocation *inv = createMethodInvocation( - runtime, isSyncInvocation, mustCopyBytes, methodName, selector, args, count, retainedObjectsForInvocation); + runtime, + isSyncInvocation, + useRCTArrayBuffer, + mustCopyBytes, + methodName, + selector, + args, + count, + retainedObjectsForInvocation); jsi::Value returnValue = jsi::Value::undefined(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTSampleTurboModule.mm b/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTSampleTurboModule.mm index 6ec73228f76e..62366e0b507f 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTSampleTurboModule.mm +++ b/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTSampleTurboModule.mm @@ -20,6 +20,16 @@ using namespace facebook::react; +static RCTArrayBuffer *RCTArrayBufferFromNSData(NSData *data) +{ + return [RCTArrayBuffer arrayBufferWithCopiedBytes:data.bytes length:data.length]; +} + +static NSMutableData *NSMutableDataFromRCTArrayBuffer(RCTArrayBuffer *arrayBuffer) +{ + return [NSMutableData dataWithBytes:arrayBuffer.mutableBytes length:arrayBuffer.length]; +} + @interface RCTSampleTurboModule () @end @@ -149,8 +159,13 @@ - (NSDictionary *)getValue:(double)x y:(NSString *)y z:(NSDictionary *)z }; } -// The argument aliases the JS ArrayBuffer's bytes, so mutating in place is visible to JS. -- (RCTArrayBuffer *)getArrayBuffer:(RCTArrayBuffer *)buffer +- (NSMutableData *)getArrayBuffer:(NSData *)buffer +{ + return NSMutableDataFromRCTArrayBuffer([self getArrayBufferWithRCTArrayBuffer:RCTArrayBufferFromNSData(buffer)]); +} + +// Mutate the native buffer and return it without an additional native-to-JS copy. +- (RCTArrayBuffer *)getArrayBufferWithRCTArrayBuffer:(RCTArrayBuffer *)buffer { auto *bytes = static_cast(buffer.mutableBytes); if (bytes == nullptr) { @@ -164,14 +179,26 @@ - (RCTArrayBuffer *)getArrayBuffer:(RCTArrayBuffer *)buffer return buffer; } -- (RCTArrayBuffer *)createNativeBuffer:(double)size +- (NSMutableData *)createNativeBuffer:(double)size +{ + return NSMutableDataFromRCTArrayBuffer([self createNativeBufferWithRCTArrayBuffer:size]); +} + +- (RCTArrayBuffer *)createNativeBufferWithRCTArrayBuffer:(double)size { return [RCTArrayBuffer arrayBufferWithLength:(NSUInteger)size]; } -- (void)processAsyncBuffer:(RCTArrayBuffer *)payload +- (void)processAsyncBuffer:(NSData *)payload resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject +{ + [self processAsyncBufferWithRCTArrayBuffer:RCTArrayBufferFromNSData(payload) resolve:resolve reject:reject]; +} + +- (void)processAsyncBufferWithRCTArrayBuffer:(RCTArrayBuffer *)payload + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject { resolve(@(payload.length)); } diff --git a/packages/rn-tester/RNTesterUnitTests/RCTTurboModuleArrayBufferTests.mm b/packages/rn-tester/RNTesterUnitTests/RCTTurboModuleArrayBufferTests.mm index 14f32b48e849..e25bcaafc2fa 100644 --- a/packages/rn-tester/RNTesterUnitTests/RCTTurboModuleArrayBufferTests.mm +++ b/packages/rn-tester/RNTesterUnitTests/RCTTurboModuleArrayBufferTests.mm @@ -40,6 +40,16 @@ return buffer; } +RCTArrayBuffer *arrayBufferFromData(NSData *data) +{ + return [RCTArrayBuffer arrayBufferWithCopiedBytes:data.bytes length:data.length]; +} + +NSMutableData *mutableDataFromArrayBuffer(RCTArrayBuffer *arrayBuffer) +{ + return [NSMutableData dataWithBytes:arrayBuffer.mutableBytes length:arrayBuffer.length]; +} + std::vector bytesFromData(NSData *data) { if (data == nil) { @@ -99,8 +109,9 @@ void flushQueue() @interface RCTTestArrayBufferTurboModule : NSObject @property (nonatomic, copy) NSData *lastReceivedPayload; -@property (nonatomic, assign) BOOL sawAliasedBytes; -@property (nonatomic, assign) BOOL sawUnownedBytes; +@property (nonatomic, assign) BOOL sawLegacyNSData; +@property (nonatomic, assign) BOOL sawWritableBytes; +@property (nonatomic, assign) BOOL sawOwningBytes; @end @@ -108,7 +119,13 @@ @implementation RCTTestArrayBufferTurboModule RCT_EXPORT_MODULE() -- (RCTArrayBuffer *)testMethodWhichTransformsArrayBuffer:(RCTArrayBuffer *)buffer +- (NSMutableData *)testMethodWhichTransformsArrayBuffer:(NSData *)buffer +{ + return mutableDataFromArrayBuffer( + [self testMethodWhichTransformsArrayBufferWithRCTArrayBuffer:arrayBufferFromData(buffer)]); +} + +- (RCTArrayBuffer *)testMethodWhichTransformsArrayBufferWithRCTArrayBuffer:(RCTArrayBuffer *)buffer { RCTArrayBuffer *result = [RCTArrayBuffer arrayBufferWithLength:buffer.length]; auto *destinationBytes = static_cast(result.mutableBytes); @@ -118,7 +135,13 @@ - (RCTArrayBuffer *)testMethodWhichTransformsArrayBuffer:(RCTArrayBuffer *)buffe return result; } -- (RCTArrayBuffer *)testMethodWhichReturnsItsArgument:(RCTArrayBuffer *)buffer +- (NSMutableData *)testMethodWhichReturnsItsArgument:(NSData *)buffer +{ + return mutableDataFromArrayBuffer( + [self testMethodWhichReturnsItsArgumentWithRCTArrayBuffer:arrayBufferFromData(buffer)]); +} + +- (RCTArrayBuffer *)testMethodWhichReturnsItsArgumentWithRCTArrayBuffer:(RCTArrayBuffer *)buffer { auto *bytes = static_cast(buffer.mutableBytes); for (NSUInteger i = 0; i < buffer.length && i < 3; ++i) { @@ -127,17 +150,38 @@ - (RCTArrayBuffer *)testMethodWhichReturnsItsArgument:(RCTArrayBuffer *)buffer return buffer; } -- (NSNumber *)testMethodWhichChecksArrayBufferAliasing:(RCTArrayBuffer *)buffer +- (NSMutableData *)testLegacyMethodWhichTransformsArrayBuffer:(NSData *)buffer { - // An observable in-place write proves the bytes were aliased, not copied on the way in. + self.sawLegacyNSData = [buffer isKindOfClass:[NSData class]]; + NSMutableData *result = [NSMutableData dataWithData:buffer]; + auto *bytes = static_cast(result.mutableBytes); + for (NSUInteger i = 0; i < result.length; ++i) { + bytes[i] += 1; + } + return result; +} + +- (NSNumber *)testMethodWhichChecksArrayBufferOwnership:(NSData *)buffer +{ + return [self testMethodWhichChecksArrayBufferOwnershipWithRCTArrayBuffer:arrayBufferFromData(buffer)]; +} + +- (NSNumber *)testMethodWhichChecksArrayBufferOwnershipWithRCTArrayBuffer:(RCTArrayBuffer *)buffer +{ + // An observable in-place write verifies that RCTArrayBuffer still exposes mutable storage. auto *bytes = static_cast(buffer.mutableBytes); bytes[0] = 77; - self.sawAliasedBytes = buffer.length == 3 && bytes[0] == 77; - self.sawUnownedBytes = !buffer.isOwningBytes; + self.sawWritableBytes = buffer.length == 3 && bytes[0] == 77; + self.sawOwningBytes = buffer.isOwningBytes; return @(YES); } -- (void)testMethodWhichStoresArrayBuffer:(RCTArrayBuffer *)payload +- (void)testMethodWhichStoresArrayBuffer:(NSData *)payload +{ + [self testMethodWhichStoresArrayBufferWithRCTArrayBuffer:arrayBufferFromData(payload)]; +} + +- (void)testMethodWhichStoresArrayBufferWithRCTArrayBuffer:(RCTArrayBuffer *)payload { self.lastReceivedPayload = [NSData dataWithBytes:payload.mutableBytes length:payload.length]; } @@ -192,6 +236,7 @@ - (void)testSyncArrayBufferRoundTrip *rt, ArrayBufferKind, "testMethodWhichTransformsArrayBuffer", + @selector(testMethodWhichTransformsArrayBufferWithRCTArrayBuffer:), @selector(testMethodWhichTransformsArrayBuffer:), args, 1); @@ -207,8 +252,8 @@ - (void)testSyncArrayBufferRoundTrip XCTAssertEqual(returnedBytes[2], 30); } -// The sync argument is a live alias: an in-place write lands on the JS ArrayBuffer itself. -- (void)testJSBackedArrayBufferIsNotCopiedDuringTheCall +// The new sync entry point receives a live alias, so an in-place write reaches the JS ArrayBuffer. +- (void)testJSBackedArrayBufferIsNotCopiedDuringTheNewCall { auto hermesRuntime = createHermesRuntime(); facebook::jsi::Runtime *rt = hermesRuntime.get(); @@ -233,14 +278,55 @@ - (void)testJSBackedArrayBufferIsNotCopiedDuringTheCall module.invokeObjCMethod( *rt, BooleanKind, - "testMethodWhichChecksArrayBufferAliasing", - @selector(testMethodWhichChecksArrayBufferAliasing:), + "testMethodWhichChecksArrayBufferOwnership", + @selector(testMethodWhichChecksArrayBufferOwnershipWithRCTArrayBuffer:), + @selector(testMethodWhichChecksArrayBufferOwnership:), + args, + 1); + + XCTAssertTrue(instance.sawWritableBytes); + XCTAssertFalse(instance.sawOwningBytes); + XCTAssertEqual(bytesFromArrayBuffer(*rt, sourceBuffer)[0], 77); +} + +- (void)testLegacyNSDataSignatureRoundTrip +{ + auto hermesRuntime = createHermesRuntime(); + facebook::jsi::Runtime *rt = hermesRuntime.get(); + auto *instance = [RCTTestArrayBufferTurboModule new]; + + ObjCTurboModule::InitParams params = { + .moduleName = "TestModule", + .instance = instance, + .jsInvoker = nullptr, + .nativeMethodCallInvoker = std::make_shared(), + .isSyncModule = false, + }; + ObjCTurboModule module(params); + + auto sourceBuffer = rt->global() + .getPropertyAsFunction(*rt, "eval") + .call(*rt, "new Uint8Array([1, 2, 3]).buffer") + .asObject(*rt) + .getArrayBuffer(*rt); + facebook::jsi::Value args[1] = {facebook::jsi::Value(*rt, sourceBuffer)}; + + auto result = module.invokeObjCMethod( + *rt, + ArrayBufferKind, + "testLegacyMethodWhichTransformsArrayBuffer", + NSSelectorFromString(@"testLegacyMethodWhichTransformsArrayBufferWithRCTArrayBuffer:"), + @selector(testLegacyMethodWhichTransformsArrayBuffer:), args, 1); - XCTAssertTrue(instance.sawAliasedBytes, @"The argument must alias the JS ArrayBuffer's bytes"); - XCTAssertTrue(instance.sawUnownedBytes, @"A JS-heap argument to a sync method must not own its bytes"); - XCTAssertEqual(bytesFromArrayBuffer(*rt, sourceBuffer)[0], 77, @"The native write must land on the JS ArrayBuffer"); + auto returnedBytes = bytesFromArrayBuffer(*rt, result.asObject(*rt).getArrayBuffer(*rt)); + XCTAssertTrue(instance.sawLegacyNSData); + XCTAssertEqual(returnedBytes.size(), 3u); + XCTAssertEqual(returnedBytes[0], 2); + XCTAssertEqual(returnedBytes[1], 3); + XCTAssertEqual(returnedBytes[2], 4); + XCTAssertEqual(bytesFromArrayBuffer(*rt, sourceBuffer)[0], 1, @"The legacy argument must be an owning copy"); } // Returning the argument must hand JS the mutated bytes: it has to stay valid through the @@ -271,6 +357,7 @@ - (void)testSyncMethodCanReturnItsOwnArgument *rt, ArrayBufferKind, "testMethodWhichReturnsItsArgument", + @selector(testMethodWhichReturnsItsArgumentWithRCTArrayBuffer:), @selector(testMethodWhichReturnsItsArgument:), args, 1); @@ -308,6 +395,7 @@ - (void)testEmptyArrayBufferRoundTrip *rt, ArrayBufferKind, "testMethodWhichTransformsArrayBuffer", + @selector(testMethodWhichTransformsArrayBufferWithRCTArrayBuffer:), @selector(testMethodWhichTransformsArrayBuffer:), args, 1); @@ -341,7 +429,13 @@ - (void)testAsyncJSBackedArrayBufferIsCopied facebook::jsi::Value args[1] = {facebook::jsi::Value(*rt, sourceBuffer)}; module.invokeObjCMethod( - *rt, VoidKind, "testMethodWhichStoresArrayBuffer", @selector(testMethodWhichStoresArrayBuffer:), args, 1); + *rt, + VoidKind, + "testMethodWhichStoresArrayBuffer", + @selector(testMethodWhichStoresArrayBufferWithRCTArrayBuffer:), + @selector(testMethodWhichStoresArrayBuffer:), + args, + 1); auto *sourceBytes = sourceBuffer.data(*rt); sourceBytes[0] = 9; @@ -381,7 +475,13 @@ - (void)testAsyncNativeBackedArrayBufferRetainsBackingStore } module.invokeObjCMethod( - *rt, VoidKind, "testMethodWhichStoresArrayBuffer", @selector(testMethodWhichStoresArrayBuffer:), args, 1); + *rt, + VoidKind, + "testMethodWhichStoresArrayBuffer", + @selector(testMethodWhichStoresArrayBufferWithRCTArrayBuffer:), + @selector(testMethodWhichStoresArrayBuffer:), + args, + 1); args[0] = facebook::jsi::Value::undefined(); nativeInvoker->flushQueue(); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api index c23703c476a3..7fa964d6be40 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api @@ -2512,19 +2512,22 @@ protocol NativeSampleTurboModuleSpec : public NSObjectRCTBridgeModule, public RC public virtual NSDictionary* getObjectThrows:(NSDictionary* arg); public virtual NSDictionary* getUnsafeObject:(NSDictionary* arg); public virtual NSDictionary* getValue:y:z:(double x, NSString* y, NSDictionary* z); + public virtual NSMutableData* createNativeBuffer:(double size); + public virtual NSMutableData* getArrayBuffer:(NSData* buffer); public virtual NSNumber* getBool:(BOOL arg); public virtual NSNumber* getEnum:(double arg); public virtual NSNumber* getNumber:(double arg); public virtual NSNumber* getRootTag:(double arg); public virtual NSString* getString:(NSString* arg); - public virtual RCTArrayBuffer* createNativeBuffer:(double size); - public virtual RCTArrayBuffer* getArrayBuffer:(RCTArrayBuffer* buffer); + public virtual RCTArrayBuffer* createNativeBufferWithRCTArrayBuffer:(double size); + public virtual RCTArrayBuffer* getArrayBufferWithRCTArrayBuffer:(RCTArrayBuffer* buffer); public virtual facebook::react::ModuleConstants constantsToExport(); public virtual facebook::react::ModuleConstants getConstants(); public virtual void getImageUrl:reject:(RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); public virtual void getValueWithCallback:(RCTResponseSenderBlock callback); public virtual void getValueWithPromise:resolve:reject:(BOOL error, RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); - public virtual void processAsyncBuffer:resolve:reject:(RCTArrayBuffer* payload, RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); + public virtual void processAsyncBuffer:resolve:reject:(NSData* payload, RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); + public virtual void processAsyncBufferWithRCTArrayBuffer:resolve:reject:(RCTArrayBuffer* payload, RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); public virtual void promiseAssert:reject:(RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); public virtual void promiseThrows:reject:(RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); public virtual void voidFunc(); @@ -6169,6 +6172,7 @@ class facebook::react::ObjCInteropTurboModule : public facebook::react::ObjCTurb protected virtual NSString* getArgumentTypeName(facebook::jsi::Runtime& runtime, NSString* methodName, int argIndex) override; protected virtual facebook::jsi::Value convertReturnIdToJSIValue(facebook::jsi::Runtime& runtime, const char* methodName, facebook::react::TurboModuleMethodValueKind returnType, id result) override; protected virtual facebook::jsi::Value create(facebook::jsi::Runtime& runtime, const facebook::jsi::PropNameID& propName) override; + protected virtual void setInvocationArg(facebook::jsi::Runtime& runtime, const char* methodName, const std::string& objCArgType, const facebook::jsi::Value& arg, size_t i, NSInvocation* inv, NSMutableArray* retainedObjectsForInvocation) override; protected virtual void setInvocationArg(facebook::jsi::Runtime& runtime, const char* methodName, const std::string& objCArgType, const facebook::jsi::Value& arg, size_t i, NSInvocation* inv, NSMutableArray* retainedObjectsForInvocation, bool mustCopyBytes) override; public ObjCInteropTurboModule(const facebook::react::ObjCTurboModule::InitParams& params); public virtual std::vector getPropertyNames(facebook::jsi::Runtime& runtime) override; @@ -6184,10 +6188,12 @@ struct facebook::react::ObjCInteropTurboModule::MethodDescriptor { class facebook::react::ObjCTurboModule : public facebook::react::TurboModule { protected virtual NSString* getArgumentTypeName(facebook::jsi::Runtime& runtime, NSString* methodName, int argIndex); protected virtual facebook::jsi::Value convertReturnIdToJSIValue(facebook::jsi::Runtime& runtime, const char* methodName, facebook::react::TurboModuleMethodValueKind returnType, id result); + protected virtual void setInvocationArg(facebook::jsi::Runtime& runtime, const char* methodName, const std::string& objCArgType, const facebook::jsi::Value& arg, size_t i, NSInvocation* inv, NSMutableArray* retainedObjectsForInvocation); protected virtual void setInvocationArg(facebook::jsi::Runtime& runtime, const char* methodName, const std::string& objCArgType, const facebook::jsi::Value& arg, size_t i, NSInvocation* inv, NSMutableArray* retainedObjectsForInvocation, bool mustCopyBytes); protected void setEventEmitterCallback(facebook::react::EventEmitterCallback eventEmitterCallback); protected void setMethodArgConversionSelector(NSString* methodName, size_t argIndex, NSString* fnName); public ObjCTurboModule(const facebook::react::ObjCTurboModule::InitParams& params); + public facebook::jsi::Value invokeObjCMethod(facebook::jsi::Runtime& runtime, facebook::react::TurboModuleMethodValueKind returnType, const std::string& methodName, SEL rctArrayBufferSelector, SEL legacySelector, const facebook::jsi::Value* args, size_t count); public facebook::jsi::Value invokeObjCMethod(facebook::jsi::Runtime& runtime, facebook::react::TurboModuleMethodValueKind returnType, const std::string& methodName, SEL selector, const facebook::jsi::Value* args, size_t count); public id instance_; public std::shared_ptr nativeMethodCallInvoker_; @@ -13661,7 +13667,8 @@ struct facebook::react::dom::RNMeasureRect { facebook::jsi::Value facebook::react::TurboModuleConvertUtils::convertObjCObjectToJSIValue(facebook::jsi::Runtime& runtime, id value); -id facebook::react::TurboModuleConvertUtils::convertJSIValueToObjCObject(facebook::jsi::Runtime& runtime, const facebook::jsi::Value& value, const std::shared_ptr& jsInvoker, BOOL useNSNull = NO, BOOL mustCopyBytes = YES); +id facebook::react::TurboModuleConvertUtils::convertJSIValueToObjCObject(facebook::jsi::Runtime& runtime, const facebook::jsi::Value& value, const std::shared_ptr& jsInvoker, BOOL useNSNull = NO); +id facebook::react::TurboModuleConvertUtils::convertJSIValueToObjCObject(facebook::jsi::Runtime& runtime, const facebook::jsi::Value& value, const std::shared_ptr& jsInvoker, BOOL useNSNull, BOOL mustCopyBytes); static const facebook::react::Color facebook::react::HostPlatformColor::UndefinedColor; diff --git a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api index 95f5e8b375fc..9a9031cd2b4c 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api @@ -2505,19 +2505,22 @@ protocol NativeSampleTurboModuleSpec : public NSObjectRCTBridgeModule, public RC public virtual NSDictionary* getObjectThrows:(NSDictionary* arg); public virtual NSDictionary* getUnsafeObject:(NSDictionary* arg); public virtual NSDictionary* getValue:y:z:(double x, NSString* y, NSDictionary* z); + public virtual NSMutableData* createNativeBuffer:(double size); + public virtual NSMutableData* getArrayBuffer:(NSData* buffer); public virtual NSNumber* getBool:(BOOL arg); public virtual NSNumber* getEnum:(double arg); public virtual NSNumber* getNumber:(double arg); public virtual NSNumber* getRootTag:(double arg); public virtual NSString* getString:(NSString* arg); - public virtual RCTArrayBuffer* createNativeBuffer:(double size); - public virtual RCTArrayBuffer* getArrayBuffer:(RCTArrayBuffer* buffer); + public virtual RCTArrayBuffer* createNativeBufferWithRCTArrayBuffer:(double size); + public virtual RCTArrayBuffer* getArrayBufferWithRCTArrayBuffer:(RCTArrayBuffer* buffer); public virtual facebook::react::ModuleConstants constantsToExport(); public virtual facebook::react::ModuleConstants getConstants(); public virtual void getImageUrl:reject:(RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); public virtual void getValueWithCallback:(RCTResponseSenderBlock callback); public virtual void getValueWithPromise:resolve:reject:(BOOL error, RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); - public virtual void processAsyncBuffer:resolve:reject:(RCTArrayBuffer* payload, RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); + public virtual void processAsyncBuffer:resolve:reject:(NSData* payload, RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); + public virtual void processAsyncBufferWithRCTArrayBuffer:resolve:reject:(RCTArrayBuffer* payload, RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); public virtual void promiseAssert:reject:(RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); public virtual void promiseThrows:reject:(RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); public virtual void voidFunc(); @@ -6055,6 +6058,7 @@ class facebook::react::ObjCInteropTurboModule : public facebook::react::ObjCTurb protected virtual NSString* getArgumentTypeName(facebook::jsi::Runtime& runtime, NSString* methodName, int argIndex) override; protected virtual facebook::jsi::Value convertReturnIdToJSIValue(facebook::jsi::Runtime& runtime, const char* methodName, facebook::react::TurboModuleMethodValueKind returnType, id result) override; protected virtual facebook::jsi::Value create(facebook::jsi::Runtime& runtime, const facebook::jsi::PropNameID& propName) override; + protected virtual void setInvocationArg(facebook::jsi::Runtime& runtime, const char* methodName, const std::string& objCArgType, const facebook::jsi::Value& arg, size_t i, NSInvocation* inv, NSMutableArray* retainedObjectsForInvocation) override; protected virtual void setInvocationArg(facebook::jsi::Runtime& runtime, const char* methodName, const std::string& objCArgType, const facebook::jsi::Value& arg, size_t i, NSInvocation* inv, NSMutableArray* retainedObjectsForInvocation, bool mustCopyBytes) override; public ObjCInteropTurboModule(const facebook::react::ObjCTurboModule::InitParams& params); public virtual std::vector getPropertyNames(facebook::jsi::Runtime& runtime) override; @@ -6070,10 +6074,12 @@ struct facebook::react::ObjCInteropTurboModule::MethodDescriptor { class facebook::react::ObjCTurboModule : public facebook::react::TurboModule { protected virtual NSString* getArgumentTypeName(facebook::jsi::Runtime& runtime, NSString* methodName, int argIndex); protected virtual facebook::jsi::Value convertReturnIdToJSIValue(facebook::jsi::Runtime& runtime, const char* methodName, facebook::react::TurboModuleMethodValueKind returnType, id result); + protected virtual void setInvocationArg(facebook::jsi::Runtime& runtime, const char* methodName, const std::string& objCArgType, const facebook::jsi::Value& arg, size_t i, NSInvocation* inv, NSMutableArray* retainedObjectsForInvocation); protected virtual void setInvocationArg(facebook::jsi::Runtime& runtime, const char* methodName, const std::string& objCArgType, const facebook::jsi::Value& arg, size_t i, NSInvocation* inv, NSMutableArray* retainedObjectsForInvocation, bool mustCopyBytes); protected void setEventEmitterCallback(facebook::react::EventEmitterCallback eventEmitterCallback); protected void setMethodArgConversionSelector(NSString* methodName, size_t argIndex, NSString* fnName); public ObjCTurboModule(const facebook::react::ObjCTurboModule::InitParams& params); + public facebook::jsi::Value invokeObjCMethod(facebook::jsi::Runtime& runtime, facebook::react::TurboModuleMethodValueKind returnType, const std::string& methodName, SEL rctArrayBufferSelector, SEL legacySelector, const facebook::jsi::Value* args, size_t count); public facebook::jsi::Value invokeObjCMethod(facebook::jsi::Runtime& runtime, facebook::react::TurboModuleMethodValueKind returnType, const std::string& methodName, SEL selector, const facebook::jsi::Value* args, size_t count); public id instance_; public std::shared_ptr nativeMethodCallInvoker_; @@ -13340,7 +13346,8 @@ struct facebook::react::dom::RNMeasureRect { facebook::jsi::Value facebook::react::TurboModuleConvertUtils::convertObjCObjectToJSIValue(facebook::jsi::Runtime& runtime, id value); -id facebook::react::TurboModuleConvertUtils::convertJSIValueToObjCObject(facebook::jsi::Runtime& runtime, const facebook::jsi::Value& value, const std::shared_ptr& jsInvoker, BOOL useNSNull = NO, BOOL mustCopyBytes = YES); +id facebook::react::TurboModuleConvertUtils::convertJSIValueToObjCObject(facebook::jsi::Runtime& runtime, const facebook::jsi::Value& value, const std::shared_ptr& jsInvoker, BOOL useNSNull = NO); +id facebook::react::TurboModuleConvertUtils::convertJSIValueToObjCObject(facebook::jsi::Runtime& runtime, const facebook::jsi::Value& value, const std::shared_ptr& jsInvoker, BOOL useNSNull, BOOL mustCopyBytes); static const facebook::react::Color facebook::react::HostPlatformColor::UndefinedColor; diff --git a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api index 9e9d12b2d5f0..d6b4b07df3d5 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api @@ -2512,19 +2512,22 @@ protocol NativeSampleTurboModuleSpec : public NSObjectRCTBridgeModule, public RC public virtual NSDictionary* getObjectThrows:(NSDictionary* arg); public virtual NSDictionary* getUnsafeObject:(NSDictionary* arg); public virtual NSDictionary* getValue:y:z:(double x, NSString* y, NSDictionary* z); + public virtual NSMutableData* createNativeBuffer:(double size); + public virtual NSMutableData* getArrayBuffer:(NSData* buffer); public virtual NSNumber* getBool:(BOOL arg); public virtual NSNumber* getEnum:(double arg); public virtual NSNumber* getNumber:(double arg); public virtual NSNumber* getRootTag:(double arg); public virtual NSString* getString:(NSString* arg); - public virtual RCTArrayBuffer* createNativeBuffer:(double size); - public virtual RCTArrayBuffer* getArrayBuffer:(RCTArrayBuffer* buffer); + public virtual RCTArrayBuffer* createNativeBufferWithRCTArrayBuffer:(double size); + public virtual RCTArrayBuffer* getArrayBufferWithRCTArrayBuffer:(RCTArrayBuffer* buffer); public virtual facebook::react::ModuleConstants constantsToExport(); public virtual facebook::react::ModuleConstants getConstants(); public virtual void getImageUrl:reject:(RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); public virtual void getValueWithCallback:(RCTResponseSenderBlock callback); public virtual void getValueWithPromise:resolve:reject:(BOOL error, RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); - public virtual void processAsyncBuffer:resolve:reject:(RCTArrayBuffer* payload, RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); + public virtual void processAsyncBuffer:resolve:reject:(NSData* payload, RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); + public virtual void processAsyncBufferWithRCTArrayBuffer:resolve:reject:(RCTArrayBuffer* payload, RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); public virtual void promiseAssert:reject:(RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); public virtual void promiseThrows:reject:(RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject); public virtual void voidFunc(); @@ -6166,6 +6169,7 @@ class facebook::react::ObjCInteropTurboModule : public facebook::react::ObjCTurb protected virtual NSString* getArgumentTypeName(facebook::jsi::Runtime& runtime, NSString* methodName, int argIndex) override; protected virtual facebook::jsi::Value convertReturnIdToJSIValue(facebook::jsi::Runtime& runtime, const char* methodName, facebook::react::TurboModuleMethodValueKind returnType, id result) override; protected virtual facebook::jsi::Value create(facebook::jsi::Runtime& runtime, const facebook::jsi::PropNameID& propName) override; + protected virtual void setInvocationArg(facebook::jsi::Runtime& runtime, const char* methodName, const std::string& objCArgType, const facebook::jsi::Value& arg, size_t i, NSInvocation* inv, NSMutableArray* retainedObjectsForInvocation) override; protected virtual void setInvocationArg(facebook::jsi::Runtime& runtime, const char* methodName, const std::string& objCArgType, const facebook::jsi::Value& arg, size_t i, NSInvocation* inv, NSMutableArray* retainedObjectsForInvocation, bool mustCopyBytes) override; public ObjCInteropTurboModule(const facebook::react::ObjCTurboModule::InitParams& params); public virtual std::vector getPropertyNames(facebook::jsi::Runtime& runtime) override; @@ -6181,10 +6185,12 @@ struct facebook::react::ObjCInteropTurboModule::MethodDescriptor { class facebook::react::ObjCTurboModule : public facebook::react::TurboModule { protected virtual NSString* getArgumentTypeName(facebook::jsi::Runtime& runtime, NSString* methodName, int argIndex); protected virtual facebook::jsi::Value convertReturnIdToJSIValue(facebook::jsi::Runtime& runtime, const char* methodName, facebook::react::TurboModuleMethodValueKind returnType, id result); + protected virtual void setInvocationArg(facebook::jsi::Runtime& runtime, const char* methodName, const std::string& objCArgType, const facebook::jsi::Value& arg, size_t i, NSInvocation* inv, NSMutableArray* retainedObjectsForInvocation); protected virtual void setInvocationArg(facebook::jsi::Runtime& runtime, const char* methodName, const std::string& objCArgType, const facebook::jsi::Value& arg, size_t i, NSInvocation* inv, NSMutableArray* retainedObjectsForInvocation, bool mustCopyBytes); protected void setEventEmitterCallback(facebook::react::EventEmitterCallback eventEmitterCallback); protected void setMethodArgConversionSelector(NSString* methodName, size_t argIndex, NSString* fnName); public ObjCTurboModule(const facebook::react::ObjCTurboModule::InitParams& params); + public facebook::jsi::Value invokeObjCMethod(facebook::jsi::Runtime& runtime, facebook::react::TurboModuleMethodValueKind returnType, const std::string& methodName, SEL rctArrayBufferSelector, SEL legacySelector, const facebook::jsi::Value* args, size_t count); public facebook::jsi::Value invokeObjCMethod(facebook::jsi::Runtime& runtime, facebook::react::TurboModuleMethodValueKind returnType, const std::string& methodName, SEL selector, const facebook::jsi::Value* args, size_t count); public id instance_; public std::shared_ptr nativeMethodCallInvoker_; @@ -13515,7 +13521,8 @@ struct facebook::react::dom::RNMeasureRect { facebook::jsi::Value facebook::react::TurboModuleConvertUtils::convertObjCObjectToJSIValue(facebook::jsi::Runtime& runtime, id value); -id facebook::react::TurboModuleConvertUtils::convertJSIValueToObjCObject(facebook::jsi::Runtime& runtime, const facebook::jsi::Value& value, const std::shared_ptr& jsInvoker, BOOL useNSNull = NO, BOOL mustCopyBytes = YES); +id facebook::react::TurboModuleConvertUtils::convertJSIValueToObjCObject(facebook::jsi::Runtime& runtime, const facebook::jsi::Value& value, const std::shared_ptr& jsInvoker, BOOL useNSNull = NO); +id facebook::react::TurboModuleConvertUtils::convertJSIValueToObjCObject(facebook::jsi::Runtime& runtime, const facebook::jsi::Value& value, const std::shared_ptr& jsInvoker, BOOL useNSNull, BOOL mustCopyBytes); static const facebook::react::Color facebook::react::HostPlatformColor::UndefinedColor;