Skip to content

Commit

Permalink
Mark generic arguments of dynamically accessed types passed as string (
Browse files Browse the repository at this point in the history
…dotnet#1566)

* Mark dynamically accessed generic arguments

* Clean test

* Use MarkType for keeping generic args
Enable ComplexTypeHandling tests

* Use MarkTypeVisibleToReflection for generic arguments
Clean tests

* MarkType always

* Check for array types in TypeNameResolver

* Mark System.Array instead of its element type

* Whitespace

* PR feedback

* Pattern match
Whitespace

* Add extension method ResolveToMainTypeDefinition

* Add comment

* Use ResolveToMainTypeDefinition
  • Loading branch information
mateoatr authored and Mateo Torres Ruiz committed Nov 13, 2020
1 parent b6ff771 commit 8449cbd
Show file tree
Hide file tree
Showing 8 changed files with 329 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,15 @@ internal sealed class MultiDimArrayTypeName : HasElementTypeName
public MultiDimArrayTypeName(TypeName elementTypeName, int rank)
: base(elementTypeName)
{
_rank = rank;
Rank = rank;
}

public sealed override string ToString()
{
return ElementTypeName + "[" + (_rank == 1 ? "*" : new string(',', _rank - 1)) + "]";
return ElementTypeName + "[" + (Rank == 1 ? "*" : new string(',', Rank - 1)) + "]";
}

private int _rank;
public int Rank { get; }
}

//
Expand Down
2 changes: 1 addition & 1 deletion src/linker/Linker.Dataflow/MethodBodyScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,7 @@ private void ScanLdtoken (
}

if (operation.Operand is TypeReference typeReference) {
var resolvedReference = typeReference.Resolve ();
var resolvedReference = typeReference.ResolveToMainTypeDefinition ();
if (resolvedReference != null) {
StackSlot slot = new StackSlot (new RuntimeTypeHandleValue (resolvedReference));
currentStack.Push (slot);
Expand Down
96 changes: 53 additions & 43 deletions src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion src/linker/Linker/TypeNameResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,14 @@ TypeReference ResolveTypeName (AssemblyDefinition assembly, TypeName typeName)
if (elementType == null)
return null;

return elementType;
return typeName switch
{
ArrayTypeName _ => new ArrayType (elementType),
MultiDimArrayTypeName multiDimArrayTypeName => new ArrayType (elementType, multiDimArrayTypeName.Rank),
ByRefTypeName _ => new ByReferenceType (elementType),
PointerTypeName _ => new PointerType (elementType),
_ => elementType
};
}

return assembly.MainModule.GetType (typeName.ToString ());
Expand Down
12 changes: 12 additions & 0 deletions src/linker/Linker/TypeReferenceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -375,5 +375,17 @@ 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 ()
};
}
}
}
51 changes: 48 additions & 3 deletions test/Mono.Linker.Tests.Cases/DataFlow/ApplyTypeAnnotations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,7 @@ private static void RequireCombinationOnString (
{
}

// Issue: https://github.com/mono/linker/issues/1537
//[Kept]
//[KeptMember (".ctor()")]
[Kept]
class FromStringConstantWithGenericInner
{
}
Expand All @@ -143,10 +141,57 @@ class FromStringConstantWithGeneric<T>
public T GetValue () { return default (T); }
}

[Kept]
class FromStringConstantWithGenericInnerInner
{
[Kept]
public void Method ()
{
}

int unusedField;
}

[Kept]
class FromStringConstantWithGenericInnerOne<
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)]
[KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))]
T>
{
}

[Kept]
class FromStringConstantWithGenericInnerTwo
{
void UnusedMethod ()
{
}
}

[Kept]
class FromStringConstantWitGenericInnerMultiDimArray
{
}

[Kept]
class FromStringConstantWithMultiDimArray
{
public void UnusedMethod () { }
}

[Kept]
[KeptMember (".ctor()")]
class FromStringConstantWithGenericTwoParameters<T, S>
{
}

