| File: Compiler\DelegateCreationInfo.cs | Web Access |
| Project: src\runtime\src\coreclr\tools\aot\ILCompiler.Compiler\ILCompiler.Compiler.csproj (ILCompiler.Compiler) |
// 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 Internal.IL; using Internal.TypeSystem; using Internal.Text; using ILCompiler.DependencyAnalysis; using Debug = System.Diagnostics.Debug; namespace ILCompiler { /// <summary> /// Captures information required to generate a ReadyToRun helper to create a delegate type instance /// pointing to a specific target method. /// </summary> public sealed class DelegateCreationInfo { private enum TargetKind { CanonicalEntrypoint, ExactCallableAddress, Dispatch, VTableLookup, ConstrainedMethod, } private TargetKind _targetKind; private TypeDesc _constrainedType; private MethodDesc _targetMethod; /// <summary> /// Gets the node corresponding to the method that initializes the delegate. /// </summary> public IMethodNode Constructor { get; } public MethodDesc TargetMethod { get { Debug.Assert(_constrainedType == null); return _targetMethod; } } // The target method might be constrained if this was a "constrained ldftn" IL instruction. // The real target can be computed after resolving the constraint. public MethodDesc PossiblyUnresolvedTargetMethod { get { return _targetMethod; } } private bool TargetMethodIsUnboxingThunk { get { return TargetMethod.OwningType.IsValueType && !TargetMethod.Signature.IsStatic; } } public bool TargetNeedsVTableLookup => _targetKind == TargetKind.VTableLookup; public bool NeedsVirtualMethodUseTracking { get { return _targetKind == TargetKind.VTableLookup || (_targetKind == TargetKind.Dispatch && !TargetMethod.HasInstantiation); } } public bool NeedsRuntimeLookup { get { switch (_targetKind) { case TargetKind.VTableLookup: return false; case TargetKind.CanonicalEntrypoint: case TargetKind.ExactCallableAddress: case TargetKind.Dispatch: return TargetMethod.IsRuntimeDeterminedExactMethod; case TargetKind.ConstrainedMethod: Debug.Assert(_targetMethod.IsRuntimeDeterminedExactMethod || _constrainedType.IsRuntimeDeterminedSubtype); return true; default: Debug.Assert(false); return false; } } } // None of the data structures that support shared generics have been ported to the JIT // codebase which makes this a huge PITA. Not including the method for JIT since nobody // uses it in that mode anyway. #if !SUPPORT_JIT public GenericLookupResult GetLookupKind(NodeFactory factory) { Debug.Assert(NeedsRuntimeLookup); switch (_targetKind) { case TargetKind.ExactCallableAddress: return factory.GenericLookup.MethodEntry(TargetMethod, TargetMethodIsUnboxingThunk); case TargetKind.Dispatch: return factory.GenericLookup.DispatchCell(TargetMethod); case TargetKind.ConstrainedMethod: return factory.GenericLookup.ConstrainedMethodUse(_targetMethod, _constrainedType, directCall: !_targetMethod.HasInstantiation); default: Debug.Assert(false); return null; } } #endif /// <summary> /// Gets the node representing the target method of the delegate if no runtime lookup is needed. /// </summary> public ISymbolNode GetTargetNode(NodeFactory factory) { Debug.Assert(!NeedsRuntimeLookup); switch (_targetKind) { case TargetKind.CanonicalEntrypoint: return factory.AddressTakenMethodEntrypoint(TargetMethod, TargetMethodIsUnboxingThunk); case TargetKind.ExactCallableAddress: return factory.ExactCallableAddressTakenAddress(TargetMethod, TargetMethodIsUnboxingThunk); case TargetKind.Dispatch: return factory.DispatchCell(TargetMethod); case TargetKind.VTableLookup: Debug.Fail("Need to do runtime lookup"); return null; default: Debug.Assert(false); return null; } } /// <summary> /// Gets an optional node passed as an additional argument to the constructor. /// </summary> public IMethodNode Thunk { get; } public TypeDesc DelegateType { get; } private DelegateCreationInfo(TypeDesc delegateType, IMethodNode constructor, MethodDesc targetMethod, TypeDesc constrainedType, TargetKind targetKind, IMethodNode thunk = null) { Debug.Assert(targetKind != TargetKind.VTableLookup || MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethod) == targetMethod); DelegateType = delegateType; Constructor = constructor; _targetMethod = targetMethod; _constrainedType = constrainedType; _targetKind = targetKind; Thunk = thunk; } /// <summary> /// Constructs a new instance of <see cref="DelegateCreationInfo"/> set up to construct a delegate of type /// '<paramref name="delegateType"/>' pointing to '<paramref name="targetMethod"/>'. /// </summary> public static DelegateCreationInfo Create(TypeDesc delegateType, MethodDesc targetMethod, TypeDesc constrainedType, NodeFactory factory, bool followVirtualDispatch) { CompilerTypeSystemContext context = factory.TypeSystemContext; DefType systemDelegate = context.GetWellKnownType(WellKnownType.MulticastDelegate).BaseType; int paramCountTargetMethod = targetMethod.Signature.Length; if (!targetMethod.Signature.IsStatic) { paramCountTargetMethod++; } DelegateInfo delegateInfo = context.GetDelegateInfo(delegateType.GetTypeDefinition()); int paramCountDelegateClosed = delegateInfo.Signature.Length + 1; bool closed = false; if (paramCountDelegateClosed == paramCountTargetMethod) { closed = true; } else { Debug.Assert(paramCountDelegateClosed == paramCountTargetMethod + 1); } if (targetMethod.Signature.IsStatic) { MethodDesc invokeThunk; MethodDesc initMethod; if (!closed) { initMethod = systemDelegate.GetKnownMethod("InitializeOpenStaticThunk"u8, null); invokeThunk = delegateInfo.Thunks[DelegateThunkKind.OpenStaticThunk]; } else { // Closed delegate to a static method (i.e. delegate to an extension method that locks the first parameter) invokeThunk = delegateInfo.Thunks[DelegateThunkKind.ClosedStaticThunk]; initMethod = systemDelegate.GetKnownMethod("InitializeClosedStaticThunk"u8, null); } var instantiatedDelegateType = delegateType as InstantiatedType; if (instantiatedDelegateType != null) invokeThunk = context.GetMethodForInstantiatedType(invokeThunk, instantiatedDelegateType); return new DelegateCreationInfo( delegateType, factory.MethodEntrypoint(initMethod), targetMethod, constrainedType, constrainedType == null ? TargetKind.ExactCallableAddress : TargetKind.ConstrainedMethod, factory.MethodEntrypoint(invokeThunk)); } else { if (!closed) throw new NotImplementedException("Open instance delegates"); ReadOnlySpan<byte> initializeMethodName = "InitializeClosedInstance"u8; MethodDesc targetCanonMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); TargetKind kind; if (targetMethod.HasInstantiation) { if (followVirtualDispatch && targetMethod.IsVirtual) { initializeMethodName = "InitializeClosedInstanceWithGVMResolution"u8; kind = TargetKind.Dispatch; } else { if (targetMethod != targetCanonMethod) { // Closed delegates to generic instance methods need to be constructed through a slow helper that // checks for the fat function pointer case (function pointer + instantiation argument in a single // pointer) and injects an invocation thunk to unwrap the fat function pointer as part of // the invocation if necessary. initializeMethodName = "InitializeClosedInstanceSlow"u8; } kind = TargetKind.ExactCallableAddress; } } else { if (followVirtualDispatch && targetMethod.IsVirtual) { if (targetMethod.OwningType.IsInterface) { kind = TargetKind.Dispatch; initializeMethodName = "InitializeClosedInstanceToInterface"u8; } else { kind = TargetKind.VTableLookup; targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); } } else { kind = TargetKind.CanonicalEntrypoint; targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); } } Debug.Assert(constrainedType == null); return new DelegateCreationInfo( delegateType, factory.MethodEntrypoint(systemDelegate.GetKnownMethod(initializeMethodName, null)), targetMethod, constrainedType, kind); } } public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append("__DelegateCtor_"u8); if (TargetNeedsVTableLookup) sb.Append("FromVtbl_"u8); Constructor.AppendMangledName(nameMangler, sb); sb.Append("__"u8); sb.Append(nameMangler.GetMangledMethodName(_targetMethod)); if (_constrainedType != null) { sb.Append("__"u8); nameMangler.GetMangledTypeName(_constrainedType); } if (Thunk != null) { sb.Append("__"u8); Thunk.AppendMangledName(nameMangler, sb); } } public override bool Equals(object obj) { var other = obj as DelegateCreationInfo; return other != null && Constructor == other.Constructor && _targetMethod == other._targetMethod && _constrainedType == other._constrainedType && _targetKind == other._targetKind && Thunk == other.Thunk; } public override int GetHashCode() { return Constructor.GetHashCode() ^ _targetMethod.GetHashCode(); } #if !SUPPORT_JIT internal int CompareTo(DelegateCreationInfo other, TypeSystemComparer comparer) { var compare = _targetKind - other._targetKind; if (compare != 0) return compare; compare = comparer.Compare(_targetMethod, other._targetMethod); if (compare != 0) return compare; compare = comparer.Compare(Constructor.Method, other.Constructor.Method); if (compare != 0) return compare; if (_constrainedType != null && other._constrainedType != null) { compare = comparer.Compare(_constrainedType, other._constrainedType); if (compare != 0) return compare; } else { if (_constrainedType != null) return 1; if (other._constrainedType != null) return -1; } if (Thunk == other.Thunk) return 0; if (Thunk == null) return -1; if (other.Thunk == null) return 1; return comparer.Compare(Thunk.Method, other.Thunk.Method); } #endif } }