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

Precise reflection-visible delegate target analysis #96166

Merged
merged 3 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,46 @@ public static bool HandleCall(
}
break;

//
// System.Delegate
//
// get_Method ()
//
// System.Reflection.RuntimeReflectionExtensions
//
// GetMethodInfo (System.Delegate)
//
case IntrinsicId.RuntimeReflectionExtensions_GetMethodInfo:
case IntrinsicId.Delegate_get_Method:
{
// Find the parameter: first is an instance method, second is an extension method.
MultiValue param = intrinsicId == IntrinsicId.RuntimeReflectionExtensions_GetMethodInfo
? argumentValues[0] : instanceValue;

// If this is Delegate.Method accessed from RuntimeReflectionExtensions.GetMethodInfo, ignore
// because we handle the callsites to that one here as well.
if (Intrinsics.GetIntrinsicIdForMethod(callingMethodDefinition) == IntrinsicId.RuntimeReflectionExtensions_GetMethodInfo)
break;

foreach (var valueNode in param.AsEnumerable())
{
TypeDesc? staticType = (valueNode as IValueWithStaticType)?.StaticType?.Type;
if (staticType is null || !staticType.IsDelegate)
{
// The static type is unknown or something useless like Delegate or MulticastDelegate.
reflectionMarker.Dependencies.Add(reflectionMarker.Factory.ReflectedDelegate(null), "Delegate.Method access on unknown delegate type");
}
else
{
if (staticType.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true))
reflectionMarker.Dependencies.Add(reflectionMarker.Factory.ReflectedDelegate(staticType.GetTypeDefinition()), "Delegate.Method access (on inexact type)");
else
reflectionMarker.Dependencies.Add(reflectionMarker.Factory.ReflectedDelegate(staticType.ConvertToCanonForm(CanonicalFormKind.Specific)), "Delegate.Method access");
}
}
}
break;

//
// System.Object
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,16 @@ public IMethodNode Thunk
get;
}

private DelegateCreationInfo(IMethodNode constructor, MethodDesc targetMethod, TypeDesc constrainedType, TargetKind targetKind, IMethodNode thunk = null)
public TypeDesc DelegateType
{
get;
}

private DelegateCreationInfo(TypeDesc delegateType, IMethodNode constructor, MethodDesc targetMethod, TypeDesc constrainedType, TargetKind targetKind, IMethodNode thunk = null)
{
Debug.Assert(targetKind != TargetKind.VTableLookup
|| MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethod) == targetMethod);
DelegateType = delegateType;
Constructor = constructor;
_targetMethod = targetMethod;
_constrainedType = constrainedType;
Expand Down Expand Up @@ -230,6 +236,7 @@ public static DelegateCreationInfo Create(TypeDesc delegateType, MethodDesc targ
invokeThunk = context.GetMethodForInstantiatedType(invokeThunk, instantiatedDelegateType);

return new DelegateCreationInfo(
delegateType,
factory.MethodEntrypoint(initMethod),
targetMethod,
constrainedType,
Expand Down Expand Up @@ -289,6 +296,7 @@ public static DelegateCreationInfo Create(TypeDesc delegateType, MethodDesc targ

Debug.Assert(constrainedType == null);
return new DelegateCreationInfo(
delegateType,
factory.MethodEntrypoint(systemDelegate.GetKnownMethod(initializeMethodName, null)),
targetMethod,
constrainedType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,9 @@ public sealed override IEnumerable<DependencyListEntry> GetStaticDependencies(No
}
}

GetNonRelocationDependencies(ref dependencies, factory);

return dependencies;
}

public virtual void GetNonRelocationDependencies(ref DependencyList dependencies, NodeFactory factory)
{
}

public abstract void EncodeContents(ref ObjectDataBuilder dataBuilder, NodeFactory factory, bool relocsOnly);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,11 @@ private void CreateNodeCaches()
return new DelegateTargetVirtualMethodNode(method);
});

_reflectedDelegates = new NodeCache<TypeDesc, ReflectedDelegateNode>(type =>
{
return new ReflectedDelegateNode(type);
});

_reflectedMethods = new NodeCache<MethodDesc, ReflectedMethodNode>(method =>
{
return new ReflectedMethodNode(method);
Expand Down Expand Up @@ -996,6 +1001,16 @@ public DelegateTargetVirtualMethodNode DelegateTargetVirtualMethod(MethodDesc me
return _delegateTargetMethods.GetOrAdd(method);
}

private ReflectedDelegateNode _unknownReflectedDelegate = new ReflectedDelegateNode(null);
private NodeCache<TypeDesc, ReflectedDelegateNode> _reflectedDelegates;
public ReflectedDelegateNode ReflectedDelegate(TypeDesc type)
{
if (type == null)
return _unknownReflectedDelegate;

return _reflectedDelegates.GetOrAdd(type);
}

private NodeCache<MethodDesc, ReflectedMethodNode> _reflectedMethods;
public ReflectedMethodNode ReflectedMethod(MethodDesc method)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,6 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact
dependencies.Add(new DependencyListEntry(dependency, "GenericLookupResultDependency"));
}

if (_id == ReadyToRunHelperId.DelegateCtor)
{
MethodDesc targetMethod = ((DelegateCreationInfo)_target).PossiblyUnresolvedTargetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific);
factory.MetadataManager.GetDependenciesDueToDelegateCreation(ref dependencies, factory, targetMethod);
}

