Skip to content

Commit

Permalink
Unified handling of Resolve calls (dotnet/linker#1979)
Browse files Browse the repository at this point in the history
* Unified handling of Resolve calls

This change introduces a commonplace for resolving definition for types,
methods and fields. This is useful for following reasons

- Speeds up linker by 20% for default Blazor app (more for large apps)
- Any custom step can avoid building local mapping cache
- Custom steps could support `--skip-unresolved` linker option
- Consistent error handling for unresolved references

Most of the changes are just mechanical method replacement and
Link context passing.

Contributes to dotnet/linker#918
Fixes dotnet/linker#1953

* Update src/linker/Linker/LinkContext.cs

Co-authored-by: Vitek Karas <[email protected]>

* PR feedback

* Simplify MethodReturnValue node construction

Co-authored-by: Vitek Karas <[email protected]>

Commit migrated from dotnet/linker@012efef
  • Loading branch information
marek-safar authored Apr 22, 2021
1 parent 2874d20 commit 7e3d89f
Show file tree
Hide file tree
Showing 21 changed files with 522 additions and 354 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal static class DynamicallyAccessedMembersBinder
// Returns the members of the type bound by memberTypes. For MemberTypes.All, this returns a single null result.
// This sentinel value allows callers to handle the case where MemberTypes.All conceptually binds to the entire type
// including all recursive nested members.
public static IEnumerable<IMemberDefinition> GetDynamicallyAccessedMembers (this TypeDefinition typeDefinition, DynamicallyAccessedMemberTypes memberTypes)
public static IEnumerable<IMemberDefinition> GetDynamicallyAccessedMembers (this TypeDefinition typeDefinition, LinkContext context, DynamicallyAccessedMemberTypes memberTypes)
{
if (memberTypes == DynamicallyAccessedMemberTypes.All) {
yield return null;
Expand All @@ -38,22 +38,22 @@ public static IEnumerable<IMemberDefinition> GetDynamicallyAccessedMembers (this
}

if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.NonPublicMethods)) {
foreach (var m in typeDefinition.GetMethodsOnTypeHierarchy (filter: null, bindingFlags: BindingFlags.NonPublic))
foreach (var m in typeDefinition.GetMethodsOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.NonPublic))
yield return m;
}

if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicMethods)) {
foreach (var m in typeDefinition.GetMethodsOnTypeHierarchy (filter: null, bindingFlags: BindingFlags.Public))
foreach (var m in typeDefinition.GetMethodsOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.Public))
yield return m;
}

if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.NonPublicFields)) {
foreach (var f in typeDefinition.GetFieldsOnTypeHierarchy (filter: null, bindingFlags: BindingFlags.NonPublic))
foreach (var f in typeDefinition.GetFieldsOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.NonPublic))
yield return f;
}

if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicFields)) {
foreach (var f in typeDefinition.GetFieldsOnTypeHierarchy (filter: null, bindingFlags: BindingFlags.Public))
foreach (var f in typeDefinition.GetFieldsOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.Public))
yield return f;
}

Expand All @@ -68,22 +68,22 @@ public static IEnumerable<IMemberDefinition> GetDynamicallyAccessedMembers (this
}

if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.NonPublicProperties)) {
foreach (var p in typeDefinition.GetPropertiesOnTypeHierarchy (filter: null, bindingFlags: BindingFlags.NonPublic))
foreach (var p in typeDefinition.GetPropertiesOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.NonPublic))
yield return p;
}

if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicProperties)) {
foreach (var p in typeDefinition.GetPropertiesOnTypeHierarchy (filter: null, bindingFlags: BindingFlags.Public))
foreach (var p in typeDefinition.GetPropertiesOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.Public))
yield return p;
}

if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.NonPublicEvents)) {
foreach (var e in typeDefinition.GetEventsOnTypeHierarchy (filter: null, bindingFlags: BindingFlags.NonPublic))
foreach (var e in typeDefinition.GetEventsOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.NonPublic))
yield return e;
}

if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicEvents)) {
foreach (var e in typeDefinition.GetEventsOnTypeHierarchy (filter: null, bindingFlags: BindingFlags.Public))
foreach (var e in typeDefinition.GetEventsOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.Public))
yield return e;
}
}
Expand Down Expand Up @@ -113,7 +113,7 @@ public static IEnumerable<MethodDefinition> GetConstructorsOnType (this TypeDefi
}
}

public static IEnumerable<MethodDefinition> GetMethodsOnTypeHierarchy (this TypeDefinition type, Func<MethodDefinition, bool> filter, BindingFlags? bindingFlags = null)
public static IEnumerable<MethodDefinition> GetMethodsOnTypeHierarchy (this TypeDefinition type, LinkContext context, Func<MethodDefinition, bool> filter, BindingFlags? bindingFlags = null)
{
bool onBaseType = false;
while (type != null) {
Expand Down Expand Up @@ -148,12 +148,12 @@ public static IEnumerable<MethodDefinition> GetMethodsOnTypeHierarchy (this Type
yield return method;
}

type = type.BaseType?.Resolve ();
type = context.TryResolveTypeDefinition (type.BaseType);
onBaseType = true;
}
}

