1// Copyright © 2018 650 Industries. All rights reserved.
2
3#import <objc/runtime.h>
4
5#import <ABI48_0_0ExpoModulesCore/ABI48_0_0EXModuleRegistry.h>
6#import <ABI48_0_0ExpoModulesCore/ABI48_0_0EXModuleRegistryConsumer.h>
7
8@interface ABI48_0_0EXModuleRegistry ()
9
10@property (nonatomic, weak) id<ABI48_0_0EXModuleRegistryDelegate> delegate;
11
12@property NSMutableSet<id<ABI48_0_0EXInternalModule>> *internalModulesSet;
13@property NSMapTable<Protocol *, id<ABI48_0_0EXInternalModule>> *internalModules;
14@property NSMapTable<Protocol *, NSMutableArray<id<ABI48_0_0EXInternalModule>> *> *internalModulesPreResolution;
15@property NSMapTable<Class, ABI48_0_0EXExportedModule *> *exportedModulesByClass;
16@property NSMutableDictionary<const NSString *, ABI48_0_0EXExportedModule *> *exportedModules;
17@property NSMutableDictionary<const NSString *, ABI48_0_0EXViewManager *> *viewManagerModules;
18@property NSMutableDictionary<const NSString *, id> *singletonModules;
19
20@property NSPointerArray *registryConsumers;
21
22@end
23
24@implementation ABI48_0_0EXModuleRegistry
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<ABI48_0_0EXInternalModule>> *)internalModules
42                        exportedModules:(NSSet<ABI48_0_0EXExportedModule *> *)exportedModules
43                           viewManagers:(NSSet<ABI48_0_0EXViewManager *> *)viewManagers
44                       singletonModules:(NSSet *)singletonModules
45{
46  if (self = [self init]) {
47    for (id<ABI48_0_0EXInternalModule> internalModule in internalModules) {
48      [self registerInternalModule:internalModule];
49    }
50
51    for (ABI48_0_0EXExportedModule *exportedModule in exportedModules) {
52      [self registerExportedModule:exportedModule];
53    }
54
55    for (ABI48_0_0EXViewManager *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<ABI48_0_0EXModuleRegistryDelegate>)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<ABI48_0_0EXInternalModule>> *conflictingModules = [_internalModulesPreResolution objectForKey:protocol];
83
84    id<ABI48_0_0EXInternalModule> 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<ABI48_0_0EXModuleRegistryConsumer> registryConsumer in _registryConsumers) {
103    [registryConsumer setModuleRegistry:self];
104  }
105}
106
107# pragma mark - Registering modules
108
109- (void)registerInternalModule:(id<ABI48_0_0EXInternalModule>)internalModule
110{
111  for (Protocol *exportedInterface in [[internalModule class] exportedInterfaces]) {
112    if (_internalModules) {
113      id<ABI48_0_0EXInternalModule> resolvedModule = internalModule;
114      if (_delegate && [_internalModules objectForKey:exportedInterface]) {
115        id<ABI48_0_0EXInternalModule> 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:(ABI48_0_0EXExportedModule *)exportedModule
132{
133  const NSString *exportedModuleName = [[exportedModule class] exportedModuleName];
134  if (_exportedModules[exportedModuleName]) {
135    ABI48_0_0EXLogInfo(@"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:(ABI48_0_0EXViewManager *)viewManager
144{
145  const NSString *exportedModuleName = [[viewManager class] exportedModuleName];
146  if (_viewManagerModules[exportedModuleName]) {
147    ABI48_0_0EXLogInfo(@"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    ABI48_0_0EXLogWarn(@"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 ABI48_0_0EXSingletonModule interface and the ABI48_0_0EXModuleRegistry 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(ABI48_0_0EXModuleRegistryConsumer)]) {
169    [self addRegistryConsumer:(id<ABI48_0_0EXModuleRegistryConsumer>)maybeConsumer];
170  }
171}
172
173- (void)addRegistryConsumer:(id<ABI48_0_0EXModuleRegistryConsumer>)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- (ABI48_0_0EXExportedModule *)getExportedModuleForName:(NSString *)name
194{
195  return _exportedModules[name];
196}
197
198- (ABI48_0_0EXExportedModule *)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<ABI48_0_0EXInternalModule>> *)getAllInternalModules
209{
210  [self resolveInternalModulesConflicts];
211  return [_internalModulesSet allObjects];
212}
213
214- (NSArray<ABI48_0_0EXExportedModule *> *)getAllExportedModules
215{
216  return [_exportedModules allValues];
217}
218
219- (NSArray<ABI48_0_0EXViewManager *> *)getAllViewManagers
220{
221  return [_viewManagerModules allValues];
222}
223
224- (NSArray *)getAllSingletonModules
225{
226  return [_singletonModules allValues];
227}
228
229@end
230