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