From 1e87461ba8c716c2a90db523f8ece83b9fe7aca4 Mon Sep 17 00:00:00 2001 From: Marek Safar Date: Tue, 20 Apr 2021 11:30:51 +0200 Subject: [PATCH 1/4] 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 #918 Fixes #1953 --- .../DynamicallyAccessedMembersBinder.cs | 34 ++-- src/linker/Linker.Dataflow/FlowAnnotations.cs | 21 +- .../Linker.Dataflow/MethodBodyScanner.cs | 28 ++- .../ReflectionMethodBodyScanner.cs | 111 +++++----- src/linker/Linker.Dataflow/ValueNode.cs | 16 +- src/linker/Linker.Steps/CodeRewriterStep.cs | 16 +- .../Linker.Steps/LinkAttributesParser.cs | 4 +- src/linker/Linker.Steps/MarkStep.cs | 190 +++++++----------- src/linker/Linker.Steps/SealerStep.cs | 4 +- src/linker/Linker.Steps/SweepStep.cs | 11 +- .../UnreachableBlocksOptimizer.cs | 27 ++- src/linker/Linker/Annotations.cs | 2 +- src/linker/Linker/BCL.cs | 12 +- src/linker/Linker/LinkContext.cs | 163 +++++++++++++++ src/linker/Linker/MarkingHelpers.cs | 2 +- src/linker/Linker/MethodBodyScanner.cs | 49 ++++- src/linker/Linker/TypeDefinitionExtensions.cs | 16 -- src/linker/Linker/TypeHierarchyCache.cs | 19 +- src/linker/Linker/TypeMapInfo.cs | 70 +++++-- src/linker/Linker/TypeReferenceExtensions.cs | 53 +---- src/linker/ref/Linker/LinkContext.cs | 8 + 21 files changed, 506 insertions(+), 350 deletions(-) diff --git a/src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs b/src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs index a2800947d334..f71e7c9d2972 100644 --- a/src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs +++ b/src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs @@ -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 GetDynamicallyAccessedMembers (this TypeDefinition typeDefinition, DynamicallyAccessedMemberTypes memberTypes) + public static IEnumerable GetDynamicallyAccessedMembers (this TypeDefinition typeDefinition, LinkContext context, DynamicallyAccessedMemberTypes memberTypes) { if (memberTypes == DynamicallyAccessedMemberTypes.All) { yield return null; @@ -38,22 +38,22 @@ public static IEnumerable 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; } @@ -68,22 +68,22 @@ public static IEnumerable 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; } } @@ -113,7 +113,7 @@ public static IEnumerable GetConstructorsOnType (this TypeDefi } } - public static IEnumerable GetMethodsOnTypeHierarchy (this TypeDefinition type, Func filter, BindingFlags? bindingFlags = null) + public static IEnumerable GetMethodsOnTypeHierarchy (this TypeDefinition type, LinkContext context, Func filter, BindingFlags? bindingFlags = null) { bool onBaseType = false; while (type != null) { @@ -148,12 +148,12 @@ public static IEnumerable GetMethodsOnTypeHierarchy (this Type yield return method; } - type = type.BaseType?.Resolve (); + type = context.TryResolveTypeDefinition (type.BaseType); onBaseType = true; } } - public static IEnumerable GetFieldsOnTypeHierarchy (this TypeDefinition type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + public static IEnumerable GetFieldsOnTypeHierarchy (this TypeDefinition type, LinkContext context, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) { bool onBaseType = false; while (type != null) { @@ -184,7 +184,7 @@ public static IEnumerable GetFieldsOnTypeHierarchy (this TypeDe yield return field; } - type = type.BaseType?.Resolve (); + type = context.TryResolveTypeDefinition (type.BaseType); onBaseType = true; } } @@ -209,7 +209,7 @@ public static IEnumerable GetNestedTypesOnType (this TypeDefinit } } - public static IEnumerable GetPropertiesOnTypeHierarchy (this TypeDefinition type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + public static IEnumerable GetPropertiesOnTypeHierarchy (this TypeDefinition type, LinkContext context, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) { bool onBaseType = false; while (type != null) { @@ -249,12 +249,12 @@ public static IEnumerable GetPropertiesOnTypeHierarchy (this yield return property; } - type = type.BaseType?.Resolve (); + type = context.TryResolveTypeDefinition (type.BaseType); onBaseType = true; } } - public static IEnumerable GetEventsOnTypeHierarchy (this TypeDefinition type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + public static IEnumerable GetEventsOnTypeHierarchy (this TypeDefinition type, LinkContext context, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) { bool onBaseType = false; while (type != null) { @@ -294,7 +294,7 @@ public static IEnumerable GetEventsOnTypeHierarchy (this TypeDe yield return @event; } - type = type.BaseType?.Resolve (); + type = context.TryResolveTypeDefinition (type.BaseType); onBaseType = true; } } diff --git a/src/linker/Linker.Dataflow/FlowAnnotations.cs b/src/linker/Linker.Dataflow/FlowAnnotations.cs index 45dcd5181068..9fd7f3514149 100644 --- a/src/linker/Linker.Dataflow/FlowAnnotations.cs +++ b/src/linker/Linker.Dataflow/FlowAnnotations.cs @@ -15,11 +15,12 @@ class FlowAnnotations { readonly LinkContext _context; readonly Dictionary _annotations = new Dictionary (); - readonly TypeHierarchyCache _hierarchyInfo = new TypeHierarchyCache (); + readonly TypeHierarchyCache _hierarchyInfo; public FlowAnnotations (LinkContext context) { _context = context; + _hierarchyInfo = new TypeHierarchyCache (context); } public bool RequiresDataFlowAnalysis (MethodDefinition method) @@ -76,7 +77,7 @@ 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; @@ -84,7 +85,7 @@ public DynamicallyAccessedMemberTypes GetGenericParameterAnnotation (GenericPara 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; @@ -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. @@ -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 @@ -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) diff --git a/src/linker/Linker.Dataflow/MethodBodyScanner.cs b/src/linker/Linker.Dataflow/MethodBodyScanner.cs index a13773f3fc8f..6fd11ad35717 100644 --- a/src/linker/Linker.Dataflow/MethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/MethodBodyScanner.cs @@ -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) @@ -706,7 +713,7 @@ private void ScanLdloc ( } } - private static void ScanLdtoken (Instruction operation, Stack currentStack) + void ScanLdtoken (Instruction operation, Stack currentStack) { if (operation.Operand is GenericParameter genericParameter) { StackSlot slot = new StackSlot (new RuntimeTypeHandleForGenericParameterValue (genericParameter)); @@ -715,14 +722,14 @@ private static void ScanLdtoken (Instruction operation, Stack 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); @@ -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); @@ -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); } @@ -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, diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs index 061a985f7bfa..fb67bc7b0292 100644 --- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs @@ -17,41 +17,34 @@ namespace Mono.Linker.Dataflow { class ReflectionMethodBodyScanner : MethodBodyScanner { - readonly LinkContext _context; readonly MarkStep _markStep; public static bool RequiresReflectionMethodBodyScannerForCallSite (LinkContext context, MethodReference calledMethod) { - MethodDefinition methodDefinition = calledMethod.Resolve (); - if (methodDefinition != null) { - return - GetIntrinsicIdForMethod (methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || - context.Annotations.FlowAnnotations.RequiresDataFlowAnalysis (methodDefinition) || - context.Annotations.HasLinkerAttribute (methodDefinition); - } + MethodDefinition methodDefinition = context.TryResolveMethodDefinition (calledMethod); + if (methodDefinition == null) + return false; - return false; + return + GetIntrinsicIdForMethod (methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || + context.Annotations.FlowAnnotations.RequiresDataFlowAnalysis (methodDefinition) || + context.Annotations.HasLinkerAttribute (methodDefinition); } - public static bool RequiresReflectionMethodBodyScannerForMethodBody (FlowAnnotations flowAnnotations, MethodReference method) + public static bool RequiresReflectionMethodBodyScannerForMethodBody (FlowAnnotations flowAnnotations, MethodDefinition methodDefinition) { - MethodDefinition methodDefinition = method.Resolve (); - if (methodDefinition != null) { - return - GetIntrinsicIdForMethod (methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || - flowAnnotations.RequiresDataFlowAnalysis (methodDefinition); - } - - return false; + return + GetIntrinsicIdForMethod (methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || + flowAnnotations.RequiresDataFlowAnalysis (methodDefinition); } - public static bool RequiresReflectionMethodBodyScannerForAccess (FlowAnnotations flowAnnotations, FieldReference field) + public static bool RequiresReflectionMethodBodyScannerForAccess (LinkContext context, FieldReference field) { - FieldDefinition fieldDefinition = field.Resolve (); - if (fieldDefinition != null) - return flowAnnotations.RequiresDataFlowAnalysis (fieldDefinition); + FieldDefinition fieldDefinition = context.TryResolveFieldDefinition (field); + if (fieldDefinition == null) + return false; - return false; + return context.Annotations.FlowAnnotations.RequiresDataFlowAnalysis (fieldDefinition); } bool ShouldEnableReflectionPatternReporting (MethodDefinition method) @@ -60,8 +53,8 @@ bool ShouldEnableReflectionPatternReporting (MethodDefinition method) } public ReflectionMethodBodyScanner (LinkContext context, MarkStep parent) + : base (context) { - _context = context; _markStep = parent; } @@ -118,11 +111,11 @@ public void ApplyDynamicallyAccessedMembersToType (ref ReflectionPatternContext MarkTypeForDynamicallyAccessedMembers (ref reflectionPatternContext, type, annotation); } - static ValueNode GetValueNodeForCustomAttributeArgument (CustomAttributeArgument argument) + ValueNode GetValueNodeForCustomAttributeArgument (CustomAttributeArgument argument) { ValueNode valueNode; if (argument.Type.Name == "Type") { - TypeDefinition referencedType = ((TypeReference) argument.Value).ResolveToMainTypeDefinition (); + TypeDefinition referencedType = ResolveToTypeDefinition ((TypeReference) argument.Value); if (referencedType == null) valueNode = UnknownValue.Instance; else @@ -159,7 +152,7 @@ ValueNode GetTypeValueNodeFromGenericArgument (TypeReference genericArgument) // That said we only use it to perform the dynamically accessed members checks and for that purpose treating it as System.Type is perfectly valid. return new SystemTypeForGenericParameterValue (inputGenericParameter, _context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (inputGenericParameter)); } else { - TypeDefinition genericArgumentTypeDef = genericArgument.ResolveToMainTypeDefinition (); + TypeDefinition genericArgumentTypeDef = ResolveToTypeDefinition (genericArgument); if (genericArgumentTypeDef != null) { return new SystemTypeValue (genericArgumentTypeDef); } else { @@ -185,7 +178,7 @@ protected override void WarnAboutInvalidILInMethod (MethodBody method, int ilOff protected override ValueNode GetMethodParameterValue (MethodDefinition method, int parameterIndex) { DynamicallyAccessedMemberTypes memberTypes = _context.Annotations.FlowAnnotations.GetParameterAnnotation (method, parameterIndex); - return new MethodParameterValue (method, parameterIndex, memberTypes, DiagnosticUtilities.GetMethodParameterFromIndex (method, parameterIndex)); + return new MethodParameterValue (this, method, parameterIndex, memberTypes, DiagnosticUtilities.GetMethodParameterFromIndex (method, parameterIndex)); } protected override ValueNode GetFieldValue (MethodDefinition method, FieldDefinition field) @@ -200,7 +193,7 @@ protected override ValueNode GetFieldValue (MethodDefinition method, FieldDefini default: { DynamicallyAccessedMemberTypes memberTypes = _context.Annotations.FlowAnnotations.GetFieldAnnotation (field); - return new LoadFieldValue (field, memberTypes); + return new LoadFieldValue (this, field, memberTypes); } } } @@ -612,19 +605,19 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c return false; var callingMethodDefinition = callingMethodBody.Method; + var calledMethodDefinition = _context.TryResolveMethodDefinition (calledMethod); + if (calledMethodDefinition == null) + return false; + var reflectionContext = new ReflectionPatternContext ( _context, ShouldEnableReflectionPatternReporting (callingMethodDefinition), callingMethodDefinition, - calledMethod.Resolve (), + calledMethodDefinition, operation); DynamicallyAccessedMemberTypes returnValueDynamicallyAccessedMemberTypes = 0; - var calledMethodDefinition = calledMethod.Resolve (); - if (calledMethodDefinition == null) - return false; - try { bool requiresDataFlowAnalysis = _context.Annotations.FlowAnnotations.RequiresDataFlowAnalysis (calledMethodDefinition); @@ -818,7 +811,7 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c if (value is SystemTypeValue systemTypeValue) { foreach (var stringParam in methodParams[1].UniqueValues ()) { if (stringParam is KnownStringValue stringValue) { - foreach (var method in systemTypeValue.TypeRepresented.GetMethodsOnTypeHierarchy (m => m.Name == stringValue.Contents, bindingFlags)) { + foreach (var method in systemTypeValue.TypeRepresented.GetMethodsOnTypeHierarchy (_context, m => m.Name == stringValue.Contents, bindingFlags)) { ValidateGenericMethodInstantiation (ref reflectionContext, method, methodParams[2], calledMethod); MarkMethod (ref reflectionContext, method); } @@ -955,8 +948,8 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c foreach (var valueNode in methodParams[0].UniqueValues ()) { TypeDefinition staticType = valueNode.StaticType; if (staticType is null) { - // We don�t know anything about the type GetType was called on. Track this as a usual �result of a method call without any annotations� - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (calledMethod.MethodReturnType, DynamicallyAccessedMemberTypes.None)); + // We don�t know anything about the type GetType was called on. Track this as a usual �result of a method call without any annotations� + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (this, calledMethod.MethodReturnType, DynamicallyAccessedMemberTypes.None)); } else if (staticType.IsSealed || staticType.IsTypeOf ("System", "Delegate")) { // We can treat this one the same as if it was a typeof() expression @@ -987,7 +980,7 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c // Return a value which is "unknown type" with annotation. For now we'll use the return value node // for the method, which means we're loosing the information about which staticType this // started with. For now we don't need it, but we can add it later on. - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (calledMethod.MethodReturnType, annotation)); + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (this, calledMethod.MethodReturnType, annotation)); } } } @@ -1015,12 +1008,12 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c foreach (var typeNameValue in methodParams[0].UniqueValues ()) { if (typeNameValue is KnownStringValue knownStringValue) { TypeReference foundTypeRef = _context.TypeNameResolver.ResolveTypeName (knownStringValue.Contents, callingMethodDefinition, out AssemblyDefinition typeAssembly, false); - TypeDefinition foundType = foundTypeRef?.ResolveToMainTypeDefinition (); + TypeDefinition foundType = ResolveToTypeDefinition (foundTypeRef); if (foundType == null) { // Intentionally ignore - it's not wrong for code to call Type.GetType on non-existing name, the code might expect null/exception back. reflectionContext.RecordHandledPattern (); } else { - reflectionContext.RecordRecognizedPattern (foundType, () => _markStep.MarkTypeVisibleToReflection (foundTypeRef, new DependencyInfo (DependencyKind.AccessedViaReflection, callingMethodDefinition), callingMethodDefinition)); + reflectionContext.RecordRecognizedPattern (foundType, () => _markStep.MarkTypeVisibleToReflection (foundTypeRef, foundType, new DependencyInfo (DependencyKind.AccessedViaReflection, callingMethodDefinition), callingMethodDefinition)); methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemTypeValue (foundType)); _context.MarkingHelpers.MarkMatchingExportedType (foundType, typeAssembly, new DependencyInfo (DependencyKind.AccessedViaReflection, foundType)); } @@ -1030,7 +1023,7 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c // Propagate the annotation from the type name to the return value. Annotation on a string value will be fullfilled whenever a value is assigned to the string with annotation. // So while we don't know which type it is, we can guarantee that it will fulfill the annotation. reflectionContext.RecordHandledPattern (); - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (calledMethodDefinition.MethodReturnType, valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes)); + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (this, calledMethodDefinition.MethodReturnType, valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes)); } else { reflectionContext.RecordUnrecognizedPattern (2057, $"Unrecognized value passed to the parameter 'typeName' of method '{calledMethod.GetDisplayName ()}'. It's not possible to guarantee the availability of the target type."); } @@ -1201,7 +1194,7 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c // Note it's OK to blindly overwrite any potential annotation on the return value from the method definition // since DynamicallyAccessedMemberTypes.All is a superset of any other annotation. if (everyParentTypeHasAll && methodReturnValue == null) - methodReturnValue = new MethodReturnValue (calledMethodDefinition.MethodReturnType, DynamicallyAccessedMemberTypes.All); + methodReturnValue = new MethodReturnValue (this, calledMethodDefinition.MethodReturnType, DynamicallyAccessedMemberTypes.All); } break; @@ -1264,19 +1257,19 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicProperties; } - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (calledMethod.MethodReturnType, propagatedMemberTypes)); + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (this, calledMethod.MethodReturnType, propagatedMemberTypes)); } else if (value is SystemTypeValue systemTypeValue) { - TypeDefinition baseTypeDefinition = systemTypeValue.TypeRepresented.BaseType.Resolve (); + TypeDefinition baseTypeDefinition = _context.TryResolveTypeDefinition (systemTypeValue.TypeRepresented.BaseType); if (baseTypeDefinition != null) methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemTypeValue (baseTypeDefinition)); else - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (calledMethod.MethodReturnType, DynamicallyAccessedMemberTypes.None)); + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (this, calledMethod.MethodReturnType, DynamicallyAccessedMemberTypes.None)); } else if (value == NullValue.Instance) { // Ignore nulls - null.BaseType will fail at runtime, but it has no effect on static analysis continue; } else { // Unknown input - propagate a return value without any annotation - we know it's a Type but we know nothing about it - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (calledMethod.MethodReturnType, DynamicallyAccessedMemberTypes.None)); + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (this, calledMethod.MethodReturnType, DynamicallyAccessedMemberTypes.None)); } } } @@ -1683,7 +1676,7 @@ methodParams[argsParam] is ArrayValue arrayValue && // To get good reporting of errors we need to track the origin of the value for all method calls // but except Newobj as those are special. if (calledMethodDefinition.ReturnType.MetadataType != MetadataType.Void) { - methodReturnValue = new MethodReturnValue (calledMethodDefinition.MethodReturnType, returnValueDynamicallyAccessedMemberTypes); + methodReturnValue = new MethodReturnValue (this, calledMethodDefinition.MethodReturnType, returnValueDynamicallyAccessedMemberTypes); return true; } @@ -1699,7 +1692,7 @@ methodParams[argsParam] is ArrayValue arrayValue && // unknown value with the return type of the method. if (methodReturnValue == null) { if (calledMethod.ReturnType.MetadataType != MetadataType.Void) { - methodReturnValue = new MethodReturnValue (calledMethodDefinition.MethodReturnType, returnValueDynamicallyAccessedMemberTypes); + methodReturnValue = new MethodReturnValue (this, calledMethodDefinition.MethodReturnType, returnValueDynamicallyAccessedMemberTypes); } } @@ -1784,7 +1777,7 @@ void ProcessCreateInstanceByName (ref ReflectionPatternContext reflectionContext } var typeRef = _context.TypeNameResolver.ResolveTypeName (resolvedAssembly, typeNameStringValue.Contents); - var resolvedType = typeRef?.Resolve (); + var resolvedType = _context.TryResolveTypeDefinition (typeRef); if (resolvedType == null || typeRef is ArrayType) { // It's not wrong to have a reference to non-existing type - the code may well expect to get an exception in this case // Note that we did find the assembly, so it's not a linker config problem, it's either intentional, or wrong versions of assemblies @@ -1813,7 +1806,7 @@ void ProcessGetMethodByName ( ref ValueNode methodReturnValue) { bool foundAny = false; - foreach (var method in typeDefinition.GetMethodsOnTypeHierarchy (m => m.Name == methodName, bindingFlags)) { + foreach (var method in typeDefinition.GetMethodsOnTypeHierarchy (_context, m => m.Name == methodName, bindingFlags)) { MarkMethod (ref reflectionContext, method); methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemReflectionMethodBaseValue (method)); foundAny = true; @@ -2049,7 +2042,7 @@ void RequireDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionC MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, systemTypeValue.TypeRepresented, requiredMemberTypes); } else if (uniqueValue is KnownStringValue knownStringValue) { TypeReference typeRef = _context.TypeNameResolver.ResolveTypeName (knownStringValue.Contents, reflectionContext.Source, out AssemblyDefinition typeAssembly); - TypeDefinition foundType = typeRef?.ResolveToMainTypeDefinition (); + TypeDefinition foundType = ResolveToTypeDefinition (typeRef); if (foundType == null) { // Intentionally ignore - it's not wrong for code to call Type.GetType on non-existing name, the code might expect null/exception back. reflectionContext.RecordHandledPattern (); @@ -2083,7 +2076,7 @@ void RequireDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionC $"Value passed to implicit 'this' parameter of method '{methodDefinition.GetDisplayName ()}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); break; case GenericParameter genericParameter: - // Unknown value to generic parameter - this is possible if the generic argumnet fails to resolve + // Unknown value to generic parameter - this is possible if the generic argument fails to resolve reflectionContext.RecordUnrecognizedPattern ( 2066, $"Type passed to generic parameter '{genericParameter.Name}' of '{DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (genericParameter)}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); @@ -2104,7 +2097,7 @@ void RequireDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionC void MarkTypeForDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionContext, TypeDefinition typeDefinition, DynamicallyAccessedMemberTypes requiredMemberTypes) { - foreach (var member in typeDefinition.GetDynamicallyAccessedMembers (requiredMemberTypes)) { + foreach (var member in typeDefinition.GetDynamicallyAccessedMembers (_context, requiredMemberTypes)) { switch (member) { case MethodDefinition method: MarkMethod (ref reflectionContext, method, DependencyKind.DynamicallyAccessedMember); @@ -2132,7 +2125,8 @@ void MarkTypeForDynamicallyAccessedMembers (ref ReflectionPatternContext reflect void MarkType (ref ReflectionPatternContext reflectionContext, TypeReference typeReference) { var source = reflectionContext.Source; - reflectionContext.RecordRecognizedPattern (typeReference?.Resolve (), () => _markStep.MarkTypeVisibleToReflection (typeReference, new DependencyInfo (DependencyKind.AccessedViaReflection, source), source)); + TypeDefinition type = _context.TryResolveTypeDefinition (typeReference); + reflectionContext.RecordRecognizedPattern (type, () => _markStep.MarkTypeVisibleToReflection (typeReference, type, new DependencyInfo (DependencyKind.AccessedViaReflection, source), source)); } void MarkMethod (ref ReflectionPatternContext reflectionContext, MethodDefinition method, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) @@ -2145,7 +2139,8 @@ void MarkMethod (ref ReflectionPatternContext reflectionContext, MethodDefinitio void MarkNestedType (ref ReflectionPatternContext reflectionContext, TypeDefinition nestedType, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) { var source = reflectionContext.Source; - reflectionContext.RecordRecognizedPattern (nestedType, () => _markStep.MarkTypeVisibleToReflection (nestedType, new DependencyInfo (dependencyKind, source), source)); + TypeDefinition type = _context.TryResolveTypeDefinition (nestedType); + reflectionContext.RecordRecognizedPattern (nestedType, () => _markStep.MarkTypeVisibleToReflection (nestedType, type, new DependencyInfo (dependencyKind, source), source)); } void MarkField (ref ReflectionPatternContext reflectionContext, FieldDefinition field, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) @@ -2188,7 +2183,7 @@ void MarkConstructorsOnType (ref ReflectionPatternContext reflectionContext, Typ void MarkFieldsOnTypeHierarchy (ref ReflectionPatternContext reflectionContext, TypeDefinition type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) { - foreach (var field in type.GetFieldsOnTypeHierarchy (filter, bindingFlags)) + foreach (var field in type.GetFieldsOnTypeHierarchy (_context, filter, bindingFlags)) MarkField (ref reflectionContext, field); } @@ -2206,13 +2201,13 @@ TypeDefinition[] MarkNestedTypesOnType (ref ReflectionPatternContext reflectionC void MarkPropertiesOnTypeHierarchy (ref ReflectionPatternContext reflectionContext, TypeDefinition type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) { - foreach (var property in type.GetPropertiesOnTypeHierarchy (filter, bindingFlags)) + foreach (var property in type.GetPropertiesOnTypeHierarchy (_context, filter, bindingFlags)) MarkProperty (ref reflectionContext, property); } void MarkEventsOnTypeHierarchy (ref ReflectionPatternContext reflectionContext, TypeDefinition type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) { - foreach (var @event in type.GetEventsOnTypeHierarchy (filter, bindingFlags)) + foreach (var @event in type.GetEventsOnTypeHierarchy (_context, filter, bindingFlags)) MarkEvent (ref reflectionContext, @event); } diff --git a/src/linker/Linker.Dataflow/ValueNode.cs b/src/linker/Linker.Dataflow/ValueNode.cs index f1b5b3b6d913..7962f01517d9 100644 --- a/src/linker/Linker.Dataflow/ValueNode.cs +++ b/src/linker/Linker.Dataflow/ValueNode.cs @@ -833,14 +833,14 @@ public override bool Equals (ValueNode other) /// class MethodParameterValue : LeafValueWithDynamicallyAccessedMemberNode { - public MethodParameterValue (MethodDefinition method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, IMetadataTokenProvider sourceContext) + public MethodParameterValue (MethodBodyScanner scanner, MethodDefinition method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, IMetadataTokenProvider sourceContext) { Kind = ValueNodeKind.MethodParameter; StaticType = method.HasImplicitThis () ? (parameterIndex == 0 ? method.DeclaringType - : method.Parameters[parameterIndex - 1].ParameterType.ResolveToMainTypeDefinition ()) - : method.Parameters[parameterIndex].ParameterType.ResolveToMainTypeDefinition (); + : scanner.ResolveToTypeDefinition (method.Parameters[parameterIndex - 1].ParameterType)) + : scanner.ResolveToTypeDefinition (method.Parameters[parameterIndex].ParameterType); ParameterIndex = parameterIndex; DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; SourceContext = sourceContext; @@ -905,10 +905,10 @@ protected override string NodeToString () /// class MethodReturnValue : LeafValueWithDynamicallyAccessedMemberNode { - public MethodReturnValue (MethodReturnType methodReturnType, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + public MethodReturnValue (MethodBodyScanner scanner, MethodReturnType methodReturnType, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) { Kind = ValueNodeKind.MethodReturn; - StaticType = methodReturnType.ReturnType.ResolveToMainTypeDefinition (); + StaticType = scanner.ResolveToTypeDefinition (methodReturnType.ReturnType); DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; SourceContext = methodReturnType; } @@ -1144,10 +1144,10 @@ protected override string NodeToString () /// class LoadFieldValue : LeafValueWithDynamicallyAccessedMemberNode { - public LoadFieldValue (FieldDefinition fieldToLoad, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + public LoadFieldValue (MethodBodyScanner scanner, FieldDefinition fieldToLoad, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) { Kind = ValueNodeKind.LoadField; - StaticType = fieldToLoad.FieldType.ResolveToMainTypeDefinition (); + StaticType = scanner.ResolveToTypeDefinition (fieldToLoad.FieldType); Field = fieldToLoad; DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; SourceContext = fieldToLoad; @@ -1227,7 +1227,7 @@ public ArrayValue (ValueNode size, TypeReference elementType) StaticType = null; Size = size ?? UnknownValue.Instance; - ElementType = elementType.ResolveToMainTypeDefinition (); + ElementType = elementType; IndexValues = new Dictionary (); } diff --git a/src/linker/Linker.Steps/CodeRewriterStep.cs b/src/linker/Linker.Steps/CodeRewriterStep.cs index d62f6fdde9d8..aedf7cd979b8 100644 --- a/src/linker/Linker.Steps/CodeRewriterStep.cs +++ b/src/linker/Linker.Steps/CodeRewriterStep.cs @@ -78,7 +78,7 @@ void AddFieldsInitializations (TypeDefinition type) Context.Annotations.TryGetFieldUserValue (field, out object value); - var valueInstr = CreateConstantResultInstruction (field.FieldType, value); + var valueInstr = CreateConstantResultInstruction (Context, field.FieldType, value); if (valueInstr == null) throw new NotImplementedException (field.FieldType.ToString ()); @@ -153,7 +153,11 @@ MethodBody CreateStubBody (MethodDefinition method) var il = body.GetILProcessor (); if (method.IsInstanceConstructor () && !method.DeclaringType.IsValueType) { - var base_ctor = method.DeclaringType.BaseType.GetDefaultInstanceConstructor (); + var baseType = Context.ResolveTypeDefinition (method.DeclaringType.BaseType); + if (baseType is null) + return body; + + MethodReference base_ctor = baseType.GetDefaultInstanceConstructor (); if (base_ctor == null) throw new NotSupportedException ($"Cannot replace constructor for '{method.DeclaringType}' when no base default constructor exists"); @@ -207,21 +211,21 @@ static void StubComplexBody (MethodDefinition method, MethodBody body, ILProcess public static Instruction CreateConstantResultInstruction (LinkContext context, MethodDefinition method) { context.Annotations.TryGetMethodStubValue (method, out object value); - return CreateConstantResultInstruction (method.ReturnType, value); + return CreateConstantResultInstruction (context, method.ReturnType, value); } - public static Instruction CreateConstantResultInstruction (TypeReference rtype, object value = null) + public static Instruction CreateConstantResultInstruction (LinkContext context, TypeReference rtype, object value = null) { switch (rtype.MetadataType) { case MetadataType.ValueType: - var definition = rtype.Resolve (); + var definition = context.TryResolveTypeDefinition (rtype); if (definition?.IsEnum == true) { rtype = definition.GetEnumUnderlyingType (); } break; case MetadataType.GenericInstance: - rtype = rtype.Resolve (); + rtype = context.TryResolveTypeDefinition (rtype); break; } diff --git a/src/linker/Linker.Steps/LinkAttributesParser.cs b/src/linker/Linker.Steps/LinkAttributesParser.cs index dde944c2064c..486b694b7c19 100644 --- a/src/linker/Linker.Steps/LinkAttributesParser.cs +++ b/src/linker/Linker.Steps/LinkAttributesParser.cs @@ -258,7 +258,7 @@ CustomAttributeArgument[] ReadCustomAttributeArguments (XPathNodeIterator iterat return new CustomAttributeArgument (typeref, ConvertStringValue (svalue, typeref)); case MetadataType.ValueType: - var enumType = typeref.Resolve (); + var enumType = _context.ResolveTypeDefinition (typeref); if (enumType?.IsEnum != true) goto default; @@ -376,7 +376,7 @@ bool GetAttributeType (XPathNodeIterator iterator, string attributeFullName, out return false; } - attributeType = _context.TypeNameResolver.ResolveTypeName (assembly, attributeFullName)?.Resolve (); + attributeType = _context.TryResolveTypeDefinition (assembly, attributeFullName); } if (attributeType == null) { diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs index 0e6cd8981c4d..bb7c48bb4074 100644 --- a/src/linker/Linker.Steps/MarkStep.cs +++ b/src/linker/Linker.Steps/MarkStep.cs @@ -279,8 +279,7 @@ static bool TypeIsDynamicInterfaceCastableImplementation (TypeDefinition type) return false; foreach (var ca in type.CustomAttributes) { - TypeDefinition caType = ca.AttributeType.Resolve (); - if (caType?.Name == "DynamicInterfaceCastableImplementationAttribute" && caType.Namespace == "System.Runtime.InteropServices") + if (ca.AttributeType.IsTypeOf ("System.Runtime.InteropServices", "DynamicInterfaceCastableImplementationAttribute")) return true; } return false; @@ -324,7 +323,7 @@ private void MarkEntireTypeInternal (TypeDefinition type, bool includeBaseTypes, } Annotations.Mark (type, reason); - var baseTypeDefinition = type.BaseType?.Resolve (); + var baseTypeDefinition = _context.ResolveTypeDefinition (type.BaseType); if (includeBaseTypes && baseTypeDefinition != null) { MarkEntireTypeInternal (baseTypeDefinition, includeBaseTypes: true, includeInterfaceTypes, new DependencyInfo (DependencyKind.BaseType, type), type); } @@ -333,7 +332,7 @@ private void MarkEntireTypeInternal (TypeDefinition type, bool includeBaseTypes, if (type.HasInterfaces) { foreach (InterfaceImplementation iface in type.Interfaces) { - var interfaceTypeDefinition = iface.InterfaceType.Resolve (); + var interfaceTypeDefinition = _context.ResolveTypeDefinition (iface.InterfaceType); if (includeInterfaceTypes && interfaceTypeDefinition != null) MarkEntireTypeInternal (interfaceTypeDefinition, includeBaseTypes, includeInterfaceTypes: true, new DependencyInfo (reason.Kind, type), type); @@ -699,7 +698,7 @@ bool IsInterfaceImplementationMarkedRecursively (TypeDefinition type, TypeDefini { if (type.HasInterfaces) { foreach (var intf in type.Interfaces) { - TypeDefinition resolvedInterface = intf.InterfaceType.Resolve (); + TypeDefinition resolvedInterface = _context.ResolveTypeDefinition (intf.InterfaceType); if (resolvedInterface == null) continue; @@ -718,7 +717,7 @@ bool RequiresInterfaceRecursively (TypeDefinition typeToExamine, TypeDefinition if (typeToExamine.HasInterfaces) { foreach (var iface in typeToExamine.Interfaces) { - var resolved = iface.InterfaceType.Resolve (); + var resolved = _context.TryResolveTypeDefinition (iface.InterfaceType); if (resolved == null) continue; @@ -745,12 +744,10 @@ void MarkMarshalSpec (IMarshalInfoProvider spec, in DependencyInfo reason, IMemb if (spec.MarshalInfo is CustomMarshalInfo marshaler) { MarkType (marshaler.ManagedType, reason, sourceLocationMember); - TypeDefinition type = marshaler.ManagedType.Resolve (); + TypeDefinition type = _context.ResolveTypeDefinition (marshaler.ManagedType); if (type != null) { MarkICustomMarshalerMethods (type, in reason, sourceLocationMember); MarkCustomMarshalerGetInstance (type, in reason, sourceLocationMember); - } else { - HandleUnresolvedType (marshaler.ManagedType); } } } @@ -773,9 +770,8 @@ void MarkCustomAttributes (ICustomAttributeProvider provider, in DependencyInfo if (UnconditionalSuppressMessageAttributeState.TypeRefHasUnconditionalSuppressions (ca.Constructor.DeclaringType)) _context.Suppressions.AddSuppression (ca, provider); - var resolvedAttributeType = ca.AttributeType.Resolve (); + var resolvedAttributeType = _context.ResolveTypeDefinition (ca.AttributeType); if (resolvedAttributeType == null) { - HandleUnresolvedType (ca.AttributeType); continue; } @@ -875,13 +871,13 @@ void MarkDynamicDependency (DynamicDependency dynamicDependency, IMemberDefiniti MarkingHelpers.MarkMatchingExportedType (type, assembly, new DependencyInfo (DependencyKind.DynamicDependency, type)); } else if (dynamicDependency.Type is TypeReference typeReference) { - type = typeReference.Resolve (); + type = _context.TryResolveTypeDefinition (typeReference); if (type == null) { _context.LogWarning ($"Unresolved type '{typeReference}' in DynamicDependencyAtribute", 2036, context); return; } } else { - type = context.DeclaringType.Resolve (); + type = _context.TryResolveTypeDefinition (context.DeclaringType); if (type == null) { _context.LogWarning ($"Unresolved type '{context.DeclaringType}' in DynamicDependencyAttribute", 2036, context); return; @@ -897,7 +893,7 @@ void MarkDynamicDependency (DynamicDependency dynamicDependency, IMemberDefiniti } } else { var memberTypes = dynamicDependency.MemberTypes; - members = DynamicallyAccessedMembersBinder.GetDynamicallyAccessedMembers (type, memberTypes); + members = type.GetDynamicallyAccessedMembers (_context, memberTypes); if (!members.Any ()) { _context.LogWarning ($"No members were resolved for '{memberTypes}'.", 2037, context); return; @@ -963,12 +959,10 @@ protected virtual void MarkUserDependency (IMemberDefinition context, CustomAttr assembly = null; } - TypeDefinition td = null; + TypeDefinition td; if (args.Count >= 2 && args[1].Value is string typeName) { AssemblyDefinition assemblyDef = assembly ?? (context as MemberReference).Module.Assembly; - TypeReference tr = _context.TypeNameResolver.ResolveTypeName (assemblyDef, typeName); - if (tr != null) - td = tr.Resolve (); + td = _context.TryResolveTypeDefinition (assemblyDef, typeName); if (td == null) { _context.LogWarning ( @@ -978,7 +972,7 @@ protected virtual void MarkUserDependency (IMemberDefinition context, CustomAttr MarkingHelpers.MarkMatchingExportedType (td, assemblyDef, new DependencyInfo (DependencyKind.PreservedDependency, ca)); } else { - td = context.DeclaringType.Resolve (); + td = _context.TryResolveTypeDefinition (context.DeclaringType); } string member = null; @@ -1075,10 +1069,9 @@ protected virtual void MarkCustomAttribute (CustomAttribute ca, in DependencyInf MarkCustomAttributeArguments (source, ca); TypeReference constructor_type = ca.Constructor.DeclaringType; - TypeDefinition type = constructor_type.Resolve (); + TypeDefinition type = _context.ResolveTypeDefinition (constructor_type); if (type == null) { - HandleUnresolvedType (constructor_type); return; } @@ -1105,7 +1098,8 @@ protected virtual bool ShouldMarkCustomAttribute (CustomAttribute ca, ICustomAtt return true; } - if (!Annotations.IsMarked (attr_type.Resolve ())) + TypeDefinition type = _context.ResolveTypeDefinition (attr_type); + if (type is null || !Annotations.IsMarked (type)) return false; } @@ -1178,9 +1172,8 @@ protected virtual void MarkSecurityDeclaration (SecurityDeclaration sd, in Depen protected virtual void MarkSecurityAttribute (SecurityAttribute sa, in DependencyInfo reason, IMemberDefinition sourceLocationMember) { TypeReference security_type = sa.AttributeType; - TypeDefinition type = security_type.Resolve (); + TypeDefinition type = _context.ResolveTypeDefinition (security_type); if (type == null) { - HandleUnresolvedType (security_type); return; } @@ -1214,14 +1207,14 @@ protected void MarkCustomAttributeProperty (CustomAttributeNamedArgument namedAr } } - static PropertyDefinition GetProperty (TypeDefinition type, string propertyname) + PropertyDefinition GetProperty (TypeDefinition type, string propertyname) { while (type != null) { PropertyDefinition property = type.Properties.FirstOrDefault (p => p.Name == propertyname); if (property != null) return property; - type = type.BaseType?.Resolve (); + type = _context.TryResolveTypeDefinition (type.BaseType); } return null; @@ -1250,27 +1243,27 @@ protected void MarkCustomAttributeField (CustomAttributeNamedArgument namedArgum } } - static FieldDefinition GetField (TypeDefinition type, string fieldname) + FieldDefinition GetField (TypeDefinition type, string fieldname) { while (type != null) { FieldDefinition field = type.Fields.FirstOrDefault (f => f.Name == fieldname); if (field != null) return field; - type = type.BaseType?.Resolve (); + type = _context.TryResolveTypeDefinition (type.BaseType); } return null; } - static MethodDefinition GetMethodWithNoParameters (TypeDefinition type, string methodname) + MethodDefinition GetMethodWithNoParameters (TypeDefinition type, string methodname) { while (type != null) { MethodDefinition method = type.Methods.FirstOrDefault (m => m.Name == methodname && !m.HasParameters); if (method != null) return method; - type = type.BaseType?.Resolve (); + type = _context.TryResolveTypeDefinition (type.BaseType); } return null; @@ -1284,7 +1277,7 @@ void MarkCustomAttributeArguments (IMemberDefinition source, CustomAttribute ca) foreach (var argument in ca.ConstructorArguments) MarkCustomAttributeArgument (argument, ca, source); - var resolvedConstructor = ca.Constructor.Resolve (); + var resolvedConstructor = _context.TryResolveMethodDefinition (ca.Constructor); if (resolvedConstructor != null && _context.Annotations.FlowAnnotations.RequiresDataFlowAnalysis (resolvedConstructor)) { var scanner = new ReflectionMethodBodyScanner (_context, this); scanner.ProcessAttributeDataflow (source, resolvedConstructor, ca.ConstructorArguments); @@ -1408,9 +1401,8 @@ bool ProcessLazyAttributes () var assemblyLevelAttribute = _assemblyLevelAttributes.Dequeue (); var customAttribute = assemblyLevelAttribute.Attribute; - var resolved = customAttribute.Constructor.Resolve (); + var resolved = _context.ResolveMethodDefinition (customAttribute.Constructor); if (resolved == null) { - HandleUnresolvedMethod (customAttribute.Constructor); continue; } @@ -1460,9 +1452,8 @@ bool ProcessLateMarkedAttributes () var customAttribute = attributeProviderPair.Attribute; var provider = attributeProviderPair.Provider; - var resolved = customAttribute.Constructor.Resolve (); + var resolved = _context.ResolveMethodDefinition (customAttribute.Constructor); if (resolved == null) { - HandleUnresolvedMethod (customAttribute.Constructor); continue; } @@ -1495,10 +1486,9 @@ protected internal void MarkField (FieldReference reference, DependencyInfo reas reason = new DependencyInfo (DependencyKind.FieldOnGenericInstance, reference); } - FieldDefinition field = reference.Resolve (); + FieldDefinition field = _context.ResolveFieldDefinition (reference); if (field == null) { - HandleUnresolvedField (reference); return; } @@ -1541,7 +1531,7 @@ void MarkField (FieldDefinition field, in DependencyInfo reason) TypeDefinition typeWithFields = field.DeclaringType; while (typeWithFields != null) { MarkImplicitlyUsedFields (typeWithFields); - typeWithFields = typeWithFields.BaseType?.Resolve (); + typeWithFields = _context.TryResolveTypeDefinition (typeWithFields.BaseType); } } @@ -1595,16 +1585,16 @@ protected virtual void MarkSerializable (TypeDefinition type) MarkMethodsIf (type.Methods, HasOnSerializeOrDeserializeAttribute, new DependencyInfo (DependencyKind.SerializationMethodForType, type), type); } - protected internal virtual TypeDefinition MarkTypeVisibleToReflection (TypeReference reference, DependencyInfo reason, IMemberDefinition sourceLocationMember) + protected internal virtual TypeDefinition MarkTypeVisibleToReflection (TypeReference type, TypeDefinition definition, DependencyInfo reason, IMemberDefinition sourceLocationMember) { // 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 // could be incorrect) or IsAssignableFrom (where assignability of unconstructed types might change). - Annotations.MarkRelevantToVariantCasting (reference.Resolve ()); + Annotations.MarkRelevantToVariantCasting (definition); - MarkImplicitlyUsedFields (reference.Resolve ()); + MarkImplicitlyUsedFields (definition); - return MarkType (reference, reason, sourceLocationMember); + return MarkType (type, reason, sourceLocationMember); } /// @@ -1614,8 +1604,7 @@ protected internal virtual TypeDefinition MarkTypeVisibleToReflection (TypeRefer /// The reason why the marking is occuring /// The member which is the "source location" for the marking. /// For example if the type is marked due to an instruction in a method's body, this should be the method which body it is. - /// The resolved type definition if the reference can be resolved and if the call ended up actually marking. - /// If the type definition was already marked, the method returns null. + /// The resolved type definition if the reference can be resolved protected internal virtual TypeDefinition MarkType (TypeReference reference, DependencyInfo reason, IMemberDefinition sourceLocationMember) { #if DEBUG @@ -1633,12 +1622,10 @@ protected internal virtual TypeDefinition MarkType (TypeReference reference, Dep if (reference is GenericParameter) return null; - TypeDefinition type = reference.Resolve (); + TypeDefinition type = _context.ResolveTypeDefinition (reference); - if (type == null) { - HandleUnresolvedType (reference); + if (type == null) return null; - } // Track a mark reason for each call to MarkType. switch (reason.Kind) { @@ -1670,7 +1657,7 @@ protected internal virtual TypeDefinition MarkType (TypeReference reference, Dep } if (CheckProcessed (type)) - return null; + return type; MarkModule (type.Scope as ModuleDefinition, new DependencyInfo (DependencyKind.ScopeOfType, type)); @@ -1790,17 +1777,17 @@ TypeDefinition GetDebuggerAttributeTargetType (CustomAttribute ca, AssemblyDefin { foreach (var property in ca.Properties) { if (property.Name == "Target") - return ((TypeReference) property.Argument.Value)?.Resolve (); + return _context.ResolveTypeDefinition ((TypeReference) property.Argument.Value); if (property.Name == "TargetTypeName") { string targetTypeName = (string) property.Argument.Value; TypeName typeName = TypeParser.ParseTypeName (targetTypeName); if (typeName is AssemblyQualifiedTypeName assemblyQualifiedTypeName) { AssemblyDefinition assembly = _context.TryResolve (assemblyQualifiedTypeName.AssemblyName.Name); - return _context.TypeNameResolver.ResolveTypeName (assembly, targetTypeName)?.Resolve (); + return _context.TryResolveTypeDefinition (assembly, targetTypeName); } - return _context.TypeNameResolver.ResolveTypeName (asm, targetTypeName)?.Resolve (); + return _context.TryResolveTypeDefinition (asm, targetTypeName); } } @@ -1814,9 +1801,8 @@ void MarkTypeSpecialCustomAttributes (TypeDefinition type) foreach (CustomAttribute attribute in type.CustomAttributes) { var attrType = attribute.Constructor.DeclaringType; - var resolvedAttributeType = attrType.Resolve (); + var resolvedAttributeType = _context.ResolveTypeDefinition (attrType); if (resolvedAttributeType == null) { - HandleUnresolvedType (attrType); continue; } @@ -1899,7 +1885,7 @@ protected virtual void MarkTypeConverterLikeDependency (CustomAttribute attribut break; case TypeReference type: - typeDefinition = type.Resolve (); + typeDefinition = _context.ResolveTypeDefinition (type); break; } @@ -1973,7 +1959,7 @@ void MarkTypeWithDebuggerDisplayAttribute (TypeDefinition type, CustomAttribute // This can be improved: mono/linker/issues/1873 MarkMethods (type, new DependencyInfo (DependencyKind.KeptForSpecialAttribute, attribute), type); MarkFields (type, includeStatic: true, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute)); - type = type.BaseType?.Resolve (); + type = _context.TryResolveTypeDefinition (type.BaseType); } return; } @@ -1998,7 +1984,7 @@ void MarkTypeWithDebuggerTypeProxyAttribute (TypeDefinition type, CustomAttribut Tracer.AddDirectDependency (attribute, new DependencyInfo (DependencyKind.CustomAttribute, type), marked: false); MarkType (proxyTypeReference, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), type); - TypeDefinition proxyType = proxyTypeReference.Resolve (); + TypeDefinition proxyType = _context.TryResolveTypeDefinition (proxyTypeReference); if (proxyType != null) { MarkMethods (proxyType, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), type); MarkFields (proxyType, includeStatic: true, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute)); @@ -2083,9 +2069,8 @@ void MarkInterfaceImplementations (TypeDefinition type) foreach (var iface in type.Interfaces) { // Only mark interface implementations of interface types that have been marked. // This enables stripping of interfaces that are never used - var resolvedInterfaceType = iface.InterfaceType.Resolve (); + var resolvedInterfaceType = _context.ResolveTypeDefinition (iface.InterfaceType); if (resolvedInterfaceType == null) { - HandleUnresolvedType (iface.InterfaceType); continue; } @@ -2235,9 +2220,8 @@ void MarkICustomMarshalerMethods (TypeDefinition type, in DependencyInfo reason, // Instead of trying to guess where to find the interface declaration linker walks // the list of implemented interfaces and resolve the declaration from there // - var tdef = iface_type.Resolve (); + var tdef = _context.ResolveTypeDefinition (iface_type); if (tdef == null) { - HandleUnresolvedType (iface_type); return; } @@ -2246,7 +2230,7 @@ void MarkICustomMarshalerMethods (TypeDefinition type, in DependencyInfo reason, MarkInterfaceImplementation (iface, type); return; } - } while ((type = type.BaseType?.Resolve ()) != null); + } while ((type = _context.TryResolveTypeDefinition (type.BaseType)) != null); } static bool IsNonEmptyStaticConstructor (MethodDefinition method) @@ -2369,7 +2353,7 @@ void MarkGenericArguments (IGenericInstance instance, IMemberDefinition sourceLo var argument = arguments[i]; var parameter = parameters[i]; - MarkType (argument, new DependencyInfo (DependencyKind.GenericArgumentType, instance), sourceLocationMember); + TypeDefinition argumentTypeDef = MarkType (argument, new DependencyInfo (DependencyKind.GenericArgumentType, instance), sourceLocationMember); if (_context.Annotations.FlowAnnotations.RequiresDataFlowAnalysis (parameter)) { // The only two implementations of IGenericInstance both derive from MemberReference @@ -2379,21 +2363,23 @@ void MarkGenericArguments (IGenericInstance instance, IMemberDefinition sourceLo scanner.ProcessGenericArgumentDataFlow (parameter, argument, sourceLocationMember ?? (instance as MemberReference).Resolve ()); } - var argument_definition = argument.Resolve (); - Annotations.MarkRelevantToVariantCasting (argument_definition); + if (argumentTypeDef == null) + continue; + + Annotations.MarkRelevantToVariantCasting (argumentTypeDef); if (parameter.HasDefaultConstructorConstraint) - MarkDefaultConstructor (argument_definition, new DependencyInfo (DependencyKind.DefaultCtorForNewConstrainedGenericArgument, instance), sourceLocationMember); + MarkDefaultConstructor (argumentTypeDef, new DependencyInfo (DependencyKind.DefaultCtorForNewConstrainedGenericArgument, instance), sourceLocationMember); } } - static IGenericParameterProvider GetGenericProviderFromInstance (IGenericInstance instance) + IGenericParameterProvider GetGenericProviderFromInstance (IGenericInstance instance) { if (instance is GenericInstanceMethod method) - return method.ElementMethod.Resolve (); + return _context.TryResolveMethodDefinition (method.ElementMethod); if (instance is GenericInstanceType type) - return type.ElementType.Resolve (); + return _context.TryResolveTypeDefinition (type.ElementType); return null; } @@ -2587,7 +2573,7 @@ protected virtual MethodDefinition MarkMethod (MethodReference reference, Depend MarkType (reference.DeclaringType, new DependencyInfo (DependencyKind.DeclaringType, reference), origin?.MemberDefinition); if (reference.Name == ".ctor") { - Annotations.MarkRelevantToVariantCasting (arrayType.Resolve ()); + Annotations.MarkRelevantToVariantCasting (_context.TryResolveTypeDefinition (arrayType)); } return null; } @@ -2600,12 +2586,9 @@ protected virtual MethodDefinition MarkMethod (MethodReference reference, Depend reason = new DependencyInfo (DependencyKind.MethodOnGenericInstance, reference); } - MethodDefinition method = reference.Resolve (); - - if (method == null) { - HandleUnresolvedMethod (reference); + MethodDefinition method = _context.ResolveMethodDefinition (reference); + if (method == null) return null; - } if (Annotations.GetAction (method) == MethodAction.Nothing) Annotations.SetAction (method, MethodAction.Parse); @@ -2840,18 +2823,15 @@ protected virtual IEnumerable GetRequiredMethodsForInstantiate void MarkExplicitInterfaceImplementation (MethodDefinition method, MethodReference ov) { - var resolvedOverride = ov.Resolve (); + MethodDefinition resolvedOverride = _context.ResolveMethodDefinition (ov); - if (resolvedOverride == null) { - HandleUnresolvedMethod (ov); + if (resolvedOverride == null) return; - } if (resolvedOverride.DeclaringType.IsInterface) { foreach (var ifaceImpl in method.DeclaringType.Interfaces) { - var resolvedInterfaceType = ifaceImpl.InterfaceType.Resolve (); + var resolvedInterfaceType = _context.ResolveTypeDefinition (ifaceImpl.InterfaceType); if (resolvedInterfaceType == null) { - HandleUnresolvedType (ifaceImpl.InterfaceType); continue; } @@ -2870,7 +2850,7 @@ void MarkNewCodeDependencies (MethodDefinition method) if (!method.IsInstanceConstructor ()) return; - var baseType = method.DeclaringType.BaseType.Resolve (); + var baseType = _context.ResolveTypeDefinition (method.DeclaringType.BaseType); if (!MarkDefaultConstructor (baseType, new DependencyInfo (DependencyKind.BaseDefaultCtorForStubbedMethod, method), method)) throw new LinkerFatalErrorException (MessageContainer.CreateErrorMessage ($"Cannot stub constructor on '{method.DeclaringType}' when base type does not have default constructor", 1006, origin: new MessageOrigin (method))); @@ -2957,7 +2937,7 @@ void ProcessInteropMethod (MethodDefinition method) } } - TypeDefinition returnTypeDefinition = method.ReturnType.Resolve (); + TypeDefinition returnTypeDefinition = _context.TryResolveTypeDefinition (method.ReturnType); bool didWarnAboutCom = false; @@ -2993,7 +2973,7 @@ void ProcessInteropMethod (MethodDefinition method) if (paramTypeReference is TypeSpecification) { paramTypeReference = (paramTypeReference as TypeSpecification).ElementType; } - TypeDefinition paramTypeDefinition = paramTypeReference?.Resolve (); + TypeDefinition paramTypeDefinition = _context.TryResolveTypeDefinition (paramTypeReference); if (paramTypeDefinition != null) { if (!paramTypeDefinition.IsImport) { // What we keep here is correct most of the time, but not every time. Fine for now. @@ -3017,7 +2997,7 @@ void ProcessInteropMethod (MethodDefinition method) } } - private static bool IsComInterop (IMarshalInfoProvider marshalInfoProvider, TypeReference parameterType) + bool IsComInterop (IMarshalInfoProvider marshalInfoProvider, TypeReference parameterType) { // This is best effort. One can likely find ways how to get COM without triggering these alarms. // AsAny marshalling of a struct with an object-typed field would be one, for example. @@ -3046,7 +3026,7 @@ private static bool IsComInterop (IMarshalInfoProvider marshalInfoProvider, Type return false; } - var parameterTypeDef = parameterType.Resolve (); + var parameterTypeDef = _context.TryResolveTypeDefinition (parameterType); if (parameterTypeDef != null) { if (parameterTypeDef.IsValueType) { // Value types don't marshal as COM @@ -3167,7 +3147,7 @@ void MarkInterfacesNeededByBodyStack (MethodBody body) // If a type could be on the stack in the body and an interface it implements could be on the stack on the body // then we need to mark that interface implementation. When this occurs it is not safe to remove the interface implementation from the type // even if the type is never instantiated - var implementations = MethodBodyScanner.GetReferencedInterfaces (body); + var implementations = new InterfacesOnStackScanner (_context).GetReferencedInterfaces (body); if (implementations == null) return; @@ -3185,7 +3165,7 @@ protected virtual void MarkInstruction (Instruction instruction, MethodDefinitio case Code.Ldflda: // Field address loads (as those can be used to store values to annotated field and thus must be checked) case Code.Ldsflda: requiresReflectionMethodBodyScanner |= - ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForAccess (_context.Annotations.FlowAnnotations, (FieldReference) instruction.Operand); + ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForAccess (_context, (FieldReference) instruction.Operand); break; default: // Other field operations are not interesting as they don't need to be checked @@ -3204,6 +3184,7 @@ protected virtual void MarkInstruction (Instruction instruction, MethodDefinitio Code.Ldftn => DependencyKind.Ldftn, _ => throw new InvalidOperationException ($"unexpected opcode {instruction.OpCode}") }; + requiresReflectionMethodBodyScanner |= ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForCallSite (_context, (MethodReference) instruction.Operand); MarkMethod ((MethodReference) instruction.Operand, new DependencyInfo (dependencyKind, method), new MessageOrigin (method, instruction.Offset)); @@ -3215,7 +3196,9 @@ protected virtual void MarkInstruction (Instruction instruction, MethodDefinitio Debug.Assert (instruction.OpCode.Code == Code.Ldtoken); var reason = new DependencyInfo (DependencyKind.Ldtoken, method); if (token is TypeReference typeReference) { - MarkTypeVisibleToReflection (typeReference, reason, method); + // Error will be reported as part of MarkType + TypeDefinition type = _context.TryResolveTypeDefinition (typeReference); + MarkTypeVisibleToReflection (typeReference, type, reason, method); } else if (token is MethodReference methodReference) { MarkMethod (methodReference, reason, new MessageOrigin (method, instruction.Offset)); } else { @@ -3228,7 +3211,7 @@ protected virtual void MarkInstruction (Instruction instruction, MethodDefinitio var operand = (TypeReference) instruction.Operand; switch (instruction.OpCode.Code) { case Code.Newarr: - Annotations.MarkRelevantToVariantCasting (operand.Resolve ()); + Annotations.MarkRelevantToVariantCasting (_context.TryResolveTypeDefinition (operand)); break; case Code.Isinst: if (operand is TypeSpecification || operand is GenericParameter) @@ -3237,11 +3220,9 @@ protected virtual void MarkInstruction (Instruction instruction, MethodDefinitio if (!_context.CanApplyOptimization (CodeOptimizations.UnusedTypeChecks, method.DeclaringType.Module.Assembly)) break; - TypeDefinition type = operand.Resolve (); - if (type == null) { - HandleUnresolvedType (operand); + TypeDefinition type = _context.ResolveTypeDefinition (operand); + if (type == null) return; - } if (type.IsInterface) break; @@ -3259,27 +3240,6 @@ protected virtual void MarkInstruction (Instruction instruction, MethodDefinitio } } - protected virtual void HandleUnresolvedType (TypeReference reference) - { - if (!_context.IgnoreUnresolved) { - throw new ResolutionException (reference); - } - } - - protected virtual void HandleUnresolvedMethod (MethodReference reference) - { - if (!_context.IgnoreUnresolved) { - throw new ResolutionException (reference); - } - } - - protected virtual void HandleUnresolvedField (FieldReference reference) - { - if (!_context.IgnoreUnresolved) { - throw new ResolutionException (reference); - } - } - protected virtual bool ShouldMarkInterfaceImplementation (TypeDefinition type, InterfaceImplementation iface, TypeDefinition resolvedInterfaceType) { if (Annotations.IsMarked (iface)) diff --git a/src/linker/Linker.Steps/SealerStep.cs b/src/linker/Linker.Steps/SealerStep.cs index 93ff505aab5c..a33d9482052f 100644 --- a/src/linker/Linker.Steps/SealerStep.cs +++ b/src/linker/Linker.Steps/SealerStep.cs @@ -44,7 +44,7 @@ bool IsSubclassed (TypeDefinition type) void PopulateCache (Collection types) { foreach (var t in types) { - var btd = t.BaseType?.Resolve (); + var btd = Context.TryResolveTypeDefinition (t.BaseType); if (btd != null) referencedBaseTypeCache.Add (btd); @@ -56,7 +56,7 @@ void PopulateCache (Collection types) } } - var bt = type.Resolve (); + var bt = Context.TryResolveTypeDefinition (type); return referencedBaseTypeCache.Contains (bt); } diff --git a/src/linker/Linker.Steps/SweepStep.cs b/src/linker/Linker.Steps/SweepStep.cs index d8cb9522581a..e23d6ffff9cd 100644 --- a/src/linker/Linker.Steps/SweepStep.cs +++ b/src/linker/Linker.Steps/SweepStep.cs @@ -380,19 +380,19 @@ protected void SweepCustomAttributes (MethodDefinition method) method.HasSecurity = false; } - static bool ShouldSetHasSecurityToFalse (ISecurityDeclarationProvider providerAsSecurity, ICustomAttributeProvider provider) + bool ShouldSetHasSecurityToFalse (ISecurityDeclarationProvider providerAsSecurity, ICustomAttributeProvider provider) { if (!providerAsSecurity.HasSecurityDeclarations) { // If the method or type had security before and all attributes were removed, or no remaining attributes are security attributes, // then we need to set HasSecurity to false - if (provider.CustomAttributes.Count == 0 || provider.CustomAttributes.All (attr => !IsSecurityAttributeType (attr.AttributeType.Resolve ()))) + if (!provider.HasCustomAttributes || provider.CustomAttributes.All (attr => !IsSecurityAttributeType (Context.TryResolveTypeDefinition (attr.AttributeType)))) return true; } return false; } - static bool IsSecurityAttributeType (TypeDefinition definition) + bool IsSecurityAttributeType (TypeDefinition definition) { if (definition == null) return false; @@ -406,10 +406,11 @@ static bool IsSecurityAttributeType (TypeDefinition definition) }; } - if (definition.BaseType == null) + definition = Context.TryResolveTypeDefinition (definition.BaseType); + if (definition == null) return false; - return IsSecurityAttributeType (definition.BaseType.Resolve ()); + return IsSecurityAttributeType (definition); } protected bool SweepCustomAttributes (ICustomAttributeProvider provider) diff --git a/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs b/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs index 47839d664f70..3b4dcabc754f 100644 --- a/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs +++ b/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs @@ -283,7 +283,7 @@ Instruction AnalyzeMethodForConstantResult (MethodDefinition method, Collection< if (!_context.IsOptimizationEnabled (CodeOptimizations.IPConstantPropagation, method)) return null; - var analyzer = new ConstantExpressionMethodAnalyzer (method, instructions ?? method.Body.Instructions); + var analyzer = new ConstantExpressionMethodAnalyzer (_context, method, instructions ?? method.Body.Instructions); if (analyzer.Analyze ()) { return analyzer.Result; } @@ -355,8 +355,7 @@ bool TryInlineBodyDependencies (ref BodyReducer reducer, bool treatUnprocessedDe case Code.Call: case Code.Callvirt: - var target = (MethodReference) instr.Operand; - var md = target.Resolve (); + var md = _context.TryResolveMethodDefinition ((MethodReference) instr.Operand); if (md == null) break; @@ -415,12 +414,12 @@ bool TryInlineBodyDependencies (ref BodyReducer reducer, bool treatUnprocessedDe case Code.Ldsfld: var ftarget = (FieldReference) instr.Operand; - var field = ftarget.Resolve (); + var field = _context.TryResolveFieldDefinition (ftarget); if (field == null) break; if (_context.Annotations.TryGetFieldUserValue (field, out object value)) { - targetResult = CodeRewriterStep.CreateConstantResultInstruction (field.FieldType, value); + targetResult = CodeRewriterStep.CreateConstantResultInstruction (_context, field.FieldType, value); if (targetResult == null) break; reducer.Rewrite (i, targetResult); @@ -438,11 +437,9 @@ bool TryInlineBodyDependencies (ref BodyReducer reducer, bool treatUnprocessedDe var operand = (TypeReference) instr.Operand; if (operand.MetadataType == MetadataType.UIntPtr) { - sizeOfImpl = (UIntPtrSize ??= FindSizeMethod (operand.Resolve ())); - } - - if (operand.MetadataType == MetadataType.IntPtr) { - sizeOfImpl = (IntPtrSize ??= FindSizeMethod (operand.Resolve ())); + sizeOfImpl = (UIntPtrSize ??= FindSizeMethod (_context.TryResolveTypeDefinition (operand))); + } else if (operand.MetadataType == MetadataType.IntPtr) { + sizeOfImpl = (IntPtrSize ??= FindSizeMethod (_context.TryResolveTypeDefinition (operand))); } if (sizeOfImpl != null) { @@ -1253,12 +1250,14 @@ struct ConstantExpressionMethodAnalyzer { readonly MethodDefinition method; readonly Collection instructions; + readonly LinkContext context; Stack stack_instr; Dictionary locals; - public ConstantExpressionMethodAnalyzer (MethodDefinition method) + public ConstantExpressionMethodAnalyzer (LinkContext context, MethodDefinition method) { + this.context = context; this.method = method; instructions = method.Body.Instructions; stack_instr = null; @@ -1266,8 +1265,8 @@ public ConstantExpressionMethodAnalyzer (MethodDefinition method) Result = null; } - public ConstantExpressionMethodAnalyzer (MethodDefinition method, Collection instructions) - : this (method) + public ConstantExpressionMethodAnalyzer (LinkContext context, MethodDefinition method, Collection instructions) + : this (context, method) { this.instructions = instructions; } @@ -1436,7 +1435,7 @@ Instruction GetLocalsValue (int index, MethodBody body) return null; // local variables don't need to be explicitly initialized - return CodeRewriterStep.CreateConstantResultInstruction (body.Variables[index].VariableType); + return CodeRewriterStep.CreateConstantResultInstruction (context, body.Variables[index].VariableType); } void PushOnStack (Instruction instruction) diff --git a/src/linker/Linker/Annotations.cs b/src/linker/Linker/Annotations.cs index 38dcf4e783ae..ff3b6bc095f5 100644 --- a/src/linker/Linker/Annotations.cs +++ b/src/linker/Linker/Annotations.cs @@ -72,7 +72,7 @@ public AnnotationStore (LinkContext context) this.context = context; FlowAnnotations = new FlowAnnotations (context); VirtualMethodsWithAnnotationsToValidate = new HashSet (); - TypeMapInfo = new TypeMapInfo (); + TypeMapInfo = new TypeMapInfo (context); MemberActions = new MemberActionStore (context); } diff --git a/src/linker/Linker/BCL.cs b/src/linker/Linker/BCL.cs index 42fb246d3296..175af0af17b1 100644 --- a/src/linker/Linker/BCL.cs +++ b/src/linker/Linker/BCL.cs @@ -6,20 +6,16 @@ public static class BCL { public static class EventTracingForWindows { - public static bool IsEventSourceImplementation (TypeDefinition type, LinkContext context = null) + public static bool IsEventSourceImplementation (TypeDefinition type, LinkContext context) { if (!type.IsClass) return false; while (type.BaseType != null) { - var bt = type.BaseType.Resolve (); + var bt = context.ResolveTypeDefinition (type.BaseType); - if (bt == null) { - if (context != null && !context.IgnoreUnresolved) - throw new ResolutionException (type.BaseType); - - break; - } + if (bt == null) + return false; if (IsEventSourceType (bt)) return true; diff --git a/src/linker/Linker/LinkContext.cs b/src/linker/Linker/LinkContext.cs index 3a4bb3e423d0..f7a52d23dcb4 100644 --- a/src/linker/Linker/LinkContext.cs +++ b/src/linker/Linker/LinkContext.cs @@ -663,6 +663,169 @@ public TargetRuntimeVersion GetTargetRuntimeVersion () return _targetRuntime.Value; } + + readonly Dictionary methodresolveCache = new (); + readonly Dictionary fieldresolveCache = new (); + readonly Dictionary typeresolveCache = new (); + + public MethodDefinition ResolveMethodDefinition (MethodReference methodReference) + { + if (methodReference is MethodDefinition md) + return md; + + if (methodReference is null) + return null; + + if (methodresolveCache.TryGetValue (methodReference, out md)) { + if (md == null && !IgnoreUnresolved) + ReportUnresolved (methodReference); + + return md; + } + + md = methodReference.Resolve (); + if (md == null && !IgnoreUnresolved) { + ReportUnresolved (methodReference); + } + + methodresolveCache.Add (methodReference, md); + return md; + } + + public MethodDefinition TryResolveMethodDefinition (MethodReference methodReference) + { + if (methodReference is MethodDefinition md) + return md; + + if (methodReference is null) + return null; + + if (methodresolveCache.TryGetValue (methodReference, out md)) + return md; + + md = methodReference.Resolve (); + methodresolveCache.Add (methodReference, md); + return md; + } + + public FieldDefinition ResolveFieldDefinition (FieldReference fieldReference) + { + if (fieldReference is FieldDefinition fd) + return fd; + + if (fieldReference is null) + return null; + + if (fieldresolveCache.TryGetValue (fieldReference, out fd)) { + if (fd == null && !IgnoreUnresolved) + ReportUnresolved (fieldReference); + + return fd; + } + + fd = fieldReference.Resolve (); + if (fd == null && !IgnoreUnresolved) { + ReportUnresolved (fieldReference); + } + + fieldresolveCache.Add (fieldReference, fd); + return fd; + } + + public FieldDefinition TryResolveFieldDefinition (FieldReference fieldReference) + { + if (fieldReference is FieldDefinition fd) + return fd; + + if (fieldReference is null) + return null; + + if (fieldresolveCache.TryGetValue (fieldReference, out fd)) + return fd; + + fd = fieldReference.Resolve (); + fieldresolveCache.Add (fieldReference, fd); + return fd; + } + + public TypeDefinition ResolveTypeDefinition (TypeReference typeReference) + { + if (typeReference is TypeDefinition td) + return td; + + if (typeReference is null) + return null; + + if (typeresolveCache.TryGetValue (typeReference, out td)) { + if (td == null && !IgnoreUnresolved) + ReportUnresolved (typeReference); + + return td; + } + + // + // Types which never have TypeDefinition or can have ambiguous definition should not be passed in + // + if (typeReference is GenericParameter || (typeReference is TypeSpecification && typeReference is not GenericInstanceType)) + throw new NotSupportedException ($"TypeDefinition cannot be resolved from '{typeReference.GetType ()}' type"); + + td = typeReference.Resolve (); + if (td == null && !IgnoreUnresolved) { + ReportUnresolved (typeReference); + } + + typeresolveCache.Add (typeReference, td); + return td; + } + + public TypeDefinition TryResolveTypeDefinition (TypeReference typeReference) + { + if (typeReference is TypeDefinition td) + return td; + + if (typeReference is null || typeReference is GenericParameter) + return null; + + if (typeresolveCache.TryGetValue (typeReference, out td)) + return td; + + if (typeReference is TypeSpecification ts) { + if (typeReference is FunctionPointerType) { + td = null; + } else { + // + // It returns element-type for arrays and also element type for wrapping types like ByReference, PinnedType, etc + // + td = TryResolveTypeDefinition (ts.GetElementType ()); + } + } else { + td = typeReference.Resolve (); + } + + typeresolveCache.Add (typeReference, td); + return td; + } + + public TypeDefinition TryResolveTypeDefinition (AssemblyDefinition assembly, string typeNameString) + { + // It could be cached if shows up on fast path + return TryResolveTypeDefinition (_typeNameResolver.ResolveTypeName (assembly, typeNameString)); + } + + protected virtual void ReportUnresolved (FieldReference fieldReference) + { + LogError ($"Field '{fieldReference.FullName}' reference could not be resolved", 1040); + } + + protected virtual void ReportUnresolved (MethodReference methodReference) + { + LogError ($"Method '{methodReference.GetDisplayName ()}' reference could not be resolved", 1040); + } + + protected virtual void ReportUnresolved (TypeReference typeReference) + { + LogError ($"Type '{typeReference.GetDisplayName ()}' reference could not be resolved", 1040); + } } public class CodeOptimizationsSettings diff --git a/src/linker/Linker/MarkingHelpers.cs b/src/linker/Linker/MarkingHelpers.cs index ca2a9b36c73c..d57234d3e04c 100644 --- a/src/linker/Linker/MarkingHelpers.cs +++ b/src/linker/Linker/MarkingHelpers.cs @@ -35,7 +35,7 @@ public void MarkForwardedScope (TypeReference typeReference) if (typeReference.Scope is AssemblyNameReference) { var assembly = _context.Resolve (typeReference.Scope); - if (assembly != null && assembly.MainModule.GetMatchingExportedType (typeReference.Resolve (), out var exportedType)) + if (assembly != null && assembly.MainModule.GetMatchingExportedType (_context.TryResolveTypeDefinition (typeReference), out var exportedType)) MarkExportedType (exportedType, assembly.MainModule, new DependencyInfo (DependencyKind.ExportedType, typeReference)); } } diff --git a/src/linker/Linker/MethodBodyScanner.cs b/src/linker/Linker/MethodBodyScanner.cs index f25c9825c1c8..7e08b6333523 100644 --- a/src/linker/Linker/MethodBodyScanner.cs +++ b/src/linker/Linker/MethodBodyScanner.cs @@ -50,8 +50,17 @@ public static bool IsWorthConvertingToThrow (MethodBody body) return true; } + } + readonly struct InterfacesOnStackScanner + { + readonly LinkContext context; + + public InterfacesOnStackScanner (LinkContext context) + { + this.context = context; + } - public static IEnumerable<(InterfaceImplementation, TypeDefinition)> GetReferencedInterfaces (MethodBody body) + public IEnumerable<(InterfaceImplementation, TypeDefinition)> GetReferencedInterfaces (MethodBody body) { var possibleStackTypes = AllPossibleStackTypes (body.Method); if (possibleStackTypes.Count == 0) @@ -75,14 +84,14 @@ public static bool IsWorthConvertingToThrow (MethodBody body) while (currentType?.BaseType != null) // Checking BaseType != null to skip System.Object { AddMatchingInterfaces (interfaceImplementations, currentType, interfaceTypes); - currentType = currentType.BaseType.Resolve (); + currentType = context.TryResolveTypeDefinition (currentType.BaseType); } } return interfaceImplementations; } - static HashSet AllPossibleStackTypes (MethodDefinition method) + HashSet AllPossibleStackTypes (MethodDefinition method) { if (!method.HasBody) throw new ArgumentException ("Method does not have body", nameof (method)); @@ -104,7 +113,7 @@ static HashSet AllPossibleStackTypes (MethodDefinition method) foreach (Instruction instruction in body.Instructions) { if (instruction.Operand is FieldReference fieldReference) { - AddIfResolved (types, fieldReference.Resolve ()?.FieldType); + AddIfResolved (types, context.TryResolveFieldDefinition (fieldReference)?.FieldType); } else if (instruction.Operand is MethodReference methodReference) { if (methodReference is GenericInstanceMethod genericInstanceMethod) AddFromGenericInstance (types, genericInstanceMethod); @@ -112,7 +121,7 @@ static HashSet AllPossibleStackTypes (MethodDefinition method) if (methodReference.DeclaringType is GenericInstanceType genericInstanceType) AddFromGenericInstance (types, genericInstanceType); - var resolvedMethod = methodReference.Resolve (); + var resolvedMethod = context.TryResolveMethodDefinition (methodReference); if (resolvedMethod != null) { if (resolvedMethod.HasParameters) { foreach (var param in resolvedMethod.Parameters) @@ -126,21 +135,38 @@ static HashSet AllPossibleStackTypes (MethodDefinition method) } } + return types; } - static void AddMatchingInterfaces (HashSet<(InterfaceImplementation, TypeDefinition)> results, TypeDefinition type, TypeDefinition[] interfaceTypes) + void AddMatchingInterfaces (HashSet<(InterfaceImplementation, TypeDefinition)> results, TypeDefinition type, TypeDefinition[] interfaceTypes) { if (!type.HasInterfaces) return; foreach (var interfaceType in interfaceTypes) { - if (type.HasInterface (interfaceType, out InterfaceImplementation implementation)) + if (HasInterface (type, interfaceType, out InterfaceImplementation implementation)) results.Add ((implementation, type)); } } - static void AddFromGenericInstance (HashSet set, IGenericInstance instance) + bool HasInterface (TypeDefinition type, TypeDefinition interfaceType, out InterfaceImplementation implementation) + { + implementation = null; + if (!type.HasInterfaces) + return false; + + foreach (var iface in type.Interfaces) { + if (context.TryResolveTypeDefinition (iface.InterfaceType) == interfaceType) { + implementation = iface; + return true; + } + } + + return false; + } + + void AddFromGenericInstance (HashSet set, IGenericInstance instance) { if (!instance.HasGenericArguments) return; @@ -149,7 +175,7 @@ static void AddFromGenericInstance (HashSet set, IGenericInstanc AddIfResolved (set, genericArgument); } - static void AddFromGenericParameterProvider (HashSet set, IGenericParameterProvider provider) + void AddFromGenericParameterProvider (HashSet set, IGenericParameterProvider provider) { if (!provider.HasGenericParameters) return; @@ -160,11 +186,12 @@ static void AddFromGenericParameterProvider (HashSet set, IGener } } - static void AddIfResolved (HashSet set, TypeReference item) + void AddIfResolved (HashSet set, TypeReference item) { - var resolved = item?.Resolve (); + var resolved = context.TryResolveTypeDefinition (item); if (resolved == null) return; + set.Add (resolved); } } diff --git a/src/linker/Linker/TypeDefinitionExtensions.cs b/src/linker/Linker/TypeDefinitionExtensions.cs index d33b37013b1e..6d7d91ac5010 100644 --- a/src/linker/Linker/TypeDefinitionExtensions.cs +++ b/src/linker/Linker/TypeDefinitionExtensions.cs @@ -5,22 +5,6 @@ namespace Mono.Linker { public static class TypeDefinitionExtensions { - public static bool HasInterface (this TypeDefinition type, TypeDefinition interfaceType, out InterfaceImplementation implementation) - { - implementation = null; - if (!type.HasInterfaces) - return false; - - foreach (var iface in type.Interfaces) { - if (iface.InterfaceType.Resolve () == interfaceType) { - implementation = iface; - return true; - } - } - - return false; - } - public static TypeReference GetEnumUnderlyingType (this TypeDefinition enumType) { foreach (var field in enumType.Fields) { diff --git a/src/linker/Linker/TypeHierarchyCache.cs b/src/linker/Linker/TypeHierarchyCache.cs index 8ec9e5c05e19..9098177aba7b 100644 --- a/src/linker/Linker/TypeHierarchyCache.cs +++ b/src/linker/Linker/TypeHierarchyCache.cs @@ -14,13 +14,15 @@ private enum HierarchyFlags } readonly Dictionary _cache = new Dictionary (); + readonly LinkContext context; - private HierarchyFlags GetFlags (TypeReference type) + public TypeHierarchyCache (LinkContext context) { - TypeDefinition resolvedType = type.Resolve (); - if (resolvedType == null) - return 0; + this.context = context; + } + private HierarchyFlags GetFlags (TypeDefinition resolvedType) + { if (_cache.TryGetValue (resolvedType, out var flags)) { return flags; } @@ -43,20 +45,21 @@ private HierarchyFlags GetFlags (TypeReference type) } } - baseType = baseType.BaseType?.Resolve (); + baseType = context.TryResolveTypeDefinition (baseType.BaseType); } - _cache.Add (resolvedType, flags); + if (resolvedType != null) + _cache.Add (resolvedType, flags); return flags; } - public bool IsSystemType (TypeReference type) + public bool IsSystemType (TypeDefinition type) { return (GetFlags (type) & HierarchyFlags.IsSystemType) != 0; } - public bool IsSystemReflectionIReflect (TypeReference type) + public bool IsSystemReflectionIReflect (TypeDefinition type) { return (GetFlags (type) & HierarchyFlags.IsSystemReflectionIReflect) != 0; } diff --git a/src/linker/Linker/TypeMapInfo.cs b/src/linker/Linker/TypeMapInfo.cs index eaca2071ac27..89db8b403f96 100644 --- a/src/linker/Linker/TypeMapInfo.cs +++ b/src/linker/Linker/TypeMapInfo.cs @@ -35,10 +35,16 @@ namespace Mono.Linker public class TypeMapInfo { readonly HashSet assemblies = new HashSet (); + readonly LinkContext context; protected readonly Dictionary> base_methods = new Dictionary> (); protected readonly Dictionary> override_methods = new Dictionary> (); protected readonly Dictionary> default_interface_implementations = new Dictionary> (); + public TypeMapInfo (LinkContext context) + { + this.context = context; + } + void EnsureProcessed (AssemblyDefinition assembly) { if (!assemblies.Add (assembly)) @@ -118,8 +124,8 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) // Foreach interface and for each newslot virtual method on the interface, try // to find the method implementation and record it. foreach (var interfaceImpl in type.GetInflatedInterfaces ()) { - foreach (MethodReference interfaceMethod in interfaceImpl.InflatedInterface.GetMethods ()) { - MethodDefinition resolvedInterfaceMethod = interfaceMethod.Resolve (); + foreach (MethodReference interfaceMethod in interfaceImpl.InflatedInterface.GetMethods (context)) { + MethodDefinition resolvedInterfaceMethod = context.TryResolveMethodDefinition (interfaceMethod); if (resolvedInterfaceMethod == null) continue; @@ -182,7 +188,7 @@ void MapVirtualMethod (MethodDefinition method) void MapOverrides (MethodDefinition method) { foreach (MethodReference override_ref in method.Overrides) { - MethodDefinition @override = override_ref.Resolve (); + MethodDefinition @override = context.TryResolveMethodDefinition (override_ref); if (@override == null) continue; @@ -196,35 +202,64 @@ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, Interf AddOverride (@base, @override, matchingInterfaceImplementation); } - static MethodDefinition GetBaseMethodInTypeHierarchy (MethodDefinition method) + MethodDefinition GetBaseMethodInTypeHierarchy (MethodDefinition method) { return GetBaseMethodInTypeHierarchy (method.DeclaringType, method); } - static MethodDefinition GetBaseMethodInTypeHierarchy (TypeDefinition type, MethodReference method) + MethodDefinition GetBaseMethodInTypeHierarchy (TypeDefinition type, MethodReference method) { - TypeReference @base = type.GetInflatedBaseType (); + TypeReference @base = GetInflatedBaseType (type); while (@base != null) { MethodDefinition base_method = TryMatchMethod (@base, method); if (base_method != null) return base_method; - @base = @base.GetInflatedBaseType (); + @base = GetInflatedBaseType (@base); } return null; } + TypeReference GetInflatedBaseType (TypeReference type) + { + if (type == null) + return null; + + if (type.IsGenericParameter || type.IsByReference || type.IsPointer) + return null; + + if (type is SentinelType sentinelType) + return GetInflatedBaseType (sentinelType.ElementType); + + if (type is PinnedType pinnedType) + return GetInflatedBaseType (pinnedType.ElementType); + + if (type is RequiredModifierType requiredModifierType) + return GetInflatedBaseType (requiredModifierType.ElementType); + + if (type is GenericInstanceType genericInstance) { + var baseType = context.TryResolveTypeDefinition (type)?.BaseType; + + if (baseType is GenericInstanceType) + return TypeReferenceExtensions.InflateGenericType (genericInstance, baseType); + + return baseType; + } + + return context.TryResolveTypeDefinition (type)?.BaseType; + } + // Returns a list of default implementations of the given interface method on this type. // Note that this returns a list to potentially cover the diamond case (more than one // most specific implementation of the given interface methods). Linker needs to preserve // all the implementations so that the proper exception can be thrown at runtime. - static IEnumerable GetDefaultInterfaceImplementations (TypeDefinition type, MethodDefinition interfaceMethod) + IEnumerable GetDefaultInterfaceImplementations (TypeDefinition type, MethodDefinition interfaceMethod) { // Go over all interfaces, trying to find a method that is an explicit MethodImpl of the // interface method in question. foreach (var interfaceImpl in type.Interfaces) { - var potentialImplInterface = interfaceImpl.InterfaceType.Resolve (); + var potentialImplInterface = context.TryResolveTypeDefinition (interfaceImpl.InterfaceType); if (potentialImplInterface == null) continue; @@ -241,7 +276,7 @@ static IEnumerable GetDefaultInterfaceImplementations ( // This method is an override of something. Let's see if it's the method we are looking for. foreach (var @override in potentialImplMethod.Overrides) { - if (@override.Resolve () == interfaceMethod) { + if (context.TryResolveMethodDefinition (@override) == interfaceMethod) { yield return interfaceImpl; foundImpl = true; break; @@ -262,11 +297,15 @@ static IEnumerable GetDefaultInterfaceImplementations ( } } - static MethodDefinition TryMatchMethod (TypeReference type, MethodReference method) + MethodDefinition TryMatchMethod (TypeReference type, MethodReference method) { - foreach (var candidate in type.GetMethods ()) { + foreach (var candidate in type.GetMethods (context)) { + var md = context.TryResolveMethodDefinition (candidate); + if (md?.IsVirtual != true) + continue; + if (MethodMatch (candidate, method)) - return candidate.Resolve (); + return md; } return null; @@ -274,11 +313,6 @@ static MethodDefinition TryMatchMethod (TypeReference type, MethodReference meth static bool MethodMatch (MethodReference candidate, MethodReference method) { - var candidateDef = candidate.Resolve (); - - if (!candidateDef.IsVirtual) - return false; - if (candidate.HasParameters != method.HasParameters) return false; diff --git a/src/linker/Linker/TypeReferenceExtensions.cs b/src/linker/Linker/TypeReferenceExtensions.cs index 4fa20f68625d..283ebe9d895b 100644 --- a/src/linker/Linker/TypeReferenceExtensions.cs +++ b/src/linker/Linker/TypeReferenceExtensions.cs @@ -109,35 +109,6 @@ void parseArrayDimensions (ArrayType at) } } - public static TypeReference GetInflatedBaseType (this TypeReference type) - { - if (type == null) - return null; - - if (type.IsGenericParameter || type.IsByReference || type.IsPointer) - return null; - - if (type is SentinelType sentinelType) - return sentinelType.ElementType.GetInflatedBaseType (); - - if (type is PinnedType pinnedType) - return pinnedType.ElementType.GetInflatedBaseType (); - - if (type is RequiredModifierType requiredModifierType) - return requiredModifierType.ElementType.GetInflatedBaseType (); - - if (type is GenericInstanceType genericInstance) { - var baseType = type.Resolve ()?.BaseType; - - if (baseType is GenericInstanceType) - return InflateGenericType (genericInstance, baseType); - - return baseType; - } - - return type.Resolve ()?.BaseType; - } - public static TypeReference GetInflatedDeclaringType (this TypeReference type) { if (type == null) @@ -287,10 +258,9 @@ private static GenericInstanceType MakeGenericType (GenericInstanceType genericI return result; } - public static IEnumerable GetMethods (this TypeReference type) + public static IEnumerable GetMethods (this TypeReference type, LinkContext context) { - var typeDef = type.Resolve (); - + TypeDefinition typeDef = context.ResolveTypeDefinition (type); if (typeDef?.HasMethods != true) yield break; @@ -325,9 +295,9 @@ public static string ToCecilName (this string fullTypeName) return fullTypeName.Replace ('+', '/'); } - public static bool HasDefaultConstructor (this TypeReference type) + public static bool HasDefaultConstructor (this TypeDefinition type) { - foreach (var m in type.GetMethods ()) { + foreach (var m in type.Methods) { if (m.HasParameters) continue; @@ -339,9 +309,9 @@ public static bool HasDefaultConstructor (this TypeReference type) return false; } - public static MethodReference GetDefaultInstanceConstructor (this TypeReference type) + public static MethodReference GetDefaultInstanceConstructor (this TypeDefinition type) { - foreach (var m in type.GetMethods ()) { + foreach (var m in type.Methods) { if (m.HasParameters) continue; @@ -378,16 +348,5 @@ public static bool IsSubclassOf (this TypeReference type, string ns, string name return false; } - - // 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 static TypeDefinition ResolveToMainTypeDefinition (this TypeReference type) - { - return type switch { - ArrayType _ => type.Module.ImportReference (typeof (Array))?.Resolve (), - _ => type?.Resolve () - }; - } } } diff --git a/src/linker/ref/Linker/LinkContext.cs b/src/linker/ref/Linker/LinkContext.cs index 812091d97f78..0e7288f1734b 100644 --- a/src/linker/ref/Linker/LinkContext.cs +++ b/src/linker/ref/Linker/LinkContext.cs @@ -20,5 +20,13 @@ internal LinkContext () { } public bool HasCustomData (string key) { throw null; } public bool TryGetCustomData (string key, out string value) { throw null; } + + public MethodDefinition ResolveMethodDefinition (MethodReference methodReference) { throw null; } + public FieldDefinition ResolveFieldDefinition (FieldReference fieldReference) { throw null; } + public TypeDefinition ResolveTypeDefinition (TypeReference typeReference) { throw null; } + + public MethodDefinition TryResolveMethodDefinition (MethodReference methodReference) { throw null; } + public FieldDefinition TryResolveFieldDefinition (FieldReference fieldReference) { throw null; } + public TypeDefinition TryResolveTypeDefinition (TypeReference typeReference) { throw null; } } } From 51000c57cb9db60402f7854281e7e8a275e4bdc6 Mon Sep 17 00:00:00 2001 From: Marek Safar Date: Tue, 20 Apr 2021 13:31:32 +0200 Subject: [PATCH 2/4] Update src/linker/Linker/LinkContext.cs Co-authored-by: Vitek Karas --- src/linker/Linker/LinkContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linker/Linker/LinkContext.cs b/src/linker/Linker/LinkContext.cs index f7a52d23dcb4..5525b4c9d3f0 100644 --- a/src/linker/Linker/LinkContext.cs +++ b/src/linker/Linker/LinkContext.cs @@ -808,7 +808,7 @@ public TypeDefinition TryResolveTypeDefinition (TypeReference typeReference) public TypeDefinition TryResolveTypeDefinition (AssemblyDefinition assembly, string typeNameString) { - // It could be cached if shows up on fast path + // It could be cached if it shows up on fast path return TryResolveTypeDefinition (_typeNameResolver.ResolveTypeName (assembly, typeNameString)); } From 4b044db08ce27cba66de37d4a2a88bc70da4a6f1 Mon Sep 17 00:00:00 2001 From: Marek Safar Date: Tue, 20 Apr 2021 13:57:48 +0200 Subject: [PATCH 3/4] PR feedback --- .../Linker.Dataflow/ReflectionMethodBodyScanner.cs | 12 +++++++++--- src/linker/Linker.Dataflow/ValueNode.cs | 12 ++++-------- src/linker/Linker.Steps/MarkStep.cs | 2 +- src/linker/Linker/LinkContext.cs | 11 ++++++++--- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs index fb67bc7b0292..0e3e9de7c24c 100644 --- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs @@ -178,7 +178,13 @@ protected override void WarnAboutInvalidILInMethod (MethodBody method, int ilOff protected override ValueNode GetMethodParameterValue (MethodDefinition method, int parameterIndex) { DynamicallyAccessedMemberTypes memberTypes = _context.Annotations.FlowAnnotations.GetParameterAnnotation (method, parameterIndex); - return new MethodParameterValue (this, method, parameterIndex, memberTypes, DiagnosticUtilities.GetMethodParameterFromIndex (method, parameterIndex)); + + var staticType = + !method.HasImplicitThis () ? ResolveToTypeDefinition (method.Parameters[parameterIndex].ParameterType) : + parameterIndex == 0 ? method.DeclaringType : + ResolveToTypeDefinition (method.Parameters[parameterIndex - 1].ParameterType); + + return new MethodParameterValue (staticType, parameterIndex, memberTypes, DiagnosticUtilities.GetMethodParameterFromIndex (method, parameterIndex)); } protected override ValueNode GetFieldValue (MethodDefinition method, FieldDefinition field) @@ -193,7 +199,7 @@ protected override ValueNode GetFieldValue (MethodDefinition method, FieldDefini default: { DynamicallyAccessedMemberTypes memberTypes = _context.Annotations.FlowAnnotations.GetFieldAnnotation (field); - return new LoadFieldValue (this, field, memberTypes); + return new LoadFieldValue (ResolveToTypeDefinition (field.FieldType), field, memberTypes); } } } @@ -948,7 +954,7 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c foreach (var valueNode in methodParams[0].UniqueValues ()) { TypeDefinition staticType = valueNode.StaticType; if (staticType is null) { - // We don�t know anything about the type GetType was called on. Track this as a usual �result of a method call without any annotations� + // We don't know anything about the type GetType was called on. Track this as a usual result of a method call without any annotations methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (this, calledMethod.MethodReturnType, DynamicallyAccessedMemberTypes.None)); } else if (staticType.IsSealed || staticType.IsTypeOf ("System", "Delegate")) { // We can treat this one the same as if it was a typeof() expression diff --git a/src/linker/Linker.Dataflow/ValueNode.cs b/src/linker/Linker.Dataflow/ValueNode.cs index 7962f01517d9..9ad94fc58458 100644 --- a/src/linker/Linker.Dataflow/ValueNode.cs +++ b/src/linker/Linker.Dataflow/ValueNode.cs @@ -833,14 +833,10 @@ public override bool Equals (ValueNode other) /// class MethodParameterValue : LeafValueWithDynamicallyAccessedMemberNode { - public MethodParameterValue (MethodBodyScanner scanner, MethodDefinition method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, IMetadataTokenProvider sourceContext) + public MethodParameterValue (TypeDefinition staticType, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, IMetadataTokenProvider sourceContext) { Kind = ValueNodeKind.MethodParameter; - StaticType = method.HasImplicitThis () - ? (parameterIndex == 0 - ? method.DeclaringType - : scanner.ResolveToTypeDefinition (method.Parameters[parameterIndex - 1].ParameterType)) - : scanner.ResolveToTypeDefinition (method.Parameters[parameterIndex].ParameterType); + StaticType = staticType; ParameterIndex = parameterIndex; DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; SourceContext = sourceContext; @@ -1144,10 +1140,10 @@ protected override string NodeToString () /// class LoadFieldValue : LeafValueWithDynamicallyAccessedMemberNode { - public LoadFieldValue (MethodBodyScanner scanner, FieldDefinition fieldToLoad, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + public LoadFieldValue (TypeDefinition staticType, FieldDefinition fieldToLoad, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) { Kind = ValueNodeKind.LoadField; - StaticType = scanner.ResolveToTypeDefinition (fieldToLoad.FieldType); + StaticType = staticType; Field = fieldToLoad; DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; SourceContext = fieldToLoad; diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs index bb7c48bb4074..7d549207b666 100644 --- a/src/linker/Linker.Steps/MarkStep.cs +++ b/src/linker/Linker.Steps/MarkStep.cs @@ -1777,7 +1777,7 @@ TypeDefinition GetDebuggerAttributeTargetType (CustomAttribute ca, AssemblyDefin { foreach (var property in ca.Properties) { if (property.Name == "Target") - return _context.ResolveTypeDefinition ((TypeReference) property.Argument.Value); + return _context.TryResolveTypeDefinition ((TypeReference) property.Argument.Value); if (property.Name == "TargetTypeName") { string targetTypeName = (string) property.Argument.Value; diff --git a/src/linker/Linker/LinkContext.cs b/src/linker/Linker/LinkContext.cs index 5525b4c9d3f0..9a51d7392fd8 100644 --- a/src/linker/Linker/LinkContext.cs +++ b/src/linker/Linker/LinkContext.cs @@ -812,19 +812,24 @@ public TypeDefinition TryResolveTypeDefinition (AssemblyDefinition assembly, str return TryResolveTypeDefinition (_typeNameResolver.ResolveTypeName (assembly, typeNameString)); } + readonly HashSet unresolved_reported = new (); + protected virtual void ReportUnresolved (FieldReference fieldReference) { - LogError ($"Field '{fieldReference.FullName}' reference could not be resolved", 1040); + if (unresolved_reported.Add (fieldReference)) + LogError ($"Field '{fieldReference.FullName}' reference could not be resolved", 1040); } protected virtual void ReportUnresolved (MethodReference methodReference) { - LogError ($"Method '{methodReference.GetDisplayName ()}' reference could not be resolved", 1040); + if (unresolved_reported.Add (methodReference)) + LogError ($"Method '{methodReference.GetDisplayName ()}' reference could not be resolved", 1040); } protected virtual void ReportUnresolved (TypeReference typeReference) { - LogError ($"Type '{typeReference.GetDisplayName ()}' reference could not be resolved", 1040); + if (unresolved_reported.Add (typeReference)) + LogError ($"Type '{typeReference.GetDisplayName ()}' reference could not be resolved", 1040); } } From ae674726a3eb42b814ded483943f1f23dfacc158 Mon Sep 17 00:00:00 2001 From: Marek Safar Date: Tue, 20 Apr 2021 14:28:05 +0200 Subject: [PATCH 4/4] Simplify MethodReturnValue node construction --- .../ReflectionMethodBodyScanner.cs | 23 +++++++++++-------- src/linker/Linker.Dataflow/ValueNode.cs | 6 ++--- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs index 0e3e9de7c24c..9d023652f062 100644 --- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs @@ -175,6 +175,11 @@ protected override void WarnAboutInvalidILInMethod (MethodBody method, int ilOff Debug.Fail ("Invalid IL or a bug in the scanner"); } + MethodReturnValue CreateMethodReturnValue (MethodReference method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes = DynamicallyAccessedMemberTypes.None) + { + return new MethodReturnValue (ResolveToTypeDefinition (method.ReturnType), dynamicallyAccessedMemberTypes, method.MethodReturnType); + } + protected override ValueNode GetMethodParameterValue (MethodDefinition method, int parameterIndex) { DynamicallyAccessedMemberTypes memberTypes = _context.Annotations.FlowAnnotations.GetParameterAnnotation (method, parameterIndex); @@ -955,7 +960,7 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c TypeDefinition staticType = valueNode.StaticType; if (staticType is null) { // We don't know anything about the type GetType was called on. Track this as a usual result of a method call without any annotations - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (this, calledMethod.MethodReturnType, DynamicallyAccessedMemberTypes.None)); + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod)); } else if (staticType.IsSealed || staticType.IsTypeOf ("System", "Delegate")) { // We can treat this one the same as if it was a typeof() expression @@ -986,7 +991,7 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c // Return a value which is "unknown type" with annotation. For now we'll use the return value node // for the method, which means we're loosing the information about which staticType this // started with. For now we don't need it, but we can add it later on. - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (this, calledMethod.MethodReturnType, annotation)); + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod, annotation)); } } } @@ -1029,7 +1034,7 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c // Propagate the annotation from the type name to the return value. Annotation on a string value will be fullfilled whenever a value is assigned to the string with annotation. // So while we don't know which type it is, we can guarantee that it will fulfill the annotation. reflectionContext.RecordHandledPattern (); - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (this, calledMethodDefinition.MethodReturnType, valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes)); + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethodDefinition, valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes)); } else { reflectionContext.RecordUnrecognizedPattern (2057, $"Unrecognized value passed to the parameter 'typeName' of method '{calledMethod.GetDisplayName ()}'. It's not possible to guarantee the availability of the target type."); } @@ -1200,7 +1205,7 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c // Note it's OK to blindly overwrite any potential annotation on the return value from the method definition // since DynamicallyAccessedMemberTypes.All is a superset of any other annotation. if (everyParentTypeHasAll && methodReturnValue == null) - methodReturnValue = new MethodReturnValue (this, calledMethodDefinition.MethodReturnType, DynamicallyAccessedMemberTypes.All); + methodReturnValue = CreateMethodReturnValue (calledMethodDefinition, DynamicallyAccessedMemberTypes.All); } break; @@ -1263,19 +1268,19 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicProperties; } - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (this, calledMethod.MethodReturnType, propagatedMemberTypes)); + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod, propagatedMemberTypes)); } else if (value is SystemTypeValue systemTypeValue) { TypeDefinition baseTypeDefinition = _context.TryResolveTypeDefinition (systemTypeValue.TypeRepresented.BaseType); if (baseTypeDefinition != null) methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemTypeValue (baseTypeDefinition)); else - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (this, calledMethod.MethodReturnType, DynamicallyAccessedMemberTypes.None)); + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod)); } else if (value == NullValue.Instance) { // Ignore nulls - null.BaseType will fail at runtime, but it has no effect on static analysis continue; } else { // Unknown input - propagate a return value without any annotation - we know it's a Type but we know nothing about it - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new MethodReturnValue (this, calledMethod.MethodReturnType, DynamicallyAccessedMemberTypes.None)); + methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod)); } } } @@ -1682,7 +1687,7 @@ methodParams[argsParam] is ArrayValue arrayValue && // To get good reporting of errors we need to track the origin of the value for all method calls // but except Newobj as those are special. if (calledMethodDefinition.ReturnType.MetadataType != MetadataType.Void) { - methodReturnValue = new MethodReturnValue (this, calledMethodDefinition.MethodReturnType, returnValueDynamicallyAccessedMemberTypes); + methodReturnValue = CreateMethodReturnValue (calledMethodDefinition, returnValueDynamicallyAccessedMemberTypes); return true; } @@ -1698,7 +1703,7 @@ methodParams[argsParam] is ArrayValue arrayValue && // unknown value with the return type of the method. if (methodReturnValue == null) { if (calledMethod.ReturnType.MetadataType != MetadataType.Void) { - methodReturnValue = new MethodReturnValue (this, calledMethodDefinition.MethodReturnType, returnValueDynamicallyAccessedMemberTypes); + methodReturnValue = CreateMethodReturnValue (calledMethodDefinition, returnValueDynamicallyAccessedMemberTypes); } } diff --git a/src/linker/Linker.Dataflow/ValueNode.cs b/src/linker/Linker.Dataflow/ValueNode.cs index 9ad94fc58458..7545571486a0 100644 --- a/src/linker/Linker.Dataflow/ValueNode.cs +++ b/src/linker/Linker.Dataflow/ValueNode.cs @@ -901,12 +901,12 @@ protected override string NodeToString () /// class MethodReturnValue : LeafValueWithDynamicallyAccessedMemberNode { - public MethodReturnValue (MethodBodyScanner scanner, MethodReturnType methodReturnType, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + public MethodReturnValue (TypeDefinition staticType, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, IMetadataTokenProvider sourceContext) { Kind = ValueNodeKind.MethodReturn; - StaticType = scanner.ResolveToTypeDefinition (methodReturnType.ReturnType); + StaticType = staticType; DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; - SourceContext = methodReturnType; + SourceContext = sourceContext; } public override bool Equals (ValueNode other)