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 *, EXViewManager *> *viewManagerModules;
18@property NSMutableDictionary<const NSString *, id> *singletonModules;
19
20@property NSPointerArray *registryConsumers;
21
22@end
23
24@implementation EXModuleRegistry
25
26# pragma mark - Lifecycle
27
28- (instancetype)init
29{
30  if (self = [super init]) {
31    _internalModulesPreResolution = [NSMapTable weakToStrongObjectsMapTable];
32    _exportedModulesByClass = [NSMapTable weakToWeakObjectsMapTable];
33    _exportedModules = [NSMutableDictionary dictionary];
34    _viewManagerModules = [NSMutableDictionary dictionary];
35    _singletonModules = [NSMutableDictionary dictionary];
36    _registryConsumers = [NSPointerArray weakObjectsPointerArray];
37  }
38  return self;
39}
40
41- (instancetype)initWithInternalModules:(NSSet<id<EXInternalModule>> *)internalModules
42                        exportedModules:(NSSet<EXExportedModule *> *)exportedModules
43                           viewManagers:(NSSet<EXViewManager *> *)viewManagers
44                       singletonModules:(NSSet *)singletonModules
45{
46  if (self = [self init]) {
47    for (id<EXInternalModule> internalModule in internalModules) {
48      [self registerInternalModule:internalModule];
49    }
50
51    for (EXExportedModule *exportedModule in exportedModules) {
52      [self registerExportedModule:exportedModule];
53    }
54
55    for (EXViewManager *viewManager in viewManagers) {
56      [self registerViewManager:viewManager];
57    }
58
59    for (id singletonModule in singletonModules) {
60      [self registerSingletonModule:singletonModule];
61    }
62  }
63  return self;
64}
65
66- (void)setDelegate:(id<EXModuleRegistryDelegate>)delegate
67{
68  _delegate = delegate;
69}
70
71// Fills in _internalModules and _internalModulesSet
72- (void)resolveInternalModulesConflicts
73{
74  if (_internalModules) {
75    // Conflict resolution has already been completed.
76    return;
77  }
78
79  _internalModules = [NSMapTable weakToStrongObjectsMapTable];
80  _internalModulesSet = [NSMutableSet new];
81  for (Protocol *protocol in _internalModulesPreResolution) {
82    NSArray<id<EXInternalModule>> *conflictingModules = [_internalModulesPreResolution objectForKey:protocol];
83
84    id<EXInternalModule> resolvedModule;
85    if ([conflictingModules count] > 1 && _delegate) {
86      resolvedModule = [_delegate pickInternalModuleImplementingInterface:protocol fromAmongModules:conflictingModules];
87    } else {
88      resolvedModule = [conflictingModules lastObject];
89    }
90
91    [_internalModules setObject:resolvedModule forKey:protocol];
92    [self maybeAddRegistryConsumer:resolvedModule];
93    [_internalModulesSet addObject:resolvedModule];
94  }
95
96  _internalModulesPreResolution = nil; // Remove references to discarded modules
97}
98
99- (void)initialize
100{
101  [self resolveInternalModulesConflicts];
102  for (id<EXModuleRegistryConsumer> registryConsumer in _registryConsumers) {
103    [registryConsumer setModuleRegistry:self];
104  }
105}
106
107# pragma mark - Registering modules
108
109- (void)registerInternalModule:(id<EXInternalModule>)internalModule
110{
111  for (Protocol *exportedInterface in [[internalModule class] exportedInterfaces]) {
112    if (_internalModules) {
113      id<EXInternalModule> resolvedModule = internalModule;
114      if (_delegate && [_internalModules objectForKey:exportedInterface]) {
115        id<EXInternalModule> oldModule = [_internalModules objectForKey:exportedInterface];
116        resolvedModule = [_delegate pickInternalModuleImplementingInterface:exportedInterface fromAmongModules:@[oldModule, internalModule]];
117      }
118      [_internalModules setObject:resolvedModule forKey:exportedInterface];
119      [self maybeAddRegistryConsumer:resolvedModule];
120      [_internalModulesSet addObject:resolvedModule];
121    } else {
122      if (![_internalModulesPreResolution objectForKey:exportedInterface]) {
123        [_internalModulesPreResolution setObject:[NSMutableArray array] forKey:exportedInterface];
124      }
125
126      [[_internalModulesPreResolution objectForKey:exportedInterface] addObject:internalModule];
127    }
128  }
129}
130
131- (void)registerExportedModule:(EXExportedModule *)exportedModule
132{
133  const NSString *exportedModuleName = [[exportedModule class] exportedModuleName];
134  if (_exportedModules[exportedModuleName]) {
135    EXLogInfo(@"Module %@ overrides %@ as the module exported as %@.", exportedModule, _exportedModules[exportedModuleName], exportedModuleName);
136  }
137
138  _exportedModules[exportedModuleName] = exportedModule;
139  [_exportedModulesByClass setObject:exportedModule forKey:[exportedModule class]];
140  [self maybeAddRegistryConsumer:exportedModule];
141}
142
143- (void)registerViewManager:(EXViewManager *)viewManager
144{
145  const NSString *exportedModuleName = [[viewManager class] exportedModuleName];
146  if (_viewManagerModules[exportedModuleName]) {
147    EXLogInfo(@"View manager %@ overrides %@ as the module exported as %@.", viewManager, _viewManagerModules[exportedModuleName], exportedModuleName);
148  }
149
150  _viewManagerModules[exportedModuleName] = viewManager;
151  [self maybeAddRegistryConsumer:viewManager];
152}
153
154- (void)registerSingletonModule:(id)singletonModule
155{
156  if ([[singletonModule class] respondsToSelector:@selector(name)]) {
157    #pragma clang diagnostic push
158    #pragma clang diagnostic ignored "-Wobjc-method-access"
159    [_singletonModules setObject:singletonModule forKey:[[singletonModule class] name]];
160    #pragma clang diagnostic pop
161  } else {
162    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.");
163  }
164}
165
166- (void)maybeAddRegistryConsumer:(id)maybeConsumer
167{
168  if ([maybeConsumer conformsToProtocol:@protocol(EXModuleRegistryConsumer)]) {
169    [self addRegistryConsumer:(id<EXModuleRegistryConsumer>)maybeConsumer];
170  }
171}
172
173- (void)addRegistryConsumer:(id<EXModuleRegistryConsumer>)registryConsumer
174{
175  void *ptr = (__bridge void * _Nullable)registryConsumer;
176
177  for (id consumerPtr in _registryConsumers) {
178    if (consumerPtr == ptr) {
179      return;
180    }
181  }
182  [_registryConsumers addPointer:ptr];
183}
184
185# pragma mark - Registry API
186
187- (id)getModuleImplementingProtocol:(Protocol *)protocol
188{
189  [self resolveInternalModulesConflicts];
190  return [_internalModules objectForKey:protocol];
191}
192
193- (EXExportedModule *)getExportedModuleForName:(NSString *)name
194{
195  return _exportedModules[name];
196}
197
198- (EXExportedModule *)getExportedModuleOfClass:(Class)moduleClass
199{
200  return [_exportedModulesByClass objectForKey:moduleClass];
201}
202
203- (id)getSingletonModuleForName:(NSString *)singletonModuleName
204{
205  return [_singletonModules objectForKey:singletonModuleName];
206}
207
208- (NSArray<id<EXInternalModule>> *)getAllInternalModules
209{
210  [self resolveInternalModulesConflicts];
211  return [_internalModulesSet allObjects];
212}
213
214- (NSArray<EXExportedModule *> *)getAllExportedModules
215{
216  return [_exportedModules allValues];
217}
218
219- (NSArray<EXViewManager *> *)getAllViewManagers
220{
221  return [_viewManagerModules allValues];
222}
223
224- (NSArray *)getAllSingletonModules
225{
226  return [_singletonModules allValues];
227}
228
229@end
230