1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-08 03:27:04 +09:00

Revert ILLink's usage of dependency analysis framework (#104267)

See
https://github.com/dotnet/runtime/issues/103987#issuecomment-2197798838
for context.

Custom steps rely on getting a chance to see a type before we
build the type info (and in particular the interface method
mapping), but we make no such
guarantee. https://github.com/dotnet/runtime/issues/104266 tracks
this problem.

Our use of the dependency analysis framework exacerbated this
because `MarkType` was split into two pieces, with the part that
calls into the custom step being delayed through a dependency
analysis framework node, making it more likely to be run too late
to influence the type info.

This reverts ILLink's usage of the dependency analysis framework
to bring us back to a state where the ordering still isn't
guaranteed, but works for the testcase that got regressed. We
should bring this back as soon as possible with a proper fix that
actually guarantees the ordering required by custom steps. This
doesn't look completely straightforward, but should be possible
to do with the dependency analysis framework.

Fixes https://github.com/dotnet/runtime/issues/103987 (but we
need a better long-term fix for
https://github.com/dotnet/runtime/issues/104266).
This commit is contained in:
Sven Boemer 2024-07-02 12:19:17 -07:00 committed by GitHub
parent 7a9f84e401
commit c5f2d4f122
Signed by: github
GPG key ID: B5690EEEBB952194
19 changed files with 257 additions and 353 deletions

View file

@ -30,13 +30,11 @@
<ItemGroup Condition="'$(_RequiresLiveILLink)' == 'true'">
<!-- The assembly shouldn't be referenced, nor promoted to a package dependency, nor copied to the output directory. -->
<!-- ILLink.Tasks is architecture independent, so TargetArch and TargetOS should not be specified -->
<ProjectReference Include="$(_ILLinkTasksSourceDir)ILLink.Tasks.csproj"
ReferenceOutputAssembly="false"
PrivateAssets="all"
Private="false"
SetConfiguration="Configuration=$(ToolsConfiguration)"
GlobalPropertiesToRemove="TargetArchitecture;TargetOS" >
SetConfiguration="Configuration=$(ToolsConfiguration)">
<!-- Keep TFMs in sync with ILLink.Tasks.csproj -->
<SetTargetFramework Condition="'$(MSBuildRuntimeType)' == 'Core'">TargetFramework=$(NetCoreAppToolCurrent)</SetTargetFramework>
<SetTargetFramework Condition="'$(MSBuildRuntimeType)' != 'Core'">TargetFramework=$(NetFrameworkToolCurrent)</SetTargetFramework>

View file

@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<InferPlatformFromTargetArchitecture Condition="'$(InferPlatformFromTargetArchitecture)' == ''">true</InferPlatformFromTargetArchitecture>
<InferPlatformFromTargetArchitecture>true</InferPlatformFromTargetArchitecture>
<!-- TODO: Clean-up casing and remove __BuildType to remove this block. -->
<Configuration Condition="'$(Configuration)' == ''">$(__BuildType)</Configuration>

View file

@ -1,8 +1,4 @@
<Project>
<PropertyGroup>
<PlatformName>AnyCPU</PlatformName>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<RootNamespace>ILCompiler.DependencyAnalysisFramework</RootNamespace>
@ -12,10 +8,6 @@
<Platforms>x64;x86</Platforms>
<PlatformTarget>AnyCPU</PlatformTarget>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<!-- Workaround for circular dependency (this => trim analyzers in illink.Tasks => Mono.Linker => this)-->
<_RequiresLiveILLink>false</_RequiresLiveILLink>
<!-- Mono.Linker requires the assembly to be signed -->
<SignAssembly>true</SignAssembly>
<!-- We're binplacing these into an existing publish layout so that F5 build in VS updates
the same bits tests expect to see in artifacts/crossgen2. That way we never need to wonder which
@ -58,5 +50,4 @@
<Link>Sorting\MergeSortCore.cs</Link>
</Compile>
</ItemGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>

View file

