Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ff4ace5
Implement support for weak symbols in ILC
jkoritzinsky Jun 13, 2025
7b35d82
Initial sketch of conditionally rooting IDIC when IDIC is actually used.
jkoritzinsky Jun 13, 2025
27a0fae
Manually use weak symbols in the bootstrapper to handle weak IDIC
jkoritzinsky Jun 13, 2025
c03f3b2
Remove SymbolFlags now that we're using a bootstrapper-based weak sym…
jkoritzinsky Jun 13, 2025
465e703
Only ask for maximally constructable interface types for casting if I…
jkoritzinsky Jun 13, 2025
d88c99e
Remove Weak field on RuntimeExportAttribute
jkoritzinsky Jun 13, 2025
5815948
Reduce diff
jkoritzinsky Jun 13, 2025
fe157a2
Make IDynamicCastableSupport trimmable
AustinWise Jun 16, 2025
4e7b795
Make the IDIC static constructor check live elsewhere
jkoritzinsky Jun 16, 2025
ff74f16
Remove slots and fix C99 _Pragma usage
jkoritzinsky Jun 16, 2025
7856559
Merge branch 'main' of https://github.com/dotnet/runtime into idic-co…
jkoritzinsky Jun 16, 2025
465ec2b
Downgrade interface type symbols in interface dispatch stubs
jkoritzinsky Jun 17, 2025
8ac6d6e
Substitute away the code we can't trim out easily
jkoritzinsky Jun 17, 2025
7d2a548
Scan the "feature body removed" helper always. We might remove featur…
jkoritzinsky Jun 18, 2025
515b35c
Look for constructed implementing type instead of IDIC itself.
jkoritzinsky Jun 19, 2025
68b40fb
Fix substitution condition
jkoritzinsky Jun 19, 2025
7635ea4
Merge branch 'main' of https://github.com/dotnet/runtime into idic-co…
jkoritzinsky Jun 20, 2025
ec645e4
Add ThrowFeatureBodyRemoved helper to Test.CoreLib
jkoritzinsky Jun 20, 2025
1b19031
Remove leftovers from previous impl
jkoritzinsky Jul 1, 2025
993d1e8
PR feedback
jkoritzinsky Jul 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions src/coreclr/nativeaot/Bootstrap/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,10 @@ void* PalGetModuleHandleFromPointer(void* pointer);

