diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs
index 7a242497f30826..9a1b1ad7ef985a 100644
--- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs
+++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs
@@ -82,14 +82,46 @@ private static IntPtr RhResolveDispatch(object pObject, MethodTable* interfaceTy
}
[RuntimeExport("RhResolveDispatchOnType")]
- private static IntPtr RhResolveDispatchOnType(MethodTable* pInstanceType, MethodTable* pInterfaceType, ushort slot, MethodTable** ppGenericContext)
+ private static IntPtr RhResolveDispatchOnType(MethodTable* pInstanceType, MethodTable* pInterfaceType, ushort slot)
{
return DispatchResolve.FindInterfaceMethodImplementationTarget(pInstanceType,
pInterfaceType,
slot,
+ flags: default,
+ ppGenericContext: null);
+ }
+
+ [RuntimeExport("RhResolveStaticDispatchOnType")]
+ private static IntPtr RhResolveStaticDispatchOnType(MethodTable* pInstanceType, MethodTable* pInterfaceType, ushort slot, MethodTable** ppGenericContext)
+ {
+ return DispatchResolve.FindInterfaceMethodImplementationTarget(pInstanceType,
+ pInterfaceType,
+ slot,
+ DispatchResolve.ResolveFlags.Static,
ppGenericContext);
}
+ [RuntimeExport("RhResolveDynamicInterfaceCastableDispatchOnType")]
+ private static IntPtr RhResolveDynamicInterfaceCastableDispatchOnType(MethodTable* pInstanceType, MethodTable* pInterfaceType, ushort slot, MethodTable** ppGenericContext)
+ {
+ IntPtr result = DispatchResolve.FindInterfaceMethodImplementationTarget(pInstanceType,
+ pInterfaceType,
+ slot,
+ DispatchResolve.ResolveFlags.IDynamicInterfaceCastable,
+ ppGenericContext);
+
+ if ((result & (nint)DispatchMapCodePointerFlags.RequiresInstantiatingThunkFlag) != 0)
+ {
+ result &= ~(nint)DispatchMapCodePointerFlags.RequiresInstantiatingThunkFlag;
+ }
+ else
+ {
+ *ppGenericContext = null;
+ }
+
+ return result;
+ }
+
private static unsafe IntPtr RhResolveDispatchWorker(object pObject, void* cell, ref DispatchCellInfo cellInfo)
{
// Type of object we're dispatching on.
@@ -97,13 +129,10 @@ private static unsafe IntPtr RhResolveDispatchWorker(object pObject, void* cell,
if (cellInfo.CellType == DispatchCellType.InterfaceAndSlot)
{
- // Type whose DispatchMap is used. Usually the same as the above but for types which implement IDynamicInterfaceCastable
- // we may repeat this process with an alternate type.
- MethodTable* pResolvingInstanceType = pInstanceType;
-
- IntPtr pTargetCode = DispatchResolve.FindInterfaceMethodImplementationTarget(pResolvingInstanceType,
+ IntPtr pTargetCode = DispatchResolve.FindInterfaceMethodImplementationTarget(pInstanceType,
cellInfo.InterfaceType,
cellInfo.InterfaceSlot,
+ flags: default,
ppGenericContext: null);
if (pTargetCode == IntPtr.Zero && pInstanceType->IsIDynamicInterfaceCastable)
{
diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/DispatchResolve.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/DispatchResolve.cs
index 349415c743aa9a..271fdd231c611a 100644
--- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/DispatchResolve.cs
+++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/DispatchResolve.cs
@@ -13,21 +13,21 @@ internal static unsafe class DispatchResolve
public static IntPtr FindInterfaceMethodImplementationTarget(MethodTable* pTgtType,
MethodTable* pItfType,
ushort itfSlotNumber,
+ ResolveFlags flags,
/* out */ MethodTable** ppGenericContext)
{
+ // We set this bit below during second pass, callers should not set it.
+ Debug.Assert((flags & ResolveFlags.DefaultInterfaceImplementation) == 0);
+
// Start at the current type and work up the inheritance chain
MethodTable* pCur = pTgtType;
- // We first look at non-default implementation. Default implementations are only considered
- // if the "old algorithm" didn't come up with an answer.
- bool fDoDefaultImplementationLookup = false;
-
again:
while (pCur != null)
{
ushort implSlotNumber;
if (FindImplSlotForCurrentType(
- pCur, pItfType, itfSlotNumber, fDoDefaultImplementationLookup, &implSlotNumber, ppGenericContext))
+ pCur, pItfType, itfSlotNumber, flags, &implSlotNumber, ppGenericContext))
{
IntPtr targetMethod;
if (implSlotNumber < pCur->NumVtableSlots)
@@ -58,9 +58,9 @@ public static IntPtr FindInterfaceMethodImplementationTarget(MethodTable* pTgtTy
}
// If we haven't found an implementation, do a second pass looking for a default implementation.
- if (!fDoDefaultImplementationLookup)
+ if ((flags & ResolveFlags.DefaultInterfaceImplementation) == 0)
{
- fDoDefaultImplementationLookup = true;
+ flags |= ResolveFlags.DefaultInterfaceImplementation;
pCur = pTgtType;
goto again;
}
@@ -72,10 +72,13 @@ public static IntPtr FindInterfaceMethodImplementationTarget(MethodTable* pTgtTy
private static bool FindImplSlotForCurrentType(MethodTable* pTgtType,
MethodTable* pItfType,
ushort itfSlotNumber,
- bool fDoDefaultImplementationLookup,
+ ResolveFlags flags,
ushort* pImplSlotNumber,
MethodTable** ppGenericContext)
{
+ // We set this below during second pass, callers should not set this.
+ Debug.Assert((flags & ResolveFlags.Variant) == 0);
+
bool fRes = false;
// If making a call and doing virtual resolution don't look into the dispatch map,
@@ -96,16 +99,14 @@ private static bool FindImplSlotForCurrentType(MethodTable* pTgtType,
// result in interesting behavior such as a derived type only overriding one particular instantiation
// and funneling all the dispatches to it, but its the algorithm.
- bool fDoVariantLookup = false; // do not check variance for first scan of dispatch map
-
fRes = FindImplSlotInSimpleMap(
- pTgtType, pItfType, itfSlotNumber, pImplSlotNumber, ppGenericContext, fDoVariantLookup, fDoDefaultImplementationLookup);
+ pTgtType, pItfType, itfSlotNumber, pImplSlotNumber, ppGenericContext, flags);
if (!fRes)
{
- fDoVariantLookup = true; // check variance for second scan of dispatch map
+ flags |= ResolveFlags.Variant; // check variance for second scan of dispatch map
fRes = FindImplSlotInSimpleMap(
- pTgtType, pItfType, itfSlotNumber, pImplSlotNumber, ppGenericContext, fDoVariantLookup, fDoDefaultImplementationLookup);
+ pTgtType, pItfType, itfSlotNumber, pImplSlotNumber, ppGenericContext, flags);
}
}
@@ -117,8 +118,7 @@ private static bool FindImplSlotInSimpleMap(MethodTable* pTgtType,
uint itfSlotNumber,
ushort* pImplSlotNumber,
MethodTable** ppGenericContext,
- bool actuallyCheckVariance,
- bool checkDefaultImplementations)
+ ResolveFlags flags)
{
Debug.Assert(pTgtType->HasDispatchMap, "Missing dispatch map");
@@ -130,7 +130,7 @@ private static bool FindImplSlotInSimpleMap(MethodTable* pTgtType,
bool fCheckVariance = false;
bool fArrayCovariance = false;
- if (actuallyCheckVariance)
+ if ((flags & ResolveFlags.Variant) != 0)
{
fCheckVariance = pItfType->HasGenericVariance;
fArrayCovariance = pTgtType->IsArray;
@@ -166,8 +166,8 @@ private static bool FindImplSlotInSimpleMap(MethodTable* pTgtType,
}
}
- // It only makes sense to ask for generic context if we're asking about a static method
- bool fStaticDispatch = ppGenericContext != null;
+ bool fStaticDispatch = (flags & ResolveFlags.Static) != 0;
+ bool checkDefaultImplementations = (flags & ResolveFlags.DefaultInterfaceImplementation) != 0;
// We either scan the instance or static portion of the dispatch map. Depends on what the caller wants.
DispatchMap* pMap = pTgtType->DispatchMap;
@@ -190,8 +190,11 @@ private static bool FindImplSlotInSimpleMap(MethodTable* pTgtType,
// If this is a static method, the entry point is not usable without generic context.
// (Instance methods acquire the generic context from their `this`.)
+ // Same for IDynamicInterfaceCastable (that has a `this` but it's not useful)
if (fStaticDispatch)
*ppGenericContext = GetGenericContextSource(pTgtType, i);
+ else if ((flags & ResolveFlags.IDynamicInterfaceCastable) != 0)
+ *ppGenericContext = pTgtType;
return true;
}
@@ -231,8 +234,11 @@ private static bool FindImplSlotInSimpleMap(MethodTable* pTgtType,
// If this is a static method, the entry point is not usable without generic context.
// (Instance methods acquire the generic context from their `this`.)
+ // Same for IDynamicInterfaceCastable (that has a `this` but it's not useful)
if (fStaticDispatch)
*ppGenericContext = GetGenericContextSource(pTgtType, i);
+ else if ((flags & ResolveFlags.IDynamicInterfaceCastable) != 0)
+ *ppGenericContext = pTgtType;
return true;
}
@@ -253,5 +259,13 @@ private static bool FindImplSlotInSimpleMap(MethodTable* pTgtType,
_ => pTgtType->InterfaceMap[usEncodedValue - StaticVirtualMethodContextSource.ContextFromFirstInterface]
};
}
+
+ public enum ResolveFlags
+ {
+ Variant = 0x1,
+ DefaultInterfaceImplementation = 0x2,
+ Static = 0x4,
+ IDynamicInterfaceCastable = 0x8,
+ }
}
}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
index 507b4c14936921..6bfe8b6b9ffdda 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/CompatibilitySuppressions.xml
@@ -825,6 +825,10 @@
CP0001
T:System.Runtime.CompilerServices.StaticClassConstructionContext
+
+ CP0001
+ T:Internal.TypeSystem.LockFreeReaderHashtableOfPointers`2
+
CP0002
M:System.Reflection.MethodBase.GetParametersAsSpan
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs
index 7db11a82041778..4e376c5b803922 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs
@@ -493,7 +493,7 @@ public static bool IsDynamicType(RuntimeTypeHandle typeHandle)
public static unsafe IntPtr ResolveStaticDispatchOnType(RuntimeTypeHandle instanceType, RuntimeTypeHandle interfaceType, int slot, out RuntimeTypeHandle genericContext)
{
MethodTable* genericContextPtr = default;
- IntPtr result = RuntimeImports.RhResolveDispatchOnType(instanceType.ToMethodTable(), interfaceType.ToMethodTable(), checked((ushort)slot), &genericContextPtr);
+ IntPtr result = RuntimeImports.RhResolveStaticDispatchOnType(instanceType.ToMethodTable(), interfaceType.ToMethodTable(), checked((ushort)slot), &genericContextPtr);
if (result != IntPtr.Zero)
genericContext = new RuntimeTypeHandle(genericContextPtr);
else
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SharedCodeHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SharedCodeHelpers.cs
index 482a9a7b357a7e..172f7a6590e35d 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SharedCodeHelpers.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SharedCodeHelpers.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Runtime;
+
using Debug = System.Diagnostics.Debug;
namespace Internal.Runtime.CompilerHelpers
@@ -12,15 +14,13 @@ internal static class SharedCodeHelpers
{
public static unsafe MethodTable* GetOrdinalInterface(MethodTable* pType, ushort interfaceIndex)
{
- Debug.Assert(interfaceIndex <= pType->NumInterfaces);
+ Debug.Assert(interfaceIndex < pType->NumInterfaces);
return pType->InterfaceMap[interfaceIndex];
}
public static unsafe MethodTable* GetCurrentSharedThunkContext()
{
- // TODO: We should return the current context from the ThunkPool
- // https://github.com/dotnet/runtimelab/issues/1442
- return null;
+ return (MethodTable*)RuntimeImports.GetCurrentInteropThunkContext();
}
}
}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/IDynamicInterfaceCastableSupport.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/IDynamicInterfaceCastableSupport.cs
index 54878128ff9823..a1e146fb1b0a8c 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/IDynamicInterfaceCastableSupport.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/IDynamicInterfaceCastableSupport.cs
@@ -4,6 +4,12 @@
using System;
using System.Runtime;
using System.Runtime.InteropServices;
+using System.Threading;
+
+using Internal.Runtime.Augments;
+using Internal.TypeSystem;
+
+using Debug = System.Diagnostics.Debug;
namespace Internal.Runtime
{
@@ -15,6 +21,8 @@ internal static bool IDynamicCastableIsInterfaceImplemented(IDynamicInterfaceCas
return instance.IsInterfaceImplemented(new RuntimeTypeHandle(interfaceType), throwIfNotImplemented);
}
+ private static readonly object s_thunkPoolHeap = RuntimeAugments.CreateThunksHeap(RuntimeImports.GetInteropCommonStubAddress());
+
[RuntimeExport("IDynamicCastableGetInterfaceImplementation")]
internal static IntPtr IDynamicCastableGetInterfaceImplementation(IDynamicInterfaceCastable instance, MethodTable* interfaceType, ushort slot)
{
@@ -28,11 +36,30 @@ internal static IntPtr IDynamicCastableGetInterfaceImplementation(IDynamicInterf
{
ThrowInvalidOperationException(implType);
}
- IntPtr result = RuntimeImports.RhResolveDispatchOnType(implType, interfaceType, slot);
+
+ MethodTable* genericContext = null;
+ IntPtr result = RuntimeImports.RhResolveDynamicInterfaceCastableDispatchOnType(implType, interfaceType, slot, &genericContext);
if (result == IntPtr.Zero)
{
IDynamicCastableGetInterfaceImplementationFailure(instance, interfaceType, implType);
}
+
+ if (genericContext != null)
+ {
+ if (!s_thunkHashtable.TryGetValue(new InstantiatingThunkKey(result, (nint)genericContext), out nint thunk))
+ {
+ thunk = RuntimeAugments.AllocateThunk(s_thunkPoolHeap);
+ RuntimeAugments.SetThunkData(s_thunkPoolHeap, thunk, (nint)genericContext, result);
+ nint thunkInHashtable = s_thunkHashtable.AddOrGetExisting(thunk);
+ if (thunkInHashtable != thunk)
+ {
+ RuntimeAugments.FreeThunk(s_thunkPoolHeap, thunk);
+ thunk = thunkInHashtable;
+ }
+ }
+
+ result = thunk;
+ }
return result;
}
@@ -67,5 +94,46 @@ private static void IDynamicCastableGetInterfaceImplementationFailure(object ins
throw new EntryPointNotFoundException();
}
+
+ private static readonly InstantiatingThunkHashtable s_thunkHashtable = new InstantiatingThunkHashtable();
+
+ private class InstantiatingThunkHashtable : LockFreeReaderHashtableOfPointers
+ {
+ protected override bool CompareKeyToValue(InstantiatingThunkKey key, nint value)
+ {
+ bool result = RuntimeAugments.TryGetThunkData(s_thunkPoolHeap, value, out nint context, out nint target);
+ Debug.Assert(result);
+ return key.Target == target && key.Context == context;
+ }
+
+ protected override bool CompareValueToValue(nint value1, nint value2)
+ {
+ bool result1 = RuntimeAugments.TryGetThunkData(s_thunkPoolHeap, value1, out nint context1, out nint target1);
+ Debug.Assert(result1);
+
+ bool result2 = RuntimeAugments.TryGetThunkData(s_thunkPoolHeap, value2, out nint context2, out nint target2);
+ Debug.Assert(result2);
+ return context1 == context2 && target1 == target2;
+ }
+
+ protected override nint ConvertIntPtrToValue(nint pointer) => pointer;
+ protected override nint ConvertValueToIntPtr(nint value) => value;
+ protected override nint CreateValueFromKey(InstantiatingThunkKey key) => throw new NotImplementedException();
+ protected override int GetKeyHashCode(InstantiatingThunkKey key) => HashCode.Combine(key.Target, key.Context);
+
+ protected override int GetValueHashCode(nint value)
+ {
+ bool result = RuntimeAugments.TryGetThunkData(s_thunkPoolHeap, value, out nint context, out nint target);
+ Debug.Assert(result);
+ return HashCode.Combine(target, context);
+ }
+ }
+
+ private struct InstantiatingThunkKey
+ {
+ public readonly nint Target;
+ public readonly nint Context;
+ public InstantiatingThunkKey(nint target, nint context) => (Target, Context) = (target, context);
+ }
}
}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
index fc97632716fd2a..06d92c3b8b5d5e 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
@@ -332,6 +332,9 @@
Utilities\LockFreeReaderHashtable.cs
+
+ Utilities\LockFreeReaderHashtableOfPointers.cs
+
System\Collections\Generic\LowLevelList.cs
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs
index 7f833e613e5c4b..52fe3bbaa5ad6f 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs
@@ -469,12 +469,15 @@ internal static unsafe int RhCompatibleReentrantWaitAny(bool alertable, int time
[MethodImplAttribute(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhResolveDispatchOnType")]
- internal static extern unsafe IntPtr RhResolveDispatchOnType(MethodTable* instanceType, MethodTable* interfaceType, ushort slot, MethodTable** pGenericContext);
+ internal static extern unsafe IntPtr RhResolveDispatchOnType(MethodTable* instanceType, MethodTable* interfaceType, ushort slot);
- internal static unsafe IntPtr RhResolveDispatchOnType(MethodTable* instanceType, MethodTable* interfaceType, ushort slot)
- {
- return RhResolveDispatchOnType(instanceType, interfaceType, slot, null);
- }
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "RhResolveStaticDispatchOnType")]
+ internal static extern unsafe IntPtr RhResolveStaticDispatchOnType(MethodTable* instanceType, MethodTable* interfaceType, ushort slot, MethodTable** pGenericContext);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "RhResolveDynamicInterfaceCastableDispatchOnType")]
+ internal static extern unsafe IntPtr RhResolveDynamicInterfaceCastableDispatchOnType(MethodTable* instanceType, MethodTable* interfaceType, ushort slot, MethodTable** pGenericContext);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhGetRuntimeHelperForType")]
diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj
index b00904e556f2ca..09881516e2950c 100644
--- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj
+++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj
@@ -200,9 +200,6 @@
Internal\TypeSystem\ThrowHelper.Common.cs
-
- LockFreeReaderHashtableOfPointers.cs
-
Internal\TypeSystem\Utilities\ExceptionTypeNameFormatter.cs
diff --git a/src/coreclr/tools/Common/Internal/Runtime/RuntimeConstants.cs b/src/coreclr/tools/Common/Internal/Runtime/RuntimeConstants.cs
index 8ce74a03072b4d..9d356cb066813d 100644
--- a/src/coreclr/tools/Common/Internal/Runtime/RuntimeConstants.cs
+++ b/src/coreclr/tools/Common/Internal/Runtime/RuntimeConstants.cs
@@ -43,6 +43,11 @@ internal static class SpecialDispatchMapSlot
public const ushort Reabstraction = 0xFFFF;
}
+ internal static class DispatchMapCodePointerFlags
+ {
+ public const int RequiresInstantiatingThunkFlag = 2;
+ }
+
internal static class SpecialGVMInterfaceEntry
{
public const uint Diamond = 0xFFFFFFFF;
diff --git a/src/coreclr/tools/Common/TypeSystem/Common/Utilities/LockFreeReaderHashtableOfPointers.cs b/src/coreclr/tools/Common/TypeSystem/Common/Utilities/LockFreeReaderHashtableOfPointers.cs
index 0decdff50a7a0d..af6aac636146ed 100644
--- a/src/coreclr/tools/Common/TypeSystem/Common/Utilities/LockFreeReaderHashtableOfPointers.cs
+++ b/src/coreclr/tools/Common/TypeSystem/Common/Utilities/LockFreeReaderHashtableOfPointers.cs
@@ -9,6 +9,8 @@
using System.Threading.Tasks;
using Debug = System.Diagnostics.Debug;
+#nullable disable
+
namespace Internal.TypeSystem
{
///
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.InterfaceThunks.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.InterfaceThunks.cs
index 51bf6a6888e2ff..b9c8f7aa3566bb 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.InterfaceThunks.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.InterfaceThunks.cs
@@ -62,13 +62,11 @@ namespace ILCompiler
// Contains functionality related to instantiating thunks for default interface methods
public partial class CompilerTypeSystemContext
{
- private const int UseContextFromRuntime = -1;
-
///
/// For a shared (canonical) default interface method, gets a method that can be used to call the
/// method on a specific implementing class.
///
- public MethodDesc GetDefaultInterfaceMethodImplementationThunk(MethodDesc targetMethod, TypeDesc implementingClass, DefType interfaceOnDefinition)
+ public MethodDesc GetDefaultInterfaceMethodImplementationThunk(MethodDesc targetMethod, TypeDesc implementingClass, DefType interfaceOnDefinition, out int interfaceIndex)
{
Debug.Assert(targetMethod.IsSharedByGenericInstantiations);
Debug.Assert(!targetMethod.Signature.IsStatic);
@@ -76,11 +74,16 @@ public MethodDesc GetDefaultInterfaceMethodImplementationThunk(MethodDesc target
Debug.Assert(interfaceOnDefinition.GetTypeDefinition() == targetMethod.OwningType.GetTypeDefinition());
Debug.Assert(targetMethod.OwningType.IsInterface);
- int interfaceIndex;
+ bool useContextFromRuntime = false;
if (implementingClass.IsInterface)
{
Debug.Assert(((MetadataType)implementingClass).IsDynamicInterfaceCastableImplementation());
- interfaceIndex = UseContextFromRuntime;
+ useContextFromRuntime = true;
+ }
+
+ if (useContextFromRuntime && targetMethod.OwningType == implementingClass)
+ {
+ interfaceIndex = -1;
}
else
{
@@ -90,7 +93,7 @@ public MethodDesc GetDefaultInterfaceMethodImplementationThunk(MethodDesc target
// Get a method that will inject the appropriate instantiation context to the
// target default interface method.
- var methodKey = new DefaultInterfaceMethodImplementationInstantiationThunkHashtableKey(targetMethod, interfaceIndex);
+ var methodKey = new DefaultInterfaceMethodImplementationInstantiationThunkHashtableKey(targetMethod, interfaceIndex, useContextFromRuntime);
MethodDesc thunk = _dimThunkHashtable.GetOrCreateValue(methodKey);
return thunk;
@@ -117,11 +120,13 @@ private struct DefaultInterfaceMethodImplementationInstantiationThunkHashtableKe
{
public readonly MethodDesc TargetMethod;
public readonly int InterfaceIndex;
+ public bool UseContextFromRuntime;
- public DefaultInterfaceMethodImplementationInstantiationThunkHashtableKey(MethodDesc targetMethod, int interfaceIndex)
+ public DefaultInterfaceMethodImplementationInstantiationThunkHashtableKey(MethodDesc targetMethod, int interfaceIndex, bool useContextFromRuntime)
{
TargetMethod = targetMethod;
InterfaceIndex = interfaceIndex;
+ UseContextFromRuntime = useContextFromRuntime;
}
}
@@ -138,17 +143,19 @@ protected override int GetValueHashCode(DefaultInterfaceMethodImplementationInst
protected override bool CompareKeyToValue(DefaultInterfaceMethodImplementationInstantiationThunkHashtableKey key, DefaultInterfaceMethodImplementationInstantiationThunk value)
{
return ReferenceEquals(key.TargetMethod, value.TargetMethod) &&
- key.InterfaceIndex == value.InterfaceIndex;
+ key.InterfaceIndex == value.InterfaceIndex &&
+ key.UseContextFromRuntime == value.UseContextFromRuntime;
}
protected override bool CompareValueToValue(DefaultInterfaceMethodImplementationInstantiationThunk value1, DefaultInterfaceMethodImplementationInstantiationThunk value2)
{
return ReferenceEquals(value1.TargetMethod, value2.TargetMethod) &&
- value1.InterfaceIndex == value2.InterfaceIndex;
+ value1.InterfaceIndex == value2.InterfaceIndex &&
+ value1.UseContextFromRuntime == value2.UseContextFromRuntime;
}
protected override DefaultInterfaceMethodImplementationInstantiationThunk CreateValueFromKey(DefaultInterfaceMethodImplementationInstantiationThunkHashtableKey key)
{
TypeDesc owningTypeOfThunks = ((CompilerTypeSystemContext)key.TargetMethod.Context).GeneratedAssembly.GetGlobalModuleType();
- return new DefaultInterfaceMethodImplementationInstantiationThunk(owningTypeOfThunks, key.TargetMethod, key.InterfaceIndex);
+ return new DefaultInterfaceMethodImplementationInstantiationThunk(owningTypeOfThunks, key.TargetMethod, key.InterfaceIndex, key.UseContextFromRuntime);
}
}
private DefaultInterfaceMethodImplementationInstantiationThunkHashtable _dimThunkHashtable = new DefaultInterfaceMethodImplementationInstantiationThunkHashtable();
@@ -162,8 +169,9 @@ private sealed partial class DefaultInterfaceMethodImplementationInstantiationTh
private readonly DefaultInterfaceMethodImplementationWithHiddenParameter _nakedTargetMethod;
private readonly TypeDesc _owningType;
private readonly int _interfaceIndex;
+ private readonly bool _useContextFromRuntime;
- public DefaultInterfaceMethodImplementationInstantiationThunk(TypeDesc owningType, MethodDesc targetMethod, int interfaceIndex)
+ public DefaultInterfaceMethodImplementationInstantiationThunk(TypeDesc owningType, MethodDesc targetMethod, int interfaceIndex, bool useContextFromRuntime)
{
Debug.Assert(targetMethod.OwningType.IsInterface);
Debug.Assert(!targetMethod.Signature.IsStatic);
@@ -172,6 +180,7 @@ public DefaultInterfaceMethodImplementationInstantiationThunk(TypeDesc owningTyp
_targetMethod = targetMethod;
_nakedTargetMethod = new DefaultInterfaceMethodImplementationWithHiddenParameter(targetMethod, owningType);
_interfaceIndex = interfaceIndex;
+ _useContextFromRuntime = useContextFromRuntime;
}
public override TypeSystemContext Context => _targetMethod.Context;
@@ -180,6 +189,8 @@ public DefaultInterfaceMethodImplementationInstantiationThunk(TypeDesc owningTyp
public int InterfaceIndex => _interfaceIndex;
+ public bool UseContextFromRuntime => _useContextFromRuntime;
+
public override MethodSignature Signature => _targetMethod.Signature;
public MethodDesc TargetMethod => _targetMethod;
@@ -202,7 +213,7 @@ public override string DiagnosticName
public MethodDesc BaseMethod => _targetMethod;
- public string Prefix => $"__InstantiatingStub_{_interfaceIndex}_";
+ public string Prefix => $"__InstantiatingStub_{(uint)_interfaceIndex}_{(_useContextFromRuntime ? "_FromRuntime" : "")}_";
public override MethodIL EmitIL()
{
@@ -230,7 +241,7 @@ public override MethodIL EmitIL()
}
// Load the instantiating argument.
- if (_interfaceIndex == UseContextFromRuntime)
+ if (_useContextFromRuntime)
{
codeStream.Emit(ILOpcode.call, emit.NewToken(getCurrentContext));
}
@@ -238,6 +249,10 @@ public override MethodIL EmitIL()
{
codeStream.EmitLdArg(0);
codeStream.Emit(ILOpcode.ldfld, emit.NewToken(eeTypeField));
+ }
+
+ if (_interfaceIndex >= 0)
+ {
codeStream.EmitLdc(_interfaceIndex);
codeStream.Emit(ILOpcode.call, emit.NewToken(getOrdinalInterfaceMethod));
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs
index a27a77882ab130..722618c759d562 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs
@@ -539,10 +539,10 @@ public sealed override IEnumerable GetConditionalSt
if (!isStaticInterfaceMethod && defaultIntfMethod.IsCanonicalMethod(CanonicalFormKind.Any))
{
// Canonical instance default methods need to go through a thunk that adds the right generic context
- defaultIntfMethod = factory.TypeSystemContext.GetDefaultInterfaceMethodImplementationThunk(defaultIntfMethod, defType.ConvertToCanonForm(CanonicalFormKind.Specific), providingInterfaceDefinitionType);
+ defaultIntfMethod = factory.TypeSystemContext.GetDefaultInterfaceMethodImplementationThunk(defaultIntfMethod, defType.ConvertToCanonForm(CanonicalFormKind.Specific), providingInterfaceDefinitionType, out int providingInterfaceIndex);
// The above thunk will index into interface list to find the right context. Make sure to keep all interfaces prior to this one
- for (int i = 0; i < interfaceIndex; i++)
+ for (int i = 0; i <= providingInterfaceIndex; i++)
{
result.Add(new CombinedDependencyListEntry(
factory.InterfaceUse(defTypeRuntimeInterfaces[i].GetTypeDefinition()),
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/SealedVTableNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/SealedVTableNode.cs
index dfa6c0967282e2..f0b9fbf532457e 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/SealedVTableNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/SealedVTableNode.cs
@@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using Internal.Runtime;
using Internal.Text;
using Internal.TypeSystem;
@@ -218,10 +219,10 @@ public bool BuildSealedVTableSlots(NodeFactory factory, bool relocsOnly)
{
// Canonical instance default interface methods need to go through a thunk that acquires the generic context from `this`.
// Static methods have their generic context passed explicitly.
- canonImplMethod = factory.TypeSystemContext.GetDefaultInterfaceMethodImplementationThunk(canonImplMethod, declType.ConvertToCanonForm(CanonicalFormKind.Specific), providingInterfaceDefinitionType);
+ canonImplMethod = factory.TypeSystemContext.GetDefaultInterfaceMethodImplementationThunk(canonImplMethod, declType.ConvertToCanonForm(CanonicalFormKind.Specific), providingInterfaceDefinitionType, out int providingInterfaceIndex);
// The above thunk will index into interface list to find the right context. Make sure to keep all interfaces prior to this one
- for (int i = 0; i < interfaceIndex; i++)
+ for (int i = 0; i <= providingInterfaceIndex; i++)
{
_nonRelocationDependencies ??= new DependencyList();
_nonRelocationDependencies.Add(factory.InterfaceUse(declTypeRuntimeInterfaces[i].GetTypeDefinition()), "Interface with shared default methods folows this");
@@ -267,14 +268,20 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly)
if (BuildSealedVTableSlots(factory, relocsOnly))
{
+ bool isSharedDynamicInterfaceCastableImpl = _type.IsInterface
+ && _type.IsCanonicalSubtype(CanonicalFormKind.Any)
+ && ((MetadataType)_type).IsDynamicInterfaceCastableImplementation();
+
for (int i = 0; i < _sealedVTableEntries.Count; i++)
{
IMethodNode relocTarget = _sealedVTableEntries[i].Target;
+ int delta = isSharedDynamicInterfaceCastableImpl ? DispatchMapCodePointerFlags.RequiresInstantiatingThunkFlag : 0;
+
if (factory.Target.SupportsRelativePointers)
- objData.EmitReloc(relocTarget, RelocType.IMAGE_REL_BASED_RELPTR32);
+ objData.EmitReloc(relocTarget, RelocType.IMAGE_REL_BASED_RELPTR32, delta);
else
- objData.EmitPointerReloc(relocTarget);
+ objData.EmitPointerReloc(relocTarget, delta);
}
}
diff --git a/src/tests/Interop/IDynamicInterfaceCastable/Program.cs b/src/tests/Interop/IDynamicInterfaceCastable/Program.cs
index dc3e35d0d48129..ccf91480faaa3f 100644
--- a/src/tests/Interop/IDynamicInterfaceCastable/Program.cs
+++ b/src/tests/Interop/IDynamicInterfaceCastable/Program.cs
@@ -380,7 +380,6 @@ public static void ValidateBasicInterface()
}
[Fact]
- [ActiveIssue("https://github.com/dotnet/runtimelab/issues/1442", typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsNativeAot))]
public static void ValidateGenericInterface()
{
Console.WriteLine($"Running {nameof(ValidateGenericInterface)}");
@@ -394,26 +393,38 @@ public static void ValidateGenericInterface()
Console.WriteLine(" -- Validate cast");
// ITestGeneric -> ITestGenericIntImpl
- Assert.True(castableObj is ITestGeneric, $"Should be castable to {nameof(ITestGeneric)} via is");
- Assert.NotNull(castableObj as ITestGeneric);
+ if (!TestLibrary.Utilities.IsNativeAot) // https://github.com/dotnet/runtime/issues/108229
+ {
+ Assert.True(castableObj is ITestGeneric, $"Should be castable to {nameof(ITestGeneric)} via is");
+ Assert.NotNull(castableObj as ITestGeneric);
+ }
ITestGeneric testInt = (ITestGeneric)castableObj;
// ITestGeneric -> ITestGenericImpl
- Assert.True(castableObj is ITestGeneric, $"Should be castable to {nameof(ITestGeneric)} via is");
- Assert.NotNull(castableObj as ITestGeneric);
+ if (!TestLibrary.Utilities.IsNativeAot) // https://github.com/dotnet/runtime/issues/108229
+ {
+ Assert.True(castableObj is ITestGeneric, $"Should be castable to {nameof(ITestGeneric)} via is");
+ Assert.NotNull(castableObj as ITestGeneric);
+ }
ITestGeneric testStr = (ITestGeneric)castableObj;
// Validate Variance
// ITestGeneric -> ITestGenericImpl