return dependencies;
}

Expand Down Expand Up @@ -263,6 +257,13 @@ public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDep
}
}

if (_id == ReadyToRunHelperId.DelegateCtor)
{
var delegateCreationInfo = (DelegateCreationInfo)_target;
MethodDesc targetMethod = delegateCreationInfo.PossiblyUnresolvedTargetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific);
factory.MetadataManager.GetDependenciesDueToDelegateCreation(ref conditionalDependencies, factory, delegateCreationInfo.DelegateType, targetMethod);
}

return conditionalDependencies;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,22 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact
#endif
}

factory.MetadataManager.GetDependenciesDueToDelegateCreation(ref dependencyList, factory, info.PossiblyUnresolvedTargetMethod);

return dependencyList;
}

return null;
}

public override bool HasConditionalStaticDependencies => _id == ReadyToRunHelperId.DelegateCtor;

public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory)
{
List<CombinedDependencyListEntry> dependencyList = new List<CombinedDependencyListEntry>();
var info = (DelegateCreationInfo)_target;
factory.MetadataManager.GetDependenciesDueToDelegateCreation(ref dependencyList, factory, info.DelegateType, info.PossiblyUnresolvedTargetMethod);
return dependencyList;
}

IEnumerable<NativeSequencePoint> INodeWithDebugInfo.GetNativeSequencePoints()
{
if (_id == ReadyToRunHelperId.VirtualCall)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;

using ILCompiler.DependencyAnalysisFramework;

using Internal.TypeSystem;

using Debug = System.Diagnostics.Debug;

namespace ILCompiler.DependencyAnalysis
{
/// <summary>
/// Represents a delegate type that was reflected on using <see cref="System.Delegate.Method"/>.
/// </summary>
public class ReflectedDelegateNode : DependencyNodeCore<NodeFactory>
{
private readonly TypeDesc _delegateType;

public ReflectedDelegateNode(TypeDesc delegateType)
{
Debug.Assert(delegateType is null || delegateType.IsDelegate);

// We accept 3 kinds of types:
// * Null: Delegate.get_Method was used on an unknown type. All delegate targets are reflection-visible.
// * A type definition: An unknown instantiation of the delegate was reflected on. Typically caused by dataflow analysis analyzing uninstantiated code.
// * Canonical form: Some canonical form was reflected on.
Debug.Assert(delegateType is null
|| delegateType.IsGenericDefinition
|| delegateType.ConvertToCanonForm(CanonicalFormKind.Specific) == delegateType);

_delegateType = delegateType;
}

protected override string GetName(NodeFactory factory)
{
return "Reflectable delegate type: " + _delegateType?.ToString() ?? "All delegates";
}

public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory) => null;
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 factory) => null;
public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory factory) => null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

using Internal.Text;
using Internal.TypeSystem;

using CombinedDependencyList = System.Collections.Generic.List<ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.CombinedDependencyListEntry>;

namespace ILCompiler.DependencyAnalysis
{
/// <summary>
Expand Down Expand Up @@ -53,9 +56,13 @@ public override void EncodeContents(ref ObjectDataBuilder dataBuilder, NodeFacto

protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);

public override void GetNonRelocationDependencies(ref DependencyList dependencies, NodeFactory factory)
public override bool HasConditionalStaticDependencies => _data.HasConditionalDependencies;

public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory)
{
_data.GetNonRelocationDependencies(ref dependencies, factory);
CombinedDependencyList result = null;
_data.GetConditionalDependencies(ref result, factory);
return result;
}

public override int ClassCode => 1789429316;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ public virtual void GetDependenciesDueToLdToken(ref DependencyList dependencies,
/// <summary>
/// This method is an extension point that can provide additional metadata-based dependencies to delegate targets.
/// </summary>
public virtual void GetDependenciesDueToDelegateCreation(ref DependencyList dependencies, NodeFactory factory, MethodDesc target)
public virtual void GetDependenciesDueToDelegateCreation(ref CombinedDependencyList dependencies, NodeFactory factory, TypeDesc delegateType, MethodDesc target)
{
// MetadataManagers can override this to provide additional dependencies caused by the construction
// of a delegate to a method.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
using Internal.IL;
using Internal.TypeSystem;

using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.DependencyList;
using CombinedDependencyList = System.Collections.Generic.List<ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.CombinedDependencyListEntry>;

namespace ILCompiler
{
Expand Down Expand Up @@ -2144,7 +2144,8 @@ public interface ISerializableReference : ISerializableValue
{
TypeDesc Type { get; }
void WriteContent(ref ObjectDataBuilder builder, ISymbolNode thisNode, NodeFactory factory);
void GetNonRelocationDependencies(ref DependencyList dependencies, NodeFactory factory);
bool HasConditionalDependencies { get; }
void GetConditionalDependencies(ref CombinedDependencyList dependencies, NodeFactory factory);
bool IsKnownImmutable { get; }
int ArrayLength { get; }
}
Expand Down Expand Up @@ -2694,7 +2695,9 @@ public override bool GetRawData(NodeFactory factory, out object data)
return false;
}

public virtual void GetNonRelocationDependencies(ref DependencyList dependencies, NodeFactory factory)
public virtual bool HasConditionalDependencies => false;

public virtual void GetConditionalDependencies(ref CombinedDependencyList dependencies, NodeFactory factory)
{
}
}
Expand All @@ -2719,12 +2722,16 @@ private DelegateCreationInfo GetDelegateCreationInfo(NodeFactory factory)
factory,
followVirtualDispatch: false);