@ -1,43 +0,0 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using ILCompiler.DependencyAnalysisFramework;
using Mono.Cecil;
namespace Mono.Linker.Steps
{
public partial class MarkStep
{
internal sealed class MethodDefinitionNode : DependencyNodeCore<NodeFactory>
{
readonly MethodDefinition method;
readonly DependencyInfo reason;
public MethodDefinitionNode (MethodDefinition method, DependencyInfo reason)
{
this.method = method;
this.reason = reason;
}
public override bool InterestingForDynamicDependencyAnalysis => false;
public override bool HasDynamicDependencies => false;
public override bool HasConditionalStaticDependencies => false;
public override bool StaticDependenciesAreComputed => true;
public override IEnumerable<DependencyListEntry>? GetStaticDependencies (NodeFactory context)
{
context.MarkStep.ProcessMethod (method, reason);
return null;
}
public override IEnumerable<CombinedDependencyListEntry>? GetConditionalStaticDependencies (NodeFactory context) => null;
public override IEnumerable<CombinedDependencyListEntry>? SearchDynamicDependencies (List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory context) => null;
protected override string GetName (NodeFactory context) => method.GetDisplayName();
}
}
}

View file

@ -1,71 +0,0 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Mono.Cecil;
namespace Mono.Linker.Steps
{
public partial class MarkStep
{
internal sealed class NodeFactory (MarkStep markStep)
{
public MarkStep MarkStep { get; } = markStep;
readonly NodeCache<TypeDefinition, TypeDefinitionNode> _typeNodes = new (static t => new TypeDefinitionNode(t));
readonly NodeCache<MethodDefinition, MethodDefinitionNode> _methodNodes = new (static _ => throw new InvalidOperationException ("Creation of node requires more than the key."));
readonly NodeCache<PropertyDefinition, PropertyDefinitionNode> _propertyNodes = new (static p => new PropertyDefinitionNode(p));
readonly NodeCache<TypeDefinition, TypeIsRelevantToVariantCastingNode> _typeIsRelevantToVariantCastingNodes = new (static (t) => new TypeIsRelevantToVariantCastingNode (t));
internal TypeDefinitionNode GetTypeNode (TypeDefinition definition)
{
return _typeNodes.GetOrAdd (definition);
}
internal MethodDefinitionNode GetMethodDefinitionNode (MethodDefinition method, DependencyInfo reason)
{
return _methodNodes.GetOrAdd (method, (k) => new MethodDefinitionNode (k, reason));
}
internal TypeIsRelevantToVariantCastingNode GetTypeIsRelevantToVariantCastingNode (TypeDefinition type)
{
return _typeIsRelevantToVariantCastingNodes.GetOrAdd (type);
}
internal PropertyDefinitionNode GetPropertyNode (PropertyDefinition prop)
{
return _propertyNodes.GetOrAdd (prop);
}
struct NodeCache<TKey, TValue> where TKey : notnull
{
// Change to concurrent dictionary if/when multithreaded marking is enabled
readonly Dictionary<TKey, TValue> _cache;
readonly Func<TKey, TValue> _creator;
public NodeCache (Func<TKey, TValue> creator, IEqualityComparer<TKey> comparer)
{
_creator = creator;
_cache = new (comparer);
}
public NodeCache (Func<TKey, TValue> creator)
{
_creator = creator;
_cache = new ();
}
public TValue GetOrAdd (TKey key)
{
return _cache.GetOrAdd (key, _creator);
}
public TValue GetOrAdd (TKey key, Func<TKey, TValue> creator)
{
return _cache.GetOrAdd (key, creator);
}
}
}
}
}

View file

@ -1,42 +0,0 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using ILCompiler.DependencyAnalysisFramework;
namespace Mono.Linker.Steps
{
public partial class MarkStep
{
sealed class ProcessCallbackNode : DependencyNodeCore<NodeFactory>
{
Func<bool> _processAction;
DependencyList? _dependencies;
public ProcessCallbackNode (Func<bool> action) => _processAction = action;
public void Process ()
{
_dependencies = new DependencyList ();
if (_processAction ()) {
_dependencies.Add (new ProcessCallbackNode (_processAction), "Some processing was done, continuation required");
}
}
public override bool InterestingForDynamicDependencyAnalysis => false;
public override bool HasDynamicDependencies => false;
public override bool HasConditionalStaticDependencies => false;
public override bool StaticDependenciesAreComputed => _dependencies != null;
public override IEnumerable<DependencyListEntry>? GetStaticDependencies (NodeFactory context) => _dependencies;
public override IEnumerable<CombinedDependencyListEntry>? GetConditionalStaticDependencies (NodeFactory context) => null;
public override IEnumerable<CombinedDependencyListEntry>? SearchDynamicDependencies (List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory context) => null;
protected override string GetName (NodeFactory context) => "Process";
}
}
}

