void metadata_cache::process_class_dependencies()

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());
        }
    }
}