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