View file

@ -1,46 +0,0 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using ILCompiler.DependencyAnalysisFramework;
using Mono.Cecil;
namespace Mono.Linker.Steps
{
public partial class MarkStep
{
internal sealed class PropertyDefinitionNode : DependencyNodeCore<NodeFactory>
{
PropertyDefinition _property;
public PropertyDefinitionNode(PropertyDefinition property)
{
_property = property;
}
public override bool InterestingForDynamicDependencyAnalysis => false;
public override bool HasDynamicDependencies => false;
public override bool HasConditionalStaticDependencies => false;
public override bool StaticDependenciesAreComputed => true;
public override IEnumerable<CombinedDependencyListEntry>? GetConditionalStaticDependencies (NodeFactory context) => null;
public override IEnumerable<DependencyListEntry>? GetStaticDependencies (NodeFactory context)
{
var propertyOrigin = new MessageOrigin (_property);
// Consider making this more similar to MarkEvent method?
context.MarkStep.MarkCustomAttributes (_property, new DependencyInfo (DependencyKind.CustomAttribute, _property), propertyOrigin);
context.MarkStep.DoAdditionalPropertyProcessing (_property);
return null;
}
public override IEnumerable<CombinedDependencyListEntry>? SearchDynamicDependencies (List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory context) => null;
protected override string GetName (NodeFactory context) => _property.GetDisplayName ();
}
}
}

View file

@ -1,40 +0,0 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using ILCompiler.DependencyAnalysisFramework;
using Mono.Cecil;
namespace Mono.Linker.Steps
{
public partial class MarkStep
{
internal sealed class TypeDefinitionNode : DependencyNodeCore<NodeFactory>
{
readonly TypeDefinition type;
public TypeDefinitionNode (TypeDefinition type)
{
this.type = type;
}
public override bool InterestingForDynamicDependencyAnalysis => false;
public override bool HasDynamicDependencies => false;
public override bool HasConditionalStaticDependencies => false;
public override bool StaticDependenciesAreComputed => true;
public override IEnumerable<DependencyListEntry>? GetStaticDependencies (NodeFactory context)
{
context.MarkStep.ProcessType (type);
return null;
}
public override IEnumerable<CombinedDependencyListEntry>? GetConditionalStaticDependencies (NodeFactory context) => null;
public override IEnumerable<CombinedDependencyListEntry>? SearchDynamicDependencies (List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory context) => null;
protected override string GetName (NodeFactory context) => type.GetDisplayName();
}
}
}

View file

@ -1,39 +0,0 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using ILCompiler.DependencyAnalysisFramework;
using Mono.Cecil;
namespace Mono.Linker.Steps
{
public partial class MarkStep
{
internal sealed class TypeIsRelevantToVariantCastingNode : DependencyNodeCore<NodeFactory>
{
TypeDefinition type;
public TypeIsRelevantToVariantCastingNode (TypeDefinition type) => this.type = type;
public override bool InterestingForDynamicDependencyAnalysis => false;
public override bool HasDynamicDependencies => false;
public override bool HasConditionalStaticDependencies => false;
public override bool StaticDependenciesAreComputed => true;
public override IEnumerable<DependencyListEntry>? GetStaticDependencies (NodeFactory context)
{
yield break;
}
public override IEnumerable<CombinedDependencyListEntry>? GetConditionalStaticDependencies (NodeFactory context) => null;
public override IEnumerable<CombinedDependencyListEntry>? SearchDynamicDependencies (List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory context) => null;
protected override string GetName (NodeFactory context) => $"{type.GetDisplayName()} is relevant to variant casting";
protected override void OnMarked (NodeFactory context)
{
context.MarkStep.Annotations.MarkRelevantToVariantCasting (type);
}
}
}
}