[Kept]
static void TestFromStringConstantWithGeneric ()
{
RequireCombinationOnString ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGeneric`1[[Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGenericInner]]");
RequireCombinationOnString ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGenericTwoParameters`2[Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGenericInnerOne`1[Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGenericInnerInner],Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGenericInnerTwo]");
RequireCombinationOnString ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGeneric`1[[Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWitGenericInnerMultiDimArray[,]]]");
RequireCombinationOnString ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithMultiDimArray[,]");
}

[Kept]
Expand Down
193 changes: 193 additions & 0 deletions test/Mono.Linker.Tests.Cases/DataFlow/ComplexTypeHandling.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Mono.Linker.Tests.Cases.Expectations.Assertions;

namespace Mono.Linker.Tests.Cases.DataFlow
{
public class ComplexTypeHandling
{
public static void Main ()
{
TestArray ();
TestArrayOnGeneric ();
TestGenericArray ();
TestGenericArrayOnGeneric ();
TestArrayGetTypeFromMethodParam ();
TestArrayGetTypeFromField ();
TestArrayTypeGetType ();
TestArrayCreateInstanceByName ();
TestArrayInAttributeParameter ();
}

[Kept]
class ArrayElementType
{
public ArrayElementType () { }

public void PublicMethod () { }

private int _privateField;
}

[Kept]
static void TestArray ()
{
RequirePublicMethods (typeof (ArrayElementType[]));
}

[Kept]
static void TestGenericArray ()
{
RequirePublicMethodsOnArrayOfGeneric<ArrayElementType> ();
}

[Kept]
static void RequirePublicMethodsOnArrayOfGeneric<T> ()
{
RequirePublicMethods (typeof (T[]));
}

[Kept]
class ArrayElementInGenericType
{
public ArrayElementInGenericType () { }

public void PublicMethod () { }

private int _privateField;
}

[Kept]
[KeptMember (".ctor()")]
class RequirePublicMethodsGeneric<
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)]
[KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))]
T>
{
}

[Kept]
static void TestArrayOnGeneric ()
{
_ = new RequirePublicMethodsGeneric<ArrayElementInGenericType[]> ();
}

[Kept]
static void TestGenericArrayOnGeneric ()
{
RequirePublicMethodsOnArrayOfGenericParameter<ArrayElementInGenericType> ();
}

[Kept]
static void RequirePublicMethodsOnArrayOfGenericParameter<T> ()
{
_ = new RequirePublicMethodsGeneric<T[]> ();
}

[Kept]
sealed class ArrayGetTypeFromMethodParamElement
{
// This method should not be marked, instead Array.* should be marked
public void PublicMethod () { }
}

[Kept]
static void TestArrayGetTypeFromMethodParamHelper (ArrayGetTypeFromMethodParamElement[] p)
{
RequirePublicMethods (p.GetType ());
}

[Kept]
static void TestArrayGetTypeFromMethodParam ()
{
TestArrayGetTypeFromMethodParamHelper (null);
}

[Kept]
sealed class ArrayGetTypeFromFieldElement
{
// This method should not be marked, instead Array.* should be marked
public void PublicMethod () { }
}

[Kept]
static ArrayGetTypeFromFieldElement[] _arrayGetTypeFromField;

[Kept]
static void TestArrayGetTypeFromField ()
{
RequirePublicMethods (_arrayGetTypeFromField.GetType ());
}

[Kept]
sealed class ArrayTypeGetTypeElement
{
// This method should not be marked, instead Array.* should be marked
public void PublicMethod () { }
}

[Kept]
static void TestArrayTypeGetType ()
{
RequirePublicMethods (Type.GetType ("Mono.Linker.Tests.Cases.DataFlow.ComplexTypeHandling+ArrayTypeGetTypeElement[]"));
}

// Nothing should be marked as CreateInstance doesn't work on arrays
class ArrayCreateInstanceByNameElement
{
public ArrayCreateInstanceByNameElement ()
{
}
}

[Kept]
static void TestArrayCreateInstanceByName ()
{
Activator.CreateInstance ("test", "Mono.Linker.Tests.Cases.DataFlow.ComplexTypeHandling+ArrayCreateInstanceByNameElement[]");
}

[Kept]
class ArrayInAttributeParamElement
{
// This method should not be marked, instead Array.* should be marked
public void PublicMethod () { }
}

[Kept]
[KeptAttributeAttribute (typeof (RequiresPublicMethodAttribute))]
[RequiresPublicMethod (typeof (ArrayInAttributeParamElement[]))]
static void TestArrayInAttributeParameter ()
{
}


[Kept]
private static void RequirePublicMethods (
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
[KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))]
Type type)
{
}

[Kept]
[KeptBaseType (typeof (Attribute))]
class RequiresPublicMethodAttribute : Attribute
{
[Kept]
public RequiresPublicMethodAttribute (
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
[KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))]
Type t)
{
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public static void Main ()
TestNullName ();
TestEmptyName ();
TestNonExistingName ();
TestPropertyOfArray ();
TestNullType ();
TestDataFlowType ();
TestIfElse (1);
Expand Down Expand Up @@ -82,6 +83,16 @@ static void TestNonExistingName ()
var property = typeof (PropertyUsedViaReflection).GetProperty ("NonExisting");
}

[Kept]
[RecognizedReflectionAccessPattern (
typeof (Type), nameof (Type.GetProperty), new Type[] { typeof (string) },
typeof (Array), nameof (Array.LongLength))]
static void TestPropertyOfArray ()
{
var property = typeof (int[]).GetProperty ("LongLength");
property.GetValue (null);
}

[Kept]
static void TestNullType ()
{
Expand Down

0 comments on commit 8449cbd

Please sign in to comment.