public override void GetNonRelocationDependencies(ref DependencyList dependencies, NodeFactory factory)
public override bool HasConditionalDependencies => true;

public override void GetConditionalDependencies(ref CombinedDependencyList dependencies, NodeFactory factory)
{
dependencies ??= new CombinedDependencyList();

DelegateCreationInfo creationInfo = GetDelegateCreationInfo(factory);

MethodDesc targetMethod = creationInfo.PossiblyUnresolvedTargetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific);
factory.MetadataManager.GetDependenciesDueToDelegateCreation(ref dependencies, factory, targetMethod);
factory.MetadataManager.GetDependenciesDueToDelegateCreation(ref dependencies, factory, creationInfo.DelegateType, targetMethod);
}

public void WriteContent(ref ObjectDataBuilder builder, ISymbolNode thisNode, NodeFactory factory)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -552,15 +552,55 @@ public override void GetDependenciesDueToLdToken(ref DependencyList dependencies
}
}

public override void GetDependenciesDueToDelegateCreation(ref DependencyList dependencies, NodeFactory factory, MethodDesc target)
public override void GetDependenciesDueToDelegateCreation(ref CombinedDependencyList dependencies, NodeFactory factory, TypeDesc delegateType, MethodDesc target)
{
if (!IsReflectionBlocked(target))
{
dependencies ??= new DependencyList();
dependencies.Add(factory.ReflectedMethod(target.GetCanonMethodTarget(CanonicalFormKind.Specific)), "Target of a delegate");
dependencies ??= new CombinedDependencyList();

ReflectedMethodNode reflectedMethod = factory.ReflectedMethod(target.GetCanonMethodTarget(CanonicalFormKind.Specific));

TypeDesc canonDelegateType = delegateType.ConvertToCanonForm(CanonicalFormKind.Specific);
dependencies.Add(new DependencyNodeCore<NodeFactory>.CombinedDependencyListEntry(
reflectedMethod,
factory.ReflectedDelegate(canonDelegateType),
"Target of delegate could be reflection-visible"));

if (canonDelegateType.HasInstantiation)
{
dependencies.Add(new DependencyNodeCore<NodeFactory>.CombinedDependencyListEntry(
reflectedMethod,
factory.ReflectedDelegate(canonDelegateType.GetTypeDefinition()),
"Target of delegate could be reflection-visible"));
}

dependencies.Add(new DependencyNodeCore<NodeFactory>.CombinedDependencyListEntry(
reflectedMethod,
factory.ReflectedDelegate(null),
"Target of delegate could be reflection-visible"));

if (target.IsVirtual)
dependencies.Add(factory.DelegateTargetVirtualMethod(target.GetCanonMethodTarget(CanonicalFormKind.Specific)), "Target of a delegate");
{
DelegateTargetVirtualMethodNode targetVirtualMethod = factory.DelegateTargetVirtualMethod(target.GetCanonMethodTarget(CanonicalFormKind.Specific));

dependencies.Add(new DependencyNodeCore<NodeFactory>.CombinedDependencyListEntry(
targetVirtualMethod,
factory.ReflectedDelegate(canonDelegateType),
"Target of delegate could be reflection-visible"));

if (canonDelegateType.HasInstantiation)
{
dependencies.Add(new DependencyNodeCore<NodeFactory>.CombinedDependencyListEntry(
targetVirtualMethod,
factory.ReflectedDelegate(canonDelegateType.GetTypeDefinition()),
"Target of delegate could be reflection-visible"));
}

dependencies.Add(new DependencyNodeCore<NodeFactory>.CombinedDependencyListEntry(
targetVirtualMethod,
factory.ReflectedDelegate(null),
"Target of delegate could be reflection-visible"));
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@
<Compile Include="Compiler\DependencyAnalysis\NotReadOnlyFieldNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ObjectGetTypeFlowDependenciesNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\PointerTypeMapNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReflectedDelegateNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReflectedFieldNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReflectedTypeNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReflectionInvokeSupportDependencyAlgorithm.cs" />
Expand Down
Loading
Loading