View file

@ -35,8 +35,8 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.Text.RegularExpressions;
using ILCompiler.DependencyAnalysisFramework;
using ILLink.Shared;
using ILLink.Shared.TrimAnalysis;
using ILLink.Shared.TypeSystemProxy;
@ -62,7 +62,7 @@ namespace Mono.Linker.Steps
}
}
private bool _completed;
protected Queue<(MethodDefinition, DependencyInfo)> _methods;
protected Dictionary<MethodDefinition, MessageOrigin> _interface_methods;
protected Queue<AttributeProviderPair> _assemblyLevelAttributes;
protected Queue<(AttributeProviderPair, DependencyInfo, MessageOrigin)> _lateMarkedAttributes;
@ -76,8 +76,6 @@ namespace Mono.Linker.Steps
// Stores, for compiler-generated methods only, whether they require the reflection
// method body scanner.
readonly Dictionary<MethodBody, bool> _compilerGeneratedMethodRequiresScanner;
private readonly NodeFactory _nodeFactory;
private readonly DependencyAnalyzer<NoLogStrategy<NodeFactory>, NodeFactory> _dependencyGraph;
MarkStepContext? _markContext;
MarkStepContext MarkContext {
@ -216,7 +214,7 @@ namespace Mono.Linker.Steps
public MarkStep ()
{
_completed = false;
_methods = new Queue<(MethodDefinition, DependencyInfo)> ();
_interface_methods = new Dictionary<MethodDefinition, MessageOrigin> ();
_assemblyLevelAttributes = new Queue<AttributeProviderPair> ();
_lateMarkedAttributes = new Queue<(AttributeProviderPair, DependencyInfo, MessageOrigin)> ();
@ -227,8 +225,6 @@ namespace Mono.Linker.Steps
_pending_isinst_instr = new List<(TypeDefinition, MethodBody, Instruction)> ();
_entireTypesMarked = new HashSet<TypeDefinition> ();
_compilerGeneratedMethodRequiresScanner = new Dictionary<MethodBody, bool> ();
_nodeFactory = new NodeFactory (this);
_dependencyGraph = new DependencyAnalyzer<NoLogStrategy<NodeFactory>, NodeFactory> (_nodeFactory, null);
}
public AnnotationStore Annotations => Context.Annotations;
@ -362,23 +358,13 @@ namespace Mono.Linker.Steps
void Process ()
{
_dependencyGraph.ComputeDependencyRoutine += (List<DependencyNodeCore<NodeFactory>> nodes) => {
foreach (DependencyNodeCore<NodeFactory> node in nodes) {
if (node is ProcessCallbackNode processNode)
processNode.Process ();
}
};
_dependencyGraph.AddRoot (new ProcessCallbackNode (ProcessAllPendingItems), "start");
_dependencyGraph.ComputeMarkedNodes ();
ProcessPendingTypeChecks ();
bool ProcessAllPendingItems ()
=> ProcessPrimaryQueue () ||
while (ProcessPrimaryQueue () ||
ProcessMarkedPending () ||
ProcessLazyAttributes () ||
ProcessLateMarkedAttributes () ||
MarkFullyPreservedAssemblies ();
MarkFullyPreservedAssemblies ()) ;
ProcessPendingTypeChecks ();
}
static bool IsFullyPreservedAction (AssemblyAction action) => action == AssemblyAction.Copy || action == AssemblyAction.Save;
@ -430,11 +416,11 @@ namespace Mono.Linker.Steps
bool ProcessPrimaryQueue ()
{
if (_completed)
if (QueueIsEmpty ())
return false;
while (!_completed) {
_completed = true;
while (!QueueIsEmpty ()) {
ProcessQueue ();
ProcessInterfaceMethods ();
ProcessMarkedTypesWithInterfaces ();
ProcessDynamicCastableImplementationInterfaces ();
@ -505,6 +491,24 @@ namespace Mono.Linker.Steps
}
}
void ProcessQueue ()
{
while (!QueueIsEmpty ()) {
(MethodDefinition method, DependencyInfo reason) = _methods.Dequeue ();
ProcessMethod (method, reason);
}
}
bool QueueIsEmpty ()
{
return _methods.Count == 0;
}
protected virtual void EnqueueMethod (MethodDefinition method, in DependencyInfo reason)
{
_methods.Enqueue ((method, reason));
}
void ProcessInterfaceMethods ()
{
foreach ((var method, var origin) in _interface_methods) {
@ -1855,7 +1859,7 @@ namespace Mono.Linker.Steps
MarkMethodsIf (type.Methods, HasOnSerializeOrDeserializeAttribute, new DependencyInfo (DependencyKind.SerializationMethodForType, type), origin);
}
protected internal virtual void MarkTypeVisibleToReflection (TypeReference type, TypeDefinition definition, in DependencyInfo reason, in MessageOrigin origin)
protected internal virtual TypeDefinition? MarkTypeVisibleToReflection (TypeReference type, TypeDefinition definition, in DependencyInfo reason, in MessageOrigin origin)
{
// If a type is visible to reflection, we need to stop doing optimization that could cause observable difference
// in reflection APIs. This includes APIs like MakeGenericType (where variant castability of the produced type
@ -1866,7 +1870,7 @@ namespace Mono.Linker.Steps
MarkImplicitlyUsedFields (definition, origin);
MarkType (type, reason, origin);
return MarkType (type, reason, origin);
}
internal void MarkMethodVisibleToReflection (MethodReference method, in DependencyInfo reason, in MessageOrigin origin)
@ -1947,7 +1951,6 @@ namespace Mono.Linker.Steps
MarkStaticConstructor (type, reason, origin);
}
/// <summary>
/// Marks the specified <paramref name="reference"/> as referenced.
/// </summary>
@ -2007,12 +2010,6 @@ namespace Mono.Linker.Steps
if (type.Scope is ModuleDefinition module)
MarkModule (module, new DependencyInfo (DependencyKind.ScopeOfType, type), origin);
_dependencyGraph.AddRoot (_nodeFactory.GetTypeNode (type), Enum.GetName (reason.Kind));
return type;
}
protected internal virtual void ProcessType (TypeDefinition type)
{
var typeOrigin = new MessageOrigin (type);
foreach (Action<TypeDefinition> handleMarkType in MarkContext.MarkTypeActions)
@ -2098,7 +2095,7 @@ namespace Mono.Linker.Steps
MarkMethod (method, new DependencyInfo (DependencyKind.VirtualNeededDueToPreservedScope, type), typeOrigin);
}
}
if (ShouldMarkTypeStaticConstructor (type)) {
if (ShouldMarkTypeStaticConstructor (type) && reason.Kind != DependencyKind.TriggersCctorForCalledMethod) {
MarkStaticConstructor (type, new DependencyInfo (DependencyKind.CctorForType, type), typeOrigin);
}
}
@ -2108,7 +2105,7 @@ namespace Mono.Linker.Steps
ApplyPreserveInfo (type);
ApplyPreserveMethods (type, typeOrigin);
return;
return type;
}
/// <summary>
@ -2751,7 +2748,7 @@ namespace Mono.Linker.Steps
if (argumentTypeDef == null)
continue;
_dependencyGraph.AddRoot (_nodeFactory.GetTypeIsRelevantToVariantCastingNode (argumentTypeDef), "Generic Argument");
Annotations.MarkRelevantToVariantCasting (argumentTypeDef);
if (parameter?.HasDefaultConstructorConstraint == true)
MarkDefaultConstructor (argumentTypeDef, new DependencyInfo (DependencyKind.DefaultCtorForNewConstrainedGenericArgument, instance), origin);
@ -2997,8 +2994,7 @@ namespace Mono.Linker.Steps
// We will only enqueue a method to be processed if it hasn't been processed yet.
if (!CheckProcessed (method))
_completed = false;
_dependencyGraph.AddRoot (_nodeFactory.GetMethodDefinitionNode (method, reason), Enum.GetName (reason.Kind));
EnqueueMethod (method, reason);
return method;
}
@ -3390,7 +3386,7 @@ namespace Mono.Linker.Steps
return;
foreach (OverrideInformation ov in base_methods) {
// We should add all interface base methods to _interface_methods for virtual override annotation validation
// We should add all interface base methods to _virtual_methods for virtual override annotation validation
// Interfaces from preserved scope will be missed if we don't add them here
// This will produce warnings for all interface methods and virtual methods regardless of whether the interface, interface implementation, or interface method is kept or not.
if (ov.Base.DeclaringType.IsInterface && !method.DeclaringType.IsInterface) {
@ -3492,8 +3488,11 @@ namespace Mono.Linker.Steps
if (!Annotations.MarkProcessed (prop, reason))
return;
PropertyDefinitionNode propertyNode = _nodeFactory.GetPropertyNode (prop);
_dependencyGraph.AddRoot (propertyNode, reason.Kind.ToString ());
var propertyOrigin = new MessageOrigin (prop);
// Consider making this more similar to MarkEvent method?
MarkCustomAttributes (prop, new DependencyInfo (DependencyKind.CustomAttribute, prop), propertyOrigin);
DoAdditionalPropertyProcessing (prop);
}
protected internal virtual void MarkEvent (EventDefinition evt, in DependencyInfo reason, MessageOrigin origin)

View file

@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
namespace Mono.Linker
@ -17,15 +16,5 @@ namespace Mono.Linker
}
valueList.Add (value);
}
public static U GetOrAdd<T, U> (this Dictionary<T, U> dict, T key, Func<T, U> createValue) where T : notnull
{
if (dict.TryGetValue (key, out var value)) {
return value;
}
U val = createValue (key);
dict.Add (key, val);
return val;
}
}
}

