/* * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #import "RCTNativeModule.h" #import #import #import #import #import #import #import #import #ifdef WITH_FBSYSTRACE #include #endif namespace facebook { namespace react { static MethodCallResult invokeInner(RCTBridge *bridge, RCTModuleData *moduleData, unsigned int methodId, const folly::dynamic ¶ms); RCTNativeModule::RCTNativeModule(RCTBridge *bridge, RCTModuleData *moduleData) : m_bridge(bridge), m_moduleData(moduleData) { } std::string RCTNativeModule::getName() { return [m_moduleData.name UTF8String]; } std::vector RCTNativeModule::getMethods() { std::vector descs; for (id method in m_moduleData.methods) { descs.emplace_back(method.JSMethodName, RCTFunctionDescriptorFromType(method.functionType)); } return descs; } folly::dynamic RCTNativeModule::getConstants() { RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"[RCTNativeModule getConstants] moduleData.exportedConstants", nil); NSDictionary *constants = m_moduleData.exportedConstants; folly::dynamic ret = convertIdToFollyDynamic(constants); RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @""); return ret; } void RCTNativeModule::invoke(unsigned int methodId, folly::dynamic &¶ms, int callId) { // capture by weak pointer so that we can safely use these variables in a callback __weak RCTBridge *weakBridge = m_bridge; __weak RCTModuleData *weakModuleData = m_moduleData; // The BatchedBridge version of this buckets all the callbacks by thread, and // queues one block on each. This is much simpler; we'll see how it goes and // iterate. dispatch_block_t block = [weakBridge, weakModuleData, methodId, params = std::move(params), callId] { #ifdef WITH_FBSYSTRACE if (callId != -1) { fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", callId); } #else (void)(callId); #endif invokeInner(weakBridge, weakModuleData, methodId, std::move(params)); }; dispatch_queue_t queue = m_moduleData.methodQueue; if (queue == RCTJSThread) { block(); } else if (queue) { dispatch_async(queue, block); } #ifdef RCT_DEV if (!queue) { RCTLog( @"Attempted to invoke `%u` (method ID) on `%@` (NativeModule name) without a method queue.", methodId, m_moduleData.name); } #endif } MethodCallResult RCTNativeModule::callSerializableNativeHook(unsigned int reactMethodId, folly::dynamic &¶ms) { return invokeInner(m_bridge, m_moduleData, reactMethodId, params); } static MethodCallResult invokeInner(RCTBridge *bridge, RCTModuleData *moduleData, unsigned int methodId, const folly::dynamic ¶ms) { if (!bridge || !bridge.valid || !moduleData) { return folly::none; } id method = moduleData.methods[methodId]; if (RCT_DEBUG && !method) { RCTLogError(@"Unknown methodID: %ud for module: %@", methodId, moduleData.name); } NSArray *objcParams = convertFollyDynamicToId(params); @try { id result = [method invokeWithBridge:bridge module:moduleData.instance arguments:objcParams]; return convertIdToFollyDynamic(result); } @catch (NSException *exception) { // Pass on JS exceptions if ([exception.name hasPrefix:RCTFatalExceptionName]) { @throw exception; } #if RCT_DEBUG NSString *message = [NSString stringWithFormat:@"Exception '%@' was thrown while invoking %s on target %@ with params %@\ncallstack: %@", exception, method.JSMethodName, moduleData.name, objcParams, exception.callStackSymbols]; RCTFatal(RCTErrorWithMessage(message)); #else RCTFatalException(exception); #endif } return folly::none; } } }