11// Licensed to the .NET Foundation under one or more agreements.
22// The .NET Foundation licenses this file to you under the MIT license.
33
4+ using System . Collections . Generic ;
5+
46using Internal . IL ;
57using Internal . TypeSystem ;
68using Internal . TypeSystem . Ecma ;
@@ -11,6 +13,141 @@ namespace ILCompiler
1113{
1214 public partial class CompilerTypeSystemContext
1315 {
16+ private sealed class AsyncAwareVirtualMethodResolutionAlgorithm : MetadataVirtualMethodAlgorithm
17+ {
18+ private readonly CompilerTypeSystemContext _context ;
19+
20+ public AsyncAwareVirtualMethodResolutionAlgorithm ( CompilerTypeSystemContext context )
21+ => _context = context ;
22+
23+ private MethodDesc DecomposeAsyncVariant ( MethodDesc method , out bool isAsyncVariant )
24+ {
25+ isAsyncVariant = method . IsAsyncVariant ( ) ;
26+ return isAsyncVariant ? _context . GetTargetOfAsyncVariantMethod ( method ) : method ;
27+ }
28+
29+ public override MethodDesc FindVirtualFunctionTargetMethodOnObjectType ( MethodDesc targetMethod , TypeDesc objectType )
30+ {
31+ targetMethod = DecomposeAsyncVariant ( targetMethod , out bool isAsyncSlot ) ;
32+ MethodDesc result = base . FindVirtualFunctionTargetMethodOnObjectType ( targetMethod , objectType ) ;
33+ if ( result != null && isAsyncSlot )
34+ result = _context . GetAsyncVariantMethod ( result ) ;
35+
36+ return result ;
37+ }
38+
39+ public override DefaultInterfaceMethodResolution ResolveInterfaceMethodToDefaultImplementationOnType ( MethodDesc interfaceMethod , TypeDesc currentType , out MethodDesc impl )
40+ {
41+ interfaceMethod = DecomposeAsyncVariant ( interfaceMethod , out bool isAsyncSlot ) ;
42+ DefaultInterfaceMethodResolution result = base . ResolveInterfaceMethodToDefaultImplementationOnType ( interfaceMethod , currentType , out impl ) ;
43+ if ( impl != null && isAsyncSlot )
44+ impl = _context . GetAsyncVariantMethod ( impl ) ;
45+
46+ return result ;
47+ }
48+
49+ public override MethodDesc ResolveInterfaceMethodToStaticVirtualMethodOnType ( MethodDesc interfaceMethod , TypeDesc currentType )
50+ {
51+ interfaceMethod = DecomposeAsyncVariant ( interfaceMethod , out bool isAsyncSlot ) ;
52+ MethodDesc result = base . ResolveInterfaceMethodToStaticVirtualMethodOnType ( interfaceMethod , currentType ) ;
53+ if ( result != null && isAsyncSlot )
54+ result = _context . GetAsyncVariantMethod ( result ) ;
55+
56+ return result ;
57+ }
58+ public override MethodDesc ResolveInterfaceMethodToVirtualMethodOnType ( MethodDesc interfaceMethod , TypeDesc currentType )
59+ {
60+ interfaceMethod = DecomposeAsyncVariant ( interfaceMethod , out bool isAsyncSlot ) ;
61+ MethodDesc result = base . ResolveInterfaceMethodToVirtualMethodOnType ( interfaceMethod , currentType ) ;
62+ if ( result != null && isAsyncSlot )
63+ result = _context . GetAsyncVariantMethod ( result ) ;
64+
65+ return result ;
66+ }
67+ public override DefaultInterfaceMethodResolution ResolveVariantInterfaceMethodToDefaultImplementationOnType ( MethodDesc interfaceMethod , TypeDesc currentType , out MethodDesc impl )
68+ {
69+ interfaceMethod = DecomposeAsyncVariant ( interfaceMethod , out bool isAsyncSlot ) ;
70+ DefaultInterfaceMethodResolution result = base . ResolveVariantInterfaceMethodToDefaultImplementationOnType ( interfaceMethod , currentType , out impl ) ;
71+ if ( impl != null && isAsyncSlot )
72+ impl = _context . GetAsyncVariantMethod ( impl ) ;
73+
74+ return result ;
75+ }
76+ public override MethodDesc ResolveVariantInterfaceMethodToStaticVirtualMethodOnType ( MethodDesc interfaceMethod , TypeDesc currentType )
77+ {
78+ interfaceMethod = DecomposeAsyncVariant ( interfaceMethod , out bool isAsyncSlot ) ;
79+ MethodDesc result = base . ResolveVariantInterfaceMethodToStaticVirtualMethodOnType ( interfaceMethod , currentType ) ;
80+ if ( result != null && isAsyncSlot )
81+ result = _context . GetAsyncVariantMethod ( result ) ;
82+
83+ return result ;
84+ }
85+ public override MethodDesc ResolveVariantInterfaceMethodToVirtualMethodOnType ( MethodDesc interfaceMethod , TypeDesc currentType )
86+ {
87+ interfaceMethod = DecomposeAsyncVariant ( interfaceMethod , out bool isAsyncSlot ) ;
88+ MethodDesc result = base . ResolveVariantInterfaceMethodToVirtualMethodOnType ( interfaceMethod , currentType ) ;
89+ if ( result != null && isAsyncSlot )
90+ result = _context . GetAsyncVariantMethod ( result ) ;
91+
92+ return result ;
93+ }
94+
95+ public override IEnumerable < MethodDesc > ComputeAllVirtualSlots ( TypeDesc type )
96+ {
97+ foreach ( MethodDesc method in base . ComputeAllVirtualSlots ( type ) )
98+ {
99+ yield return method ;
100+
101+ // We create an async variant slot for any Task-returning method, not just runtime-async.
102+ // This is not a problem in practice because the slot is still subject to dependency
103+ // analysis and if not used, will not be generated.
104+ //
105+ // The reason why we need it is this:
106+ //
107+ // interface IFoo
108+ // {
109+ // [RuntimeAsyncMethodGeneration(true)]
110+ // Task Method();
111+ // }
112+ //
113+ // class Base
114+ // {
115+ // [RuntimeAsyncMethodGeneration(false)]
116+ // public virtual Task Method();
117+ // }
118+ //
119+ // class Derived : Base, IFoo
120+ // {
121+ // // Q: The runtime-async implementation for IFoo.Method
122+ // // comes from Base. However Base was not runtime-async and we
123+ // // didn't know about IFoo in Base either. Who has the slot?
124+ // // A: Base has the runtime-async slot, despite the method not being runtime-async.
125+ // }
126+ if ( method . GetTypicalMethodDefinition ( ) . Signature . ReturnsTaskOrValueTask ( ) )
127+ yield return _context . GetAsyncVariantMethod ( method ) ;
128+ }
129+ }
130+ }
131+
132+ public MethodDesc GetTargetOfAsyncVariantMethod ( MethodDesc asyncVariantMethod )
133+ {
134+ var asyncMethodVariantDefinition = ( AsyncMethodVariant ) asyncVariantMethod . GetTypicalMethodDefinition ( ) ;
135+ MethodDesc result = asyncMethodVariantDefinition . Target ;
136+
137+ // If there are generics involved, we need to specialize
138+ if ( asyncVariantMethod != asyncMethodVariantDefinition )
139+ {
140+ TypeDesc owningType = asyncVariantMethod . OwningType ;
141+ if ( owningType != asyncMethodVariantDefinition . OwningType )
142+ result = GetMethodForInstantiatedType ( result , ( InstantiatedType ) owningType ) ;
143+
144+ if ( asyncVariantMethod . HasInstantiation && ! asyncVariantMethod . IsMethodDefinition )
145+ result = GetInstantiatedMethod ( result , asyncVariantMethod . Instantiation ) ;
146+ }
147+
148+ return result ;
149+ }
150+
14151 public MethodDesc GetAsyncVariantMethod ( MethodDesc taskReturningMethod )
15152 {
16153 Debug . Assert ( taskReturningMethod . Signature . ReturnsTaskOrValueTask ( ) ) ;
0 commit comments