View file

@ -82,12 +82,6 @@
<InternalsVisibleTo Include="Mono.Linker.Tests" />
</ItemGroup>
<ItemGroup>
<!-- PrivateAssets prevents ILCompiler.DependencyAnalysisFramework from becoming a dependency of the Microsoft.NET.ILLink package, which is designed to
be referenced by custom step authors as the public surface available to custom steps (which should not use the dependency analysis framework). -->
<ProjectReference Include="$(CoreClrProjectRoot)tools\aot\ILCompiler.DependencyAnalysisFramework\ILCompiler.DependencyAnalysisFramework.csproj" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<PackageReference Condition="'$(DotNetBuildSourceOnly)' != 'true'" Include="StaticCs" Version="$(StaticCsVersion)">
<PrivateAssets>all</PrivateAssets>

View file

@ -0,0 +1,28 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
namespace Mono.Linker.Tests.Cases.Expectations.Assertions
{
[AttributeUsage (AttributeTargets.Class | AttributeTargets.Delegate | AttributeTargets.Struct | AttributeTargets.Enum, AllowMultiple = true, Inherited = false)]
public sealed class CreatedMemberInAssemblyAttribute : BaseInAssemblyAttribute
{
public CreatedMemberInAssemblyAttribute (string assemblyFileName, Type type, params string[] memberNames)
{
if (string.IsNullOrEmpty (assemblyFileName))
throw new ArgumentNullException (nameof (assemblyFileName));
ArgumentNullException.ThrowIfNull (type);
ArgumentNullException.ThrowIfNull (memberNames);
}
public CreatedMemberInAssemblyAttribute (string assemblyFileName, string typeName, params string[] memberNames)
{
if (string.IsNullOrEmpty (assemblyFileName))
throw new ArgumentNullException (nameof (assemblyFileName));
ArgumentNullException.ThrowIfNull (typeName);
ArgumentNullException.ThrowIfNull (memberNames);
}
}
}

