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:
parent
7a9f84e401
commit
c5f2d4f122
19 changed files with 257 additions and 353 deletions
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 ();
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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 ());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue