void ComponentStorage::createTypeInfo(TypeIndex typeIndex, void* storedSingleton, void (*destroy)(void*)) { auto itr = typeRegistry.find(typeIndex); if (itr == typeRegistry.end()) { // This type wasn't registered yet, register it. TypeInfo& typeInfo = typeRegistry[typeIndex]; typeInfo.storedSingleton = storedSingleton; typeInfo.destroy = destroy; } else { // This type was already registered. TypeInfo& ourInfo = itr->second; check(ourInfo.storedSingleton != nullptr, [=](){ return multipleBindingsError(typeIndex); }); // At this point it's guaranteed that ourInfo.storedSingleton!=nullptr. // If the stored singletons and destroy operations are equal ok, but if they aren't we // can't be sure that they're equivalent and we abort. bool equal = ourInfo.storedSingleton == storedSingleton && ourInfo.destroy == destroy; check(equal, [=](){ return multipleBindingsError(typeIndex); }); } }
void ComponentStorage::createTypeInfo(TypeIndex typeIndex, std::pair<void*, void(*)(void*)> (*create)(ComponentStorage&, void*), void* createArgument) { auto itr = typeRegistry.find(typeIndex); if (itr == typeRegistry.end()) { // This type wasn't registered yet, register it. TypeInfo& typeInfo = typeRegistry[typeIndex]; typeInfo.create = create; typeInfo.createArgument = createArgument; typeInfo.storedSingleton = nullptr; } else { // This type was already registered. TypeInfo& ourInfo = itr->second; check(ourInfo.storedSingleton == nullptr, [=](){ return multipleBindingsError(typeIndex); }); // At this point it's guaranteed that ourInfo.storedSingleton==nullptr. // If the create operations and arguments are equal ok, but if they aren't we // can't be sure that they're equivalent and we abort. bool equal = ourInfo.create == create && ourInfo.createArgument == createArgument; check(equal, [=](){ return multipleBindingsError(typeIndex); }); } }
void InjectorStorage::normalizeBindings(std::vector<std::pair<TypeId, BindingData>>& bindings_vector, FixedSizeAllocator::FixedSizeAllocatorData& fixed_size_allocator_data, std::vector<CompressedBinding>&& compressed_bindings_vector, const std::vector<std::pair<TypeId, MultibindingData>>& multibindings_vector, const std::vector<TypeId>& exposed_types, BindingCompressionInfoMap& bindingCompressionInfoMap) { std::unordered_map<TypeId, BindingData> binding_data_map; for (auto& p : bindings_vector) { auto itr = binding_data_map.find(p.first); if (itr != binding_data_map.end()) { if (!(p.second == itr->second)) { std::cerr << multipleBindingsError(p.first) << std::endl; exit(1); } // Otherwise ok, duplicate but consistent binding. } else { // New binding, add it to the map. binding_data_map[p.first] = p.second; } } for (const auto& p : bindings_vector) { if (p.second.needsAllocation()) { fixed_size_allocator_data.addType(p.first); } else { fixed_size_allocator_data.addExternallyAllocatedType(p.first); } } // Remove duplicates from `compressedBindingsVector'. // CtypeId -> (ItypeId, bindingData) std::unordered_map<TypeId, std::pair<TypeId, BindingData>> compressed_bindings_map; // This also removes any duplicates. No need to check for multiple I->C, I2->C mappings, will filter these out later when // considering deps. for (CompressedBinding& compressed_binding : compressed_bindings_vector) { compressed_bindings_map[compressed_binding.class_id] = {compressed_binding.interface_id, compressed_binding.binding_data}; } // We can't compress the binding if C is a dep of a multibinding. for (auto p : multibindings_vector) { const BindingDeps* deps = p.second.deps; if (deps != nullptr) { for (std::size_t i = 0; i < deps->num_deps; ++i) { compressed_bindings_map.erase(deps->deps[i]); } } } // We can't compress the binding if C is an exposed type (but I is likely to be exposed instead). for (TypeId type : exposed_types) { compressed_bindings_map.erase(type); } // We can't compress the binding if some type X depends on C and X!=I. for (auto& p : binding_data_map) { TypeId x_id = p.first; BindingData binding_data = p.second; if (!binding_data.isCreated()) { for (std::size_t i = 0; i < binding_data.getDeps()->num_deps; ++i) { TypeId c_id = binding_data.getDeps()->deps[i]; auto itr = compressed_bindings_map.find(c_id); if (itr != compressed_bindings_map.end() && itr->second.first != x_id) { compressed_bindings_map.erase(itr); } } } } // Two pairs of compressible bindings (I->C) and (C->X) can not exist (the C of a compressible binding is always bound either // using constructor binding or provider binding, it can't be a binding itself). So no need to check for that. // Now perform the binding compression. for (auto& p : compressed_bindings_map) { TypeId c_id = p.first; TypeId i_id = p.second.first; BindingData binding_data = p.second.second; auto i_binding_data = binding_data_map.find(i_id); auto c_binding_data = binding_data_map.find(c_id); assert(i_binding_data != binding_data_map.end()); assert(c_binding_data != binding_data_map.end()); bindingCompressionInfoMap[c_id] = BindingCompressionInfo{i_id, i_binding_data->second, c_binding_data->second}; // Note that even if I is the one that remains, C is the one that will be allocated, not I. assert(!i_binding_data->second.needsAllocation()); i_binding_data->second = binding_data; binding_data_map.erase(c_binding_data); #ifdef FRUIT_EXTRA_DEBUG std::cout << "InjectorStorage: performing binding compression for the edge " << i_id << "->" << c_id << std::endl; #endif } // Copy the resulting bindings back into the vector. bindings_vector.clear(); for (auto& p : binding_data_map) { bindings_vector.push_back(p); } }
InjectorStorage::InjectorStorage(const NormalizedComponentStorage& normalized_component, ComponentStorage&& component, std::vector<TypeId>&& exposed_types) : multibindings(normalized_component.multibindings) { FixedSizeAllocator::FixedSizeAllocatorData fixed_size_allocator_data = normalized_component.fixed_size_allocator_data; std::vector<std::pair<TypeId, BindingData>> component_bindings(std::move(component.bindings)); // Step 1: Remove duplicates among the new bindings, and check for inconsistent bindings within `component' alone. // Note that we do NOT use component.compressed_bindings here, to avoid having to check if these compressions can be undone. // We don't expect many binding compressions here that weren't already performed in the normalized component. BindingCompressionInfoMap bindingCompressionInfoMapUnused; normalizeBindings(component_bindings, fixed_size_allocator_data, std::vector<CompressedBinding>{}, component.multibindings, std::move(exposed_types), bindingCompressionInfoMapUnused); assert(bindingCompressionInfoMapUnused.empty()); std::unordered_set<TypeId> binding_compressions_to_undo; // Step 2: Filter out already-present bindings, and check for inconsistent bindings between `normalizedComponent' and // `component'. Also determine what binding compressions must be undone auto itr = std::remove_if(component_bindings.begin(), component_bindings.end(), [&normalized_component, &binding_compressions_to_undo](const std::pair<TypeId, BindingData>& p) { if (!p.second.isCreated()) { for (std::size_t i = 0; i < p.second.getDeps()->num_deps; ++i) { auto binding_compression_itr = normalized_component.bindingCompressionInfoMap.find(p.second.getDeps()->deps[i]); if (binding_compression_itr != normalized_component.bindingCompressionInfoMap.end() && binding_compression_itr->second.iTypeId != p.first) { // The binding compression for `p.second.getDeps()->deps[i]' must be undone because something // different from binding_compression_itr->iTypeId is now bound to it. binding_compressions_to_undo.insert(p.second.getDeps()->deps[i]); } } } auto node_itr = normalized_component.bindings.find(p.first); if (node_itr == normalized_component.bindings.end()) { // Not bound yet, keep the new binding. return false; } if (!(node_itr.getNode() == NormalizedBindingData(p.second))) { std::cerr << multipleBindingsError(p.first) << std::endl; exit(1); } // Already bound in the same way. Skip the new binding. return true; }); component_bindings.erase(itr, component_bindings.end()); // Step 3: undo any binding compressions that can no longer be applied. for (TypeId cTypeId : binding_compressions_to_undo) { auto binding_compression_itr = normalized_component.bindingCompressionInfoMap.find(cTypeId); assert(binding_compression_itr != normalized_component.bindingCompressionInfoMap.end()); assert(!binding_compression_itr->second.iBinding.needsAllocation()); component_bindings.emplace_back(cTypeId, binding_compression_itr->second.cBinding); // This TypeId is already in normalized_component.bindings, we overwrite it here. assert(!(normalized_component.bindings.find(binding_compression_itr->second.iTypeId) == normalized_component.bindings.end())); component_bindings.emplace_back(binding_compression_itr->second.iTypeId, binding_compression_itr->second.iBinding); #ifdef FRUIT_EXTRA_DEBUG std::cout << "InjectorStorage: undoing binding compression for: " << binding_compression_itr->second.iTypeId << "->" << cTypeId << std::endl; #endif } bindings = Graph(normalized_component.bindings, BindingDataNodeIter{component_bindings.begin()}, BindingDataNodeIter{component_bindings.end()}); // Step 4: Add multibindings. addMultibindings(multibindings, fixed_size_allocator_data, std::move(component.multibindings)); allocator = FixedSizeAllocator(fixed_size_allocator_data); #ifdef FRUIT_EXTRA_DEBUG bindings.checkFullyConstructed(); #endif }