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