#if defined(HOST_X86) && defined(HOST_WINDOWS)
#define STRINGIFY(s) #s
#define LINKER_COMMENT(comment) _Pragma(STRINGIFY(comment))
#define MANAGED_RUNTIME_EXPORT_ALTNAME(_method) STRINGIFY(/alternatename:_##_method=_method)
#define MANAGED_RUNTIME_EXPORT(_name) \
__pragma(comment (linker, MANAGED_RUNTIME_EXPORT_ALTNAME(_name))) \
LINKER_COMMENT(comment (linker, MANAGED_RUNTIME_EXPORT_ALTNAME(_name))) \
extern "C" void __cdecl _name();
#define MANAGED_RUNTIME_EXPORT_NAME(_name) _name
#define CDECL __cdecl
Expand All @@ -125,8 +126,6 @@ MANAGED_RUNTIME_EXPORT(AppendExceptionStackFrame)
MANAGED_RUNTIME_EXPORT(GetSystemArrayEEType)
MANAGED_RUNTIME_EXPORT(OnFirstChanceException)
MANAGED_RUNTIME_EXPORT(OnUnhandledException)
MANAGED_RUNTIME_EXPORT(IDynamicCastableIsInterfaceImplemented)
MANAGED_RUNTIME_EXPORT(IDynamicCastableGetInterfaceImplementation)
#ifdef FEATURE_OBJCMARSHAL
MANAGED_RUNTIME_EXPORT(ObjectiveCMarshalTryGetTaggedMemory)
MANAGED_RUNTIME_EXPORT(ObjectiveCMarshalGetIsTrackedReferenceCallback)
Expand All @@ -145,8 +144,6 @@ static const pfn c_classlibFunctions[] = {
&MANAGED_RUNTIME_EXPORT_NAME(GetSystemArrayEEType),
&MANAGED_RUNTIME_EXPORT_NAME(OnFirstChanceException),
&MANAGED_RUNTIME_EXPORT_NAME(OnUnhandledException),
&MANAGED_RUNTIME_EXPORT_NAME(IDynamicCastableIsInterfaceImplemented),
&MANAGED_RUNTIME_EXPORT_NAME(IDynamicCastableGetInterfaceImplementation),
#ifdef FEATURE_OBJCMARSHAL
&MANAGED_RUNTIME_EXPORT_NAME(ObjectiveCMarshalTryGetTaggedMemory),
&MANAGED_RUNTIME_EXPORT_NAME(ObjectiveCMarshalGetIsTrackedReferenceCallback),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

using Internal.Runtime;

Expand Down Expand Up @@ -138,9 +139,7 @@ private static unsafe IntPtr RhResolveDispatchWorker(object pObject, void* cell,
{
// Dispatch not resolved through normal dispatch map, try using the IDynamicInterfaceCastable
// This will either give us the appropriate result, or throw.
var pfnGetInterfaceImplementation = (delegate*<object, MethodTable*, ushort, IntPtr>)
pInstanceType->GetClasslibFunction(ClassLibFunctionId.IDynamicCastableGetInterfaceImplementation);
pTargetCode = pfnGetInterfaceImplementation(pObject, cellInfo.InterfaceType, cellInfo.InterfaceSlot);
pTargetCode = IDynamicInterfaceCastable.GetDynamicInterfaceImplementation((IDynamicInterfaceCastable)pObject, cellInfo.InterfaceType, cellInfo.InterfaceSlot);
Diagnostics.Debug.Assert(pTargetCode != IntPtr.Zero);
}
return pTargetCode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace System.Runtime.CompilerServices
{
// When applied to a type this custom attribute will cause any static class constructor to be run eagerly
// at module load time rather than deferred till just before the class is used.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
public class EagerStaticClassConstructionAttribute : Attribute
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,10 @@ internal enum ClassLibFunctionId
GetSystemArrayEEType = 5,
OnFirstChance = 6,
OnUnhandledException = 7,
IDynamicCastableIsInterfaceImplemented = 8,
IDynamicCastableGetInterfaceImplementation = 9,
ObjectiveCMarshalTryGetTaggedMemory = 10,
ObjectiveCMarshalGetIsTrackedReferenceCallback = 11,
ObjectiveCMarshalGetOnEnteredFinalizerQueueCallback = 12,
ObjectiveCMarshalGetUnhandledExceptionPropagationHandler = 13,
ObjectiveCMarshalTryGetTaggedMemory = 8,
ObjectiveCMarshalGetIsTrackedReferenceCallback = 9,
ObjectiveCMarshalGetOnEnteredFinalizerQueueCallback = 10,
ObjectiveCMarshalGetUnhandledExceptionPropagationHandler = 11,
}

internal static class InternalCalls
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,9 +469,7 @@ private static unsafe object CheckCastClassSpecial(MethodTable* pTargetType, obj

private static unsafe bool IsInstanceOfInterfaceViaIDynamicInterfaceCastable(MethodTable* pTargetType, object obj, bool throwing)
{
var pfnIsInterfaceImplemented = (delegate*<object, MethodTable*, bool, bool>)
pTargetType->GetClasslibFunction(ClassLibFunctionId.IDynamicCastableIsInterfaceImplemented);
return pfnIsInterfaceImplemented(obj, pTargetType, throwing);
return ((IDynamicInterfaceCastable)obj).IsInterfaceImplemented(new RuntimeTypeHandle(pTargetType), throwing);
}

internal static unsafe bool IsDerived(MethodTable* pDerivedType, MethodTable* pBaseType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,10 @@ private static void ThrowArgumentOutOfRangeException()
// exception doesn't exist in MRT: throw PlatformNotSupportedException() instead
throw new PlatformNotSupportedException();
}

private static void ThrowFeatureBodyRemoved()
{
throw new NotSupportedException();
}
}
}
10 changes: 4 additions & 6 deletions src/coreclr/nativeaot/Runtime/ICodeManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,10 @@ enum class ClasslibFunctionId
GetSystemArrayEEType = 5,
OnFirstChanceException = 6,
OnUnhandledException = 7,
IDynamicCastableIsInterfaceImplemented = 8,
IDynamicCastableGetInterfaceImplementation = 9,
ObjectiveCMarshalTryGetTaggedMemory = 10,
ObjectiveCMarshalGetIsTrackedReferenceCallback = 11,
ObjectiveCMarshalGetOnEnteredFinalizerQueueCallback = 12,
ObjectiveCMarshalGetUnhandledExceptionPropagationHandler = 13,
ObjectiveCMarshalTryGetTaggedMemory = 8,
ObjectiveCMarshalGetIsTrackedReferenceCallback = 9,
ObjectiveCMarshalGetOnEnteredFinalizerQueueCallback = 10,
ObjectiveCMarshalGetUnhandledExceptionPropagationHandler = 11,
};

