mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Introduce RCTTurboModulePerformanceLogger
Summary: In production, we'd like to be able to track the performance metrics of TurboModules. So, I'm introducing a new protocol called `RCTTurboModulePerformanceLogger`. It allows the application to be notified at points during stages of TurboModule creation, and TurboModule method invocation. **Why can't we use `RCTPerformanceLogger`?** `RCTPerformanceLogger` is meant to be used to profile RN initialization. It supports a fixed list of markers. Each marker has a type, and there can only be one instance of it. This isn't suitable for logging events in the TurboModule system. Reviewed By: mdvacca Differential Revision: D20310976 fbshipit-source-id: a49251b325c94f912cd35ab1cd8fc010f2cfc08f
This commit is contained in:
committed by
Facebook Github Bot
parent
1c1fa5b909
commit
99ceb6afba
@@ -22,6 +22,101 @@
|
||||
((RCTTurboModuleEnabled() && [(klass) conformsToProtocol:@protocol(RCTTurboModule)]))
|
||||
#define RCT_IS_TURBO_MODULE_INSTANCE(module) RCT_IS_TURBO_MODULE_CLASS([(module) class])
|
||||
|
||||
typedef int MethodCallId;
|
||||
|
||||
/**
|
||||
* This interface exists to allow the application to collect performance
|
||||
* metrics of the TurboModule system. By implementing each function, you can
|
||||
* hook into various stages of TurboModule creation and method dispatch (both async and sync).
|
||||
*
|
||||
* Note:
|
||||
* - TurboModule async method invocations can interleave, so methodCallId should be used as a unique id for a method
|
||||
* call.
|
||||
*/
|
||||
@protocol RCTTurboModulePerformanceLogger
|
||||
// Create TurboModule JS Object
|
||||
- (void)createTurboModuleStart:(const char *)moduleName;
|
||||
- (void)createTurboModuleEnd:(const char *)moduleName;
|
||||
- (void)createTurboModuleCacheHit:(const char *)moduleName;
|
||||
- (void)getCppTurboModuleFromTMMDelegateStart:(const char *)moduleName;
|
||||
- (void)getCppTurboModuleFromTMMDelegateEnd:(const char *)moduleName;
|
||||
- (void)getTurboModuleFromRCTTurboModuleStart:(const char *)moduleName;
|
||||
- (void)getTurboModuleFromRCTTurboModuleEnd:(const char *)moduleName;
|
||||
- (void)getTurboModuleFromRCTCxxModuleStart:(const char *)moduleName;
|
||||
- (void)getTurboModuleFromRCTCxxModuleEnd:(const char *)moduleName;
|
||||
- (void)getTurboModuleFromTMMDelegateStart:(const char *)moduleName;
|
||||
- (void)getTurboModuleFromTMMDelegateEnd:(const char *)moduleName;
|
||||
|
||||
// Create RCTTurboModule object
|
||||
- (void)createRCTTurboModuleStart:(const char *)moduleName;
|
||||
- (void)createRCTTurboModuleEnd:(const char *)moduleName;
|
||||
- (void)createRCTTurboModuleCacheHit:(const char *)moduleName;
|
||||
- (void)getRCTTurboModuleClassStart:(const char *)moduleName;
|
||||
- (void)getRCTTurboModuleClassEnd:(const char *)moduleName;
|
||||
- (void)getRCTTurboModuleInstanceStart:(const char *)moduleName;
|
||||
- (void)getRCTTurboModuleInstanceEnd:(const char *)moduleName;
|
||||
- (void)setupRCTTurboModuleDispatch:(const char *)moduleName;
|
||||
- (void)setupRCTTurboModuleStart:(const char *)moduleName;
|
||||
- (void)setupRCTTurboModuleEnd:(const char *)moduleName;
|
||||
- (void)attachRCTBridgeToRCTTurboModuleStart:(const char *)moduleName;
|
||||
- (void)attachRCTBridgeToRCTTurboModuleEnd:(const char *)moduleName;
|
||||
- (void)attachMethodQueueToRCTTurboModuleStart:(const char *)moduleName;
|
||||
- (void)attachMethodQueueToRCTTurboModuleEnd:(const char *)moduleName;
|
||||
- (void)registerRCTTurboModuleForFrameUpdatesStart:(const char *)moduleName;
|
||||
- (void)registerRCTTurboModuleForFrameUpdatesEnd:(const char *)moduleName;
|
||||
- (void)dispatchDidInitializeModuleNotificationForRCTTurboModuleStart:(const char *)moduleName;
|
||||
- (void)dispatchDidInitializeModuleNotificationForRCTTurboModuleEnd:(const char *)moduleName;
|
||||
|
||||
// Sync method invocation
|
||||
- (void)syncMethodCallStart:(const char *)moduleName
|
||||
methodName:(const char *)methodName
|
||||
methodCallId:(MethodCallId)methodCallId;
|
||||
- (void)syncMethodCallEnd:(const char *)moduleName
|
||||
methodName:(const char *)methodName
|
||||
methodCallId:(MethodCallId)methodCallId;
|
||||
- (void)syncMethodCallArgumentConversionStart:(const char *)moduleName
|
||||
methodName:(const char *)methodName
|
||||
methodCallId:(MethodCallId)methodCallId;
|
||||
- (void)syncMethodCallArgumentConversionEnd:(const char *)moduleName
|
||||
methodName:(const char *)methodName
|
||||
methodCallId:(MethodCallId)methodCallId;
|
||||
- (void)syncRCTTurboModuleMethodCallStart:(const char *)moduleName
|
||||
methodName:(const char *)methodName
|
||||
methodCallId:(MethodCallId)methodCallId;
|
||||
- (void)syncRCTTurboModuleMethodCallEnd:(const char *)moduleName
|
||||
methodName:(const char *)methodName
|
||||
methodCallId:(MethodCallId)methodCallId;
|
||||
- (void)syncMethodCallReturnConversionStart:(const char *)moduleName
|
||||
methodName:(const char *)methodName
|
||||
methodCallId:(MethodCallId)methodCallId;
|
||||
- (void)syncMethodCallReturnConversionEnd:(const char *)moduleName
|
||||
methodName:(const char *)methodName
|
||||
methodCallId:(MethodCallId)methodCallId;
|
||||
|
||||
// Async method invocation
|
||||
- (void)asyncMethodCallStart:(const char *)moduleName
|
||||
methodName:(const char *)methodName
|
||||
methodCallId:(MethodCallId)methodCallId;
|
||||
- (void)asyncMethodCallEnd:(const char *)moduleName
|
||||
methodName:(const char *)methodName
|
||||
methodCallId:(MethodCallId)methodCallId;
|
||||
- (void)asyncMethodCallArgumentConversionStart:(const char *)moduleName
|
||||
methodName:(const char *)methodName
|
||||
methodCallId:(MethodCallId)methodCallId;
|
||||
- (void)asyncMethodCallArgumentConversionEnd:(const char *)moduleName
|
||||
methodName:(const char *)methodName
|
||||
methodCallId:(MethodCallId)methodCallId;
|
||||
- (void)asyncRCTTurboModuleMethodCallDispatch:(const char *)moduleName
|
||||
methodName:(const char *)methodName
|
||||
methodCallId:(MethodCallId)methodCallId;
|
||||
- (void)asyncRCTTurboModuleMethodCallStart:(const char *)moduleName
|
||||
methodName:(const char *)methodName
|
||||
methodCallId:(MethodCallId)methodCallId;
|
||||
- (void)asyncRCTTurboModuleMethodCallEnd:(const char *)moduleName
|
||||
methodName:(const char *)methodName
|
||||
methodCallId:(MethodCallId)methodCallId;
|
||||
@end
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
@@ -44,6 +139,11 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule {
|
||||
|
||||
id<RCTTurboModule> instance_;
|
||||
|
||||
/**
|
||||
* TODO(T63718299): Move this into the ObjCTurboModule constructor
|
||||
*/
|
||||
void setRCTTurboModulePerformanceLogger(id<RCTTurboModulePerformanceLogger> performanceLogger);
|
||||
|
||||
protected:
|
||||
void setMethodArgConversionSelector(NSString *methodName, int argIndex, NSString *fnName);
|
||||
|
||||
@@ -55,23 +155,38 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule {
|
||||
NSMutableDictionary<NSString *, NSMutableArray *> *methodArgConversionSelectors_;
|
||||
NSDictionary<NSString *, NSArray<NSString *> *> *methodArgumentTypeNames_;
|
||||
NSString *getArgumentTypeName(NSString *methodName, int argIndex);
|
||||
id<RCTTurboModulePerformanceLogger> performanceLogger_;
|
||||
|
||||
/**
|
||||
* Required for performance logging async method invocations.
|
||||
* This field is static because two nth async method calls from different
|
||||
* TurboModules can interleave, and should therefore be treated as two distinct calls.
|
||||
*/
|
||||
static MethodCallId methodCallId_;
|
||||
|
||||
static MethodCallId getNewMethodCallId();
|
||||
|
||||
NSInvocation *getMethodInvocation(
|
||||
jsi::Runtime &runtime,
|
||||
TurboModuleMethodValueKind valueKind,
|
||||
const id<RCTTurboModule> module,
|
||||
std::shared_ptr<CallInvoker> jsInvoker,
|
||||
const std::string &methodName,
|
||||
TurboModuleMethodValueKind returnType,
|
||||
const char *methodName,
|
||||
SEL selector,
|
||||
const jsi::Value *args,
|
||||
size_t count,
|
||||
NSMutableArray *retainedObjectsForInvocation);
|
||||
NSMutableArray *retainedObjectsForInvocation,
|
||||
MethodCallId methodCallId);
|
||||
jsi::Value performMethodInvocation(
|
||||
jsi::Runtime &runtime,
|
||||
TurboModuleMethodValueKind returnType,
|
||||
const char *methodName,
|
||||
NSInvocation *inv,
|
||||
NSMutableArray *retainedObjectsForInvocation,
|
||||
MethodCallId methodCallId);
|
||||
|
||||
BOOL hasMethodArgConversionSelector(NSString *methodName, int argIndex);
|
||||
SEL getMethodArgConversionSelector(NSString *methodName, int argIndex);
|
||||
|
||||
using PromiseInvocationBlock =
|
||||
void (^)(jsi::Runtime &rt, RCTPromiseResolveBlock resolveWrapper, RCTPromiseRejectBlock rejectWrapper);
|
||||
using PromiseInvocationBlock = void (^)(RCTPromiseResolveBlock resolveWrapper, RCTPromiseRejectBlock rejectWrapper);
|
||||
jsi::Value
|
||||
createPromise(jsi::Runtime &runtime, std::shared_ptr<react::CallInvoker> jsInvoker, PromiseInvocationBlock invoke);
|
||||
};
|
||||
|
||||
@@ -303,15 +303,13 @@ jsi::Value ObjCTurboModule::createPromise(
|
||||
rejectWasCalled = YES;
|
||||
};
|
||||
|
||||
invokeCopy(rt, resolveBlock, rejectBlock);
|
||||
invokeCopy(resolveBlock, rejectBlock);
|
||||
return jsi::Value::undefined();
|
||||
});
|
||||
|
||||
return Promise.callAsConstructor(runtime, fn);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Perform method invocation on a specific queue as configured by the module class.
|
||||
* This serves as a backward-compatible support for RCTBridgeModule's methodQueue API.
|
||||
@@ -321,40 +319,64 @@ namespace {
|
||||
* - ObjC module methods will be always be called from JS thread.
|
||||
* They may decide to dispatch to a different queue as needed.
|
||||
*/
|
||||
jsi::Value performMethodInvocation(
|
||||
jsi::Value ObjCTurboModule::performMethodInvocation(
|
||||
jsi::Runtime &runtime,
|
||||
TurboModuleMethodValueKind returnType,
|
||||
const char *methodName,
|
||||
NSInvocation *inv,
|
||||
TurboModuleMethodValueKind valueKind,
|
||||
const id<RCTTurboModule> module,
|
||||
std::shared_ptr<CallInvoker> jsInvoker,
|
||||
NSMutableArray *retainedObjectsForInvocation)
|
||||
NSMutableArray *retainedObjectsForInvocation,
|
||||
MethodCallId methodCallId)
|
||||
{
|
||||
__block id result;
|
||||
jsi::Runtime *rt = &runtime;
|
||||
__weak id<RCTTurboModule> weakModule = instance_;
|
||||
id<RCTTurboModulePerformanceLogger> performanceLogger = performanceLogger_;
|
||||
const char *moduleName = name_.c_str();
|
||||
const bool isSync = returnType != VoidKind && returnType != PromiseKind;
|
||||
|
||||
void (^block)() = ^{
|
||||
[inv invokeWithTarget:module];
|
||||
if (!weakModule) {
|
||||
return;
|
||||
}
|
||||
|
||||
id<RCTTurboModule> strongModule = weakModule;
|
||||
|
||||
if (isSync) {
|
||||
[performanceLogger syncRCTTurboModuleMethodCallStart:moduleName methodName:methodName methodCallId:methodCallId];
|
||||
} else {
|
||||
[performanceLogger asyncRCTTurboModuleMethodCallStart:moduleName methodName:methodName methodCallId:methodCallId];
|
||||
}
|
||||
|
||||
[inv invokeWithTarget:strongModule];
|
||||
[retainedObjectsForInvocation removeAllObjects];
|
||||
|
||||
if (valueKind == VoidKind) {
|
||||
if (returnType == VoidKind) {
|
||||
[performanceLogger asyncRCTTurboModuleMethodCallEnd:moduleName methodName:methodName methodCallId:methodCallId];
|
||||
return;
|
||||
}
|
||||
void *rawResult;
|
||||
[inv getReturnValue:&rawResult];
|
||||
result = (__bridge id)rawResult;
|
||||
[performanceLogger syncRCTTurboModuleMethodCallEnd:moduleName methodName:methodName methodCallId:methodCallId];
|
||||
};
|
||||
|
||||
// Backward-compatibility layer for calling module methods on specific queue.
|
||||
dispatch_queue_t methodQueue = NULL;
|
||||
if ([module conformsToProtocol:@protocol(RCTBridgeModule)] && [module respondsToSelector:@selector(methodQueue)]) {
|
||||
methodQueue = [module performSelector:@selector(methodQueue)];
|
||||
if ([instance_ conformsToProtocol:@protocol(RCTBridgeModule)] &&
|
||||
[instance_ respondsToSelector:@selector(methodQueue)]) {
|
||||
methodQueue = [instance_ performSelector:@selector(methodQueue)];
|
||||
}
|
||||
|
||||
if (methodQueue == NULL || methodQueue == RCTJSThread) {
|
||||
// This is the default mode of execution: on JS thread.
|
||||
block();
|
||||
} else if (methodQueue == dispatch_get_main_queue()) {
|
||||
if (valueKind == VoidKind) {
|
||||
if (returnType == VoidKind) {
|
||||
// Void methods are treated as async for now, so there's no need to block here.
|
||||
|
||||
[performanceLogger_ asyncRCTTurboModuleMethodCallDispatch:moduleName
|
||||
methodName:methodName
|
||||
methodCallId:methodCallId];
|
||||
RCTExecuteOnMainQueue(block);
|
||||
} else {
|
||||
// This is not ideal, but provides the simplest mechanism for now.
|
||||
@@ -363,7 +385,10 @@ jsi::Value performMethodInvocation(
|
||||
RCTUnsafeExecuteOnMainQueueSync(block);
|
||||
}
|
||||
} else {
|
||||
if (valueKind == VoidKind) {
|
||||
if (returnType == VoidKind) {
|
||||
[performanceLogger_ asyncRCTTurboModuleMethodCallDispatch:moduleName
|
||||
methodName:methodName
|
||||
methodCallId:methodCallId];
|
||||
dispatch_async(methodQueue, block);
|
||||
} else {
|
||||
dispatch_sync(methodQueue, block);
|
||||
@@ -372,33 +397,48 @@ jsi::Value performMethodInvocation(
|
||||
|
||||
// VoidKind can't be null
|
||||
// PromiseKind, and FunctionKind must throw errors always
|
||||
if (valueKind != VoidKind && valueKind != PromiseKind && valueKind != FunctionKind &&
|
||||
if (returnType != VoidKind && returnType != PromiseKind && returnType != FunctionKind &&
|
||||
(result == (id)kCFNull || result == nil)) {
|
||||
return jsi::Value::null();
|
||||
}
|
||||
|
||||
jsi::Value returnValue = jsi::Value::undefined();
|
||||
[performanceLogger_ syncMethodCallReturnConversionStart:moduleName methodName:methodName methodCallId:methodCallId];
|
||||
|
||||
// TODO: Re-use value conversion logic from existing impl, if possible.
|
||||
switch (valueKind) {
|
||||
case VoidKind:
|
||||
return jsi::Value::undefined();
|
||||
case BooleanKind:
|
||||
return convertNSNumberToJSIBoolean(*rt, (NSNumber *)result);
|
||||
case NumberKind:
|
||||
return convertNSNumberToJSINumber(*rt, (NSNumber *)result);
|
||||
case StringKind:
|
||||
return convertNSStringToJSIString(*rt, (NSString *)result);
|
||||
case ObjectKind:
|
||||
return convertNSDictionaryToJSIObject(*rt, (NSDictionary *)result);
|
||||
case ArrayKind:
|
||||
return convertNSArrayToJSIArray(*rt, (NSArray *)result);
|
||||
switch (returnType) {
|
||||
case VoidKind: {
|
||||
break;
|
||||
}
|
||||
case BooleanKind: {
|
||||
returnValue = convertNSNumberToJSIBoolean(*rt, (NSNumber *)result);
|
||||
break;
|
||||
}
|
||||
case NumberKind: {
|
||||
returnValue = convertNSNumberToJSINumber(*rt, (NSNumber *)result);
|
||||
break;
|
||||
}
|
||||
case StringKind: {
|
||||
returnValue = convertNSStringToJSIString(*rt, (NSString *)result);
|
||||
break;
|
||||
}
|
||||
case ObjectKind: {
|
||||
returnValue = convertNSDictionaryToJSIObject(*rt, (NSDictionary *)result);
|
||||
break;
|
||||
}
|
||||
case ArrayKind: {
|
||||
returnValue = convertNSArrayToJSIArray(*rt, (NSArray *)result);
|
||||
break;
|
||||
}
|
||||
case FunctionKind:
|
||||
throw std::runtime_error("convertInvocationResultToJSIValue: FunctionKind is not supported yet.");
|
||||
case PromiseKind:
|
||||
throw std::runtime_error("convertInvocationResultToJSIValue: PromiseKind wasn't handled properly.");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
[performanceLogger_ syncMethodCallReturnConversionEnd:moduleName methodName:methodName methodCallId:methodCallId];
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a method name, and an argument index, return type of that argument.
|
||||
@@ -460,15 +500,28 @@ NSString *ObjCTurboModule::getArgumentTypeName(NSString *methodName, int argInde
|
||||
|
||||
NSInvocation *ObjCTurboModule::getMethodInvocation(
|
||||
jsi::Runtime &runtime,
|
||||
TurboModuleMethodValueKind valueKind,
|
||||
const id<RCTTurboModule> module,
|
||||
std::shared_ptr<CallInvoker> jsInvoker,
|
||||
const std::string &methodName,
|
||||
TurboModuleMethodValueKind returnType,
|
||||
const char *methodName,
|
||||
SEL selector,
|
||||
const jsi::Value *args,
|
||||
size_t count,
|
||||
NSMutableArray *retainedObjectsForInvocation)
|
||||
NSMutableArray *retainedObjectsForInvocation,
|
||||
MethodCallId methodCallId)
|
||||
{
|
||||
const bool isSync = returnType != VoidKind && returnType != PromiseKind;
|
||||
const char *moduleName = name_.c_str();
|
||||
const id<RCTTurboModule> module = instance_;
|
||||
|
||||
if (isSync) {
|
||||
[performanceLogger_ syncMethodCallArgumentConversionStart:moduleName
|
||||
methodName:methodName
|
||||
methodCallId:methodCallId];
|
||||
} else {
|
||||
[performanceLogger_ asyncMethodCallArgumentConversionStart:moduleName
|
||||
methodName:methodName
|
||||
methodCallId:methodCallId];
|
||||
}
|
||||
|
||||
NSInvocation *inv =
|
||||
[NSInvocation invocationWithMethodSignature:[[module class] instanceMethodSignatureForSelector:selector]];
|
||||
[inv setSelector:selector];
|
||||
@@ -516,10 +569,10 @@ NSInvocation *ObjCTurboModule::getMethodInvocation(
|
||||
/**
|
||||
* Convert arg to ObjC objects.
|
||||
*/
|
||||
id objCArg = convertJSIValueToObjCObject(runtime, *arg, jsInvoker);
|
||||
id objCArg = convertJSIValueToObjCObject(runtime, *arg, jsInvoker_);
|
||||
|
||||
if (objCArg) {
|
||||
NSString *methodNameNSString = @(methodName.c_str());
|
||||
NSString *methodNameNSString = @(methodName);
|
||||
|
||||
/**
|
||||
* Convert objects using RCTConvert.
|
||||
@@ -570,6 +623,14 @@ NSInvocation *ObjCTurboModule::getMethodInvocation(
|
||||
}
|
||||
}
|
||||
|
||||
if (isSync) {
|
||||
[performanceLogger_ syncMethodCallArgumentConversionEnd:moduleName methodName:methodName methodCallId:methodCallId];
|
||||
} else {
|
||||
[performanceLogger_ asyncMethodCallArgumentConversionEnd:moduleName
|
||||
methodName:methodName
|
||||
methodCallId:methodCallId];
|
||||
}
|
||||
|
||||
return inv;
|
||||
}
|
||||
|
||||
@@ -581,35 +642,62 @@ ObjCTurboModule::ObjCTurboModule(
|
||||
{
|
||||
}
|
||||
|
||||
void ObjCTurboModule::setRCTTurboModulePerformanceLogger(id<RCTTurboModulePerformanceLogger> performanceLogger)
|
||||
{
|
||||
performanceLogger_ = performanceLogger;
|
||||
}
|
||||
|
||||
MethodCallId ObjCTurboModule::methodCallId_{0};
|
||||
|
||||
MethodCallId ObjCTurboModule::getNewMethodCallId()
|
||||
{
|
||||
return methodCallId_++;
|
||||
}
|
||||
|
||||
jsi::Value ObjCTurboModule::invokeObjCMethod(
|
||||
jsi::Runtime &runtime,
|
||||
TurboModuleMethodValueKind valueKind,
|
||||
const std::string &methodName,
|
||||
TurboModuleMethodValueKind returnType,
|
||||
const std::string &methodNameStr,
|
||||
SEL selector,
|
||||
const jsi::Value *args,
|
||||
size_t count)
|
||||
{
|
||||
NSMutableArray *retainedObjectsForInvocation = [NSMutableArray arrayWithCapacity:count + 2];
|
||||
NSInvocation *inv = getMethodInvocation(
|
||||
runtime, valueKind, instance_, jsInvoker_, methodName, selector, args, count, retainedObjectsForInvocation);
|
||||
MethodCallId methodCallId = getNewMethodCallId();
|
||||
const bool isSync = returnType != VoidKind && returnType != PromiseKind;
|
||||
const char *moduleName = name_.c_str();
|
||||
const char *methodName = methodNameStr.c_str();
|
||||
|
||||
if (valueKind == PromiseKind) {
|
||||
// Promise return type is special cased today, i.e. it needs extra 2 function args for resolve() and reject(), to
|
||||
// be passed to the actual ObjC++ class method.
|
||||
return createPromise(
|
||||
runtime,
|
||||
jsInvoker_,
|
||||
^(jsi::Runtime &rt, RCTPromiseResolveBlock resolveBlock, RCTPromiseRejectBlock rejectBlock) {
|
||||
[inv setArgument:(void *)&resolveBlock atIndex:count + 2];
|
||||
[inv setArgument:(void *)&rejectBlock atIndex:count + 3];
|
||||
[retainedObjectsForInvocation addObject:resolveBlock];
|
||||
[retainedObjectsForInvocation addObject:rejectBlock];
|
||||
// The return type becomes void in the ObjC side.
|
||||
performMethodInvocation(rt, inv, VoidKind, instance_, jsInvoker_, retainedObjectsForInvocation);
|
||||
});
|
||||
if (isSync) {
|
||||
[performanceLogger_ syncMethodCallStart:moduleName methodName:methodName methodCallId:methodCallId];
|
||||
} else {
|
||||
[performanceLogger_ asyncMethodCallStart:moduleName methodName:methodName methodCallId:methodCallId];
|
||||
}
|
||||
|
||||
return performMethodInvocation(runtime, inv, valueKind, instance_, jsInvoker_, retainedObjectsForInvocation);
|
||||
NSMutableArray *retainedObjectsForInvocation = [NSMutableArray arrayWithCapacity:count + 2];
|
||||
NSInvocation *inv = getMethodInvocation(
|
||||
runtime, returnType, methodName, selector, args, count, retainedObjectsForInvocation, methodCallId);
|
||||
|
||||
jsi::Value returnValue = returnType == PromiseKind
|
||||
? createPromise(
|
||||
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];
|
||||
// The return type becomes void in the ObjC side.
|
||||
performMethodInvocation(runtime, VoidKind, methodName, inv, retainedObjectsForInvocation, methodCallId);
|
||||
})
|
||||
: performMethodInvocation(runtime, returnType, methodName, inv, retainedObjectsForInvocation, methodCallId);
|
||||
|
||||
if (isSync) {
|
||||
[performanceLogger_ syncMethodCallEnd:moduleName methodName:methodName methodCallId:methodCallId];
|
||||
} else {
|
||||
[performanceLogger_ asyncMethodCallEnd:moduleName methodName:methodName methodCallId:methodCallId];
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
BOOL ObjCTurboModule::hasMethodArgConversionSelector(NSString *methodName, int argIndex)
|
||||
|
||||
@@ -42,6 +42,11 @@
|
||||
delegate:(id<RCTTurboModuleManagerDelegate>)delegate
|
||||
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker;
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
delegate:(id<RCTTurboModuleManagerDelegate>)delegate
|
||||
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
|
||||
performanceLogger:(id<RCTTurboModulePerformanceLogger>)performanceLogger;
|
||||
|
||||
- (void)installJSBindingWithRuntime:(facebook::jsi::Runtime *)runtime;
|
||||
|
||||
- (void)invalidate;
|
||||
|
||||
@@ -37,6 +37,7 @@ static Class getFallbackClassFromName(const char *name)
|
||||
@implementation RCTTurboModuleManager {
|
||||
jsi::Runtime *_runtime;
|
||||
std::shared_ptr<facebook::react::CallInvoker> _jsInvoker;
|
||||
id<RCTTurboModulePerformanceLogger> _performanceLogger;
|
||||
__weak id<RCTTurboModuleManagerDelegate> _delegate;
|
||||
__weak RCTBridge *_bridge;
|
||||
/**
|
||||
@@ -65,12 +66,21 @@ static Class getFallbackClassFromName(const char *name)
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
delegate:(id<RCTTurboModuleManagerDelegate>)delegate
|
||||
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
|
||||
{
|
||||
return [self initWithBridge:bridge delegate:delegate jsInvoker:jsInvoker performanceLogger:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
delegate:(id<RCTTurboModuleManagerDelegate>)delegate
|
||||
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
|
||||
performanceLogger:(id<RCTTurboModulePerformanceLogger>)performanceLogger
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_jsInvoker = jsInvoker;
|
||||
_delegate = delegate;
|
||||
_bridge = bridge;
|
||||
_invalidating = false;
|
||||
_performanceLogger = performanceLogger;
|
||||
|
||||
// Necessary to allow NativeModules to lookup TurboModules
|
||||
[bridge setRCTTurboModuleLookupDelegate:self];
|
||||
@@ -114,6 +124,7 @@ static Class getFallbackClassFromName(const char *name)
|
||||
{
|
||||
auto turboModuleLookup = _turboModuleCache.find(moduleName);
|
||||
if (turboModuleLookup != _turboModuleCache.end()) {
|
||||
[_performanceLogger createTurboModuleCacheHit:moduleName];
|
||||
return turboModuleLookup->second;
|
||||
}
|
||||
|
||||
@@ -122,7 +133,9 @@ static Class getFallbackClassFromName(const char *name)
|
||||
* Pure C++ modules get priority.
|
||||
*/
|
||||
if ([_delegate respondsToSelector:@selector(getTurboModule:jsInvoker:)]) {
|
||||
[_performanceLogger getCppTurboModuleFromTMMDelegateStart:moduleName];
|
||||
auto turboModule = [_delegate getTurboModule:moduleName jsInvoker:_jsInvoker];
|
||||
[_performanceLogger getCppTurboModuleFromTMMDelegateEnd:moduleName];
|
||||
if (turboModule != nullptr) {
|
||||
_turboModuleCache.insert({moduleName, turboModule});
|
||||
return turboModule;
|
||||
@@ -132,7 +145,9 @@ static Class getFallbackClassFromName(const char *name)
|
||||
/**
|
||||
* Step 2: Look for platform-specific modules.
|
||||
*/
|
||||
[_performanceLogger createRCTTurboModuleStart:moduleName];
|
||||
id<RCTTurboModule> module = [self provideRCTTurboModule:moduleName];
|
||||
[_performanceLogger createRCTTurboModuleEnd:moduleName];
|
||||
|
||||
// If we request that a TurboModule be created, its respective ObjC class must exist
|
||||
// If the class doesn't exist, then provideRCTTurboModule returns nil
|
||||
@@ -145,7 +160,9 @@ static Class getFallbackClassFromName(const char *name)
|
||||
// If RCTTurboModule supports creating its own C++ TurboModule object,
|
||||
// allow it to do so.
|
||||
if ([module respondsToSelector:@selector(getTurboModuleWithJsInvoker:)]) {
|
||||
[_performanceLogger getTurboModuleFromRCTTurboModuleStart:moduleName];
|
||||
auto turboModule = [module getTurboModuleWithJsInvoker:_jsInvoker];
|
||||
[_performanceLogger getTurboModuleFromRCTTurboModuleEnd:moduleName];
|
||||
assert(turboModule != nullptr);
|
||||
_turboModuleCache.insert({moduleName, turboModule});
|
||||
return turboModule;
|
||||
@@ -158,7 +175,9 @@ static Class getFallbackClassFromName(const char *name)
|
||||
if ([moduleClass isSubclassOfClass:RCTCxxModule.class]) {
|
||||
// Use TurboCxxModule compat class to wrap the CxxModule instance.
|
||||
// This is only for migration convenience, despite less performant.
|
||||
[_performanceLogger getTurboModuleFromRCTCxxModuleStart:moduleName];
|
||||
auto turboModule = std::make_shared<react::TurboCxxModule>([((RCTCxxModule *)module) createModule], _jsInvoker);
|
||||
[_performanceLogger getTurboModuleFromRCTCxxModuleEnd:moduleName];
|
||||
_turboModuleCache.insert({moduleName, turboModule});
|
||||
return turboModule;
|
||||
}
|
||||
@@ -166,7 +185,9 @@ static Class getFallbackClassFromName(const char *name)
|
||||
/**
|
||||
* Step 2d: Return an exact sub-class of ObjC TurboModule
|
||||
*/
|
||||
[_performanceLogger getTurboModuleFromTMMDelegateStart:moduleName];
|
||||
auto turboModule = [_delegate getTurboModule:moduleName instance:module jsInvoker:_jsInvoker];
|
||||
[_performanceLogger getTurboModuleFromTMMDelegateEnd:moduleName];
|
||||
if (turboModule != nullptr) {
|
||||
_turboModuleCache.insert({moduleName, turboModule});
|
||||
}
|
||||
@@ -191,6 +212,7 @@ static Class getFallbackClassFromName(const char *name)
|
||||
|
||||
auto rctTurboModuleCacheLookup = _rctTurboModuleCache.find(moduleName);
|
||||
if (rctTurboModuleCacheLookup != _rctTurboModuleCache.end()) {
|
||||
[_performanceLogger createRCTTurboModuleCacheHit:moduleName];
|
||||
return rctTurboModuleCacheLookup->second;
|
||||
}
|
||||
|
||||
@@ -202,6 +224,8 @@ static Class getFallbackClassFromName(const char *name)
|
||||
/**
|
||||
* Step 2a: Resolve platform-specific class.
|
||||
*/
|
||||
[_performanceLogger getRCTTurboModuleClassStart:moduleName];
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(getModuleClassFromName:)]) {
|
||||
moduleClass = [_delegate getModuleClassFromName:moduleName];
|
||||
}
|
||||
@@ -210,6 +234,8 @@ static Class getFallbackClassFromName(const char *name)
|
||||
moduleClass = getFallbackClassFromName(moduleName);
|
||||
}
|
||||
|
||||
[_performanceLogger getRCTTurboModuleClassEnd:moduleName];
|
||||
|
||||
if (![moduleClass conformsToProtocol:@protocol(RCTTurboModule)]) {
|
||||
return nil;
|
||||
}
|
||||
@@ -217,12 +243,16 @@ static Class getFallbackClassFromName(const char *name)
|
||||
/**
|
||||
* Step 2b: Ask hosting application/delegate to instantiate this class
|
||||
*/
|
||||
[_performanceLogger getRCTTurboModuleInstanceStart:moduleName];
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(getModuleInstanceFromClass:)]) {
|
||||
module = [_delegate getModuleInstanceFromClass:moduleClass];
|
||||
} else {
|
||||
module = [moduleClass new];
|
||||
}
|
||||
|
||||
[_performanceLogger getRCTTurboModuleInstanceEnd:moduleName];
|
||||
|
||||
if ([module respondsToSelector:@selector(setTurboModuleLookupDelegate:)]) {
|
||||
[module setTurboModuleLookupDelegate:self];
|
||||
}
|
||||
@@ -232,12 +262,15 @@ static Class getFallbackClassFromName(const char *name)
|
||||
|
||||
__weak id<RCTBridgeModule> weakModule = (id<RCTBridgeModule>)module;
|
||||
__weak RCTBridge *weakBridge = _bridge;
|
||||
id<RCTTurboModulePerformanceLogger> performanceLogger = _performanceLogger;
|
||||
|
||||
auto setupTurboModule = ^{
|
||||
if (!weakModule) {
|
||||
return;
|
||||
}
|
||||
|
||||
[performanceLogger setupRCTTurboModuleStart:moduleName];
|
||||
|
||||
id<RCTBridgeModule> strongModule = weakModule;
|
||||
RCTBridge *strongBridge = weakBridge;
|
||||
|
||||
@@ -251,6 +284,8 @@ static Class getFallbackClassFromName(const char *name)
|
||||
* NativeModule.
|
||||
*/
|
||||
if ([strongModule respondsToSelector:@selector(bridge)] && strongBridge) {
|
||||
[performanceLogger attachRCTBridgeToRCTTurboModuleStart:moduleName];
|
||||
|
||||
/**
|
||||
* Just because a NativeModule has the `bridge` method, it doesn't mean
|
||||
* that it has synthesized the bridge in its implementation. Therefore,
|
||||
@@ -275,6 +310,8 @@ static Class getFallbackClassFromName(const char *name)
|
||||
"or provide your own setter method.",
|
||||
RCTBridgeModuleNameForClass(strongModule));
|
||||
}
|
||||
|
||||
[performanceLogger attachRCTBridgeToRCTTurboModuleEnd:moduleName];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,6 +320,8 @@ static Class getFallbackClassFromName(const char *name)
|
||||
* `@synthesize methodQueue = _methodQueue`
|
||||
*/
|
||||
if ([strongModule respondsToSelector:@selector(methodQueue)]) {
|
||||
[performanceLogger attachMethodQueueToRCTTurboModuleStart:moduleName];
|
||||
|
||||
dispatch_queue_t methodQueue = [strongModule performSelector:@selector(methodQueue)];
|
||||
if (!methodQueue) {
|
||||
NSString *moduleClassName = NSStringFromClass(strongModule.class);
|
||||
@@ -299,6 +338,8 @@ static Class getFallbackClassFromName(const char *name)
|
||||
moduleClassName);
|
||||
}
|
||||
}
|
||||
|
||||
[performanceLogger attachMethodQueueToRCTTurboModuleEnd:moduleName];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -309,8 +350,10 @@ static Class getFallbackClassFromName(const char *name)
|
||||
* rollout.
|
||||
*/
|
||||
if (strongBridge) {
|
||||
[performanceLogger registerRCTTurboModuleForFrameUpdatesStart:moduleName];
|
||||
RCTModuleData *data = [[RCTModuleData alloc] initWithModuleInstance:strongModule bridge:strongBridge];
|
||||
[strongBridge registerModuleForFrameUpdates:strongModule withModuleData:data];
|
||||
[performanceLogger registerRCTTurboModuleForFrameUpdatesEnd:moduleName];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -319,10 +362,14 @@ static Class getFallbackClassFromName(const char *name)
|
||||
* TODO(T41180176): Investigate whether we can delete this after TM
|
||||
* rollout.
|
||||
*/
|
||||
[performanceLogger dispatchDidInitializeModuleNotificationForRCTTurboModuleStart:moduleName];
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
postNotificationName:RCTDidInitializeModuleNotification
|
||||
object:strongBridge
|
||||
userInfo:@{@"module" : module, @"bridge" : RCTNullIfNil([strongBridge parentBridge])}];
|
||||
[performanceLogger dispatchDidInitializeModuleNotificationForRCTTurboModuleEnd:moduleName];
|
||||
|
||||
[performanceLogger setupRCTTurboModuleEnd:moduleName];
|
||||
};
|
||||
|
||||
if ([[module class] respondsToSelector:@selector(requiresMainQueueSetup)] &&
|
||||
@@ -334,6 +381,7 @@ static Class getFallbackClassFromName(const char *name)
|
||||
*
|
||||
* TODO(T63807674): Investigate the right migration plan off of this
|
||||
*/
|
||||
[_performanceLogger setupRCTTurboModuleDispatch:moduleName];
|
||||
RCTUnsafeExecuteOnMainQueueSync(setupTurboModule);
|
||||
} else {
|
||||
setupTurboModule();
|
||||
@@ -354,7 +402,9 @@ static Class getFallbackClassFromName(const char *name)
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
|
||||
react::TurboModuleBinding::install(
|
||||
*_runtime, [weakSelf](const std::string &name) -> std::shared_ptr<react::TurboModule> {
|
||||
*_runtime,
|
||||
[weakSelf,
|
||||
performanceLogger = _performanceLogger](const std::string &name) -> std::shared_ptr<react::TurboModule> {
|
||||
if (!weakSelf) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -367,6 +417,8 @@ static Class getFallbackClassFromName(const char *name)
|
||||
[strongSelf->_bridge.performanceLogger markStartForTag:RCTPLTurboModuleSetup];
|
||||
}
|
||||
|
||||
[performanceLogger createTurboModuleStart:moduleName];
|
||||
|
||||
/**
|
||||
* By default, all TurboModules are long-lived.
|
||||
* Additionally, if a TurboModule with the name `name` isn't found, then we
|
||||
@@ -374,6 +426,16 @@ static Class getFallbackClassFromName(const char *name)
|
||||
*/
|
||||
auto turboModule = [strongSelf provideTurboModule:moduleName];
|
||||
|
||||
/**
|
||||
* TODO(T63718299): Move this setter into the ObjCTurboModule constructor
|
||||
*/
|
||||
if (std::shared_ptr<facebook::react::ObjCTurboModule> objCTurboModule =
|
||||
std::dynamic_pointer_cast<facebook::react::ObjCTurboModule>(turboModule)) {
|
||||
objCTurboModule->setRCTTurboModulePerformanceLogger(performanceLogger);
|
||||
};
|
||||
|
||||
[performanceLogger createTurboModuleEnd:moduleName];
|
||||
|
||||
if (moduleWasNotInitialized && [strongSelf moduleIsInitialized:moduleName]) {
|
||||
[strongSelf->_bridge.performanceLogger markStopForTag:RCTPLTurboModuleSetup];
|
||||
[strongSelf notifyAboutTurboModuleSetup:moduleName];
|
||||
|
||||
Reference in New Issue
Block a user