• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 Google Inc. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef FRUIT_BINDING_NORMALIZATION_TEMPLATES_H
18 #define FRUIT_BINDING_NORMALIZATION_TEMPLATES_H
19 
20 #if !IN_FRUIT_CPP_FILE
21 // We don't want to include it in public headers to save some compile time.
22 #error "binding_normalization.templates.h included in non-cpp file."
23 #endif
24 
25 #include <fruit/impl/component_storage/component_storage_entry.h>
26 #include <fruit/impl/normalized_component_storage/binding_normalization.h>
27 #include <fruit/impl/util/type_info.h>
28 
29 using namespace fruit::impl;
30 
31 namespace fruit {
32 namespace impl {
33 
34 template <typename... Functors>
BindingNormalizationContext(FixedSizeVector<ComponentStorageEntry> & toplevel_entries,FixedSizeAllocator::FixedSizeAllocatorData & fixed_size_allocator_data,MemoryPool & memory_pool,MemoryPool & memory_pool_for_fully_expanded_components_maps,MemoryPool & memory_pool_for_component_replacements_maps,HashMapWithArenaAllocator<TypeId,ComponentStorageEntry> & binding_data_map,BindingNormalizationFunctors<Functors...> functors)35 BindingNormalization::BindingNormalizationContext<Functors...>::BindingNormalizationContext(
36     FixedSizeVector<ComponentStorageEntry>& toplevel_entries,
37     FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data, MemoryPool& memory_pool,
38     MemoryPool& memory_pool_for_fully_expanded_components_maps, MemoryPool& memory_pool_for_component_replacements_maps,
39     HashMapWithArenaAllocator<TypeId, ComponentStorageEntry>& binding_data_map,
40     BindingNormalizationFunctors<Functors...> functors)
41     : fixed_size_allocator_data(fixed_size_allocator_data), memory_pool(memory_pool),
42       memory_pool_for_fully_expanded_components_maps(memory_pool_for_fully_expanded_components_maps),
43       memory_pool_for_component_replacements_maps(memory_pool_for_component_replacements_maps),
44       binding_data_map(binding_data_map), functors(functors),
45       entries_to_process(toplevel_entries.begin(), toplevel_entries.end(),
46                          ArenaAllocator<ComponentStorageEntry>(memory_pool)) {
47 
48   toplevel_entries.clear();
49 }
50 
51 template <typename... Functors>
~BindingNormalizationContext()52 BindingNormalization::BindingNormalizationContext<Functors...>::~BindingNormalizationContext() {
53   FruitAssert(components_with_no_args_with_expansion_in_progress.empty());
54   FruitAssert(components_with_args_with_expansion_in_progress.empty());
55 
56   for (const ComponentStorageEntry::LazyComponentWithArgs& x : fully_expanded_components_with_args) {
57     x.destroy();
58   }
59 
60   for (const auto& pair : component_with_args_replacements) {
61     const LazyComponentWithArgs& replaced_component = pair.first;
62     const ComponentStorageEntry& replacement_component = pair.second;
63     replaced_component.destroy();
64     replacement_component.destroy();
65   }
66 
67   for (const auto& pair : component_with_no_args_replacements) {
68     const ComponentStorageEntry& replacement_component = pair.second;
69     replacement_component.destroy();
70   }
71 }
72 
73 template <typename... Functors>
normalizeBindings(FixedSizeVector<ComponentStorageEntry> && toplevel_entries,FixedSizeAllocator::FixedSizeAllocatorData & fixed_size_allocator_data,MemoryPool & memory_pool,MemoryPool & memory_pool_for_fully_expanded_components_maps,MemoryPool & memory_pool_for_component_replacements_maps,HashMapWithArenaAllocator<TypeId,ComponentStorageEntry> & binding_data_map,Functors...functors)74 void BindingNormalization::normalizeBindings(FixedSizeVector<ComponentStorageEntry>&& toplevel_entries,
75                                              FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data,
76                                              MemoryPool& memory_pool,
77                                              MemoryPool& memory_pool_for_fully_expanded_components_maps,
78                                              MemoryPool& memory_pool_for_component_replacements_maps,
79                                              HashMapWithArenaAllocator<TypeId, ComponentStorageEntry>& binding_data_map,
80                                              Functors... functors) {
81 
82   FruitAssert(binding_data_map.empty());
83 
84   using Context = BindingNormalizationContext<Functors...>;
85 
86   Context context(toplevel_entries, fixed_size_allocator_data, memory_pool,
87                   memory_pool_for_fully_expanded_components_maps, memory_pool_for_component_replacements_maps,
88                   binding_data_map, BindingNormalizationFunctors<Functors...>{functors...});
89 
90   // When we expand a lazy component, instead of removing it from the stack we change its kind (in entries_to_process)
91   // to one of the *_END_MARKER kinds. This allows to keep track of the "call stack" for the expansion.
92 
93   while (!context.entries_to_process.empty()) {
94     switch (context.entries_to_process.back().kind) { // LCOV_EXCL_BR_LINE
95     case ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT:
96       handleBindingForConstructedObject(context);
97       break;
98 
99     case ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION:
100       handleBindingForObjectToConstructThatNeedsAllocation(context);
101       break;
102 
103     case ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION:
104       handleBindingForObjectToConstructThatNeedsNoAllocation(context);
105       break;
106 
107     case ComponentStorageEntry::Kind::COMPRESSED_BINDING:
108       handleCompressedBinding(context);
109       break;
110 
111     case ComponentStorageEntry::Kind::MULTIBINDING_FOR_CONSTRUCTED_OBJECT:
112     case ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION:
113     case ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION:
114       handleMultibinding(context);
115       break;
116 
117     case ComponentStorageEntry::Kind::MULTIBINDING_VECTOR_CREATOR:
118       handleMultibindingVectorCreator(context);
119       break;
120 
121     case ComponentStorageEntry::Kind::COMPONENT_WITHOUT_ARGS_END_MARKER:
122       handleComponentWithoutArgsEndMarker(context);
123       break;
124 
125     case ComponentStorageEntry::Kind::COMPONENT_WITH_ARGS_END_MARKER:
126       handleComponentWithArgsEndMarker(context);
127       break;
128 
129     case ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_ARGS:
130       handleReplacedLazyComponentWithArgs(context);
131       break;
132 
133     case ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_NO_ARGS:
134       handleReplacedLazyComponentWithNoArgs(context);
135       break;
136 
137     case ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_ARGS:
138       handleLazyComponentWithArgs(context);
139       break;
140 
141     case ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_NO_ARGS:
142       handleLazyComponentWithNoArgs(context);
143       break;
144 
145     default:
146 #if FRUIT_EXTRA_DEBUG
147       std::cerr << "Unexpected kind: " << (std::size_t)context.entries_to_process.back().kind << std::endl;
148 #endif
149       FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
150     }
151   }
152 
153   context.functors.save_fully_expanded_components_with_no_args(context.fully_expanded_components_with_no_args);
154   context.functors.save_fully_expanded_components_with_args(context.fully_expanded_components_with_args);
155   context.functors.save_component_replacements_with_no_args(context.component_with_no_args_replacements);
156   context.functors.save_component_replacements_with_args(context.component_with_args_replacements);
157 }
158 
159 template <typename... Params>
160 FRUIT_ALWAYS_INLINE inline void
handleBindingForConstructedObject(BindingNormalizationContext<Params...> & context)161 BindingNormalization::handleBindingForConstructedObject(BindingNormalizationContext<Params...>& context) {
162   ComponentStorageEntry entry = context.entries_to_process.back();
163   FruitAssert(entry.kind == ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT);
164 
165   context.entries_to_process.pop_back();
166 
167   auto itr = context.functors.find_normalized_binding(entry.type_id);
168   if (context.functors.is_valid_itr(itr)) {
169     if (!context.functors.is_normalized_binding_itr_for_constructed_object(itr) ||
170         context.functors.get_object_ptr(itr) != entry.binding_for_constructed_object.object_ptr) {
171       printMultipleBindingsError(entry.type_id);
172       FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
173     }
174     // Otherwise ok, duplicate but consistent binding.
175     return;
176   }
177 
178   ComponentStorageEntry& entry_in_map = context.binding_data_map[entry.type_id];
179   if (entry_in_map.type_id.type_info != nullptr) {
180     if (entry_in_map.kind != ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT ||
181         entry.binding_for_constructed_object.object_ptr != entry_in_map.binding_for_constructed_object.object_ptr) {
182       printMultipleBindingsError(entry.type_id);
183       FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
184     }
185 // Otherwise ok, duplicate but consistent binding.
186 
187 // This avoids assertion failures when injecting a non-const pointer and there is a const duplicate binding that
188 // appears before the non-const one (so we'd otherwise ignore the non-const one).
189 #if FRUIT_EXTRA_DEBUG
190     entry_in_map.binding_for_constructed_object.is_nonconst |= entry.binding_for_constructed_object.is_nonconst;
191 #endif
192     return;
193   }
194 
195   // New binding, add it to the map.
196   entry_in_map = std::move(entry);
197 }
198 
199 template <typename... Params>
handleBindingForObjectToConstructThatNeedsAllocation(BindingNormalizationContext<Params...> & context)200 FRUIT_ALWAYS_INLINE inline void BindingNormalization::handleBindingForObjectToConstructThatNeedsAllocation(
201     BindingNormalizationContext<Params...>& context) {
202   ComponentStorageEntry entry = context.entries_to_process.back();
203   FruitAssert(entry.kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION);
204   context.entries_to_process.pop_back();
205 
206   auto itr = context.functors.find_normalized_binding(entry.type_id);
207   if (context.functors.is_valid_itr(itr)) {
208     if (context.functors.is_normalized_binding_itr_for_constructed_object(itr) ||
209         context.functors.get_create(itr) != entry.binding_for_object_to_construct.create) {
210       printMultipleBindingsError(entry.type_id);
211       FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
212     }
213     // Otherwise ok, duplicate but consistent binding.
214     return;
215   }
216 
217   ComponentStorageEntry& entry_in_map = context.binding_data_map[entry.type_id];
218   context.fixed_size_allocator_data.addType(entry.type_id);
219   if (entry_in_map.type_id.type_info != nullptr) {
220     if (entry_in_map.kind != ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION ||
221         entry.binding_for_object_to_construct.create != entry_in_map.binding_for_object_to_construct.create) {
222       printMultipleBindingsError(entry.type_id);
223       FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
224     }
225     // Otherwise ok, duplicate but consistent binding.
226     return;
227   }
228 
229   // New binding, add it to the map.
230   entry_in_map = std::move(entry);
231 }
232 
233 template <typename... Params>
handleBindingForObjectToConstructThatNeedsNoAllocation(BindingNormalizationContext<Params...> & context)234 FRUIT_ALWAYS_INLINE inline void BindingNormalization::handleBindingForObjectToConstructThatNeedsNoAllocation(
235     BindingNormalizationContext<Params...>& context) {
236   ComponentStorageEntry entry = context.entries_to_process.back();
237   FruitAssert(entry.kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION);
238   context.entries_to_process.pop_back();
239 
240   auto itr = context.functors.find_normalized_binding(entry.type_id);
241   if (context.functors.is_valid_itr(itr)) {
242     if (context.functors.is_normalized_binding_itr_for_constructed_object(itr) ||
243         context.functors.get_create(itr) != entry.binding_for_object_to_construct.create) {
244       printMultipleBindingsError(entry.type_id);
245       FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
246     }
247     // Otherwise ok, duplicate but consistent binding.
248     return;
249   }
250 
251   ComponentStorageEntry& entry_in_map = context.binding_data_map[entry.type_id];
252   context.fixed_size_allocator_data.addExternallyAllocatedType(entry.type_id);
253   if (entry_in_map.type_id.type_info != nullptr) {
254     if (entry_in_map.kind != ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION ||
255         entry.binding_for_object_to_construct.create != entry_in_map.binding_for_object_to_construct.create) {
256       printMultipleBindingsError(entry.type_id);
257       FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
258     }
259     // Otherwise ok, duplicate but consistent binding.
260     return;
261   }
262 
263   // New binding, add it to the map.
264   entry_in_map = std::move(entry);
265 }
266 
267 template <typename... Params>
268 FRUIT_ALWAYS_INLINE inline void
handleCompressedBinding(BindingNormalizationContext<Params...> & context)269 BindingNormalization::handleCompressedBinding(BindingNormalizationContext<Params...>& context) {
270   ComponentStorageEntry entry = context.entries_to_process.back();
271   FruitAssert(entry.kind == ComponentStorageEntry::Kind::COMPRESSED_BINDING);
272   context.entries_to_process.pop_back();
273   context.functors.handle_compressed_binding(entry);
274 }
275 
276 template <typename... Params>
handleMultibinding(BindingNormalizationContext<Params...> & context)277 void BindingNormalization::handleMultibinding(BindingNormalizationContext<Params...>& context) {
278   ComponentStorageEntry entry = context.entries_to_process.back();
279   FruitAssert(entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_FOR_CONSTRUCTED_OBJECT ||
280               entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION ||
281               entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION);
282   context.entries_to_process.pop_back();
283   FruitAssert(!context.entries_to_process.empty());
284   ComponentStorageEntry vector_creator_entry = std::move(context.entries_to_process.back());
285   context.entries_to_process.pop_back();
286   FruitAssert(vector_creator_entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_VECTOR_CREATOR);
287   context.functors.handle_multibinding(entry, vector_creator_entry);
288 }
289 
290 template <typename... Params>
handleMultibindingVectorCreator(BindingNormalizationContext<Params...> & context)291 void BindingNormalization::handleMultibindingVectorCreator(BindingNormalizationContext<Params...>& context) {
292   ComponentStorageEntry entry = context.entries_to_process.back();
293   FruitAssert(entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_VECTOR_CREATOR);
294   context.entries_to_process.pop_back();
295   FruitAssert(!context.entries_to_process.empty());
296   ComponentStorageEntry multibinding_entry = std::move(context.entries_to_process.back());
297   context.entries_to_process.pop_back();
298   FruitAssert(multibinding_entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_FOR_CONSTRUCTED_OBJECT ||
299               multibinding_entry.kind ==
300                   ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION ||
301               multibinding_entry.kind ==
302                   ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION);
303   context.functors.handle_multibinding(multibinding_entry, entry);
304 }
305 
306 template <typename... Params>
307 FRUIT_ALWAYS_INLINE inline void
handleComponentWithoutArgsEndMarker(BindingNormalizationContext<Params...> & context)308 BindingNormalization::handleComponentWithoutArgsEndMarker(BindingNormalizationContext<Params...>& context) {
309   ComponentStorageEntry entry = context.entries_to_process.back();
310   FruitAssert(entry.kind == ComponentStorageEntry::Kind::COMPONENT_WITHOUT_ARGS_END_MARKER);
311   context.entries_to_process.pop_back();
312   // A lazy component expansion has completed; we now move the component from
313   // components_with_*_with_expansion_in_progress to fully_expanded_components_*.
314 
315   context.components_with_no_args_with_expansion_in_progress.erase(entry.lazy_component_with_no_args);
316   context.fully_expanded_components_with_no_args.insert(std::move(entry.lazy_component_with_no_args));
317 }
318 
319 template <typename... Params>
320 FRUIT_ALWAYS_INLINE inline void
handleComponentWithArgsEndMarker(BindingNormalizationContext<Params...> & context)321 BindingNormalization::handleComponentWithArgsEndMarker(BindingNormalizationContext<Params...>& context) {
322   ComponentStorageEntry entry = context.entries_to_process.back();
323   FruitAssert(entry.kind == ComponentStorageEntry::Kind::COMPONENT_WITH_ARGS_END_MARKER);
324   context.entries_to_process.pop_back();
325   // A lazy component expansion has completed; we now move the component from
326   // components_with_*_with_expansion_in_progress to fully_expanded_components_*.
327 
328   context.components_with_args_with_expansion_in_progress.erase(entry.lazy_component_with_args);
329   context.fully_expanded_components_with_args.insert(std::move(entry.lazy_component_with_args));
330 }
331 
332 template <typename... Params>
handleReplacedLazyComponentWithArgs(BindingNormalizationContext<Params...> & context)333 void BindingNormalization::handleReplacedLazyComponentWithArgs(BindingNormalizationContext<Params...>& context) {
334   ComponentStorageEntry entry = context.entries_to_process.back();
335   FruitAssert(entry.kind == ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_ARGS);
336   ComponentStorageEntry replaced_component_entry = std::move(entry);
337   context.entries_to_process.pop_back();
338   FruitAssert(!context.entries_to_process.empty());
339 
340   ComponentStorageEntry replacement_component_entry = std::move(context.entries_to_process.back());
341   context.entries_to_process.pop_back();
342   FruitAssert(replacement_component_entry.kind ==
343                   ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS ||
344               replacement_component_entry.kind == ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS);
345 
346   if (context.components_with_args_with_expansion_in_progress.count(entry.lazy_component_with_args) != 0 ||
347       context.fully_expanded_components_with_args.count(entry.lazy_component_with_args) != 0 ||
348       context.functors.is_component_with_args_already_expanded_in_normalized_component(
349           entry.lazy_component_with_args)) {
350     printComponentReplacementFailedBecauseTargetAlreadyExpanded(replaced_component_entry, replacement_component_entry);
351     FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
352   }
353 
354   auto replacement_in_normalized_component_itr =
355       context.functors.get_component_with_args_replacement_in_normalized_component(
356           replaced_component_entry.lazy_component_with_args);
357   if (context.functors.is_lazy_component_with_args_iterator_valid(replacement_in_normalized_component_itr)) {
358     handlePreexistingLazyComponentWithArgsReplacement(
359         replaced_component_entry,
360         context.functors.dereference_lazy_component_with_args_iterator(replacement_in_normalized_component_itr),
361         replacement_component_entry);
362     return;
363   }
364 
365   ComponentStorageEntry& replacement_component_entry_in_map =
366       context.component_with_args_replacements[replaced_component_entry.lazy_component_with_args];
367   if (replacement_component_entry_in_map.type_id.type_info != nullptr) {
368     handlePreexistingLazyComponentWithArgsReplacement(replaced_component_entry, replacement_component_entry_in_map,
369                                                       replacement_component_entry);
370     return;
371   }
372 
373   // We just inserted replaced_component_entry.lazy_component_with_args in the map, so it's now owned by the
374   // map.
375   replacement_component_entry_in_map = replacement_component_entry;
376 }
377 
378 template <typename... Params>
handleReplacedLazyComponentWithNoArgs(BindingNormalizationContext<Params...> & context)379 void BindingNormalization::handleReplacedLazyComponentWithNoArgs(BindingNormalizationContext<Params...>& context) {
380   ComponentStorageEntry entry = context.entries_to_process.back();
381   FruitAssert(entry.kind == ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_NO_ARGS);
382   ComponentStorageEntry replaced_component_entry = std::move(entry);
383   context.entries_to_process.pop_back();
384   FruitAssert(!context.entries_to_process.empty());
385 
386   ComponentStorageEntry replacement_component_entry = std::move(context.entries_to_process.back());
387   context.entries_to_process.pop_back();
388   FruitAssert(replacement_component_entry.kind ==
389                   ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS ||
390               replacement_component_entry.kind == ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS);
391 
392   if (context.components_with_no_args_with_expansion_in_progress.count(entry.lazy_component_with_no_args) != 0 ||
393       context.fully_expanded_components_with_no_args.count(entry.lazy_component_with_no_args) != 0 ||
394       context.functors.is_component_with_no_args_already_expanded_in_normalized_component(
395           entry.lazy_component_with_no_args)) {
396     printComponentReplacementFailedBecauseTargetAlreadyExpanded(replaced_component_entry, replacement_component_entry);
397     FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
398   }
399 
400   auto replacement_in_normalized_component_itr =
401       context.functors.get_component_with_no_args_replacement_in_normalized_component(
402           replaced_component_entry.lazy_component_with_no_args);
403   if (context.functors.is_lazy_component_with_no_args_iterator_valid(replacement_in_normalized_component_itr)) {
404     handlePreexistingLazyComponentWithNoArgsReplacement(
405         replaced_component_entry,
406         context.functors.dereference_lazy_component_with_no_args_iterator(replacement_in_normalized_component_itr),
407         replacement_component_entry);
408     return;
409   }
410 
411   ComponentStorageEntry& replacement_component_entry_in_map =
412       context.component_with_no_args_replacements[replaced_component_entry.lazy_component_with_no_args];
413   if (replacement_component_entry_in_map.type_id.type_info != nullptr) {
414     handlePreexistingLazyComponentWithNoArgsReplacement(replaced_component_entry, replacement_component_entry_in_map,
415                                                         replacement_component_entry);
416     return;
417   }
418 
419   // We just inserted replaced_component_entry.lazy_component_with_args in the map, so it's now owned by the
420   // map.
421   replacement_component_entry_in_map = replacement_component_entry;
422 }
423 
424 template <typename... Params>
performComponentReplacement(BindingNormalizationContext<Params...> & context,const ComponentStorageEntry & replacement)425 void BindingNormalization::performComponentReplacement(BindingNormalizationContext<Params...>& context,
426                                                        const ComponentStorageEntry& replacement) {
427 
428   // Instead of removing the entry from context.entries_to_process, we just replace it with the new component entry.
429 
430   ComponentStorageEntry& entry = context.entries_to_process.back();
431 
432   switch (replacement.kind) { // LCOV_EXCL_BR_LINE
433   case ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS:
434     entry.kind = ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_ARGS;
435     entry.type_id = replacement.type_id;
436     entry.lazy_component_with_args = replacement.lazy_component_with_args.copy();
437     break;
438 
439   case ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS:
440     entry.kind = ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_NO_ARGS;
441     entry.type_id = replacement.type_id;
442     entry.lazy_component_with_no_args = replacement.lazy_component_with_no_args;
443     break;
444 
445   default:
446     FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
447   }
448 }
449 
450 template <typename... Params>
451 FRUIT_ALWAYS_INLINE inline void
handleLazyComponentWithArgs(BindingNormalizationContext<Params...> & context)452 BindingNormalization::handleLazyComponentWithArgs(BindingNormalizationContext<Params...>& context) {
453   ComponentStorageEntry entry = context.entries_to_process.back();
454   FruitAssert(entry.kind == ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_ARGS);
455   if (context.fully_expanded_components_with_args.count(entry.lazy_component_with_args) ||
456       context.functors.is_component_with_args_already_expanded_in_normalized_component(
457           entry.lazy_component_with_args)) {
458     // This lazy component was already inserted, skip it.
459     entry.lazy_component_with_args.destroy();
460     context.entries_to_process.pop_back();
461     return;
462   }
463 
464   auto replacement_component_in_normalized_component_itr =
465       context.functors.get_component_with_args_replacement_in_normalized_component(entry.lazy_component_with_args);
466   if (context.functors.is_lazy_component_with_args_iterator_valid(replacement_component_in_normalized_component_itr)) {
467     entry.lazy_component_with_args.destroy();
468     performComponentReplacement(context,
469                                 context.functors.dereference_lazy_component_with_args_iterator(
470                                     replacement_component_in_normalized_component_itr));
471     return;
472   }
473 
474   auto replacement_component_itr = context.component_with_args_replacements.find(entry.lazy_component_with_args);
475   if (replacement_component_itr != context.component_with_args_replacements.end()) {
476     entry.lazy_component_with_args.destroy();
477     performComponentReplacement(context, replacement_component_itr->second);
478     return;
479   }
480 
481   bool actually_inserted =
482       context.components_with_args_with_expansion_in_progress.insert(entry.lazy_component_with_args).second;
483   if (!actually_inserted) {
484     printLazyComponentInstallationLoop(context.entries_to_process, entry);
485     FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
486   }
487 
488 #if FRUIT_EXTRA_DEBUG
489   std::cout << "Expanding lazy component: " << entry.lazy_component_with_args.component->getFunTypeId() << std::endl;
490 #endif
491 
492   // Instead of removing the component from component.lazy_components, we just change its kind to the
493   // corresponding *_END_MARKER kind.
494   // When we pop this marker, this component's expansion will be complete.
495   context.entries_to_process.back().kind = ComponentStorageEntry::Kind::COMPONENT_WITH_ARGS_END_MARKER;
496 
497   // Note that this can also add other lazy components, so the resulting bindings can have a non-intuitive
498   // (although deterministic) order.
499   context.entries_to_process.back().lazy_component_with_args.component->addBindings(context.entries_to_process);
500 }
501 
502 template <typename... Params>
503 FRUIT_ALWAYS_INLINE inline void
handleLazyComponentWithNoArgs(BindingNormalizationContext<Params...> & context)504 BindingNormalization::handleLazyComponentWithNoArgs(BindingNormalizationContext<Params...>& context) {
505   ComponentStorageEntry entry = context.entries_to_process.back();
506   FruitAssert(entry.kind == ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_NO_ARGS);
507 
508   if (context.fully_expanded_components_with_no_args.count(entry.lazy_component_with_no_args) ||
509       context.functors.is_component_with_no_args_already_expanded_in_normalized_component(
510           entry.lazy_component_with_no_args)) {
511     // This lazy component was already inserted, skip it.
512     context.entries_to_process.pop_back();
513     return;
514   }
515 
516   auto replacement_component_in_normalized_component_itr =
517       context.functors.get_component_with_no_args_replacement_in_normalized_component(
518           entry.lazy_component_with_no_args);
519   if (context.functors.is_lazy_component_with_no_args_iterator_valid(
520           replacement_component_in_normalized_component_itr)) {
521     performComponentReplacement(context,
522                                 context.functors.dereference_lazy_component_with_no_args_iterator(
523                                     replacement_component_in_normalized_component_itr));
524     return;
525   }
526 
527   auto replacement_component_itr = context.component_with_no_args_replacements.find(entry.lazy_component_with_no_args);
528   if (replacement_component_itr != context.component_with_no_args_replacements.end()) {
529     performComponentReplacement(context, replacement_component_itr->second);
530     return;
531   }
532 
533   bool actually_inserted =
534       context.components_with_no_args_with_expansion_in_progress.insert(entry.lazy_component_with_no_args).second;
535   if (!actually_inserted) {
536     printLazyComponentInstallationLoop(context.entries_to_process, entry);
537     FRUIT_UNREACHABLE; // LCOV_EXCL_LINE
538   }
539 
540 #if FRUIT_EXTRA_DEBUG
541   std::cout << "Expanding lazy component: " << entry.type_id << std::endl;
542 #endif
543 
544   // Instead of removing the component from component.lazy_components, we just change its kind to the
545   // corresponding *_END_MARKER kind.
546   // When we pop this marker, this component's expansion will be complete.
547   context.entries_to_process.back().kind = ComponentStorageEntry::Kind::COMPONENT_WITHOUT_ARGS_END_MARKER;
548 
549   // Note that this can also add other lazy components, so the resulting bindings can have a non-intuitive
550   // (although deterministic) order.
551   context.entries_to_process.back().lazy_component_with_no_args.addBindings(context.entries_to_process);
552 }
553 
554 template <typename SaveCompressedBindingUndoInfo>
555 std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>
performBindingCompression(HashMapWithArenaAllocator<TypeId,ComponentStorageEntry> && binding_data_map,HashMapWithArenaAllocator<TypeId,BindingCompressionInfo> && compressed_bindings_map,MemoryPool & memory_pool,const multibindings_vector_t & multibindings_vector,const std::vector<TypeId,ArenaAllocator<TypeId>> & exposed_types,SaveCompressedBindingUndoInfo save_compressed_binding_undo_info)556 BindingNormalization::performBindingCompression(
557     HashMapWithArenaAllocator<TypeId, ComponentStorageEntry>&& binding_data_map,
558     HashMapWithArenaAllocator<TypeId, BindingCompressionInfo>&& compressed_bindings_map, MemoryPool& memory_pool,
559     const multibindings_vector_t& multibindings_vector,
560     const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types,
561     SaveCompressedBindingUndoInfo save_compressed_binding_undo_info) {
562   using result_t = std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>;
563   result_t result = result_t(ArenaAllocator<ComponentStorageEntry>(memory_pool));
564 
565   // We can't compress the binding if C is a dep of a multibinding.
566   for (const std::pair<ComponentStorageEntry, ComponentStorageEntry>& multibinding_entry_pair : multibindings_vector) {
567     const ComponentStorageEntry& entry = multibinding_entry_pair.first;
568     FruitAssert(entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_FOR_CONSTRUCTED_OBJECT ||
569                 entry.kind == ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION ||
570                 entry.kind ==
571                     ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION);
572     if (entry.kind != ComponentStorageEntry::Kind::MULTIBINDING_FOR_CONSTRUCTED_OBJECT) {
573       const BindingDeps* deps = entry.multibinding_for_object_to_construct.deps;
574       FruitAssert(deps != nullptr);
575       for (std::size_t i = 0; i < deps->num_deps; ++i) {
576         compressed_bindings_map.erase(deps->deps[i]);
577 #if FRUIT_EXTRA_DEBUG
578         std::cout << "InjectorStorage: ignoring compressed binding for " << deps->deps[i]
579                   << " because it's a dep of a multibinding." << std::endl;
580 #endif
581       }
582     }
583   }
584 
585   // We can't compress the binding if C is an exposed type (but I is likely to be exposed instead).
586   for (TypeId type : exposed_types) {
587     compressed_bindings_map.erase(type);
588 #if FRUIT_EXTRA_DEBUG
589     std::cout << "InjectorStorage: ignoring compressed binding for " << type << " because it's an exposed type."
590               << std::endl;
591 #endif
592   }
593 
594   // We can't compress the binding if some type X depends on C and X!=I.
595   for (auto& binding_data_map_entry : binding_data_map) {
596     TypeId x_id = binding_data_map_entry.first;
597     ComponentStorageEntry entry = binding_data_map_entry.second;
598     FruitAssert(entry.kind == ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT ||
599                 entry.kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION ||
600                 entry.kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION);
601 
602     if (entry.kind != ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT) {
603       for (std::size_t i = 0; i < entry.binding_for_object_to_construct.deps->num_deps; ++i) {
604         TypeId c_id = entry.binding_for_object_to_construct.deps->deps[i];
605         auto itr = compressed_bindings_map.find(c_id);
606         if (itr != compressed_bindings_map.end() && itr->second.i_type_id != x_id) {
607           compressed_bindings_map.erase(itr);
608 #if FRUIT_EXTRA_DEBUG
609           std::cout << "InjectorStorage: ignoring compressed binding for " << c_id << " because the type " << x_id
610                     << " depends on it." << std::endl;
611 #endif
612         }
613       }
614     }
615   }
616 
617   // Two pairs of compressible bindings (I->C) and (C->X) can not exist (the C of a compressible binding is always bound
618   // either
619   // using constructor binding or provider binding, it can't be a binding itself). So no need to check for that.
620 
621   // Now perform the binding compression.
622   for (auto& entry : compressed_bindings_map) {
623     TypeId c_id = entry.first;
624     TypeId i_id = entry.second.i_type_id;
625     auto i_binding_data = binding_data_map.find(i_id);
626     auto c_binding_data = binding_data_map.find(c_id);
627     FruitAssert(i_binding_data != binding_data_map.end());
628     FruitAssert(c_binding_data != binding_data_map.end());
629     NormalizedComponentStorage::CompressedBindingUndoInfo undo_info;
630     undo_info.i_type_id = i_id;
631     FruitAssert(i_binding_data->second.kind ==
632                 ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION);
633     undo_info.i_binding = i_binding_data->second.binding_for_object_to_construct;
634     FruitAssert(c_binding_data->second.kind ==
635                     ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION ||
636                 c_binding_data->second.kind ==
637                     ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION);
638     undo_info.c_binding = c_binding_data->second.binding_for_object_to_construct;
639     save_compressed_binding_undo_info(c_id, undo_info);
640 
641     // Note that even if I is the one that remains, C is the one that will be allocated, not I.
642 
643     i_binding_data->second.kind = c_binding_data->second.kind;
644     i_binding_data->second.binding_for_object_to_construct.create = entry.second.create_i_with_compression;
645     i_binding_data->second.binding_for_object_to_construct.deps =
646         c_binding_data->second.binding_for_object_to_construct.deps;
647 #if FRUIT_EXTRA_DEBUG
648     i_binding_data->second.binding_for_object_to_construct.is_nonconst |=
649         c_binding_data->second.binding_for_object_to_construct.is_nonconst;
650 #endif
651 
652     binding_data_map.erase(c_binding_data);
653 #if FRUIT_EXTRA_DEBUG
654     std::cout << "InjectorStorage: performing binding compression for the edge " << i_id << "->" << c_id << std::endl;
655 #endif
656   }
657 
658   // Copy the normalized bindings into the result vector.
659   result.reserve(binding_data_map.size());
660   for (auto& p : binding_data_map) {
661     result.push_back(p.second);
662   }
663 
664   return result;
665 }
666 
667 template <typename SaveCompressedBindingUndoInfo, typename SaveFullyExpandedComponentsWithNoArgs,
668           typename SaveFullyExpandedComponentsWithArgs, typename SaveComponentReplacementsWithNoArgs,
669           typename SaveComponentReplacementsWithArgs>
normalizeBindingsWithBindingCompression(FixedSizeVector<ComponentStorageEntry> && toplevel_entries,FixedSizeAllocator::FixedSizeAllocatorData & fixed_size_allocator_data,MemoryPool & memory_pool,MemoryPool & memory_pool_for_fully_expanded_components_maps,MemoryPool & memory_pool_for_component_replacements_maps,const std::vector<TypeId,ArenaAllocator<TypeId>> & exposed_types,std::vector<ComponentStorageEntry,ArenaAllocator<ComponentStorageEntry>> & bindings_vector,std::unordered_map<TypeId,NormalizedMultibindingSet> & multibindings,SaveCompressedBindingUndoInfo save_compressed_binding_undo_info,SaveFullyExpandedComponentsWithNoArgs save_fully_expanded_components_with_no_args,SaveFullyExpandedComponentsWithArgs save_fully_expanded_components_with_args,SaveComponentReplacementsWithNoArgs save_component_replacements_with_no_args,SaveComponentReplacementsWithArgs save_component_replacements_with_args)670 void BindingNormalization::normalizeBindingsWithBindingCompression(
671     FixedSizeVector<ComponentStorageEntry>&& toplevel_entries,
672     FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data, MemoryPool& memory_pool,
673     MemoryPool& memory_pool_for_fully_expanded_components_maps, MemoryPool& memory_pool_for_component_replacements_maps,
674     const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types,
675     std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>& bindings_vector,
676     std::unordered_map<TypeId, NormalizedMultibindingSet>& multibindings,
677     SaveCompressedBindingUndoInfo save_compressed_binding_undo_info,
678     SaveFullyExpandedComponentsWithNoArgs save_fully_expanded_components_with_no_args,
679     SaveFullyExpandedComponentsWithArgs save_fully_expanded_components_with_args,
680     SaveComponentReplacementsWithNoArgs save_component_replacements_with_no_args,
681     SaveComponentReplacementsWithArgs save_component_replacements_with_args) {
682 
683   HashMapWithArenaAllocator<TypeId, ComponentStorageEntry> binding_data_map =
684       createHashMapWithArenaAllocator<TypeId, ComponentStorageEntry>(20 /* capacity */, memory_pool);
685   // CtypeId -> (ItypeId, bindingData)
686   HashMapWithArenaAllocator<TypeId, BindingNormalization::BindingCompressionInfo> compressed_bindings_map =
687       createHashMapWithArenaAllocator<TypeId, BindingCompressionInfo>(20 /* capacity */, memory_pool);
688 
689   multibindings_vector_t multibindings_vector =
690       multibindings_vector_t(ArenaAllocator<multibindings_vector_elem_t>(memory_pool));
691 
692   struct DummyIterator {};
693 
694   normalizeBindings(
695       std::move(toplevel_entries), fixed_size_allocator_data, memory_pool,
696       memory_pool_for_fully_expanded_components_maps, memory_pool_for_component_replacements_maps, binding_data_map,
697       [&compressed_bindings_map](ComponentStorageEntry entry) {
698         BindingCompressionInfo& compression_info = compressed_bindings_map[entry.compressed_binding.c_type_id];
699         compression_info.i_type_id = entry.type_id;
700         compression_info.create_i_with_compression = entry.compressed_binding.create;
701       },
702       [&multibindings_vector](ComponentStorageEntry multibinding, ComponentStorageEntry multibinding_vector_creator) {
703         multibindings_vector.emplace_back(multibinding, multibinding_vector_creator);
704       },
705       [](TypeId) { return DummyIterator(); }, [](DummyIterator) { return false; }, [](DummyIterator) { return false; },
706       [](DummyIterator) { return nullptr; }, [](DummyIterator) { return nullptr; },
707       [](const LazyComponentWithNoArgs&) { return false; }, [](const LazyComponentWithArgs&) { return false; },
708       save_fully_expanded_components_with_no_args, save_fully_expanded_components_with_args,
709       [](const LazyComponentWithNoArgs&) { return (ComponentStorageEntry*)nullptr; },
710       [](const LazyComponentWithArgs&) { return (ComponentStorageEntry*)nullptr; },
711       [](ComponentStorageEntry*) { return false; }, [](ComponentStorageEntry*) { return false; },
712       [](ComponentStorageEntry* p) { return *p; }, [](ComponentStorageEntry* p) { return *p; },
713       save_component_replacements_with_no_args, save_component_replacements_with_args);
714 
715   bindings_vector = BindingNormalization::performBindingCompression(
716       std::move(binding_data_map), std::move(compressed_bindings_map), memory_pool, multibindings_vector, exposed_types,
717       save_compressed_binding_undo_info);
718 
719   addMultibindings(multibindings, fixed_size_allocator_data, multibindings_vector);
720 }
721 
722 } // namespace impl
723 } // namespace fruit
724 
725 #endif // FRUIT_BINDING_NORMALIZATION_TEMPLATES_H
726