|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Internal.IL;
using Internal.Text;
using Internal.TypeSystem;
using ILCompiler.DependencyAnalysisFramework;
namespace ILCompiler.DependencyAnalysis
{
public abstract partial class ReadyToRunGenericHelperNode : AssemblyStubNode, INodeWithRuntimeDeterminedDependencies
{
private readonly ReadyToRunHelperId _id;
private readonly object _target;
protected readonly TypeSystemEntity _dictionaryOwner;
protected readonly GenericLookupResult _lookupSignature;
// True if any of slots in dictionaries associated with this layout could not be filled
// at compile time due to a TypeSystemException. Only query through HandlesInvalidEntries
// below so that we can assert this is not queried at an inappropriate time before
// the whole program view has been established.
private bool _hasInvalidEntries;
public ReadyToRunHelperId Id => _id;
public object Target => _target;
public TypeSystemEntity DictionaryOwner => _dictionaryOwner;
public GenericLookupResult LookupSignature => _lookupSignature;
public bool HandlesInvalidEntries(NodeFactory factory)
{
Debug.Assert(factory.MarkingComplete);
return _hasInvalidEntries;
}
public ReadyToRunGenericHelperNode(NodeFactory factory, ReadyToRunHelperId helperId, object target, TypeSystemEntity dictionaryOwner)
{
Debug.Assert(
(dictionaryOwner is TypeDesc type && type.HasInstantiation)
|| (dictionaryOwner is MethodDesc method && method.HasInstantiation));
_id = helperId;
_dictionaryOwner = dictionaryOwner;
_target = target;
_lookupSignature = GetLookupSignature(factory, helperId, target);
}
public static GenericLookupResult GetLookupSignature(NodeFactory factory, ReadyToRunHelperId id, object target)
{
switch (id)
{
case ReadyToRunHelperId.TypeHandle:
return factory.GenericLookup.Type((TypeDesc)target);
case ReadyToRunHelperId.NecessaryTypeHandle:
return factory.GenericLookup.NecessaryType((TypeDesc)target);
case ReadyToRunHelperId.MetadataTypeHandle:
return factory.GenericLookup.MetadataType((TypeDesc)target);
case ReadyToRunHelperId.TypeHandleForCasting:
// Check that we unwrapped the cases that could be unwrapped to prevent duplicate entries
Debug.Assert(factory.GenericLookup.Type((TypeDesc)target) != factory.GenericLookup.UnwrapNullableType((TypeDesc)target));
return factory.GenericLookup.UnwrapNullableType((TypeDesc)target);
case ReadyToRunHelperId.MethodHandle:
return factory.GenericLookup.MethodHandle((MethodDesc)target);
case ReadyToRunHelperId.FieldHandle:
return factory.GenericLookup.FieldHandle((FieldDesc)target);
case ReadyToRunHelperId.GetGCStaticBase:
return factory.GenericLookup.TypeGCStaticBase((TypeDesc)target);
case ReadyToRunHelperId.GetNonGCStaticBase:
return factory.GenericLookup.TypeNonGCStaticBase((TypeDesc)target);
case ReadyToRunHelperId.GetThreadStaticBase:
return factory.GenericLookup.TypeThreadStaticBaseIndex((TypeDesc)target);
case ReadyToRunHelperId.MethodDictionary:
return factory.GenericLookup.MethodDictionary((MethodDesc)target);
case ReadyToRunHelperId.VirtualDispatchCell:
return factory.GenericLookup.VirtualDispatchCell((MethodDesc)target);
case ReadyToRunHelperId.MethodEntry:
return factory.GenericLookup.MethodEntry((MethodDesc)target);
case ReadyToRunHelperId.DelegateCtor:
return ((DelegateCreationInfo)target).GetLookupKind(factory);
case ReadyToRunHelperId.DefaultConstructor:
return factory.GenericLookup.DefaultCtorLookupResult((TypeDesc)target);
case ReadyToRunHelperId.ObjectAllocator:
return factory.GenericLookup.ObjectAllocator((TypeDesc)target);
case ReadyToRunHelperId.ConstrainedDirectCall:
return factory.GenericLookup.ConstrainedMethodUse(
((ConstrainedCallInfo)target).Method,
((ConstrainedCallInfo)target).ConstrainedType,
directCall: !((ConstrainedCallInfo)target).Method.HasInstantiation);
default:
throw new NotImplementedException();
}
}
protected override bool IsVisibleFromManagedCode => false;
protected sealed override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
public override bool IsShareable => true;
protected sealed override void OnMarked(NodeFactory factory)
{
DictionaryLayoutNode layout = factory.GenericDictionaryLayout(_dictionaryOwner);
if (layout.HasUnfixedSlots)
{
// When the helper call gets marked, ensure the generic layout for the associated dictionaries
// includes the signature.
layout.EnsureEntry(_lookupSignature);
if ((_id == ReadyToRunHelperId.GetGCStaticBase || _id == ReadyToRunHelperId.GetThreadStaticBase) &&
TriggersLazyStaticConstructor(factory))
{
// If the type has a lazy static constructor, we also need the non-GC static base
// because that's where the class constructor context is.
layout.EnsureEntry(factory.GenericLookup.TypeNonGCStaticBase((TypeDesc)_target));
}
}
}
private bool TriggersLazyStaticConstructor(NodeFactory factory)
{
TypeDesc type = (TypeDesc)_target;
return factory.PreinitializationManager.HasLazyStaticConstructor(type.ConvertToCanonForm(CanonicalFormKind.Specific));
}
public IEnumerable<DependencyListEntry> InstantiateDependencies(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, bool isConcreteInstantiation)
{
DependencyList result = new DependencyList();
var lookupContext = new GenericLookupResultContext(_dictionaryOwner, typeInstantiation, methodInstantiation);
switch (_id)
{
case ReadyToRunHelperId.GetGCStaticBase:
case ReadyToRunHelperId.GetThreadStaticBase:
{
// If the type has a lazy static constructor, we also need the non-GC static base
// because that's where the class constructor context is.
if (TriggersLazyStaticConstructor(factory))
{
var lookupTarget = factory.GenericLookup.TypeNonGCStaticBase((TypeDesc)_target).GetTarget(factory, lookupContext, isConcreteInstantiation);
if (lookupTarget != null)
{
result.Add(new DependencyListEntry(lookupTarget, "Dictionary dependency"));
}
}
}
break;
case ReadyToRunHelperId.DelegateCtor:
{
DelegateCreationInfo createInfo = (DelegateCreationInfo)_target;
if (createInfo.NeedsVirtualMethodUseTracking)
{
MethodDesc instantiatedTargetMethod = createInfo.TargetMethod.GetNonRuntimeDeterminedMethodFromRuntimeDeterminedMethodViaSubstitution(typeInstantiation, methodInstantiation);
if (!factory.VTable(instantiatedTargetMethod.OwningType).HasKnownVirtualMethodUse)
{
result.Add(
new DependencyListEntry(
factory.VirtualMethodUse(instantiatedTargetMethod),
"Dictionary dependency"));
}
factory.MetadataManager.GetDependenciesDueToVirtualMethodReflectability(ref result, factory, instantiatedTargetMethod);
}
}
break;
}
try
{
var lookupTarget = _lookupSignature.GetTarget(factory, lookupContext, isConcreteInstantiation);
if (lookupTarget != null)
{
// All generic lookups depend on the thing they point to
result.Add(new DependencyListEntry(lookupTarget, "Dictionary dependency"));
}
}
catch (TypeSystemException)
{
// If there was an exception, we're going to generate a null slot in the associated
// dictionary. The helper needs to be able to handle a null slot and tailcall
// and exception throwing helper instead of returning a result.
_hasInvalidEntries = true;
result.Add(GetBadSlotHelper(factory), "Failure to build dictionary slot");
}
return result.ToArray();
}
private static IMethodNode GetBadSlotHelper(NodeFactory factory)
{
return factory.MethodEntrypoint(factory.TypeSystemContext.GetHelperEntryPoint("ThrowHelpers"u8, "ThrowUnavailableType"u8));
}
protected void AppendLookupSignatureMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
if (_id != ReadyToRunHelperId.DelegateCtor)
{
_lookupSignature.AppendMangledName(nameMangler, sb);
}
else
{
((DelegateCreationInfo)_target).AppendMangledName(nameMangler, sb);
}
}
protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)
{
DependencyList dependencies = new DependencyList();
if (_dictionaryOwner is TypeDesc type)
{
// The generic lookup will need to consult the vtable of the owning type to find the
// vtable slot where the generic dictionary is placed - report the dependency.
dependencies.Add(factory.VTable(type), "Owning type vtable");
}
dependencies.Add(factory.GenericDictionaryLayout(_dictionaryOwner), "Layout");
foreach (DependencyNodeCore<NodeFactory> dependency in _lookupSignature.NonRelocDependenciesFromUsage(factory))
{
dependencies.Add(new DependencyListEntry(dependency, "GenericLookupResultDependency"));
}
if (_id == ReadyToRunHelperId.DelegateCtor)
{
var delegateCreationInfo = (DelegateCreationInfo)_target;
MethodDesc targetMethod = delegateCreationInfo.PossiblyUnresolvedTargetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific);
factory.MetadataManager.GetDependenciesDueToDelegateCreation(ref dependencies, factory, delegateCreationInfo.DelegateType, targetMethod);
}
return dependencies;
}
public override bool HasConditionalStaticDependencies => true;
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory)
{
List<CombinedDependencyListEntry> conditionalDependencies = new List<CombinedDependencyListEntry>();
NativeLayoutSavedVertexNode templateLayout;
if (_dictionaryOwner is MethodDesc)
{
templateLayout = factory.NativeLayout.TemplateMethodLayout((MethodDesc)_dictionaryOwner);
conditionalDependencies.Add(new CombinedDependencyListEntry(_lookupSignature.TemplateDictionaryNode(factory),
templateLayout,
"Type loader template"));
}
else
{
templateLayout = factory.NativeLayout.TemplateTypeLayout((TypeDesc)_dictionaryOwner);
conditionalDependencies.Add(new CombinedDependencyListEntry(_lookupSignature.TemplateDictionaryNode(factory),
templateLayout,
"Type loader template"));
}
if (_id == ReadyToRunHelperId.GetGCStaticBase || _id == ReadyToRunHelperId.GetThreadStaticBase)
{
// If the type has a lazy static constructor, we also need the non-GC static base to be available as
// a template dictionary node.
TypeDesc type = (TypeDesc)_target;
Debug.Assert(templateLayout != null);
if (TriggersLazyStaticConstructor(factory))
{
GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(type);
conditionalDependencies.Add(new CombinedDependencyListEntry(nonGcRegionLookup.TemplateDictionaryNode(factory),
templateLayout,
"Type loader template"));
}
}
if (_id == ReadyToRunHelperId.DelegateCtor)
{
var delegateCreationInfo = (DelegateCreationInfo)_target;
MethodDesc targetMethod = delegateCreationInfo.PossiblyUnresolvedTargetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific);
factory.MetadataManager.GetDependenciesDueToDelegateCreation(ref conditionalDependencies, factory, delegateCreationInfo.DelegateType, targetMethod);
}
return conditionalDependencies;
}
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
{
var compare = _id.CompareTo(((ReadyToRunGenericHelperNode)other)._id);
if (compare != 0)
return compare;
if (_dictionaryOwner is MethodDesc)
{
if (((ReadyToRunGenericHelperNode)other)._dictionaryOwner is TypeDesc)
return -1;
compare = comparer.Compare((MethodDesc)_dictionaryOwner, (MethodDesc)((ReadyToRunGenericHelperNode)other)._dictionaryOwner);
}
else
{
if (((ReadyToRunGenericHelperNode)other)._dictionaryOwner is MethodDesc)
return 1;
compare = comparer.Compare((TypeDesc)_dictionaryOwner, (TypeDesc)((ReadyToRunGenericHelperNode)other)._dictionaryOwner);
}
if (compare != 0)
return compare;
switch (_id)
{
case ReadyToRunHelperId.TypeHandle:
case ReadyToRunHelperId.NecessaryTypeHandle:
case ReadyToRunHelperId.GetGCStaticBase:
case ReadyToRunHelperId.GetNonGCStaticBase:
case ReadyToRunHelperId.GetThreadStaticBase:
case ReadyToRunHelperId.DefaultConstructor:
case ReadyToRunHelperId.ObjectAllocator:
return comparer.Compare((TypeDesc)_target, (TypeDesc)((ReadyToRunGenericHelperNode)other)._target);
case ReadyToRunHelperId.MethodHandle:
case ReadyToRunHelperId.MethodDictionary:
case ReadyToRunHelperId.VirtualDispatchCell:
case ReadyToRunHelperId.MethodEntry:
return comparer.Compare((MethodDesc)_target, (MethodDesc)((ReadyToRunGenericHelperNode)other)._target);
case ReadyToRunHelperId.FieldHandle:
return comparer.Compare((FieldDesc)_target, (FieldDesc)((ReadyToRunGenericHelperNode)other)._target);
case ReadyToRunHelperId.DelegateCtor:
return ((DelegateCreationInfo)_target).CompareTo((DelegateCreationInfo)((ReadyToRunGenericHelperNode)other)._target, comparer);
case ReadyToRunHelperId.ConstrainedDirectCall:
return ((ConstrainedCallInfo)_target).CompareTo((ConstrainedCallInfo)((ReadyToRunGenericHelperNode)other)._target, comparer);
default:
throw new NotImplementedException();
}
}
}
public partial class ReadyToRunGenericLookupFromDictionaryNode : ReadyToRunGenericHelperNode
{
public ReadyToRunGenericLookupFromDictionaryNode(NodeFactory factory, ReadyToRunHelperId helperId, object target, TypeSystemEntity dictionaryOwner)
: base(factory, helperId, target, dictionaryOwner)
{
}
public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
Utf8String mangledContextName;
if (_dictionaryOwner is MethodDesc)
mangledContextName = nameMangler.GetMangledMethodName((MethodDesc)_dictionaryOwner);
else
mangledContextName = nameMangler.GetMangledTypeName((TypeDesc)_dictionaryOwner);
sb.Append("__GenericLookupFromDict_"u8).Append(mangledContextName).Append("_"u8);
AppendLookupSignatureMangledName(nameMangler, sb);
}
public override int ClassCode => 1055354299;
}
public partial class ReadyToRunGenericLookupFromTypeNode : ReadyToRunGenericHelperNode
{
public ReadyToRunGenericLookupFromTypeNode(NodeFactory factory, ReadyToRunHelperId helperId, object target, TypeSystemEntity dictionaryOwner)
: base(factory, helperId, target, dictionaryOwner)
{
}
public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
Utf8String mangledContextName;
if (_dictionaryOwner is MethodDesc)
mangledContextName = nameMangler.GetMangledMethodName((MethodDesc)_dictionaryOwner);
else
mangledContextName = nameMangler.GetMangledTypeName((TypeDesc)_dictionaryOwner);
sb.Append("__GenericLookupFromType_"u8).Append(mangledContextName).Append("_"u8);
AppendLookupSignatureMangledName(nameMangler, sb);
}
public override int ClassCode => 913214059;
}
}
|