Skip to content

Commit

Permalink
Use reflection or a compile-time only shim assembly to reference unex…
Browse files Browse the repository at this point in the history
…posed corelib types.

Use reflection for the hosting tests as they'll be around indefinitely.

Use the ref-assembly trick for ICastable testing since ICastable testing would require Ref-emit which makes the test significantly less readable and since ICastable will be going away within the .NET 7 timeframe. Supercedes dotnet#61754
  • Loading branch information
jkoritzinsky committed Nov 18, 2021
1 parent 6ff57f1 commit 0cf62d2
Show file tree
Hide file tree
Showing 15 changed files with 135 additions and 106 deletions.
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
3 changes: 1 addition & 2 deletions src/tests/Interop/COM/Activator/Activator.csproj
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
<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>
</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,13 @@
<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>
</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
{
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

0 comments on commit 0cf62d2

Please sign in to comment.