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_0RCTModuleMethod.h" 9 10#import <objc/message.h> 11 12#import "ABI48_0_0RCTAssert.h" 13#import "ABI48_0_0RCTBridge+Private.h" 14#import "ABI48_0_0RCTBridge.h" 15#import "ABI48_0_0RCTConvert.h" 16#import "ABI48_0_0RCTCxxConvert.h" 17#import "ABI48_0_0RCTLog.h" 18#import "ABI48_0_0RCTManagedPointer.h" 19#import "ABI48_0_0RCTParserUtils.h" 20#import "ABI48_0_0RCTProfile.h" 21#import "ABI48_0_0RCTUtils.h" 22 23typedef BOOL (^ABI48_0_0RCTArgumentBlock)(ABI48_0_0RCTBridge *, NSUInteger, id); 24 25/** 26 * Get the converter function for the specified type 27 */ 28static SEL selectorForType(NSString *type) 29{ 30 const char *input = type.UTF8String; 31 return NSSelectorFromString([ABI48_0_0RCTParseType(&input) stringByAppendingString:@":"]); 32} 33 34@implementation ABI48_0_0RCTMethodArgument 35 36- (instancetype)initWithType:(NSString *)type nullability:(ABI48_0_0RCTNullability)nullability unused:(BOOL)unused 37{ 38 if (self = [super init]) { 39 _type = [type copy]; 40 _nullability = nullability; 41 _unused = unused; 42 } 43 return self; 44} 45 46@end 47 48@implementation ABI48_0_0RCTModuleMethod { 49 Class _moduleClass; 50 const ABI48_0_0RCTMethodInfo *_methodInfo; 51 NSString *_JSMethodName; 52 53 SEL _selector; 54 NSInvocation *_invocation; 55 NSArray<ABI48_0_0RCTArgumentBlock> *_argumentBlocks; 56 NSMutableArray *_retainedObjects; 57} 58 59static void ABI48_0_0RCTLogArgumentError(ABI48_0_0RCTModuleMethod *method, NSUInteger index, id valueOrType, const char *issue) 60{ 61 ABI48_0_0RCTLogError( 62 @"Argument %tu (%@) of %@.%s %s", 63 index, 64 valueOrType, 65 ABI48_0_0RCTBridgeModuleNameForClass(method->_moduleClass), 66 method.JSMethodName, 67 issue); 68} 69 70ABI48_0_0RCT_NOT_IMPLEMENTED(-(instancetype)init) 71 72ABI48_0_0RCT_EXTERN_C_BEGIN 73 74// returns YES if the selector ends in a colon (indicating that there is at 75// least one argument, and maybe more selector parts) or NO if it doesn't. 76static BOOL ABI48_0_0RCTParseSelectorPart(const char **input, NSMutableString *selector) 77{ 78 NSString *selectorPart; 79 if (ABI48_0_0RCTParseSelectorIdentifier(input, &selectorPart)) { 80 [selector appendString:selectorPart]; 81 } 82 ABI48_0_0RCTSkipWhitespace(input); 83 if (ABI48_0_0RCTReadChar(input, ':')) { 84 [selector appendString:@":"]; 85 ABI48_0_0RCTSkipWhitespace(input); 86 return YES; 87 } 88 return NO; 89} 90 91static BOOL ABI48_0_0RCTParseUnused(const char **input) 92{ 93 return ABI48_0_0RCTReadString(input, "__attribute__((unused))") || ABI48_0_0RCTReadString(input, "__attribute__((__unused__))") || 94 ABI48_0_0RCTReadString(input, "__unused") || ABI48_0_0RCTReadString(input, "[[maybe_unused]]"); 95} 96 97static ABI48_0_0RCTNullability ABI48_0_0RCTParseNullability(const char **input) 98{ 99 if (ABI48_0_0RCTReadString(input, "nullable")) { 100 return ABI48_0_0RCTNullable; 101 } else if (ABI48_0_0RCTReadString(input, "nonnull")) { 102 return ABI48_0_0RCTNonnullable; 103 } 104 return ABI48_0_0RCTNullabilityUnspecified; 105} 106 107static ABI48_0_0RCTNullability ABI48_0_0RCTParseNullabilityPostfix(const char **input) 108{ 109 if (ABI48_0_0RCTReadString(input, "_Nullable") || ABI48_0_0RCTReadString(input, "__nullable")) { 110 return ABI48_0_0RCTNullable; 111 } else if (ABI48_0_0RCTReadString(input, "_Nonnull") || ABI48_0_0RCTReadString(input, "__nonnull")) { 112 return ABI48_0_0RCTNonnullable; 113 } 114 return ABI48_0_0RCTNullabilityUnspecified; 115} 116 117// returns YES if execution is safe to proceed (enqueue callback invocation), NO if callback has already been invoked 118#if ABI48_0_0RCT_DEBUG 119static BOOL checkCallbackMultipleInvocations(BOOL *didInvoke) 120{ 121 if (*didInvoke) { 122 ABI48_0_0RCTFatal(ABI48_0_0RCTErrorWithMessage( 123 @"Illegal callback invocation from native module. This callback type only permits a single invocation from native code.")); 124 return NO; 125 } else { 126 *didInvoke = YES; 127 return YES; 128 } 129} 130#endif 131 132NSString *ABI48_0_0RCTParseMethodSignature(const char *input, NSArray<ABI48_0_0RCTMethodArgument *> **arguments) 133{ 134 ABI48_0_0RCTSkipWhitespace(&input); 135 136 NSMutableArray *args; 137 NSMutableString *selector = [NSMutableString new]; 138 while (ABI48_0_0RCTParseSelectorPart(&input, selector)) { 139 if (!args) { 140 args = [NSMutableArray new]; 141 } 142 143 // Parse type 144 if (ABI48_0_0RCTReadChar(&input, '(')) { 145 ABI48_0_0RCTSkipWhitespace(&input); 146 147 // 5 cases that both nullable and __unused exist 148 // 1: foo:(nullable __unused id)foo 2: foo:(nullable id __unused)foo 149 // 3: foo:(__unused id _Nullable)foo 4: foo:(id __unused _Nullable)foo 150 // 5: foo:(id _Nullable __unused)foo 151 ABI48_0_0RCTNullability nullability = ABI48_0_0RCTParseNullability(&input); 152 ABI48_0_0RCTSkipWhitespace(&input); 153 154 BOOL unused = ABI48_0_0RCTParseUnused(&input); 155 ABI48_0_0RCTSkipWhitespace(&input); 156 157 NSString *type = ABI48_0_0RCTParseType(&input); 158 ABI48_0_0RCTSkipWhitespace(&input); 159 160 if (nullability == ABI48_0_0RCTNullabilityUnspecified) { 161 nullability = ABI48_0_0RCTParseNullabilityPostfix(&input); 162 ABI48_0_0RCTSkipWhitespace(&input); 163 if (!unused) { 164 unused = ABI48_0_0RCTParseUnused(&input); 165 ABI48_0_0RCTSkipWhitespace(&input); 166 if (unused && nullability == ABI48_0_0RCTNullabilityUnspecified) { 167 nullability = ABI48_0_0RCTParseNullabilityPostfix(&input); 168 ABI48_0_0RCTSkipWhitespace(&input); 169 } 170 } 171 } else if (!unused) { 172 unused = ABI48_0_0RCTParseUnused(&input); 173 ABI48_0_0RCTSkipWhitespace(&input); 174 } 175 [args addObject:[[ABI48_0_0RCTMethodArgument alloc] initWithType:type nullability:nullability unused:unused]]; 176 ABI48_0_0RCTSkipWhitespace(&input); 177 ABI48_0_0RCTReadChar(&input, ')'); 178 ABI48_0_0RCTSkipWhitespace(&input); 179 } else { 180 // Type defaults to id if unspecified 181 [args addObject:[[ABI48_0_0RCTMethodArgument alloc] initWithType:@"id" nullability:ABI48_0_0RCTNullable unused:NO]]; 182 } 183 184 // Argument name 185 ABI48_0_0RCTParseArgumentIdentifier(&input, NULL); 186 ABI48_0_0RCTSkipWhitespace(&input); 187 } 188 189 *arguments = [args copy]; 190 return selector; 191} 192 193ABI48_0_0RCT_EXTERN_C_END 194 195- (instancetype)initWithExportedMethod:(const ABI48_0_0RCTMethodInfo *)exportedMethod moduleClass:(Class)moduleClass 196{ 197 if (self = [super init]) { 198 _moduleClass = moduleClass; 199 _methodInfo = exportedMethod; 200 } 201 return self; 202} 203 204- (void)processMethodSignature 205{ 206 NSArray<ABI48_0_0RCTMethodArgument *> *arguments; 207 _selector = NSSelectorFromString(ABI48_0_0RCTParseMethodSignature(_methodInfo->objcName, &arguments)); 208 ABI48_0_0RCTAssert(_selector, @"%s is not a valid selector", _methodInfo->objcName); 209 210 // Create method invocation 211 NSMethodSignature *methodSignature = [_moduleClass instanceMethodSignatureForSelector:_selector]; 212 ABI48_0_0RCTAssert(methodSignature, @"%s is not a recognized Objective-C method.", sel_getName(_selector)); 213 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; 214 invocation.selector = _selector; 215 _invocation = invocation; 216 NSMutableArray *retainedObjects = [NSMutableArray array]; 217 _retainedObjects = retainedObjects; 218 219 // Process arguments 220 NSUInteger numberOfArguments = methodSignature.numberOfArguments; 221 NSMutableArray<ABI48_0_0RCTArgumentBlock> *argumentBlocks = [[NSMutableArray alloc] initWithCapacity:numberOfArguments - 2]; 222 223#if ABI48_0_0RCT_DEBUG 224 __weak ABI48_0_0RCTModuleMethod *weakSelf = self; 225#endif 226 227#define ABI48_0_0RCT_RETAINED_ARG_BLOCK(_logic) \ 228 [argumentBlocks addObject:^(__unused __weak ABI48_0_0RCTBridge * bridge, NSUInteger index, id json) { \ 229 _logic [invocation setArgument:&value atIndex:(index) + 2]; \ 230 if (value) { \ 231 [retainedObjects addObject:value]; \ 232 } \ 233 return YES; \ 234 }] 235 236#define __PRIMITIVE_CASE(_type, _nullable) \ 237 { \ 238 isNullableType = _nullable; \ 239 _type (*convert)(id, SEL, id) = (__typeof__(convert))objc_msgSend; \ 240 [argumentBlocks addObject:^(__unused ABI48_0_0RCTBridge * bridge, NSUInteger index, id json) { \ 241 _type value = convert([ABI48_0_0RCTConvert class], selector, json); \ 242 [invocation setArgument:&value atIndex:(index) + 2]; \ 243 return YES; \ 244 }]; \ 245 break; \ 246 } 247 248#define PRIMITIVE_CASE(_type) __PRIMITIVE_CASE(_type, NO) 249#define NULLABLE_PRIMITIVE_CASE(_type) __PRIMITIVE_CASE(_type, YES) 250 251// Explicitly copy the block 252#define __COPY_BLOCK(block...) \ 253 id value = [block copy]; \ 254 if (value) { \ 255 [retainedObjects addObject:value]; \ 256 } 257 258#if ABI48_0_0RCT_DEBUG 259#define BLOCK_CASE(_block_args, _block) \ 260 ABI48_0_0RCT_RETAINED_ARG_BLOCK(if (json && ![json isKindOfClass:[NSNumber class]]) { \ 261 ABI48_0_0RCTLogArgumentError(weakSelf, index, json, "should be a function"); \ 262 return NO; \ 263 } __block BOOL didInvoke = NO; \ 264 __COPY_BLOCK(^_block_args { \ 265 if (checkCallbackMultipleInvocations(&didInvoke)) \ 266 _block \ 267 });) 268#else 269#define BLOCK_CASE(_block_args, _block) \ 270 ABI48_0_0RCT_RETAINED_ARG_BLOCK(__COPY_BLOCK(^_block_args{ \ 271 _block});) 272#endif 273 274 for (NSUInteger i = 2; i < numberOfArguments; i++) { 275 const char *objcType = [methodSignature getArgumentTypeAtIndex:i]; 276 BOOL isNullableType = NO; 277 ABI48_0_0RCTMethodArgument *argument = arguments[i - 2]; 278 NSString *typeName = argument.type; 279 SEL selector = selectorForType(typeName); 280 if ([ABI48_0_0RCTConvert respondsToSelector:selector]) { 281 switch (objcType[0]) { 282 // Primitives 283 case _C_CHR: 284 PRIMITIVE_CASE(char) 285 case _C_UCHR: 286 PRIMITIVE_CASE(unsigned char) 287 case _C_SHT: 288 PRIMITIVE_CASE(short) 289 case _C_USHT: 290 PRIMITIVE_CASE(unsigned short) 291 case _C_INT: 292 PRIMITIVE_CASE(int) 293 case _C_UINT: 294 PRIMITIVE_CASE(unsigned int) 295 case _C_LNG: 296 PRIMITIVE_CASE(long) 297 case _C_ULNG: 298 PRIMITIVE_CASE(unsigned long) 299 case _C_LNG_LNG: 300 PRIMITIVE_CASE(long long) 301 case _C_ULNG_LNG: 302 PRIMITIVE_CASE(unsigned long long) 303 case _C_FLT: 304 PRIMITIVE_CASE(float) 305 case _C_DBL: 306 PRIMITIVE_CASE(double) 307 case _C_BOOL: 308 PRIMITIVE_CASE(BOOL) 309 case _C_SEL: 310 NULLABLE_PRIMITIVE_CASE(SEL) 311 case _C_CHARPTR: 312 NULLABLE_PRIMITIVE_CASE(const char *) 313 case _C_PTR: 314 NULLABLE_PRIMITIVE_CASE(void *) 315 316 case _C_ID: { 317 isNullableType = YES; 318 id (*convert)(id, SEL, id) = (__typeof__(convert))objc_msgSend; 319 ABI48_0_0RCT_RETAINED_ARG_BLOCK(id value = convert([ABI48_0_0RCTConvert class], selector, json);); 320 break; 321 } 322 323 case _C_STRUCT_B: { 324 NSMethodSignature *typeSignature = [ABI48_0_0RCTConvert methodSignatureForSelector:selector]; 325 NSInvocation *typeInvocation = [NSInvocation invocationWithMethodSignature:typeSignature]; 326 typeInvocation.selector = selector; 327 typeInvocation.target = [ABI48_0_0RCTConvert class]; 328 329 [argumentBlocks addObject:^(__unused ABI48_0_0RCTBridge *bridge, NSUInteger index, id json) { 330 void *returnValue = malloc(typeSignature.methodReturnLength); 331 if (!returnValue) { 332 // CWE - 391 : Unchecked error condition 333 // https://www.cvedetails.com/cwe-details/391/Unchecked-Error-Condition.html 334 // https://eli.thegreenplace.net/2009/10/30/handling-out-of-memory-conditions-in-c 335 abort(); 336 } 337 [typeInvocation setArgument:&json atIndex:2]; 338 [typeInvocation invoke]; 339 [typeInvocation getReturnValue:returnValue]; 340 [invocation setArgument:returnValue atIndex:index + 2]; 341 free(returnValue); 342 return YES; 343 }]; 344 break; 345 } 346 347 default: { 348 static const char *blockType = @encode(__typeof__(^{ 349 })); 350 if (!strcmp(objcType, blockType)) { 351 BLOCK_CASE((NSArray * args), { [bridge enqueueCallback:json args:args]; }); 352 } else { 353 ABI48_0_0RCTLogError(@"Unsupported argument type '%@' in method %@.", typeName, [self methodName]); 354 } 355 } 356 } 357 } else if ([typeName isEqualToString:@"ABI48_0_0RCTResponseSenderBlock"]) { 358 BLOCK_CASE((NSArray * args), { [bridge enqueueCallback:json args:args]; }); 359 } else if ([typeName isEqualToString:@"ABI48_0_0RCTResponseErrorBlock"]) { 360 BLOCK_CASE((NSError * error), { [bridge enqueueCallback:json args:@[ ABI48_0_0RCTJSErrorFromNSError(error) ]]; }); 361 } else if ([typeName isEqualToString:@"ABI48_0_0RCTPromiseResolveBlock"]) { 362 ABI48_0_0RCTAssert( 363 i == numberOfArguments - 2, 364 @"The ABI48_0_0RCTPromiseResolveBlock must be the second to last parameter in %@", 365 [self methodName]); 366 BLOCK_CASE((id result), { [bridge enqueueCallback:json args:result ? @[ result ] : @[]]; }); 367 } else if ([typeName isEqualToString:@"ABI48_0_0RCTPromiseRejectBlock"]) { 368 ABI48_0_0RCTAssert( 369 i == numberOfArguments - 1, @"The ABI48_0_0RCTPromiseRejectBlock must be the last parameter in %@", [self methodName]); 370 BLOCK_CASE((NSString * code, NSString * message, NSError * error), { 371 NSDictionary *errorJSON = ABI48_0_0RCTJSErrorFromCodeMessageAndNSError(code, message, error); 372 [bridge enqueueCallback:json args:@[ errorJSON ]]; 373 }); 374 } else if ([typeName hasPrefix:@"ABI48_0_0JS::"]) { 375 NSString *selectorNameForCxxType = 376 [[typeName stringByReplacingOccurrencesOfString:@"::" withString:@"_"] stringByAppendingString:@":"]; 377 selector = NSSelectorFromString(selectorNameForCxxType); 378 379 [argumentBlocks addObject:^(__unused ABI48_0_0RCTBridge *bridge, NSUInteger index, id json) { 380 ABI48_0_0RCTManagedPointer *(*convert)(id, SEL, id) = (__typeof__(convert))objc_msgSend; 381 ABI48_0_0RCTManagedPointer *box = convert([ABI48_0_0RCTCxxConvert class], selector, json); 382 383 void *pointer = box.voidPointer; 384 [invocation setArgument:&pointer atIndex:index + 2]; 385 [retainedObjects addObject:box]; 386 387 return YES; 388 }]; 389 } else { 390 // Unknown argument type 391 ABI48_0_0RCTLogError( 392 @"Unknown argument type '%@' in method %@. Extend ABI48_0_0RCTConvert to support this type.", 393 typeName, 394 [self methodName]); 395 } 396 397#if ABI48_0_0RCT_DEBUG 398 ABI48_0_0RCTNullability nullability = argument.nullability; 399 if (!isNullableType) { 400 if (nullability == ABI48_0_0RCTNullable) { 401 ABI48_0_0RCTLogArgumentError( 402 weakSelf, 403 i - 2, 404 typeName, 405 "is marked as " 406 "nullable, but is not a nullable type."); 407 } 408 nullability = ABI48_0_0RCTNonnullable; 409 } 410 411 /** 412 * Special case - Numbers are not nullable in Android, so we 413 * don't support this for now. In future we may allow it. 414 */ 415 if ([typeName isEqualToString:@"NSNumber"]) { 416 BOOL unspecified = (nullability == ABI48_0_0RCTNullabilityUnspecified); 417 if (!argument.unused && (nullability == ABI48_0_0RCTNullable || unspecified)) { 418 ABI48_0_0RCTLogArgumentError( 419 weakSelf, 420 i - 2, 421 typeName, 422 [unspecified ? @"has unspecified nullability" : @"is marked as nullable" 423 stringByAppendingString:@" but ABI48_0_0React requires that all NSNumber " 424 "arguments are explicitly marked as `nonnull` to ensure " 425 "compatibility with Android."] 426 .UTF8String); 427 } 428 nullability = ABI48_0_0RCTNonnullable; 429 } 430 431 if (nullability == ABI48_0_0RCTNonnullable) { 432 ABI48_0_0RCTArgumentBlock oldBlock = argumentBlocks[i - 2]; 433 argumentBlocks[i - 2] = ^(ABI48_0_0RCTBridge *bridge, NSUInteger index, id json) { 434 if (json != nil) { 435 if (!oldBlock(bridge, index, json)) { 436 return NO; 437 } 438 if (isNullableType) { 439 // Check converted value wasn't null either, as method probably 440 // won't gracefully handle a nil value for a nonull argument 441 void *value; 442 [invocation getArgument:&value atIndex:index + 2]; 443 if (value == NULL) { 444 return NO; 445 } 446 } 447 return YES; 448 } 449 ABI48_0_0RCTLogArgumentError(weakSelf, index, typeName, "must not be null"); 450 return NO; 451 }; 452 } 453#endif 454 } 455 456#if ABI48_0_0RCT_DEBUG 457 const char *objcType = _invocation.methodSignature.methodReturnType; 458 if (_methodInfo->isSync && objcType[0] != _C_ID) { 459 ABI48_0_0RCTLogError( 460 @"Return type of %@.%s should be (id) as the method is \"sync\"", 461 ABI48_0_0RCTBridgeModuleNameForClass(_moduleClass), 462 self.JSMethodName); 463 } 464#endif 465 466 _argumentBlocks = argumentBlocks; 467} 468 469- (SEL)selector 470{ 471 if (_selector == NULL) { 472 ABI48_0_0RCT_PROFILE_BEGIN_EVENT( 473 ABI48_0_0RCTProfileTagAlways, 474 @"", 475 (@{@"module" : NSStringFromClass(_moduleClass), @"method" : @(_methodInfo->objcName)})); 476 [self processMethodSignature]; 477 ABI48_0_0RCT_PROFILE_END_EVENT(ABI48_0_0RCTProfileTagAlways, @""); 478 } 479 return _selector; 480} 481 482- (const char *)JSMethodName 483{ 484 NSString *methodName = _JSMethodName; 485 if (!methodName) { 486 const char *jsName = _methodInfo->jsName; 487 if (jsName && strlen(jsName) > 0) { 488 methodName = @(jsName); 489 } else { 490 methodName = @(_methodInfo->objcName); 491 NSRange colonRange = [methodName rangeOfString:@":"]; 492 if (colonRange.location != NSNotFound) { 493 methodName = [methodName substringToIndex:colonRange.location]; 494 } 495 methodName = [methodName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 496 ABI48_0_0RCTAssert( 497 methodName.length, 498 @"%s is not a valid JS function name, please" 499 " supply an alternative using ABI48_0_0RCT_REMAP_METHOD()", 500 _methodInfo->objcName); 501 } 502 _JSMethodName = methodName; 503 } 504 return methodName.UTF8String; 505} 506 507- (ABI48_0_0RCTFunctionType)functionType 508{ 509 if (strstr(_methodInfo->objcName, "ABI48_0_0RCTPromise") != NULL) { 510 ABI48_0_0RCTAssert(!_methodInfo->isSync, @"Promises cannot be used in sync functions"); 511 return ABI48_0_0RCTFunctionTypePromise; 512 } else if (_methodInfo->isSync) { 513 return ABI48_0_0RCTFunctionTypeSync; 514 } else { 515 return ABI48_0_0RCTFunctionTypeNormal; 516 } 517} 518 519- (id)invokeWithBridge:(ABI48_0_0RCTBridge *)bridge module:(id)module arguments:(NSArray *)arguments 520{ 521 if (_argumentBlocks == nil) { 522 [self processMethodSignature]; 523 } 524 525#if ABI48_0_0RCT_DEBUG 526 // Sanity check 527 ABI48_0_0RCTAssert([module class] == _moduleClass, @"Attempted to invoke method \ 528 %@ on a module of class %@", [self methodName], [module class]); 529 530 // Safety check 531 if (arguments.count != _argumentBlocks.count) { 532 NSInteger actualCount = arguments.count; 533 NSInteger expectedCount = _argumentBlocks.count; 534 535 // Subtract the implicit Promise resolver and rejecter functions for implementations of async functions 536 if (self.functionType == ABI48_0_0RCTFunctionTypePromise) { 537 actualCount -= 2; 538 expectedCount -= 2; 539 } 540 541 ABI48_0_0RCTLogError( 542 @"%@.%s was called with %lld arguments but expects %lld arguments. " 543 @"If you haven\'t changed this method yourself, this usually means that " 544 @"your versions of the native code and JavaScript code are out of sync. " 545 @"Updating both should make this error go away.", 546 ABI48_0_0RCTBridgeModuleNameForClass(_moduleClass), 547 self.JSMethodName, 548 (long long)actualCount, 549 (long long)expectedCount); 550 return nil; 551 } 552#endif 553 554 // Set arguments 555 NSUInteger index = 0; 556 for (id json in arguments) { 557 ABI48_0_0RCTArgumentBlock block = _argumentBlocks[index]; 558 if (!block(bridge, index, ABI48_0_0RCTNilIfNull(json))) { 559 // Invalid argument, abort 560 ABI48_0_0RCTLogArgumentError(self, index, json, "could not be processed. Aborting method call."); 561 return nil; 562 } 563 index++; 564 } 565 566 // Invoke method 567#ifdef ABI48_0_0RCT_MAIN_THREAD_WATCH_DOG_THRESHOLD 568 if (ABI48_0_0RCTIsMainQueue()) { 569 CFTimeInterval start = CACurrentMediaTime(); 570 [_invocation invokeWithTarget:module]; 571 CFTimeInterval duration = CACurrentMediaTime() - start; 572 if (duration > ABI48_0_0RCT_MAIN_THREAD_WATCH_DOG_THRESHOLD) { 573 ABI48_0_0RCTLogWarn( 574 @"Main Thread Watchdog: Invocation of %@ blocked the main thread for %dms. " 575 "Consider using background-threaded modules and asynchronous calls " 576 "to spend less time on the main thread and keep the app's UI responsive.", 577 [self methodName], 578 (int)(duration * 1000)); 579 } 580 } else { 581 [_invocation invokeWithTarget:module]; 582 } 583#else 584 [_invocation invokeWithTarget:module]; 585#endif 586 587 [_retainedObjects removeAllObjects]; 588 589 if (_methodInfo->isSync) { 590 void *returnValue; 591 [_invocation getReturnValue:&returnValue]; 592 return (__bridge id)returnValue; 593 } 594 return nil; 595} 596 597- (NSString *)methodName 598{ 599 if (!_selector) { 600 [self processMethodSignature]; 601 } 602 return [NSString stringWithFormat:@"-[%@ %s]", _moduleClass, sel_getName(_selector)]; 603} 604 605- (NSString *)description 606{ 607 return [NSString stringWithFormat:@"<%@: %p; exports %@ as %s(); type: %s>", 608 [self class], 609 self, 610 [self methodName], 611 self.JSMethodName, 612 ABI48_0_0RCTFunctionDescriptorFromType(self.functionType)]; 613} 614 615@end 616