View file

@ -0,0 +1,69 @@
using Mono.Linker.Tests.Cases.Extensibility.Dependencies;
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
namespace Mono.Linker.Tests.Cases.Extensibility
{
[ExpectedNoWarnings]
[SetupCompileBefore ("FixAbstractMethods.dll", new[] { "Dependencies/FixAbstractMethods.cs" }, new[] { "illink.dll", "Mono.Cecil.dll", "netstandard.dll" })]
[SetupCompileBefore ("InterfaceType.dll", new[] { "Dependencies/InterfaceType.cs" })]
[SetupCompileAfter ("InterfaceType.dll", new[] { "Dependencies/InterfaceType.cs" }, defines: new[] { "INCLUDE_ABSTRACT_METHOD"})]
[SetupCompileBefore ("InterfaceImplementation.dll", new[] { "Dependencies/InterfaceImplementation.cs" }, references: new[] { "InterfaceType.dll" })]
[CreatedMemberInAssembly ("InterfaceImplementation.dll", typeof (InterfaceImplementationInOtherAssembly), "AbstractMethod()")]
[SetupLinkerArgument ("--custom-step", "FixAbstractMethods,FixAbstractMethods.dll")]
public class CustomStepCanFixAbstractMethods
{
public static void Main ()
{
TestReflectionAccessToOtherAssembly ();
TestReflectionAccess ();
TestDirectAccess ();
}
[Kept]
static void TestReflectionAccessToOtherAssembly ()
{
// Regression test for https://github.com/dotnet/runtime/issues/103987
// To simulate the issue, the type needs to live in a different assembly than the testcase, and it needs
// to be created through reflection instead of a direct call to the constructor, otherwise we build the
// TypeMapInfo cache too early for the custom step.
// var type = typeof (InterfaceImplementation);
var type = typeof (InterfaceImplementationInOtherAssembly);
InterfaceType instance = (InterfaceType) System.Activator.CreateInstance (type);
InterfaceType.UseInstance (instance);
}
[Kept]
static void TestReflectionAccess ()
{
var type = typeof (InterfaceImplementationAccessedViaReflection);
InterfaceType instance = (InterfaceType) System.Activator.CreateInstance (type);
InterfaceType.UseInstance (instance);
}
[Kept]
[KeptMember (".ctor()")]
[KeptInterface (typeof (InterfaceType))]
// [CreatedMember ("AbstractMethod()")] // https://github.com/dotnet/runtime/issues/104266
class InterfaceImplementationAccessedViaReflection : InterfaceType
{
}
[Kept]
static void TestDirectAccess ()
{
InterfaceType instance = new InterfaceImplementation ();
InterfaceType.UseInstance (instance);
}
[Kept]
[KeptMember (".ctor()")]
[KeptInterface (typeof (InterfaceType))]
// [CreatedMember ("AbstractMethod()")] // https://github.com/dotnet/runtime/issues/104266
class InterfaceImplementation : InterfaceType
{
}
}
}