public static IEnumerable<FieldDefinition> GetFieldsOnTypeHierarchy (this TypeDefinition type, Func<FieldDefinition, bool> filter, BindingFlags? bindingFlags = BindingFlags.Default)
public static IEnumerable<FieldDefinition> GetFieldsOnTypeHierarchy (this TypeDefinition type, LinkContext context, Func<FieldDefinition, bool> filter, BindingFlags? bindingFlags = BindingFlags.Default)
{
bool onBaseType = false;
while (type != null) {
Expand Down Expand Up @@ -184,7 +184,7 @@ public static IEnumerable<FieldDefinition> GetFieldsOnTypeHierarchy (this TypeDe
yield return field;
}

type = type.BaseType?.Resolve ();
type = context.TryResolveTypeDefinition (type.BaseType);
onBaseType = true;
}
}
Expand All @@ -209,7 +209,7 @@ public static IEnumerable<TypeDefinition> GetNestedTypesOnType (this TypeDefinit
}
}

public static IEnumerable<PropertyDefinition> GetPropertiesOnTypeHierarchy (this TypeDefinition type, Func<PropertyDefinition, bool> filter, BindingFlags? bindingFlags = BindingFlags.Default)
public static IEnumerable<PropertyDefinition> GetPropertiesOnTypeHierarchy (this TypeDefinition type, LinkContext context, Func<PropertyDefinition, bool> filter, BindingFlags? bindingFlags = BindingFlags.Default)
{
bool onBaseType = false;
while (type != null) {
Expand Down Expand Up @@ -249,12 +249,12 @@ public static IEnumerable<PropertyDefinition> GetPropertiesOnTypeHierarchy (this
yield return property;
}

type = type.BaseType?.Resolve ();
type = context.TryResolveTypeDefinition (type.BaseType);
onBaseType = true;
}
}

public static IEnumerable<EventDefinition> GetEventsOnTypeHierarchy (this TypeDefinition type, Func<EventDefinition, bool> filter, BindingFlags? bindingFlags = BindingFlags.Default)
public static IEnumerable<EventDefinition> GetEventsOnTypeHierarchy (this TypeDefinition type, LinkContext context, Func<EventDefinition, bool> filter, BindingFlags? bindingFlags = BindingFlags.Default)
{
bool onBaseType = false;
while (type != null) {
Expand Down Expand Up @@ -294,7 +294,7 @@ public static IEnumerable<EventDefinition> GetEventsOnTypeHierarchy (this TypeDe
yield return @event;
}

type = type.BaseType?.Resolve ();
type = context.TryResolveTypeDefinition (type.BaseType);
onBaseType = true;
}
}
Expand Down
21 changes: 13 additions & 8 deletions src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ class FlowAnnotations
{
readonly LinkContext _context;
readonly Dictionary<TypeDefinition, TypeAnnotations> _annotations = new Dictionary<TypeDefinition, TypeAnnotations> ();
readonly TypeHierarchyCache _hierarchyInfo = new TypeHierarchyCache ();
readonly TypeHierarchyCache _hierarchyInfo;

public FlowAnnotations (LinkContext context)
{
_context = context;
_hierarchyInfo = new TypeHierarchyCache (context);
}

public bool RequiresDataFlowAnalysis (MethodDefinition method)
Expand Down Expand Up @@ -76,15 +77,15 @@ public DynamicallyAccessedMemberTypes GetTypeAnnotation (TypeDefinition type)

public DynamicallyAccessedMemberTypes GetGenericParameterAnnotation (GenericParameter genericParameter)
{
TypeDefinition declaringType = genericParameter.DeclaringType?.Resolve ();
TypeDefinition declaringType = _context.ResolveTypeDefinition (genericParameter.DeclaringType);
if (declaringType != null) {
if (GetAnnotations (declaringType).TryGetAnnotation (genericParameter, out var annotation))
return annotation;

return DynamicallyAccessedMemberTypes.None;
}

MethodDefinition declaringMethod = genericParameter.DeclaringMethod?.Resolve ();
MethodDefinition declaringMethod = _context.ResolveMethodDefinition (genericParameter.DeclaringMethod);
if (declaringMethod != null && GetAnnotations (declaringMethod.DeclaringType).TryGetAnnotation (declaringMethod, out var methodTypeAnnotations) &&
methodTypeAnnotations.TryGetAnnotation (genericParameter, out var methodAnnotation))
return methodAnnotation;
Expand Down Expand Up @@ -349,7 +350,7 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type)
return new TypeAnnotations (type, typeAnnotation, annotatedMethods.ToArray (), annotatedFields.ToArray (), typeGenericParameterAnnotations);
}

