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