View file

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Mono.Cecil;
using Mono.Linker;
using Mono.Linker.Steps;
public class FixAbstractMethods : IMarkHandler
{
LinkContext _context;
public void Initialize (LinkContext context, MarkContext markContext)
{
_context = context;
markContext.RegisterMarkTypeAction (type => ProcessType (type));
}
void ProcessType (TypeDefinition type)
{
if (!type.Name.Contains ("InterfaceImplementation"))
return;
Assert (!type.IsAbstract && type.HasInterfaces);
var iface = type.Interfaces[0];
Assert (iface.InterfaceType.Name == "InterfaceType");
var interfaceType = iface.InterfaceType.Resolve ();
var method = interfaceType.Methods[0];
Assert (method.Name == "AbstractMethod");
var newMethod = new MethodDefinition (method.Name, (method.Attributes | MethodAttributes.Final) & ~MethodAttributes.Abstract, method.ReturnType);
Assert (!method.HasParameters);
var ilProcessor = newMethod.Body.GetILProcessor ();
ilProcessor.Append (ilProcessor.Create (Mono.Cecil.Cil.OpCodes.Ret));
type.Methods.Add (newMethod);
}
static void Assert (bool b)
{
if (!b)
throw new Exception ();
}
}

View file

@ -0,0 +1,9 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Mono.Linker.Tests.Cases.Extensibility.Dependencies
{
public class InterfaceImplementationInOtherAssembly : InterfaceType
{
}
}