static bool ScanMethodBodyForFieldAccess (MethodBody body, bool write, out FieldDefinition found)
bool ScanMethodBodyForFieldAccess (MethodBody body, bool write, out FieldDefinition found)
{
// Tries to find the backing field for a property getter/setter.
// Returns true if this is a method body that we can unambiguously analyze.
Expand Down Expand Up @@ -383,7 +384,7 @@ static bool ScanMethodBodyForFieldAccess (MethodBody body, bool write, out Field
return true;
}

found = foundReference.Resolve ();
found = _context.ResolveFieldDefinition (foundReference);

if (found == null) {
// If the field doesn't resolve, it can't be a field on the current type
Expand All @@ -405,9 +406,13 @@ static bool ScanMethodBodyForFieldAccess (MethodBody body, bool write, out Field

bool IsTypeInterestingForDataflow (TypeReference typeReference)
{
return typeReference.MetadataType == MetadataType.String ||
_hierarchyInfo.IsSystemType (typeReference) ||
_hierarchyInfo.IsSystemReflectionIReflect (typeReference);
if (typeReference.MetadataType == MetadataType.String)
return true;

TypeDefinition type = _context.TryResolveTypeDefinition (typeReference);
return type != null && (
_hierarchyInfo.IsSystemType (type) ||
_hierarchyInfo.IsSystemReflectionIReflect (type));
}

internal void ValidateMethodAnnotationsAreSame (MethodDefinition method, MethodDefinition baseMethod)
Expand Down
28 changes: 23 additions & 5 deletions src/tools/illink/src/linker/Linker.Dataflow/MethodBodyScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ public StackSlot (ValueNode value, bool isByRef = false)

abstract partial class MethodBodyScanner
{
protected readonly LinkContext _context;

protected MethodBodyScanner (LinkContext context)
{
this._context = context;
}

internal ValueNode MethodReturnValue { private set; get; }

protected virtual void WarnAboutInvalidILInMethod (MethodBody method, int ilOffset)
Expand Down Expand Up @@ -706,7 +713,7 @@ private void ScanLdloc (
}
}

private static void ScanLdtoken (Instruction operation, Stack<StackSlot> currentStack)
void ScanLdtoken (Instruction operation, Stack<StackSlot> currentStack)
{
if (operation.Operand is GenericParameter genericParameter) {
StackSlot slot = new StackSlot (new RuntimeTypeHandleForGenericParameterValue (genericParameter));
Expand All @@ -715,14 +722,14 @@ private static void ScanLdtoken (Instruction operation, Stack<StackSlot> current
}

if (operation.Operand is TypeReference typeReference) {
var resolvedReference = typeReference.ResolveToMainTypeDefinition ();
var resolvedReference = ResolveToTypeDefinition (typeReference);
if (resolvedReference != null) {
StackSlot slot = new StackSlot (new RuntimeTypeHandleValue (resolvedReference));
currentStack.Push (slot);
return;
}
} else if (operation.Operand is MethodReference methodReference) {
var resolvedMethod = methodReference.Resolve ();
var resolvedMethod = _context.TryResolveMethodDefinition (methodReference);
if (resolvedMethod != null) {
StackSlot slot = new StackSlot (new RuntimeMethodHandleValue (resolvedMethod));
currentStack.Push (slot);
Expand Down Expand Up @@ -782,7 +789,7 @@ private void ScanLdfld (

bool isByRef = code == Code.Ldflda || code == Code.Ldsflda;

FieldDefinition field = (operation.Operand as FieldReference)?.Resolve ();
FieldDefinition field = _context.TryResolveFieldDefinition (operation.Operand as FieldReference);
if (field != null) {
StackSlot slot = new StackSlot (GetFieldValue (thisMethod, field), isByRef);
currentStack.Push (slot);
Expand Down Expand Up @@ -810,7 +817,7 @@ private void ScanStfld (
if (operation.OpCode.Code == Code.Stfld)
PopUnknown (currentStack, 1, methodBody, operation.Offset);

FieldDefinition field = (operation.Operand as FieldReference)?.Resolve ();
FieldDefinition field = _context.TryResolveFieldDefinition (operation.Operand as FieldReference);
if (field != null) {
HandleStoreField (thisMethod, field, operation, valueToStoreSlot.Value);
}
Expand Down Expand Up @@ -901,6 +908,17 @@ private void HandleCall (
}
}

// Array types that are dynamically accessed should resolve to System.Array instead of its element type - which is what Cecil resolves to.
// Any data flow annotations placed on a type parameter which receives an array type apply to the array itself. None of the members in its
// element type should be marked.
public TypeDefinition ResolveToTypeDefinition (TypeReference typeReference)
{
if (typeReference is ArrayType)
return BCL.FindPredefinedType ("System", "Array", _context);

return _context.TryResolveTypeDefinition (typeReference);
}

public abstract bool HandleCall (
MethodBody callingMethodBody,
MethodReference calledMethod,
Expand Down
Loading

0 comments on commit 7e3d89f

Please sign in to comment.