1// Copyright © 2018 650 Industries. All rights reserved.
2
3#import <objc/runtime.h>
4
5#import <ExpoModulesCore/EXModuleRegistry.h>
6#import <ExpoModulesCore/EXModuleRegistryConsumer.h>
7
8@interface EXModuleRegistry ()
9
10@property (nonatomic, weak) id<EXModuleRegistryDelegate> delegate;
11
12@property NSMutableSet<id<EXInternalModule>> *internalModulesSet;
13@property NSMapTable<Protocol *, id<EXInternalModule>> *internalModules;
14@property NSMapTable<Protocol *, NSMutableArray<id<EXInternalModule>> *> *internalModulesPreResolution;
15@property NSMapTable<Class, EXExportedModule *> *exportedModulesByClass;
16@property NSMutableDictionary<const NSString *, EXExportedModule *> *exportedModules;
17@property NSMutableDictionary<const NSString *, id> *singletonModules;
18
19@property NSPointerArray *registryConsumers;
20
21@end
22
23@implementation EXModuleRegistry
24
25# pragma mark - Lifecycle
26
27- (instancetype)init
28{
29  if (self = [super init]) {
30    _internalModulesPreResolution = [NSMapTable weakToStrongObjectsMapTable];
31    _exportedModulesByClass = [NSMapTable weakToWeakObjectsMapTable];
32    _exportedModules = [NSMutableDictionary dictionary];
33    _singletonModules = [NSMutableDictionary dictionary];
34    _registryConsumers = [NSPointerArray weakObjectsPointerArray];
35  }
36  return self;
37}
38
39- (instancetype)initWithInternalModules:(NSSet<id<EXInternalModule>> *)internalModules
40                        exportedModules:(NSSet<EXExportedModule *> *)exportedModules
41                       singletonModules:(NSSet *)singletonModules
42{
43  if (self = [self init]) {
44    for (id<EXInternalModule> internalModule in internalModules) {
45      [self registerInternalModule:internalModule];
46    }
47
48    for (EXExportedModule *exportedModule in exportedModules) {
49      [self registerExportedModule:exportedModule];
50    }
51
52    for (id singletonModule in singletonModules) {
53      [self registerSingletonModule:singletonModule];
54    }
55  }
56  return self;
57}
58
59- (void)setDelegate:(id<EXModuleRegistryDelegate>)delegate
60{
61  _delegate = delegate;
62}
63
64// Fills in _internalModules and _internalModulesSet
65- (void)resolveInternalModulesConflicts
66{
67  if (_internalModules) {
68    // Conflict resolution has already been completed.
69    return;
70  }
71
72  _internalModules = [NSMapTable weakToStrongObjectsMapTable];
73  _internalModulesSet = [NSMutableSet new];
74  for (Protocol *protocol in _internalModulesPreResolution) {
75    NSArray<id<EXInternalModule>> *conflictingModules = [_internalModulesPreResolution objectForKey:protocol];
76
77    id<EXInternalModule> resolvedModule;
78    if ([conflictingModules count] > 1 && _delegate) {
79      resolvedModule = [_delegate pickInternalModuleImplementingInterface:protocol fromAmongModules:conflictingModules];
80    } else {
81      resolvedModule = [conflictingModules lastObject];
82    }
83
84    [_internalModules setObject:resolvedModule forKey:protocol];
85    [self maybeAddRegistryConsumer:resolvedModule];
86    [_internalModulesSet addObject:resolvedModule];
87  }
88
89  _internalModulesPreResolution = nil; // Remove references to discarded modules
90}
91
92- (void)initialize
93{
94  [self resolveInternalModulesConflicts];
95  for (id<EXModuleRegistryConsumer> registryConsumer in _registryConsumers) {
96    [registryConsumer setModuleRegistry:self];
97  }
98}
99
100# pragma mark - Registering modules
101
102- (void)registerInternalModule:(id<EXInternalModule>)internalModule
103{
104  for (Protocol *exportedInterface in [[internalModule class] exportedInterfaces]) {
105    if (_internalModules) {
106      id<EXInternalModule> resolvedModule = internalModule;
107      if (_delegate && [_internalModules objectForKey:exportedInterface]) {
108        id<EXInternalModule> oldModule = [_internalModules objectForKey:exportedInterface];
109        resolvedModule = [_delegate pickInternalModuleImplementingInterface:exportedInterface fromAmongModules:@[oldModule, internalModule]];
110      }
111      [_internalModules setObject:resolvedModule forKey:exportedInterface];
112      [self maybeAddRegistryConsumer:resolvedModule];
113      [_internalModulesSet addObject:resolvedModule];
114    } else {
115      if (![_internalModulesPreResolution objectForKey:exportedInterface]) {
116        [_internalModulesPreResolution setObject:[NSMutableArray array] forKey:exportedInterface];
117      }
118
119      [[_internalModulesPreResolution objectForKey:exportedInterface] addObject:internalModule];
120    }
121  }
122}
123
124- (void)registerExportedModule:(EXExportedModule *)exportedModule
125{
126  const NSString *exportedModuleName = [[exportedModule class] exportedModuleName];
127  if (_exportedModules[exportedModuleName]) {
128    EXLogInfo(@"Module %@ overrides %@ as the module exported as %@.", exportedModule, _exportedModules[exportedModuleName], exportedModuleName);
129  }
130
131  _exportedModules[exportedModuleName] = exportedModule;
132  [_exportedModulesByClass setObject:exportedModule forKey:[exportedModule class]];
133  [self maybeAddRegistryConsumer:exportedModule];
134}
135
136- (void)registerSingletonModule:(id)singletonModule
137{
138  if ([[singletonModule class] respondsToSelector:@selector(name)]) {
139    #pragma clang diagnostic push
140    #pragma clang diagnostic ignored "-Wobjc-method-access"
141    [_singletonModules setObject:singletonModule forKey:[[singletonModule class] name]];
142    #pragma clang diagnostic pop
143  } else {
144    EXLogWarn(@"One of the singleton modules does not respond to +(NSString *)name selector. This probably means you're either try to pass a strange object as a singleton module (it won't get registered in the module registry, sorry) or the EXSingletonModule interface and the EXModuleRegistry implementations versions are out of sync, which means things will probably not work as expected.");
145  }
146}
147
148- (void)maybeAddRegistryConsumer:(id)maybeConsumer
149{
150  if ([maybeConsumer conformsToProtocol:@protocol(EXModuleRegistryConsumer)]) {
151    [self addRegistryConsumer:(id<EXModuleRegistryConsumer>)maybeConsumer];
152  }
153}
154
155- (void)addRegistryConsumer:(id<EXModuleRegistryConsumer>)registryConsumer
156{
157  void *ptr = (__bridge void * _Nullable)registryConsumer;
158
159  for (id consumerPtr in _registryConsumers) {
160    if (consumerPtr == ptr) {
161      return;
162    }
163  }
164  [_registryConsumers addPointer:ptr];
165}
166
167# pragma mark - Registry API
168
169- (id)getModuleImplementingProtocol:(Protocol *)protocol
170{
171  [self resolveInternalModulesConflicts];
172  return [_internalModules objectForKey:protocol];
173}
174
175- (EXExportedModule *)getExportedModuleForName:(NSString *)name
176{
177  return _exportedModules[name];
178}
179
180- (EXExportedModule *)getExportedModuleOfClass:(Class)moduleClass
181{
182  return [_exportedModulesByClass objectForKey:moduleClass];
183}
184
185- (id)getSingletonModuleForName:(NSString *)singletonModuleName
186{
187  return [_singletonModules objectForKey:singletonModuleName];
188}
189
190- (NSArray<id<EXInternalModule>> *)getAllInternalModules
191{
192  [self resolveInternalModulesConflicts];
193  return [_internalModulesSet allObjects];
194}
195
196- (NSArray<EXExportedModule *> *)getAllExportedModules
197{
198  return [_exportedModules allValues];
199}
200
201- (NSArray *)getAllSingletonModules
202{
203  return [_singletonModules allValues];
204}
205
206@end
207