1/* 2 * Copyright (c) Meta Platforms, Inc. and affiliates. 3 * 4 * This source code is licensed under the MIT license found in the 5 * LICENSE file in the root directory of this source tree. 6 */ 7 8#import "ABI49_0_0RCTNativeModule.h" 9 10#import <Foundation/Foundation.h> 11#import <ABI49_0_0React/ABI49_0_0RCTBridge.h> 12#import <ABI49_0_0React/ABI49_0_0RCTBridgeMethod.h> 13#import <ABI49_0_0React/ABI49_0_0RCTBridgeModule.h> 14#import <ABI49_0_0React/ABI49_0_0RCTCxxUtils.h> 15#import <ABI49_0_0React/ABI49_0_0RCTFollyConvert.h> 16#import <ABI49_0_0React/ABI49_0_0RCTLog.h> 17#import <ABI49_0_0React/ABI49_0_0RCTProfile.h> 18#import <ABI49_0_0React/ABI49_0_0RCTUtils.h> 19#import <ABI49_0_0Reactperflogger/ABI49_0_0BridgeNativeModulePerfLogger.h> 20 21#ifdef WITH_FBSYSTRACE 22#include <fbsystrace.h> 23#endif 24 25namespace { 26enum SchedulingContext { Sync, Async }; 27} 28 29namespace ABI49_0_0facebook { 30namespace ABI49_0_0React { 31 32static MethodCallResult invokeInner( 33 ABI49_0_0RCTBridge *bridge, 34 ABI49_0_0RCTModuleData *moduleData, 35 unsigned int methodId, 36 const folly::dynamic ¶ms, 37 int callId, 38 SchedulingContext context); 39 40ABI49_0_0RCTNativeModule::ABI49_0_0RCTNativeModule(ABI49_0_0RCTBridge *bridge, ABI49_0_0RCTModuleData *moduleData) 41 : m_bridge(bridge), m_moduleData(moduleData) 42{ 43} 44 45std::string ABI49_0_0RCTNativeModule::getName() 46{ 47 return [m_moduleData.name UTF8String]; 48} 49 50std::string ABI49_0_0RCTNativeModule::getSyncMethodName(unsigned int methodId) 51{ 52 return m_moduleData.methods[methodId].JSMethodName; 53} 54 55std::vector<MethodDescriptor> ABI49_0_0RCTNativeModule::getMethods() 56{ 57 std::vector<MethodDescriptor> descs; 58 59 for (id<ABI49_0_0RCTBridgeMethod> method in m_moduleData.methods) { 60 descs.emplace_back(method.JSMethodName, ABI49_0_0RCTFunctionDescriptorFromType(method.functionType)); 61 } 62 63 return descs; 64} 65 66folly::dynamic ABI49_0_0RCTNativeModule::getConstants() 67{ 68 ABI49_0_0RCT_PROFILE_BEGIN_EVENT(ABI49_0_0RCTProfileTagAlways, @"[ABI49_0_0RCTNativeModule getConstants] moduleData.exportedConstants", nil); 69 NSDictionary *constants = m_moduleData.exportedConstants; 70 folly::dynamic ret = convertIdToFollyDynamic(constants); 71 ABI49_0_0RCT_PROFILE_END_EVENT(ABI49_0_0RCTProfileTagAlways, @""); 72 return ret; 73} 74 75void ABI49_0_0RCTNativeModule::invoke(unsigned int methodId, folly::dynamic &¶ms, int callId) 76{ 77 id<ABI49_0_0RCTBridgeMethod> method = m_moduleData.methods[methodId]; 78 if (method) { 79 ABI49_0_0RCT_PROFILE_BEGIN_EVENT( 80 ABI49_0_0RCTProfileTagAlways, 81 @"[ABI49_0_0RCTNativeModule invoke]", 82 @{@"method" : [NSString stringWithUTF8String:method.JSMethodName]}); 83 ABI49_0_0RCT_PROFILE_END_EVENT(ABI49_0_0RCTProfileTagAlways, @""); 84 } 85 86 const char *moduleName = [m_moduleData.name UTF8String]; 87 const char *methodName = m_moduleData.methods[methodId].JSMethodName; 88 89 dispatch_queue_t queue = m_moduleData.methodQueue; 90 const bool isSyncModule = queue == ABI49_0_0RCTJSThread; 91 92 if (isSyncModule) { 93 BridgeNativeModulePerfLogger::syncMethodCallStart(moduleName, methodName); 94 BridgeNativeModulePerfLogger::syncMethodCallArgConversionStart(moduleName, methodName); 95 } else { 96 BridgeNativeModulePerfLogger::asyncMethodCallStart(moduleName, methodName); 97 } 98 99 // capture by weak pointer so that we can safely use these variables in a callback 100 __weak ABI49_0_0RCTBridge *weakBridge = m_bridge; 101 __weak ABI49_0_0RCTModuleData *weakModuleData = m_moduleData; 102 // The BatchedBridge version of this buckets all the callbacks by thread, and 103 // queues one block on each. This is much simpler; we'll see how it goes and 104 // iterate. 105 dispatch_block_t block = [weakBridge, weakModuleData, methodId, params = std::move(params), callId, isSyncModule] { 106#ifdef WITH_FBSYSTRACE 107 if (callId != -1) { 108 fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", callId); 109 } 110#else 111 (void)(callId); 112#endif 113 @autoreleasepool { 114 invokeInner(weakBridge, weakModuleData, methodId, std::move(params), callId, isSyncModule ? Sync : Async); 115 } 116 }; 117 118 if (isSyncModule) { 119 block(); 120 BridgeNativeModulePerfLogger::syncMethodCallReturnConversionEnd(moduleName, methodName); 121 } else if (queue) { 122 BridgeNativeModulePerfLogger::asyncMethodCallDispatch(moduleName, methodName); 123 dispatch_async(queue, block); 124 } 125 126#ifdef ABI49_0_0RCT_DEV 127 if (!queue) { 128 ABI49_0_0RCTLog( 129 @"Attempted to invoke `%u` (method ID) on `%@` (NativeModule name) without a method queue.", 130 methodId, 131 m_moduleData.name); 132 } 133#endif 134 135 if (isSyncModule) { 136 BridgeNativeModulePerfLogger::syncMethodCallEnd(moduleName, methodName); 137 } else { 138 BridgeNativeModulePerfLogger::asyncMethodCallEnd(moduleName, methodName); 139 } 140} 141 142MethodCallResult ABI49_0_0RCTNativeModule::callSerializableNativeHook(unsigned int ABI49_0_0ReactMethodId, folly::dynamic &¶ms) 143{ 144 return invokeInner(m_bridge, m_moduleData, ABI49_0_0ReactMethodId, params, 0, Sync); 145} 146 147static MethodCallResult invokeInner( 148 ABI49_0_0RCTBridge *bridge, 149 ABI49_0_0RCTModuleData *moduleData, 150 unsigned int methodId, 151 const folly::dynamic ¶ms, 152 int callId, 153 SchedulingContext context) 154{ 155 if (!bridge || !bridge.valid || !moduleData) { 156 if (context == Sync) { 157 /** 158 * NOTE: moduleName and methodName are "". This shouldn't be an issue because there can only be one ongoing sync 159 * call at a time, and when we call syncMethodCallFail, that one call should terminate. This is also an 160 * exceptional scenario, so it shouldn't occur often. 161 */ 162 BridgeNativeModulePerfLogger::syncMethodCallFail("N/A", "N/A"); 163 } 164 return std::nullopt; 165 } 166 167 id<ABI49_0_0RCTBridgeMethod> method = moduleData.methods[methodId]; 168 if (ABI49_0_0RCT_DEBUG && !method) { 169 ABI49_0_0RCTLogError(@"Unknown methodID: %ud for module: %@", methodId, moduleData.name); 170 } 171 172 const char *moduleName = [moduleData.name UTF8String]; 173 const char *methodName = moduleData.methods[methodId].JSMethodName; 174 175 if (context == Async) { 176 BridgeNativeModulePerfLogger::asyncMethodCallExecutionStart(moduleName, methodName, (int32_t)callId); 177 BridgeNativeModulePerfLogger::asyncMethodCallExecutionArgConversionStart(moduleName, methodName, (int32_t)callId); 178 } 179 180 NSArray *objcParams = convertFollyDynamicToId(params); 181 182 if (context == Sync) { 183 BridgeNativeModulePerfLogger::syncMethodCallArgConversionEnd(moduleName, methodName); 184 } 185 186 ABI49_0_0RCT_PROFILE_BEGIN_EVENT( 187 ABI49_0_0RCTProfileTagAlways, 188 @"[ABI49_0_0RCTNativeModule invokeInner]", 189 @{@"method" : [NSString stringWithUTF8String:method.JSMethodName]}); 190 @try { 191 if (context == Sync) { 192 BridgeNativeModulePerfLogger::syncMethodCallExecutionStart(moduleName, methodName); 193 } else { 194 BridgeNativeModulePerfLogger::asyncMethodCallExecutionArgConversionEnd(moduleName, methodName, (int32_t)callId); 195 } 196 197 id result = [method invokeWithBridge:bridge module:moduleData.instance arguments:objcParams]; 198 199 if (context == Sync) { 200 BridgeNativeModulePerfLogger::syncMethodCallExecutionEnd(moduleName, methodName); 201 BridgeNativeModulePerfLogger::syncMethodCallReturnConversionStart(moduleName, methodName); 202 } else { 203 BridgeNativeModulePerfLogger::asyncMethodCallExecutionEnd(moduleName, methodName, (int32_t)callId); 204 } 205 206 return convertIdToFollyDynamic(result); 207 } @catch (NSException *exception) { 208 if (context == Sync) { 209 BridgeNativeModulePerfLogger::syncMethodCallFail(moduleName, methodName); 210 } else { 211 BridgeNativeModulePerfLogger::asyncMethodCallExecutionFail(moduleName, methodName, (int32_t)callId); 212 } 213 214 // Pass on JS exceptions 215 if ([exception.name hasPrefix:ABI49_0_0RCTFatalExceptionName]) { 216 @throw exception; 217 } 218 219#if ABI49_0_0RCT_DEBUG 220 NSString *message = [NSString 221 stringWithFormat:@"Exception '%@' was thrown while invoking %s on target %@ with params %@\ncallstack: %@", 222 exception, 223 method.JSMethodName, 224 moduleData.name, 225 objcParams, 226 exception.callStackSymbols]; 227 ABI49_0_0RCTFatal(ABI49_0_0RCTErrorWithMessage(message)); 228#else 229 ABI49_0_0RCTFatalException(exception); 230#endif 231 } @finally { 232 ABI49_0_0RCT_PROFILE_END_EVENT(ABI49_0_0RCTProfileTagAlways, @""); 233 } 234 235 return std::nullopt; 236} 237 238} 239} 240