1*cde16245SBen Roth// Copyright 2015-present 650 Industries. All rights reserved.
2*cde16245SBen Roth
3*cde16245SBen Roth#import "EXNativeModuleIntrospection.h"
4*cde16245SBen Roth
5*cde16245SBen Roth#import <React/RCTBridge.h>
6*cde16245SBen Roth#import <React/RCTBridgeMethod.h>
7*cde16245SBen Roth#import <React/RCTModuleData.h>
8*cde16245SBen Roth
9*cde16245SBen Roth@implementation EXNativeModuleIntrospection
10*cde16245SBen Roth
11*cde16245SBen Roth@synthesize bridge = _bridge;
12*cde16245SBen Roth
13*cde16245SBen RothRCT_EXPORT_MODULE(ExpoNativeModuleIntrospection)
14*cde16245SBen Roth
15*cde16245SBen RothRCT_REMAP_METHOD(getNativeModuleNamesAsync,
16*cde16245SBen Roth                 nativeModuleNamesWithResolver:(RCTPromiseResolveBlock)resolve
17*cde16245SBen Roth                 rejecter:(RCTPromiseRejectBlock)reject) {
18*cde16245SBen Roth  NSError *error;
19*cde16245SBen Roth  NSDictionary<NSString *, RCTModuleData *> *moduleDataByName = [self _moduleDataFromBridge:&error];
20*cde16245SBen Roth  if (!moduleDataByName) {
21*cde16245SBen Roth    reject(error.domain, error.userInfo[NSLocalizedDescriptionKey], error);
22*cde16245SBen Roth    return;
23*cde16245SBen Roth  }
24*cde16245SBen Roth
25*cde16245SBen Roth  NSArray<NSString *> *moduleNames = moduleDataByName.allKeys;
26*cde16245SBen Roth  resolve(moduleNames);
27*cde16245SBen Roth}
28*cde16245SBen Roth
29*cde16245SBen RothRCT_REMAP_METHOD(introspectNativeModuleAsync,
30*cde16245SBen Roth                 introspectNativeModule:(NSString *)name
31*cde16245SBen Roth                 resolver:(RCTPromiseResolveBlock)resolve
32*cde16245SBen Roth                 rejecter:(RCTPromiseRejectBlock)reject) {
33*cde16245SBen Roth  NSError *error;
34*cde16245SBen Roth  NSDictionary<NSString *, RCTModuleData *> *moduleDataByName = [self _moduleDataFromBridge:&error];
35*cde16245SBen Roth  if (!moduleDataByName) {
36*cde16245SBen Roth    reject(error.domain, error.userInfo[NSLocalizedDescriptionKey], error);
37*cde16245SBen Roth    return;
38*cde16245SBen Roth  }
39*cde16245SBen Roth
40*cde16245SBen Roth  RCTModuleData *moduleData = moduleDataByName[name];
41*cde16245SBen Roth  if (!moduleData) {
42*cde16245SBen Roth    resolve(nil);
43*cde16245SBen Roth    return;
44*cde16245SBen Roth  }
45*cde16245SBen Roth
46*cde16245SBen Roth  NSArray<id<RCTBridgeMethod>> *moduleMethods = moduleData.methods;
47*cde16245SBen Roth  NSMutableDictionary<NSString *, NSDictionary<NSString *, id> *> *methodDescriptions = [NSMutableDictionary dictionaryWithCapacity:moduleMethods.count];
48*cde16245SBen Roth  for (id<RCTBridgeMethod> method in moduleMethods) {
49*cde16245SBen Roth    NSString *methodName = [NSString stringWithCString:method.JSMethodName encoding:NSASCIIStringEncoding];
50*cde16245SBen Roth    NSString *functionType = [NSString stringWithCString:RCTFunctionDescriptorFromType(method.functionType)
51*cde16245SBen Roth                                                encoding:NSASCIIStringEncoding];
52*cde16245SBen Roth    methodDescriptions[methodName] = @{ @"type": functionType };
53*cde16245SBen Roth  }
54*cde16245SBen Roth
55*cde16245SBen Roth  resolve(@{ @"methods": methodDescriptions });
56*cde16245SBen Roth}
57*cde16245SBen Roth
58*cde16245SBen Roth- (NSDictionary<NSString *, RCTModuleData *> *)_moduleDataFromBridge:(NSError **)error {
59*cde16245SBen Roth  RCTBridge *bridge = _bridge;
60*cde16245SBen Roth  if (![NSStringFromClass(bridge.class) isEqualToString:@"RCTCxxBridge"]) {
61*cde16245SBen Roth    if (error) {
62*cde16245SBen Roth      *error = [NSError errorWithDomain:@"E_NATIVEMODULEINTROSPECTION_INCOMPATIBLE_BRIDGE" code:0 userInfo:@{
63*cde16245SBen Roth         NSLocalizedDescriptionKey: @"Native module introspection is compatible only with the C++ bridge",
64*cde16245SBen Roth      }];
65*cde16245SBen Roth    }
66*cde16245SBen Roth    return nil;
67*cde16245SBen Roth  }
68*cde16245SBen Roth
69*cde16245SBen Roth  NSDictionary<NSString *, RCTModuleData *> *moduleDataByName;
70*cde16245SBen Roth  @try {
71*cde16245SBen Roth    moduleDataByName = [bridge valueForKey:@"_moduleDataByName"];
72*cde16245SBen Roth  }
73*cde16245SBen Roth  @catch (NSException *e) {
74*cde16245SBen Roth    if (![e.name isEqualToString:NSUndefinedKeyException]) {
75*cde16245SBen Roth      @throw;
76*cde16245SBen Roth    }
77*cde16245SBen Roth
78*cde16245SBen Roth    if (error) {
79*cde16245SBen Roth      NSString *variableName = e.userInfo[@"NSUnknownUserInfoKey"];
80*cde16245SBen Roth      *error = [NSError errorWithDomain:@"E_NATIVEMODULEINTROSPECTION_INCOMPATIBLE_BRIDGE" code:0 userInfo:@{
81*cde16245SBen Roth        NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Bridge does not define expected variable: %@", variableName],
82*cde16245SBen Roth      }];
83*cde16245SBen Roth    }
84*cde16245SBen Roth    return nil;
85*cde16245SBen Roth  }
86*cde16245SBen Roth
87*cde16245SBen Roth  return moduleDataByName;
88*cde16245SBen Roth}
89*cde16245SBen Roth
90*cde16245SBen Roth@end
91*cde16245SBen Roth
92