enum class AssociatedDataFlags : unsigned char
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@
<Compile Include="Internal\IntrinsicSupport\ComparerHelpers.cs" />
<Compile Include="Internal\IntrinsicSupport\EqualityComparerHelpers.cs" />
<Compile Include="Internal\Reflection\Augments\ReflectionAugments.cs" />
<Compile Include="Internal\Runtime\IDynamicInterfaceCastableSupport.cs" />
<Compile Include="Internal\Runtime\MethodTable.Runtime.cs" />
<Compile Include="Internal\Runtime\CompilerHelpers\ThrowHelpers.cs" />
<Compile Include="Internal\Runtime\CompilerServices\FunctionPointerOps.cs" />
Expand Down Expand Up @@ -202,6 +201,7 @@
<Compile Include="System\Runtime\InteropServices\ComEventsHelper.NativeAot.cs" Condition="'$(FeatureCominterop)' == 'true'" />
<Compile Include="System\Runtime\InteropServices\ComWrappers.NativeAot.cs" Condition="'$(FeatureComWrappers)' == 'true'" />
<Compile Include="System\Runtime\InteropServices\GCHandle.NativeAot.cs" />
<Compile Include="System\Runtime\InteropServices\IDynamicInterfaceCastable.cs" />
<Compile Include="System\Runtime\InteropServices\NativeFunctionPointerWrapper.cs" />
<Compile Include="System\Runtime\InteropServices\NativeLibrary.NativeAot.cs" />
<Compile Include="System\Runtime\InteropServices\PInvokeMarshal.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,22 @@

using System;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;

using Internal.Runtime;
using Internal.Runtime.Augments;
using Internal.TypeSystem;

using Debug = System.Diagnostics.Debug;

