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