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