Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use reflection or a compile-time only shim assembly to reference unexposed corelib types. #61802

Merged
merged 8 commits into from
Nov 20, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
2 changes: 0 additions & 2 deletions docs/workflow/testing/coreclr/test-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
* `<JitOptimizationSensitive>true</JitOptimizationSensitive>`
* 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:
* `<ReferenceSystemPrivateCoreLib>true</ReferenceSystemPrivateCoreLib>`
* 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public partial struct ComActivationContext
}

[ComImport]
[TypeIdentifier]
[ComVisible(false)]
[Guid("00000001-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
<CompilerVisibleProperty Include="TargetArchitecture" />
<CompilerVisibleProperty Include="Priority" />
<!-- Properties that influence test harness generation -->
<CompilerVisibleProperty Include="ReferenceSystemPrivateCoreLib" />
<CompilerVisibleProperty Include="IsMergedTestRunnerAssembly" />
<CompilerVisibleProperty Include="TestFilter" />
<CompilerVisibleItemMetadata Include="AdditionalFiles" MetadataName="IsOutOfProcessTestAssembly" />
Expand Down
28 changes: 0 additions & 28 deletions src/tests/Common/override.targets

This file was deleted.

12 changes: 2 additions & 10 deletions src/tests/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@
<_WillCLRTestProjectBuild Condition="'$(BuildAllProjects)' != 'true'">true</_WillCLRTestProjectBuild>
<_WillCLRTestProjectBuild Condition="'$(BuildAllProjects)' == 'true' And '$(CLRTestPriority)' &lt;= '$(CLRTestPriorityToBuild)'">true</_WillCLRTestProjectBuild>
<_WillCLRTestProjectBuild Condition="'$(CLRTestBuildAllTargets)' != 'allTargets' And '$(CLRTestTargetUnsupported)' == 'true'">false</_WillCLRTestProjectBuild>
<_WillCLRTestProjectBuild Condition="'$(ReferenceSystemPrivateCoreLib)' == 'true' and '$(RuntimeFlavor)' == 'mono'">false</_WillCLRTestProjectBuild>
<_WillCLRTestProjectBuild Condition="'$(DisableProjectBuild)' == 'true'">false</_WillCLRTestProjectBuild>
</PropertyGroup>
<PropertyGroup>
Expand All @@ -95,8 +94,6 @@
<!-- RunOnly projects have a special build for dependent projects -->
<Import Project="$(MSBuildThisFileDirectory)Common\runonly.targets" Condition="'$(CLRTestKind)' == 'RunOnly'" />

<Import Project="$(MSBuildThisFileDirectory)Common\override.targets" />

<!-- We enable auto-unification of assembly references after importing the common targets. Binding redirects are not needed
for coreclr since it auto-unifies, so the warnings we get without this setting are just noise -->
<PropertyGroup>
Expand Down Expand Up @@ -225,12 +222,11 @@
BeforeTargets="BeforeResolveReferences"
>
<MSBuild Projects="$(MSBuildProjectFullPath)"
Targets="GetLiveRefAssemblies"
Condition="'$(ReferenceSystemPrivateCoreLib)' != 'true'">
Targets="GetLiveRefAssemblies">
<Output TaskParameter="TargetOutputs" ItemName="Reference" />
</MSBuild>

<ItemGroup Condition="'$(ReferenceSystemPrivateCoreLib)' != 'true'">
<ItemGroup >
<Reference Include="$(TargetingPackPath)/*.dll" >
<Private>false</Private>
</Reference>
Expand All @@ -255,10 +251,6 @@
<ProjectAssetsFile>$(BaseOutputPath)\packages\Common\test_dependencies\test_dependencies\project.assets.json</ProjectAssetsFile>
</PropertyGroup>

<PropertyGroup Condition="'$(ReferenceSystemPrivateCoreLib)' == 'true' and '$(UsingMicrosoftNETSdk)' != 'true'">
<ProjectAssetsFile></ProjectAssetsFile>
</PropertyGroup>

<PropertyGroup>
<DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>
</PropertyGroup>
Expand Down
4 changes: 2 additions & 2 deletions src/tests/Interop/COM/Activator/Activator.csproj
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<!-- Internal.Runtime.InteropServices is CoreCLR-only -->
<ReferenceSystemPrivateCoreLib>true</ReferenceSystemPrivateCoreLib>
<RequiresMockHostPolicy>true</RequiresMockHostPolicy>
<!-- The test fails casting from ClassFromA from the default ALC to type IGetTypeFromC from a custom ALC -->
<UnloadabilityIncompatible>true</UnloadabilityIncompatible>
<DisableProjectBuild Condition="'$(RuntimeFlavor)' == 'Mono'">true</DisableProjectBuild>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="ComActivationContextShim.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NETServer\NETServer.csproj" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<!-- Internal.Runtime.InteropServices is CoreCLR-only -->
<ReferenceSystemPrivateCoreLib>true</ReferenceSystemPrivateCoreLib>
<RequiresMockHostPolicy>true</RequiresMockHostPolicy>
<!-- The test fails casting from ClassFromA from the default ALC to type IGetTypeFromC from a custom ALC -->
<UnloadabilityIncompatible>true</UnloadabilityIncompatible>
<DisableProjectBuild Condition="'$(RuntimeFlavor)' == 'Mono'">true</DisableProjectBuild>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="ComActivationContextShim.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NETServer\NETServer.csproj" />
Expand Down
46 changes: 46 additions & 0 deletions src/tests/Interop/COM/Activator/ComActivationContextShim.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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;

namespace Activator
{
internal sealed class ComActivationContextShim
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
{
private static readonly Type _comActivationContextType = typeof(object).Assembly.GetType("Internal.Runtime.InteropServices.ComActivationContext", throwOnError: true);
private static readonly FieldInfo _classIdField = _comActivationContextType.GetField("ClassId");
private static readonly FieldInfo _interfaceIdField = _comActivationContextType.GetField("InterfaceId");
private static readonly FieldInfo _assemblyPathField = _comActivationContextType.GetField("AssemblyPath");
private static readonly FieldInfo _assemblyNameField = _comActivationContextType.GetField("AssemblyName");
private static readonly FieldInfo _typeNameField = _comActivationContextType.GetField("TypeName");

public object UnderlyingContext { get; } = System.Activator.CreateInstance(_comActivationContextType);

public Guid ClassId
{
get => (Guid)_classIdField.GetValue(UnderlyingContext);
set => _classIdField.SetValue(UnderlyingContext, value);
}
public Guid InterfaceId
{
get => (Guid)_interfaceIdField.GetValue(UnderlyingContext);
set => _interfaceIdField.SetValue(UnderlyingContext, value);
}
public string AssemblyPath
{
get => (string)_assemblyPathField.GetValue(UnderlyingContext);
set => _assemblyPathField.SetValue(UnderlyingContext, value);
}
public string AssemblyName
{
get => (string)_assemblyNameField.GetValue(UnderlyingContext);
set => _assemblyNameField.SetValue(UnderlyingContext, value);
}
public string TypeName
{
get => (string)_typeNameField.GetValue(UnderlyingContext);
set => _typeNameField.SetValue(UnderlyingContext, value);
}
}
}
85 changes: 56 additions & 29 deletions src/tests/Interop/COM/Activator/Program.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,48 @@
// 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 Internal.Runtime.InteropServices;
using TestLibrary;
using Xunit;

using System;
using System.IO;
using System.Runtime.InteropServices;

using TestLibrary;
using Xunit;
namespace Internal.Runtime.InteropServices
{
[ComImport]
[TypeIdentifier]
[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 Console = Internal.Console;
void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock);
}
}

namespace Activator
{
class Program
{
private static MethodInfo GetClassFactoryForTypeMethod = typeof(object).Assembly.GetType("Internal.Runtime.InteropServices.ComActivator", throwOnError: true).GetMethod("GetClassFactoryForType");
private static MethodInfo ClassRegistrationScenarioForTypeMethod = typeof(object).Assembly.GetType("Internal.Runtime.InteropServices.ComActivator", throwOnError: true).GetMethod("ClassRegistrationScenarioForType");

private static object GetClassFactoryForType(ComActivationContextShim context)
{
return GetClassFactoryForTypeMethod.Invoke(null, BindingFlags.DoNotWrapExceptions, binder: null, new[] { context.UnderlyingContext }, culture: null);
}
private static object ClassRegistrationScenarioForType(ComActivationContextShim context, bool register)
{
return ClassRegistrationScenarioForTypeMethod.Invoke(null, BindingFlags.DoNotWrapExceptions, binder: null, new[] { context.UnderlyingContext, (object)register }, culture: null);
}

static void InvalidInterfaceRequest()
{
Console.WriteLine($"Running {nameof(InvalidInterfaceRequest)}...");
Expand All @@ -24,11 +51,11 @@ static void InvalidInterfaceRequest()
() =>
{
var notIClassFactory = new Guid("ED53F949-63E4-43B5-A13D-5655478AADD5");
var cxt = new ComActivationContext()
var cxt = new ComActivationContextShim()
{
InterfaceId = notIClassFactory
};
ComActivator.GetClassFactoryForType(cxt);
GetClassFactoryForType(cxt);
});
}

Expand All @@ -38,12 +65,12 @@ static void NonrootedAssemblyPath(bool builtInComDisabled)

Action action = () =>
{
var cxt = new ComActivationContext()
var cxt = new ComActivationContextShim()
{
InterfaceId = typeof(IClassFactory).GUID,
AssemblyPath = "foo.dll"
};
ComActivator.GetClassFactoryForType(cxt);
GetClassFactoryForType(cxt);
};

if (!builtInComDisabled)
Expand All @@ -63,13 +90,13 @@ static void ClassNotRegistered(bool builtInComDisabled)
Action action = () =>
{
var CLSID_NotRegistered = new Guid("328FF83E-3F6C-4BE9-A742-752562032925"); // Random GUID
var cxt = new ComActivationContext()
var cxt = new ComActivationContextShim()
{
ClassId = CLSID_NotRegistered,
InterfaceId = typeof(IClassFactory).GUID,
AssemblyPath = @"C:\foo.dll"
};
ComActivator.GetClassFactoryForType(cxt);
GetClassFactoryForType(cxt);
};

if (!builtInComDisabled)
Expand Down Expand Up @@ -107,7 +134,7 @@ static void ValidateAssemblyIsolation(bool builtInComDisabled)
string.Empty,
string.Empty))
{
var cxt = new ComActivationContext()
var cxt = new ComActivationContextShim()
{
ClassId = CLSID_NotUsed,
InterfaceId = typeof(IClassFactory).GUID,
Expand All @@ -119,11 +146,11 @@ static void ValidateAssemblyIsolation(bool builtInComDisabled)
if (builtInComDisabled)
{
Assert.Throws<NotSupportedException>(
() => ComActivator.GetClassFactoryForType(cxt));
() => GetClassFactoryForType(cxt));
return;
}

var factory = (IClassFactory)ComActivator.GetClassFactoryForType(cxt);
var factory = (IClassFactory)GetClassFactoryForType(cxt);

IntPtr svrRaw;
factory.CreateInstance(null, ref iid, out svrRaw);
Expand All @@ -138,7 +165,7 @@ static void ValidateAssemblyIsolation(bool builtInComDisabled)
string.Empty,
string.Empty))
{
var cxt = new ComActivationContext()
var cxt = new ComActivationContextShim()
{
ClassId = CLSID_NotUsed,
InterfaceId = typeof(IClassFactory).GUID,
Expand All @@ -147,7 +174,7 @@ static void ValidateAssemblyIsolation(bool builtInComDisabled)
TypeName = "ClassFromB"
};

var factory = (IClassFactory)ComActivator.GetClassFactoryForType(cxt);
var factory = (IClassFactory)GetClassFactoryForType(cxt);

IntPtr svrRaw;
factory.CreateInstance(null, ref iid, out svrRaw);
Expand Down Expand Up @@ -191,7 +218,7 @@ static void ValidateUserDefinedRegistrationCallbacks()
{
Console.WriteLine($"Validating {typeName}...");

var cxt = new ComActivationContext()
var cxt = new ComActivationContextShim()
{
ClassId = CLSID_NotUsed,
InterfaceId = typeof(IClassFactory).GUID,
Expand All @@ -200,7 +227,7 @@ static void ValidateUserDefinedRegistrationCallbacks()
TypeName = typeName
};

var factory = (IClassFactory)ComActivator.GetClassFactoryForType(cxt);
var factory = (IClassFactory)GetClassFactoryForType(cxt);

IntPtr svrRaw;
factory.CreateInstance(null, ref iid, out svrRaw);
Expand All @@ -212,8 +239,8 @@ static void ValidateUserDefinedRegistrationCallbacks()
Assert.False(inst.DidUnregister());

cxt.InterfaceId = Guid.Empty;
ComActivator.ClassRegistrationScenarioForType(cxt, register: true);
ComActivator.ClassRegistrationScenarioForType(cxt, register: false);
ClassRegistrationScenarioForType(cxt, register: true);
ClassRegistrationScenarioForType(cxt, register: 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.");
Expand All @@ -230,7 +257,7 @@ static void ValidateUserDefinedRegistrationCallbacks()
{
Console.WriteLine($"Validating {typename}...");

var cxt = new ComActivationContext()
var cxt = new ComActivationContextShim()
{
ClassId = CLSID_NotUsed,
InterfaceId = typeof(IClassFactory).GUID,
Expand All @@ -239,7 +266,7 @@ static void ValidateUserDefinedRegistrationCallbacks()
TypeName = typename
};

var factory = (IClassFactory)ComActivator.GetClassFactoryForType(cxt);
var factory = (IClassFactory)GetClassFactoryForType(cxt);

IntPtr svrRaw;
factory.CreateInstance(null, ref iid, out svrRaw);
Expand All @@ -251,7 +278,7 @@ static void ValidateUserDefinedRegistrationCallbacks()
bool exceptionThrown = false;
try
{
ComActivator.ClassRegistrationScenarioForType(cxt, register: true);
ClassRegistrationScenarioForType(cxt, register: true);
}
catch
{
Expand All @@ -263,7 +290,7 @@ static void ValidateUserDefinedRegistrationCallbacks()
exceptionThrown = false;
try
{
ComActivator.ClassRegistrationScenarioForType(cxt, register: false);
ClassRegistrationScenarioForType(cxt, register: false);
}
catch
{
Expand Down
Loading