From 7099f4b12a9335690eae6b9b6170bc7431e88621 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 19 Nov 2021 19:28:42 -0800 Subject: [PATCH] Use reflection or a compile-time only shim assembly to reference unexposed corelib types. (#61802) Co-authored-by: Aaron Robinson --- .../testing/coreclr/test-configuration.md | 2 - .../ComActivationContextInternal.cs | 4 +- .../ComActivator.PlatformNotSupported.cs | 17 +--- .../Runtime/InteropServices/ComActivator.cs | 16 ++-- .../XUnitWrapperGenerator.props | 1 - src/tests/Common/override.targets | 28 ------ src/tests/Directory.Build.targets | 12 +-- .../Interop/COM/Activator/Activator.csproj | 5 +- .../ActivatorBuiltInComDisabled.csproj | 5 +- .../COM/Activator/ComActivationContext.cs | 19 ++++ src/tests/Interop/COM/Activator/Program.cs | 86 ++++++++++++++----- src/tests/Interop/Directory.Build.targets | 11 +-- src/tests/Interop/ICastable/Castable.cs | 12 --- src/tests/Interop/ICastable/Castable.csproj | 4 +- .../ICastable/ICastable.CoreLib.csproj | 17 ++++ .../LoadIjwFromModuleHandle.cs | 13 ++- .../LoadIjwFromModuleHandle.csproj | 3 +- 17 files changed, 133 insertions(+), 122 deletions(-) delete mode 100644 src/tests/Common/override.targets create mode 100644 src/tests/Interop/COM/Activator/ComActivationContext.cs create mode 100644 src/tests/Interop/ICastable/ICastable.CoreLib.csproj diff --git a/docs/workflow/testing/coreclr/test-configuration.md b/docs/workflow/testing/coreclr/test-configuration.md index 3e0f0d21302c9..a2342d3bb6ebf 100644 --- a/docs/workflow/testing/coreclr/test-configuration.md +++ b/docs/workflow/testing/coreclr/test-configuration.md @@ -52,8 +52,6 @@ Therefore the managed portion of each test **must not contain**: * Exclude test from JIT stress runs runs by adding the following to the csproj: * `true` * Add NuGet references by updating the following [test project](https://github.com/dotnet/runtime/blob/main/src/tests/Common/test_dependencies/test_dependencies.csproj). -* Get access to System.Private.CoreLib types and methods that are not exposed via public surface by adding the following to the csproj: - * `true` * Any System.Private.CoreLib types and methods used by tests must be available for building on all platforms. This means there must be enough implementation for the C# compiler to find the referenced types and methods. Unsupported target platforms should simply `throw new PlatformNotSupportedException()` in its dummy method implementations. diff --git a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivationContextInternal.cs b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivationContextInternal.cs index 1db025ed86581..c16a56efc4fdd 100644 --- a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivationContextInternal.cs +++ b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivationContextInternal.cs @@ -25,7 +25,7 @@ public unsafe struct ComActivationContextInternal // [StructLayout(LayoutKind.Sequential)] - public partial struct ComActivationContext + internal partial struct ComActivationContext { public Guid ClassId; public Guid InterfaceId; @@ -38,7 +38,7 @@ public partial struct ComActivationContext [ComVisible(false)] [Guid("00000001-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface IClassFactory + internal interface IClassFactory { [RequiresUnreferencedCode("Built-in COM support is not trim compatible", Url = "https://aka.ms/dotnet-illink/com")] void CreateInstance( diff --git a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.PlatformNotSupported.cs b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.PlatformNotSupported.cs index 6d4ddfa2b7fda..1bb64cf6f4cf3 100644 --- a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.PlatformNotSupported.cs +++ b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.PlatformNotSupported.cs @@ -12,35 +12,24 @@ public static class ComActivator /// Internal entry point for unmanaged COM activation API from native code /// /// Pointer to a instance - [CLSCompliant(false)] [UnmanagedCallersOnly] - public static unsafe int GetClassFactoryForTypeInternal(ComActivationContextInternal* pCxtInt) + private static unsafe int GetClassFactoryForTypeInternal(ComActivationContextInternal* pCxtInt) => throw new PlatformNotSupportedException(); /// /// Internal entry point for registering a managed COM server API from native code /// /// Pointer to a instance - [CLSCompliant(false)] [UnmanagedCallersOnly] - public static unsafe int RegisterClassForTypeInternal(ComActivationContextInternal* pCxtInt) + private static unsafe int RegisterClassForTypeInternal(ComActivationContextInternal* pCxtInt) => throw new PlatformNotSupportedException(); /// /// Internal entry point for unregistering a managed COM server API from native code /// /// Pointer to a instance - [CLSCompliant(false)] [UnmanagedCallersOnly] - public static unsafe int UnregisterClassForTypeInternal(ComActivationContextInternal* pCxtInt) - => throw new PlatformNotSupportedException(); - - // Exists here to allow tests to build on any platform. - public static object GetClassFactoryForType(ComActivationContext cxt) - => throw new PlatformNotSupportedException(); - - // Exists here to allow tests to build on any platform. - public static void ClassRegistrationScenarioForType(ComActivationContext cxt, bool register) + private static unsafe int UnregisterClassForTypeInternal(ComActivationContextInternal* pCxtInt) => throw new PlatformNotSupportedException(); } } diff --git a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs index a00ecd1b6196b..a78bb670a2d79 100644 --- a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs +++ b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs @@ -57,10 +57,9 @@ void CreateInstanceLic( out IntPtr ppvObject); } - public partial struct ComActivationContext + internal partial struct ComActivationContext { [RequiresUnreferencedCode("Built-in COM support is not trim compatible", Url = "https://aka.ms/dotnet-illink/com")] - [CLSCompliant(false)] public static unsafe ComActivationContext Create(ref ComActivationContextInternal cxtInt) { if (!Marshal.IsBuiltInComSupported) @@ -91,7 +90,7 @@ public static class ComActivator /// /// Reference to a instance [RequiresUnreferencedCode("Built-in COM support is not trim compatible", Url = "https://aka.ms/dotnet-illink/com")] - public static object GetClassFactoryForType(ComActivationContext cxt) + private static object GetClassFactoryForType(ComActivationContext cxt) { if (!Marshal.IsBuiltInComSupported) { @@ -125,7 +124,7 @@ public static object GetClassFactoryForType(ComActivationContext cxt) /// Reference to a instance /// true if called for register or false to indicate unregister [RequiresUnreferencedCode("Built-in COM support is not trim compatible", Url = "https://aka.ms/dotnet-illink/com")] - public static void ClassRegistrationScenarioForType(ComActivationContext cxt, bool register) + private static void ClassRegistrationScenarioForType(ComActivationContext cxt, bool register) { if (!Marshal.IsBuiltInComSupported) { @@ -219,9 +218,8 @@ public static void ClassRegistrationScenarioForType(ComActivationContext cxt, bo /// /// Pointer to a instance [RequiresUnreferencedCode("Built-in COM support is not trim compatible", Url = "https://aka.ms/dotnet-illink/com")] - [CLSCompliant(false)] [UnmanagedCallersOnly] - public static unsafe int GetClassFactoryForTypeInternal(ComActivationContextInternal* pCxtInt) + private static unsafe int GetClassFactoryForTypeInternal(ComActivationContextInternal* pCxtInt) { if (!Marshal.IsBuiltInComSupported) { @@ -262,9 +260,8 @@ public static unsafe int GetClassFactoryForTypeInternal(ComActivationContextInte /// /// Pointer to a instance [RequiresUnreferencedCode("Built-in COM support is not trim compatible", Url = "https://aka.ms/dotnet-illink/com")] - [CLSCompliant(false)] [UnmanagedCallersOnly] - public static unsafe int RegisterClassForTypeInternal(ComActivationContextInternal* pCxtInt) + private static unsafe int RegisterClassForTypeInternal(ComActivationContextInternal* pCxtInt) { if (!Marshal.IsBuiltInComSupported) { @@ -308,9 +305,8 @@ public static unsafe int RegisterClassForTypeInternal(ComActivationContextIntern /// Internal entry point for unregistering a managed COM server API from native code /// [RequiresUnreferencedCode("Built-in COM support is not trim compatible", Url = "https://aka.ms/dotnet-illink/com")] - [CLSCompliant(false)] [UnmanagedCallersOnly] - public static unsafe int UnregisterClassForTypeInternal(ComActivationContextInternal* pCxtInt) + private static unsafe int UnregisterClassForTypeInternal(ComActivationContextInternal* pCxtInt) { if (!Marshal.IsBuiltInComSupported) { diff --git a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.props b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.props index cf35f35cb24d1..b05866e981229 100644 --- a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.props +++ b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.props @@ -6,7 +6,6 @@ - diff --git a/src/tests/Common/override.targets b/src/tests/Common/override.targets deleted file mode 100644 index 0586ffe389928..0000000000000 --- a/src/tests/Common/override.targets +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets index 0c52df3659e90..84bb4bd744ea2 100644 --- a/src/tests/Directory.Build.targets +++ b/src/tests/Directory.Build.targets @@ -80,7 +80,6 @@ <_WillCLRTestProjectBuild Condition="'$(BuildAllProjects)' != 'true'">true <_WillCLRTestProjectBuild Condition="'$(BuildAllProjects)' == 'true' And '$(CLRTestPriority)' <= '$(CLRTestPriorityToBuild)'">true <_WillCLRTestProjectBuild Condition="'$(CLRTestBuildAllTargets)' != 'allTargets' And '$(CLRTestTargetUnsupported)' == 'true'">false - <_WillCLRTestProjectBuild Condition="'$(ReferenceSystemPrivateCoreLib)' == 'true' and '$(RuntimeFlavor)' == 'mono'">false <_WillCLRTestProjectBuild Condition="'$(DisableProjectBuild)' == 'true'">false @@ -95,8 +94,6 @@ - - @@ -225,12 +222,11 @@ BeforeTargets="BeforeResolveReferences" > + Targets="GetLiveRefAssemblies"> - + false @@ -255,10 +251,6 @@ $(BaseOutputPath)\packages\Common\test_dependencies\test_dependencies\project.assets.json - - - - true diff --git a/src/tests/Interop/COM/Activator/Activator.csproj b/src/tests/Interop/COM/Activator/Activator.csproj index 87b0633ad3005..6090e01d90d5e 100644 --- a/src/tests/Interop/COM/Activator/Activator.csproj +++ b/src/tests/Interop/COM/Activator/Activator.csproj @@ -1,14 +1,15 @@ Exe - - true true true + true + true + diff --git a/src/tests/Interop/COM/Activator/ActivatorBuiltInComDisabled.csproj b/src/tests/Interop/COM/Activator/ActivatorBuiltInComDisabled.csproj index b119bcc691df9..5414a400d23ab 100644 --- a/src/tests/Interop/COM/Activator/ActivatorBuiltInComDisabled.csproj +++ b/src/tests/Interop/COM/Activator/ActivatorBuiltInComDisabled.csproj @@ -1,14 +1,15 @@ Exe - - true true true + true + true + diff --git a/src/tests/Interop/COM/Activator/ComActivationContext.cs b/src/tests/Interop/COM/Activator/ComActivationContext.cs new file mode 100644 index 0000000000000..9864107f694f5 --- /dev/null +++ b/src/tests/Interop/COM/Activator/ComActivationContext.cs @@ -0,0 +1,19 @@ +// 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.Reflection; +using System.Runtime.InteropServices; + +namespace Activator +{ + [StructLayout(LayoutKind.Sequential)] + public partial struct ComActivationContext + { + public Guid ClassId; + public Guid InterfaceId; + public string AssemblyPath; + public string AssemblyName; + public string TypeName; + } +} diff --git a/src/tests/Interop/COM/Activator/Program.cs b/src/tests/Interop/COM/Activator/Program.cs index b16802db9c93b..48bd0d5f13d1e 100644 --- a/src/tests/Interop/COM/Activator/Program.cs +++ b/src/tests/Interop/COM/Activator/Program.cs @@ -1,21 +1,65 @@ // 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.IO; +using System.Reflection; +using System.Runtime.InteropServices; -namespace Activator +using Internal.Runtime.InteropServices; +using TestLibrary; +using Xunit; + +namespace Internal.Runtime.InteropServices { - using Internal.Runtime.InteropServices; + [ComImport] + [ComVisible(false)] + [Guid("00000001-0000-0000-C000-000000000046")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IClassFactory + { + void CreateInstance( + [MarshalAs(UnmanagedType.Interface)] object pUnkOuter, + ref Guid riid, + out IntPtr ppvObject); - using System; - using System.IO; - using System.Runtime.InteropServices; + void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock); + } +} - using TestLibrary; - using Xunit; +sealed class ClassFactoryWrapper +{ + private static readonly MethodInfo IClassFactory_Create = typeof(object).Assembly.GetType("Internal.Runtime.InteropServices.IClassFactory").GetMethod("CreateInstance"); + private readonly object _obj; + + public ClassFactoryWrapper(object obj) + { + _obj = obj; + } - using Console = Internal.Console; + public void CreateInstance( + object pUnkOuter, + ref Guid riid, + out IntPtr ppvObject) + { + object[] args = new object[] { pUnkOuter, riid, null }; + IClassFactory_Create.Invoke(_obj, BindingFlags.DoNotWrapExceptions, binder: null, args, culture: null); + riid = (Guid)args[1]; + ppvObject = (IntPtr)args[2]; + } +} - class Program +namespace Activator +{ + unsafe class Program { + private static delegate* GetClassFactoryForTypeMethod = (delegate*)typeof(object).Assembly.GetType("Internal.Runtime.InteropServices.ComActivator", throwOnError: true).GetMethod("GetClassFactoryForType", BindingFlags.NonPublic | BindingFlags.Static).MethodHandle.GetFunctionPointer(); + private static delegate* ClassRegistrationScenarioForType = (delegate*)typeof(object).Assembly.GetType("Internal.Runtime.InteropServices.ComActivator", throwOnError: true).GetMethod("ClassRegistrationScenarioForType", BindingFlags.NonPublic | BindingFlags.Static).MethodHandle.GetFunctionPointer(); + + private static ClassFactoryWrapper GetClassFactoryForType(ComActivationContext context) + { + return new ClassFactoryWrapper(GetClassFactoryForTypeMethod(context)); + } + static void InvalidInterfaceRequest() { Console.WriteLine($"Running {nameof(InvalidInterfaceRequest)}..."); @@ -28,7 +72,7 @@ static void InvalidInterfaceRequest() { InterfaceId = notIClassFactory }; - ComActivator.GetClassFactoryForType(cxt); + GetClassFactoryForType(cxt); }); } @@ -43,7 +87,7 @@ static void NonrootedAssemblyPath(bool builtInComDisabled) InterfaceId = typeof(IClassFactory).GUID, AssemblyPath = "foo.dll" }; - ComActivator.GetClassFactoryForType(cxt); + GetClassFactoryForType(cxt); }; if (!builtInComDisabled) @@ -69,7 +113,7 @@ static void ClassNotRegistered(bool builtInComDisabled) InterfaceId = typeof(IClassFactory).GUID, AssemblyPath = @"C:\foo.dll" }; - ComActivator.GetClassFactoryForType(cxt); + GetClassFactoryForType(cxt); }; if (!builtInComDisabled) @@ -119,11 +163,11 @@ static void ValidateAssemblyIsolation(bool builtInComDisabled) if (builtInComDisabled) { Assert.Throws( - () => ComActivator.GetClassFactoryForType(cxt)); + () => GetClassFactoryForType(cxt)); return; } - var factory = (IClassFactory)ComActivator.GetClassFactoryForType(cxt); + var factory = GetClassFactoryForType(cxt); IntPtr svrRaw; factory.CreateInstance(null, ref iid, out svrRaw); @@ -147,7 +191,7 @@ static void ValidateAssemblyIsolation(bool builtInComDisabled) TypeName = "ClassFromB" }; - var factory = (IClassFactory)ComActivator.GetClassFactoryForType(cxt); + var factory = GetClassFactoryForType(cxt); IntPtr svrRaw; factory.CreateInstance(null, ref iid, out svrRaw); @@ -200,7 +244,7 @@ static void ValidateUserDefinedRegistrationCallbacks() TypeName = typeName }; - var factory = (IClassFactory)ComActivator.GetClassFactoryForType(cxt); + var factory = GetClassFactoryForType(cxt); IntPtr svrRaw; factory.CreateInstance(null, ref iid, out svrRaw); @@ -212,8 +256,8 @@ static void ValidateUserDefinedRegistrationCallbacks() Assert.False(inst.DidUnregister()); cxt.InterfaceId = Guid.Empty; - ComActivator.ClassRegistrationScenarioForType(cxt, register: true); - ComActivator.ClassRegistrationScenarioForType(cxt, register: false); + ClassRegistrationScenarioForType(cxt, true); + ClassRegistrationScenarioForType(cxt, false); Assert.True(inst.DidRegister(), $"User-defined register function should have been called."); Assert.True(inst.DidUnregister(), $"User-defined unregister function should have been called."); @@ -239,7 +283,7 @@ static void ValidateUserDefinedRegistrationCallbacks() TypeName = typename }; - var factory = (IClassFactory)ComActivator.GetClassFactoryForType(cxt); + var factory = GetClassFactoryForType(cxt); IntPtr svrRaw; factory.CreateInstance(null, ref iid, out svrRaw); @@ -251,7 +295,7 @@ static void ValidateUserDefinedRegistrationCallbacks() bool exceptionThrown = false; try { - ComActivator.ClassRegistrationScenarioForType(cxt, register: true); + ClassRegistrationScenarioForType(cxt, true); } catch { @@ -263,7 +307,7 @@ static void ValidateUserDefinedRegistrationCallbacks() exceptionThrown = false; try { - ComActivator.ClassRegistrationScenarioForType(cxt, register: false); + ClassRegistrationScenarioForType(cxt, false); } catch { diff --git a/src/tests/Interop/Directory.Build.targets b/src/tests/Interop/Directory.Build.targets index 0d5ce0b5580ea..12c68c9564410 100644 --- a/src/tests/Interop/Directory.Build.targets +++ b/src/tests/Interop/Directory.Build.targets @@ -3,20 +3,11 @@ - + - - - - - - - diff --git a/src/tests/Interop/ICastable/Castable.cs b/src/tests/Interop/ICastable/Castable.cs index 3a20177a36e5e..5493d16418999 100644 --- a/src/tests/Interop/ICastable/Castable.cs +++ b/src/tests/Interop/ICastable/Castable.cs @@ -6,18 +6,6 @@ using System.Runtime.CompilerServices; using Xunit; -using Console = Internal.Console; - -namespace Xunit -{ - // Include an inline definition of the SkipOnMonoAttribute type as tests that reference CoreLib - // only reference CoreLib and don't reference any other assemblies. - public class SkipOnMonoAttribute : Attribute - { - public SkipOnMonoAttribute(string reason, int testPlatforms = ~0) { } - } -} - public interface IRetArg { T ReturnArg(T t); diff --git a/src/tests/Interop/ICastable/Castable.csproj b/src/tests/Interop/ICastable/Castable.csproj index 7007b694eb213..76fc6021a4ae7 100644 --- a/src/tests/Interop/ICastable/Castable.csproj +++ b/src/tests/Interop/ICastable/Castable.csproj @@ -1,9 +1,9 @@ - - true + true + diff --git a/src/tests/Interop/ICastable/ICastable.CoreLib.csproj b/src/tests/Interop/ICastable/ICastable.CoreLib.csproj new file mode 100644 index 0000000000000..482a08e0f78ca --- /dev/null +++ b/src/tests/Interop/ICastable/ICastable.CoreLib.csproj @@ -0,0 +1,17 @@ + + + + Library + SharedLibrary + System.Private.CoreLib + annotations + + + + + diff --git a/src/tests/Interop/IJW/LoadIjwFromModuleHandle/LoadIjwFromModuleHandle.cs b/src/tests/Interop/IJW/LoadIjwFromModuleHandle/LoadIjwFromModuleHandle.cs index e7df50ed4fa8b..66232d4939e00 100644 --- a/src/tests/Interop/IJW/LoadIjwFromModuleHandle/LoadIjwFromModuleHandle.cs +++ b/src/tests/Interop/IJW/LoadIjwFromModuleHandle/LoadIjwFromModuleHandle.cs @@ -5,12 +5,9 @@ using System.IO; using System.Reflection; using System.Runtime.InteropServices; -using Internal.Runtime.InteropServices; using TestLibrary; using Xunit; -using Console = Internal.Console; - namespace LoadIjwFromModuleHandle { class LoadIjwFromModuleHandle @@ -38,7 +35,15 @@ unsafe static int Main(string[] args) string.Empty)) fixed (char* path = ijwModulePath) { - InMemoryAssemblyLoader.LoadInMemoryAssembly(ijwNativeHandle, (IntPtr)path); + typeof(object).Assembly + .GetType("Internal.Runtime.InteropServices.InMemoryAssemblyLoader") + .GetMethod("LoadInMemoryAssembly") + .Invoke( + null, + BindingFlags.DoNotWrapExceptions, + binder: null, + new object[] { ijwNativeHandle, (IntPtr)path }, + culture: null); } NativeEntryPointDelegate nativeEntryPoint = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(ijwNativeHandle, "NativeEntryPoint")); diff --git a/src/tests/Interop/IJW/LoadIjwFromModuleHandle/LoadIjwFromModuleHandle.csproj b/src/tests/Interop/IJW/LoadIjwFromModuleHandle/LoadIjwFromModuleHandle.csproj index a42c985e422b8..f5f12e84db379 100644 --- a/src/tests/Interop/IJW/LoadIjwFromModuleHandle/LoadIjwFromModuleHandle.csproj +++ b/src/tests/Interop/IJW/LoadIjwFromModuleHandle/LoadIjwFromModuleHandle.csproj @@ -1,8 +1,6 @@ Exe - - true true true @@ -12,6 +10,7 @@ true true + true true