1// Copyright © 2018 650 Industries. All rights reserved.
2
3#import <objc/runtime.h>
4
5#import <ABI49_0_0ExpoModulesCore/ABI49_0_0EXModuleRegistry.h>
6#import <ABI49_0_0ExpoModulesCore/ABI49_0_0EXModuleRegistryConsumer.h>
7
8@interface ABI49_0_0EXModuleRegistry ()
9
10@property (nonatomic, weak) id<ABI49_0_0EXModuleRegistryDelegate> delegate;
11
12@property NSMutableSet<id<ABI49_0_0EXInternalModule>> *internalModulesSet;
13@property NSMapTable<Protocol *, id<ABI49_0_0EXInternalModule>> *internalModules;
14@property NSMapTable<Protocol *, NSMutableArray<id<ABI49_0_0EXInternalModule>> *> *internalModulesPreResolution;
15@property NSMapTable<Class, ABI49_0_0EXExportedModule *> *exportedModulesByClass;
16@property NSMutableDictionary<const NSString *, ABI49_0_0EXExportedModule *> *exportedModules;
17@property NSMutableDictionary<const NSString *, id> *singletonModules;
18
19@property NSPointerArray *registryConsumers;
20
21@end
22
23@implementation ABI49_0_0EXModuleRegistry
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<ABI49_0_0EXInternalModule>> *)internalModules
40                        exportedModules:(NSSet<ABI49_0_0EXExportedModule *> *)exportedModules
41                       singletonModules:(NSSet *)singletonModules
42{
43  if (self = [self init]) {
44    for (id<ABI49_0_0EXInternalModule> internalModule in internalModules) {
45      [self registerInternalModule:internalModule];
46    }
47
48    for (ABI49_0_0EXExportedModule *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<ABI49_0_0EXModuleRegistryDelegate>)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<ABI49_0_0EXInternalModule>> *conflictingModules = [_internalModulesPreResolution objectForKey:protocol];
76
77    id<ABI49_0_0EXInternalModule> 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<ABI49_0_0EXModuleRegistryConsumer> registryConsumer in _registryConsumers) {
96    [registryConsumer setModuleRegistry:self];
97  }
98}
99
100# pragma mark - Registering modules
101
102- (void)registerInternalModule:(id<ABI49_0_0EXInternalModule>)internalModule
103{
104  for (Protocol *exportedInterface in [[internalModule class] exportedInterfaces]) {
105    if (_internalModules) {
106      id<ABI49_0_0EXInternalModule> resolvedModule = internalModule;
107      if (_delegate && [_internalModules objectForKey:exportedInterface]) {
108        id<ABI49_0_0EXInternalModule> 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:(ABI49_0_0EXExportedModule *)exportedModule
125{
126  const NSString *exportedModuleName = [[exportedModule class] exportedModuleName];
127  if (_exportedModules[exportedModuleName]) {
128    ABI49_0_0EXLogInfo(@"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    ABI49_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 ABI49_0_0EXSingletonModule interface and the ABI49_0_0EXModuleRegistry 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(ABI49_0_0EXModuleRegistryConsumer)]) {
151    [self addRegistryConsumer:(id<ABI49_0_0EXModuleRegistryConsumer>)maybeConsumer];
152  }
153}
154
155- (void)addRegistryConsumer:(id<ABI49_0_0EXModuleRegistryConsumer>)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- (ABI49_0_0EXExportedModule *)getExportedModuleForName:(NSString *)name
176{
177  return _exportedModules[name];
178}
179
180- (ABI49_0_0EXExportedModule *)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<ABI49_0_0EXInternalModule>> *)getAllInternalModules
191{
192  [self resolveInternalModulesConflicts];
193  return [_internalModulesSet allObjects];
194}
195
196- (NSArray<ABI49_0_0EXExportedModule *> *)getAllExportedModules
197{
198  return [_exportedModules allValues];
199}
200
201- (NSArray *)getAllSingletonModules
202{
203  return [_singletonModules allValues];
204}
205
206@end
207