Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export type MethodSerializationOutput = Readonly<{
methodName: string,
protocolMethod: string,
selector: string,
rctArrayBufferSelector: ?string,
structParamRecords: ReadonlyArray<StructParameterRecord>,
returnJSType: ReturnJSType,
argCount: number,
Expand Down Expand Up @@ -122,41 +123,102 @@ function serializeMethod(
);
}

const hasDirectArrayBuffer =
params.some(param => isArrayBufferType(param.typeAnnotation)) ||
isArrayBufferType(propertyTypeAnnotation.returnTypeAnnotation);

/**
* Build Protocol Method
**/
const returnObjCType = getReturnObjCType(
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<string>(({paramName}) => paramName)
.reduce(($selector, paramName, i) => {
return i === 0 ? `${$selector}:` : `${$selector}${paramName}:`;
}, methodName);
const buildSelector = (selectorMethodName: string) =>
methodParams
.map<string>(({paramName}) => paramName)
.reduce(($selector, paramName, i) => {
return i === 0 ? `${$selector}:` : `${$selector}${paramName}:`;
}, selectorMethodName);

const selector = buildSelector(methodName);

/**
* Build JS Return type
Expand All @@ -168,6 +230,9 @@ function serializeMethod(
methodName,
protocolMethod,
selector: `@selector(${selector})`,
rctArrayBufferSelector: hasDirectArrayBuffer
? `@selector(${buildSelector(rctArrayBufferMethodName)})`
: null,
structParamRecords,
returnJSType,
argCount: params.length,
Expand Down Expand Up @@ -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<NativeModuleReturnTypeAnnotation>,
Expand Down Expand Up @@ -543,6 +617,7 @@ function serializeConstantsProtocolMethods(
protocolMethod,
returnJSType: 'ObjectKind',
selector: `@selector(${methodName})`,
rctArrayBufferSelector: null,
structParamRecords: [],
argCount: 0,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ namespace facebook::react {
methodName: serializedMethodParts.methodName,
returnJSType: serializedMethodParts.returnJSType,
selector: serializedMethodParts.selector,
rctArrayBufferSelector: serializedMethodParts.rctArrayBufferSelector,
}),
)
.join('\n')}
Expand Down Expand Up @@ -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<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, ${returnJSType}, "${methodName}", ${selector}, args, count);
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, ${returnJSType}, "${methodName}", ${
rctArrayBufferSelector == null
? selector
: `${rctArrayBufferSelector}, ${selector}`
}, args, count);
}`;

const MethodMapEntryTemplate = ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,18 @@ Map {

@protocol NativeSampleTurboModuleSpec <RCTBridgeModule, RCTTurboModule>

- (RCTArrayBuffer *)getArrayBuffer;
- (void)voidArrayBuffer:(RCTArrayBuffer *)arg;
- (void)voidNullableArrayBuffer:(RCTArrayBuffer * _Nullable)arg;
- (NSMutableData *)getArrayBuffer;
@optional
- (RCTArrayBuffer *)getArrayBufferWithRCTArrayBuffer;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- (RCTArrayBuffer *)getArrayBufferWithRCTArrayBuffer;
- (RCTArrayBuffer *)getArrayBufferAsRCTArrayBuffer;

@required
- (void)voidArrayBuffer:(NSData *)arg;
@optional
- (void)voidArrayBufferWithRCTArrayBuffer:(RCTArrayBuffer *)arg;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- (void)voidArrayBufferWithRCTArrayBuffer:(RCTArrayBuffer *)arg;
- (void)voidArrayBufferFromRCTArrayBuffer:(RCTArrayBuffer *)arg;

@required
- (void)voidNullableArrayBuffer:(NSData * _Nullable)arg;
@optional
- (void)voidNullableArrayBufferWithRCTArrayBuffer:(RCTArrayBuffer * _Nullable)arg;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- (void)voidNullableArrayBufferWithRCTArrayBuffer:(RCTArrayBuffer * _Nullable)arg;
- (void)voidNullableArrayBufferFromRCTArrayBuffer:(RCTArrayBuffer * _Nullable)arg;

@required

@end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, ArrayBufferKind, \\"getArrayBuffer\\", @selector(getArrayBuffer), args, count);
return static_cast<ObjCTurboModule&>(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<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, \\"voidArrayBuffer\\", @selector(voidArrayBuffer:), args, count);
return static_cast<ObjCTurboModule&>(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<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, \\"voidNullableArrayBuffer\\", @selector(voidNullableArrayBuffer:), args, count);
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, \\"voidNullableArrayBuffer\\", @selector(voidNullableArrayBufferWithRCTArrayBuffer:), @selector(voidNullableArrayBuffer:), args, count);
}

NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const ObjCTurboModule::InitParams &params)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 + "()";
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,13 @@ id convertJSIValueToObjCObject(
jsi::Runtime &runtime,
const jsi::Value &value,
const std::shared_ptr<CallInvoker> &jsInvoker,
BOOL useNSNull = NO,
BOOL mustCopyBytes = YES);
BOOL useNSNull = NO);
id convertJSIValueToObjCObject(
jsi::Runtime &runtime,
const jsi::Value &value,
const std::shared_ptr<CallInvoker> &jsInvoker,
BOOL useNSNull,
BOOL mustCopyBytes);
} // namespace TurboModuleConvertUtils

template <>
Expand Down Expand Up @@ -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<RCTBridgeModule> instance_;
std::shared_ptr<NativeMethodCallInvoker> nativeMethodCallInvoker_;

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
Loading
Loading