namespace Internal.Runtime
namespace System.Runtime.InteropServices
{
internal static unsafe class IDynamicCastableSupport
public unsafe partial interface IDynamicInterfaceCastable
{
[RuntimeExport("IDynamicCastableIsInterfaceImplemented")]
internal static bool IDynamicCastableIsInterfaceImplemented(IDynamicInterfaceCastable instance, MethodTable* interfaceType, bool throwIfNotImplemented)
{
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)
internal static nint GetDynamicInterfaceImplementation(IDynamicInterfaceCastable instance, MethodTable* interfaceType, ushort slot)
{
RuntimeTypeHandle handle = instance.GetInterfaceImplementation(new RuntimeTypeHandle(interfaceType));
MethodTable* implType = handle.ToMethodTable();
Expand All @@ -38,10 +32,10 @@ internal static IntPtr IDynamicCastableGetInterfaceImplementation(IDynamicInterf
}

MethodTable* genericContext = null;
IntPtr result = RuntimeImports.RhResolveDynamicInterfaceCastableDispatchOnType(implType, interfaceType, slot, &genericContext);
if (result == IntPtr.Zero)
nint result = RuntimeImports.RhResolveDynamicInterfaceCastableDispatchOnType(implType, interfaceType, slot, &genericContext);
if (result == nint.Zero)
{
IDynamicCastableGetInterfaceImplementationFailure(instance, interfaceType, implType);
GetInterfaceImplementationFailure(instance, interfaceType, implType);
}

if (genericContext != null)
Expand Down Expand Up @@ -73,7 +67,7 @@ private static void ThrowInvalidOperationException(MethodTable* resolvedImplType
throw new InvalidOperationException(SR.Format(SR.IDynamicInterfaceCastable_NotInterface, Type.GetTypeFromMethodTable(resolvedImplType)));
}

private static void IDynamicCastableGetInterfaceImplementationFailure(object instance, MethodTable* interfaceType, MethodTable* resolvedImplType)
private static void GetInterfaceImplementationFailure(object instance, MethodTable* interfaceType, MethodTable* resolvedImplType)
{
if (resolvedImplType->DispatchMap == null)
throw new InvalidOperationException(SR.Format(SR.IDynamicInterfaceCastable_MissingImplementationAttribute, Type.GetTypeFromMethodTable(resolvedImplType), nameof(DynamicInterfaceCastableImplementationAttribute)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ protected override RuntimeInterfacesAlgorithm GetRuntimeInterfacesAlgorithmForDe
return s_noMetadataRuntimeInterfacesAlgorithm;
}

protected internal sealed override bool IsIDynamicInterfaceCastableInterface(DefType type)
public sealed override bool IsIDynamicInterfaceCastableInterface(DefType type)
{
throw new NotImplementedException();
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Runtime.CompilerServices
{
public static partial class RuntimeFeature
{
/// <summary>
/// Name of the Portable PDB feature.
/// </summary>
public const string PortablePdb = nameof(PortablePdb);

/// <summary>
/// Indicates that this version of runtime supports default interface method implementations.
/// </summary>
public const string DefaultImplementationsOfInterfaces = nameof(DefaultImplementationsOfInterfaces);

/// <summary>
/// Indicates that this version of runtime supports the Unmanaged calling convention value.
/// </summary>
public const string UnmanagedSignatureCallingConvention = nameof(UnmanagedSignatureCallingConvention);

/// <summary>
/// Indicates that this version of runtime supports covariant returns in overrides of methods declared in classes.
/// </summary>
public const string CovariantReturnsOfClasses = nameof(CovariantReturnsOfClasses);

/// <summary>
/// Represents a runtime feature where types can define ref fields.
/// </summary>
public const string ByRefFields = nameof(ByRefFields);

/// <summary>
/// Represents a runtime feature where byref-like types can be used in Generic parameters.
/// </summary>
public const string ByRefLikeGenerics = nameof(ByRefLikeGenerics);

/// <summary>
/// Indicates that this version of runtime supports virtual static members of interfaces.
/// </summary>
public const string VirtualStaticsInInterfaces = nameof(VirtualStaticsInInterfaces);

/// <summary>
/// Indicates that this version of runtime supports <see cref="System.IntPtr" /> and <see cref="System.UIntPtr" /> as numeric types.
/// </summary>
public const string NumericIntPtr = nameof(NumericIntPtr);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Internal.Runtime;

namespace System.Runtime.InteropServices
{
public unsafe partial interface IDynamicInterfaceCastable
{
internal static IntPtr GetDynamicInterfaceImplementation(object instance, MethodTable* interfaceType, ushort slot)
{
RuntimeImports.RhpFallbackFailFast();
return default;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

using Internal.Runtime;

namespace System
{
[StructLayout(LayoutKind.Sequential)]
Expand All @@ -17,6 +19,11 @@ internal RuntimeTypeHandle(IntPtr value)
_value = value;
}

unsafe internal RuntimeTypeHandle(MethodTable* value)
:this((IntPtr)value)
{
}

[Intrinsic]
internal static unsafe IntPtr ToIntPtr(RuntimeTypeHandle handle)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,14 @@
<Compile Include="..\..\Runtime.Base\src\System\Runtime\CompilerServices\Unsafe.cs">
<Link>System\Runtime\CompilerServices\Unsafe.cs</Link>
</Compile>
<Compile Include="Internal\Runtime\IDynamicInterfaceCastableSupport.cs" />
<Compile Include="$(LibrariesProjectRoot)\System.Private.CoreLib\src\System\Runtime\InteropServices\IDynamicInterfaceCastable.cs">
<Link>Common\System\Runtime\InteropServices\IDynamicInterfaceCastable.cs</Link>
</Compile>
<Compile Include="Internal\Runtime\MethodTable.Runtime.cs" />
<Compile Include="System\Runtime\CompilerServices\CastCache.cs" />
<Compile Include="System\Runtime\CompilerServices\ClassConstructorRunner.cs" />
<Compile Include="System\Runtime\CompilerServices\InlineArrayAttribute.cs" />
<Compile Include="System\Runtime\CompilerServices\RuntimeFeature.cs" />
<Compile Include="System\Runtime\CompilerServices\StaticClassConstructionContext.cs" />
<Compile Include="System\Runtime\ExceptionServices\ExceptionHandling.cs" />
<Compile Include="System\Runtime\InteropServices\InAttribute.cs" />
Expand All @@ -222,6 +225,7 @@
<Compile Include="System\Diagnostics\CodeAnalysis\DoesNotReturnIfAttribute.cs" />
<Compile Include="System\Diagnostics\Debug.cs" />
<Compile Include="System\Runtime\InteropServices\MemoryMarshal.cs" />
<Compile Include="System\Runtime\InteropServices\IDynamicInterfaceCastable.cs" />
<Compile Include="System\Runtime\RuntimeImports.cs" />
<Compile Include="System\Runtime\RuntimeHelpers.cs" />
<Compile Include="System\Threading\Interlocked.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType
public virtual bool CanReferenceConstructedTypeOrCanonicalFormOfType(TypeDesc type) => true;

public virtual TypeDesc[] GetImplementingClasses(TypeDesc type) => null;

public virtual bool CanHaveDynamicInterfaceImplementations(TypeDesc type)
{
return !type.Context.IsIDynamicInterfaceCastableInterface(type.GetClosestDefType());
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ protected internal sealed override bool ComputeHasStaticConstructor(TypeDesc typ
return false;
}

protected internal sealed override bool IsIDynamicInterfaceCastableInterface(DefType type)
public sealed override bool IsIDynamicInterfaceCastableInterface(DefType type)
{
MetadataType t = (MetadataType)type;
return t.Module == SystemModule
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ internal TypeFlags ComputeTypeFlags(TypeDesc type, TypeFlags flags, TypeFlags ma
/// <summary>
/// Determine if the type implements <code>IDynamicInterfaceCastable</code>
/// </summary>
protected internal abstract bool IsIDynamicInterfaceCastableInterface(DefType type);
public abstract bool IsIDynamicInterfaceCastableInterface(DefType type);

public virtual bool SupportsTypeEquivalence => false;
public virtual bool SupportsCOMInterop => false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,20 @@ public ISymbolNode ComputeConstantLookup(ReadyToRunHelperId lookupKind, object t
{
var type = (TypeDesc)targetOfLookup;

// We counter-intuitively ask for a constructed type symbol. This is needed due to IDynamicInterfaceCastable.
// If this cast happens with an object that implements IDynamicIntefaceCastable, user code will
// see a RuntimeTypeHandle representing this interface.
if (type.IsInterface)
return NodeFactory.MaximallyConstructableType(type);
{
// We counter-intuitively ask for a constructed type symbol. This is needed due to IDynamicInterfaceCastable.
// If this cast happens with an object that implements IDynamicInterfaceCastable, user code will
// see a RuntimeTypeHandle representing this interface.
if (NodeFactory.DevirtualizationManager.CanHaveDynamicInterfaceImplementations(type))
{
return NodeFactory.MaximallyConstructableType(type);
}
else
{
return NecessaryTypeSymbolIfPossible(type);
}
}

if (type.IsNullable)
type = type.Instantiation[0];
Expand Down
Loading
Loading