in src/tool/abi/metadata_cache.cpp [270:420]
void metadata_cache::process_class_dependencies(init_state& state, class_type& type)
{
process_contract_dependencies(*state.target, type.type());
// We only care about instantiations of generic types, so early exit as we won't be able to resolve references
if (type.is_generic())
{
return;
}
if (auto base = try_get_base(type.type()))
{
type.base_class = &dynamic_cast<class_type const&>(this->find(base.TypeNamespace(), base.TypeName()));
}
for (auto const& iface : type.type().InterfaceImpl())
{
process_contract_dependencies(*state.target, iface);
auto ifaceType = &find_dependent_type(state, iface.Interface());
type.required_interfaces.push_back(ifaceType);
// NOTE: Types can have more than one default interface so long as they apply to different platforms. This is
// not very useful, as we have to choose one to use for function argument types, but it technically is
// allowed... If that's the case, just use the first one we encounter as this has the highest probability
// of matching MIDLRT's behavior
if (auto attr = get_attribute(iface, metadata_namespace, "DefaultAttribute"sv); attr && !type.default_interface)
{
type.default_interface = ifaceType;
}
}
if (auto fastAttr = get_attribute(type.type(), metadata_namespace, "FastAbiAttribute"sv))
{
auto attrVer = version_from_attribute(fastAttr);
interface_type* fastInterface = nullptr;
relative_version_map rankingMap;
for (auto const& ifaceImpl : type.type().InterfaceImpl())
{
// If the interface is not exclusive to this class, ignore
auto iface = dynamic_cast<interface_type const*>(&find_dependent_type(state, ifaceImpl.Interface()));
if (!iface || !is_exclusiveto(iface->type()))
{
continue;
}
// Make sure that this interface reference applies for the same versioning "scheme" as the attribute
auto verMatch = match_versioning_scheme(attrVer, ifaceImpl);
if (!verMatch)
{
// No match on the interface reference is okay so long as there is _no_ versioning information on the
// reference. If there's not, then the requirement applies to all versioning schemes, so we look at the
// interface for the versioning information
if (get_attribute(ifaceImpl, metadata_namespace, "ContractVersionAttribute"sv) ||
get_attribute(ifaceImpl, metadata_namespace, "VersionAttribute"sv))
{
continue;
}
verMatch = match_versioning_scheme(attrVer, iface->type());
if (!verMatch)
{
XLANG_ASSERT(false);
continue;
}
}
// Take note if this is the default interface
if (is_default(ifaceImpl))
{
XLANG_ASSERT(!fastInterface);
XLANG_ASSERT(!iface->fast_class);
// NOTE: 'find_dependent_type' returns a const-ref since there are some items that it returns that may
// actually be const. This is not true for 'interface_type', hence the 'const_cast' here is appropriate
fastInterface = const_cast<interface_type*>(iface);
fastInterface->fast_class = &type;
continue;
}
// Ignore this interface if it's overridable, experimental, or in a disabled state
if (is_overridable(ifaceImpl))
{
continue;
}
else if (is_experimental(ifaceImpl) || is_experimental(iface->type()))
{
continue;
}
else if (!is_enabled(ifaceImpl) || !is_enabled(iface->type()))
{
continue;
}
// Determine how this interface's inclusion in the class relates to the class' version history
relative_version relVer = {};
xlang::call(*verMatch,
[&](contract_version const& ver)
{
relVer.first = *type.contract_index(ver.type_name, ver.version);
relVer.second = ver.version;
},
[&](platform_version const& ver)
{
// For platform versioning, the "relative contract" (relVer.first) is always zero
relVer.second = ver.version;
});
process_fastabi_required_interfaces(state, iface, relVer, rankingMap);
}
if (fastInterface)
{
// The fast default interface may have gotten added to the map as a required interface. If so, remove it
if (auto itr = rankingMap.find(fastInterface); itr != rankingMap.end())
{
rankingMap.erase(itr);
}
for (auto& [iface, rank] : rankingMap)
{
version ver;
if (std::holds_alternative<contract_version>(attrVer))
{
ver = contract_version{ type.contract_from_index(rank.first)->type_name, rank.second };
}
else // platform_version
{
ver = platform_version{ std::get<platform_version>(attrVer).platform, rank.second };
}
type.supplemental_fast_interfaces.push_back({ iface, ver });
}
std::sort(type.supplemental_fast_interfaces.begin(), type.supplemental_fast_interfaces.end(),
[&](auto const& lhs, auto const& rhs)
{
auto& [lhsPtr, lhsRank] = *rankingMap.find(lhs.first);
auto& [rhsPtr, rhsRank] = *rankingMap.find(rhs.first);
if (lhsRank == rhsRank)
{
// Same ranking; sort by type name
return lhsPtr->clr_full_name() < rhsPtr->clr_full_name();
}
return lhsRank < rhsRank;
});
}
else
{
XLANG_ASSERT(rankingMap.empty());
}
}
}