Skip to content

Commit

Permalink
Unified handling of Resolve calls
Browse files Browse the repository at this point in the history
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#918
Fixes dotnet#1953
  • Loading branch information
marek-safar committed Apr 20, 2021
1 parent f6ccab5 commit 1e87461
Show file tree
Hide file tree
Showing 21 changed files with 506 additions and 350 deletions.
34 changes: 17 additions & 17 deletions src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs
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/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/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 1e87461

Please sign in to comment.