View file

@ -0,0 +1,19 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Mono.Linker.Tests.Cases.Extensibility.Dependencies
{
public interface InterfaceType
{
#if INCLUDE_ABSTRACT_METHOD
public abstract void AbstractMethod ();
#endif
public static void UseInstance (InterfaceType instance)
{
#if INCLUDE_ABSTRACT_METHOD
instance.AbstractMethod ();
#endif
}
}
}

View file

@ -25,6 +25,8 @@
<Compile Remove="CommandLine\Dependencies\CustomApplyPreserveStep.cs" />
<Compile Remove="Logging\Dependencies\LogStep.cs" />
<Compile Remove="Extensibility\Dependencies\*.cs" />
<Compile Include="Extensibility\Dependencies\InterfaceType.cs" />
<Compile Include="Extensibility\Dependencies\InterfaceImplementation.cs" />
<Compile Remove="DynamicDependencies\Dependencies\FacadeAssembly.cs" />
<Compile Remove="Warnings\Dependencies\CustomStep.cs" />
</ItemGroup>

View file

@ -370,6 +370,12 @@ namespace Mono.Linker.Tests.TestCasesRunner
VerifyKeptMemberInAssembly (checkAttrInAssembly, linkedType);
break;
case nameof (CreatedMemberInAssemblyAttribute):
if (linkedType == null)
Assert.Fail ($"Type `{expectedTypeName}` should have been kept in assembly {assemblyName}");
VerifyCreatedMemberInAssembly (checkAttrInAssembly, linkedType);
break;
case nameof (RemovedForwarderAttribute):
if (linkedAssembly.MainModule.ExportedTypes.Any (l => l.Name == expectedTypeName))
Assert.Fail ($"Forwarder `{expectedTypeName}' should have been removed from assembly {assemblyName}");
@ -642,6 +648,26 @@ namespace Mono.Linker.Tests.TestCasesRunner
}
}
void VerifyCreatedMemberInAssembly (CustomAttribute inAssemblyAttribute, TypeDefinition linkedType)
{
var memberNames = (CustomAttributeArgument[]) inAssemblyAttribute.ConstructorArguments[2].Value;
Assert.IsTrue (memberNames.Length > 0, "Invalid CreatedMemberInAssemblyAttribute. Expected member names.");
foreach (var memberNameAttr in memberNames) {
string memberName = (string) memberNameAttr.Value;
if (TryVerifyCreatedMemberInAssemblyAsField (memberName, linkedType))
continue;
if (TryVerifyCreatedMemberInAssemblyAsProperty (memberName, linkedType))
continue;
if (TryVerifyCreatedMemberInAssemblyAsMethod (memberName, linkedType))
continue;
Assert.Fail ($"Member `{memberName}` on Type `{linkedType}` should have been created");
}
}
void VerifyRemovedOverrideOnMethodInAssembly (CustomAttribute attr, TypeDefinition type)
{
var methodname = (string) attr.ConstructorArguments[2].Value;
@ -710,6 +736,24 @@ namespace Mono.Linker.Tests.TestCasesRunner
return false;
}
protected virtual bool TryVerifyCreatedMemberInAssemblyAsField (string memberName, TypeDefinition linkedType)
{
var linkedField = linkedType.Fields.FirstOrDefault (m => m.Name == memberName);
return linkedField != null;
}
protected virtual bool TryVerifyCreatedMemberInAssemblyAsProperty (string memberName, TypeDefinition linkedType)
{
var linkedProperty = linkedType.Properties.FirstOrDefault (m => m.Name == memberName);
return linkedProperty != null;
}
protected virtual bool TryVerifyCreatedMemberInAssemblyAsMethod (string memberName, TypeDefinition linkedType)
{
var linkedMethod = linkedType.Methods.FirstOrDefault (m => m.GetSignature () == memberName);
return linkedMethod != null;
}
void VerifyKeptReferencesInAssembly (CustomAttribute inAssemblyAttribute)
{
var assembly = ResolveLinkedAssembly (inAssemblyAttribute.ConstructorArguments[0].Value.ToString ());