Skip to content
Closed
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 @@ -16,8 +16,11 @@
#import <React/RCTModuleMethod.h>
#import <functional>
#import <memory>
#import <mutex>
#import <string>
#import <string_view>
#import <unordered_map>
#import <vector>

#define RCT_IS_TURBO_MODULE_CLASS(klass) \
((RCTTurboModuleEnabled() && [(klass) conformsToProtocol:@protocol(RCTTurboModule)]))
Expand Down Expand Up @@ -132,14 +135,27 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule {
// Does the NativeModule dispatch async methods to the JS thread?
const bool isSyncModule_;

/**
* TODO(ramanpreet):
* Investigate an optimization that'll let us get rid of this NSMutableDictionary.
* Perhaps, have the code-generated TurboModule subclass implement
* getMethodArgConversionSelector below.
*/
NSMutableDictionary<NSString *, NSMutableArray *> *methodArgConversionSelectors_;
NSDictionary<NSString *, NSArray<NSString *> *> *methodArgumentTypeNames_;
struct MethodInfo {
// Looked up on the method's first call, since it doesn't change.
NSMethodSignature *signature;
// Argument types declared with RCT_EXPORT_METHOD, used to convert arguments with RCTConvert.
NSArray<NSString *> *argumentTypeNames;
// RCTCxxConvert selectors that convert arguments to C++ structs, set by the generated code.
std::vector<SEL> argConversionSelectors;
};
struct MethodNameHash {
using is_transparent = void;
size_t operator()(std::string_view methodName) const
{
return std::hash<std::string_view>{}(methodName);
}
};
// Keyed by the method's JS name; looked up without allocating a std::string.
std::unordered_map<std::string, MethodInfo, MethodNameHash, std::equal_to<>> methodInfos_;
bool argumentTypeNamesLoaded_{false};
std::mutex methodInfosMutex_;
MethodInfo &findOrCreateMethodInfo(std::string_view methodName);
NSMethodSignature *getMethodSignature(const char *methodName, SEL selector);

bool isMethodSync(TurboModuleMethodValueKind returnType);
bool mustCopyJSHeapArrayBufferBytes(TurboModuleMethodValueKind returnType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -658,9 +658,8 @@ TraceSection s(
*/
NSString *ObjCTurboModule::getArgumentTypeName(jsi::Runtime &runtime, NSString *methodName, int argIndex)
{
if (methodArgumentTypeNames_ == nullptr) {
NSMutableDictionary<NSString *, NSArray<NSString *> *> *methodArgumentTypeNames = [NSMutableDictionary new];

std::lock_guard<std::mutex> lock(methodInfosMutex_);
if (!argumentTypeNamesLoaded_) {
unsigned int numberOfMethods;
Class cls = [instance_ class];
Method *methods = class_copyMethodList(object_getClass(cls), &numberOfMethods);
Expand All @@ -685,19 +684,21 @@ TraceSection s(
[argumentTypes addObject:arguments[j].type];
}

NSString *normalizedOtherMethodName = [otherMethodName componentsSeparatedByString:@":"][0];
methodArgumentTypeNames[normalizedOtherMethodName] = argumentTypes;
std::string_view otherMethodNameView = otherMethodName.UTF8String;
std::string_view normalizedOtherMethodName = otherMethodNameView.substr(0, otherMethodNameView.find(':'));
findOrCreateMethodInfo(normalizedOtherMethodName).argumentTypeNames = argumentTypes;
}

free(methods);
}

methodArgumentTypeNames_ = methodArgumentTypeNames;
argumentTypeNamesLoaded_ = true;
}

if (methodArgumentTypeNames_[methodName] != nullptr) {
assert([methodArgumentTypeNames_[methodName] count] > argIndex);
return methodArgumentTypeNames_[methodName][argIndex];
auto it = methodInfos_.find(std::string_view(methodName.UTF8String));
if (it != methodInfos_.end() && it->second.argumentTypeNames != nil) {
assert([it->second.argumentTypeNames count] > argIndex);
return it->second.argumentTypeNames[argIndex];
}

return nil;
Expand Down Expand Up @@ -820,6 +821,26 @@ TraceSection s(
}
}

ObjCTurboModule::MethodInfo &ObjCTurboModule::findOrCreateMethodInfo(std::string_view methodName)
{
auto it = methodInfos_.find(methodName);
if (it == methodInfos_.end()) {
it = methodInfos_.emplace(std::string(methodName), MethodInfo{}).first;
}
return it->second;
}

NSMethodSignature *ObjCTurboModule::getMethodSignature(const char *methodName, SEL selector)
{
std::lock_guard<std::mutex> lock(methodInfosMutex_);
MethodInfo &methodInfo = findOrCreateMethodInfo(methodName);
if (methodInfo.signature == nil) {
const NSObject<RCTBridgeModule> *module = instance_;
methodInfo.signature = [module methodSignatureForSelector:selector];
}
return methodInfo.signature;
}

NSInvocation *ObjCTurboModule::createMethodInvocation(
jsi::Runtime &runtime,
bool isSync,
Expand All @@ -837,8 +858,7 @@ TraceSection s(
TurboModulePerfLogger::asyncMethodCallArgConversionStart(moduleName, methodName);
}

const NSObject<RCTBridgeModule> *module = instance_;
NSMethodSignature *methodSignature = [module methodSignatureForSelector:selector];
NSMethodSignature *methodSignature = getMethodSignature(methodName, selector);
if (count > methodSignature.numberOfArguments - 2) {
throw jsi::JSError(
runtime,
Expand Down Expand Up @@ -964,36 +984,28 @@ TraceSection s(

BOOL ObjCTurboModule::hasMethodArgConversionSelector(NSString *methodName, size_t argIndex)
{
return (methodArgConversionSelectors_ != nullptr) && (methodArgConversionSelectors_[methodName] != nullptr) &&
![methodArgConversionSelectors_[methodName][argIndex] isEqual:(id)kCFNull];
return getMethodArgConversionSelector(methodName, argIndex) != nullptr;
}

SEL ObjCTurboModule::getMethodArgConversionSelector(NSString *methodName, size_t argIndex)
{
assert(hasMethodArgConversionSelector(methodName, argIndex));
return (SEL)((NSValue *)methodArgConversionSelectors_[methodName][argIndex]).pointerValue;
std::lock_guard<std::mutex> lock(methodInfosMutex_);
auto it = methodInfos_.find(std::string_view(methodName.UTF8String));
if (it == methodInfos_.end() || argIndex >= it->second.argConversionSelectors.size()) {
return nullptr;
}
return it->second.argConversionSelectors[argIndex];
}

void ObjCTurboModule::setMethodArgConversionSelector(NSString *methodName, size_t argIndex, NSString *fnName)
{
if (methodArgConversionSelectors_ == nullptr) {
methodArgConversionSelectors_ = [NSMutableDictionary new];
}

if (methodArgConversionSelectors_[methodName] == nullptr) {
auto metaData = methodMap_.at([methodName UTF8String]);
auto argCount = metaData.argCount;

methodArgConversionSelectors_[methodName] = [NSMutableArray arrayWithCapacity:argCount];
for (int i = 0; i < argCount; i += 1) {
[methodArgConversionSelectors_[methodName] addObject:(id)kCFNull];
}
const char *methodNameUTF8 = methodName.UTF8String;
std::lock_guard<std::mutex> lock(methodInfosMutex_);
std::vector<SEL> &selectors = findOrCreateMethodInfo(methodNameUTF8).argConversionSelectors;
if (selectors.empty()) {
selectors.resize(methodMap_.at(methodNameUTF8).argCount, nullptr);
}

SEL selector = NSSelectorFromString(fnName);
NSValue *selectorValue = [NSValue valueWithPointer:selector];

methodArgConversionSelectors_[methodName][argIndex] = selectorValue;
selectors.at(argIndex) = NSSelectorFromString(fnName);
}

void ObjCTurboModule::setEventEmitterCallback(EventEmitterCallback eventEmitterCallback)
Expand Down
Loading