Skip to content

Commit

Permalink
Annotate all patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
Mateo Torres Ruiz committed Apr 2, 2020
1 parent 7b6b245 commit 94da209
Show file tree
Hide file tree
Showing 7 changed files with 322 additions and 229 deletions.
108 changes: 56 additions & 52 deletions src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3538,6 +3538,8 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c
} else {
MarkFieldsFromReflectionCall (ref reflectionContext, systemTypeValue.TypeRepresented, stringValue.Contents, staticOnly);
}
} else if (stringParam is NullValue) {
reflectionContext.RecordHandledPattern ();
} else if (stringParam is MethodParameterValue) {
// TODO: Check if parameter is annotated.
reflectionContext.RecordUnrecognizedPattern ($"Expression call '{calledMethod.FullName}' inside '{callingMethodBody.Method.FullName}' was detected with 3rd argument which cannot be analyzed");
Expand Down Expand Up @@ -3788,6 +3790,60 @@ void MarkMethodsFromReflectionCall (ref ReflectionPatternContext reflectionConte
}
}

void MarkPropertiesFromReflectionCall (ref ReflectionPatternContext reflectionContext, TypeDefinition declaringType, string name, bool staticOnly = false)
{
bool foundMatch = false;
foreach (var property in declaringType.Properties) {
if (property.Name != name)
continue;

bool markedAny = false;
var methodCalling = reflectionContext.MethodCalling;

// It is not easy to reliably detect in the IL code whether the getter or setter (or both) are used.
// Be conservative and mark everything for the property.
var getter = property.GetMethod;
if (getter != null && (!staticOnly || staticOnly && getter.IsStatic)) {
reflectionContext.RecordRecognizedPattern (getter, () => _markStep.MarkIndirectlyCalledMethod (getter, new DependencyInfo (DependencyKind.AccessedViaReflection, methodCalling)));
markedAny = true;
}

var setter = property.SetMethod;
if (setter != null && (!staticOnly || staticOnly && setter.IsStatic)) {
reflectionContext.RecordRecognizedPattern (setter, () => _markStep.MarkIndirectlyCalledMethod (setter, new DependencyInfo (DependencyKind.AccessedViaReflection, methodCalling)));
markedAny = true;
}

if (markedAny) {
foundMatch = true;
reflectionContext.RecordRecognizedPattern (property, () => _markStep.MarkProperty (property, new DependencyInfo (DependencyKind.AccessedViaReflection, methodCalling)));
}
}

if (!foundMatch)
reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{reflectionContext.MethodCalled.FullName}' inside '{reflectionContext.MethodCalling.FullName}' could not resolve property `{name}` on type `{declaringType.FullName}`.");
}

void MarkFieldsFromReflectionCall (ref ReflectionPatternContext reflectionContext, TypeDefinition declaringType, string name, bool staticOnly = false)
{
bool foundMatch = false;
var methodCalling = reflectionContext.MethodCalling;
foreach (var field in declaringType.Fields) {
if (field.Name != name)
continue;

if (staticOnly && !field.IsStatic)
continue;

foundMatch = true;
reflectionContext.RecordRecognizedPattern (field, () => _markStep.MarkField (field, new DependencyInfo (DependencyKind.AccessedViaReflection, methodCalling)));
break;
}

if (!foundMatch)
reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{reflectionContext.MethodCalled.FullName}' inside '{reflectionContext.MethodCalling.FullName}' could not resolve field `{name}` on type `{declaringType.FullName}`.");
}

string GetValueDescriptionForErrorMessage (ValueNode value)
{
switch (value) {
Expand Down Expand Up @@ -3867,58 +3923,6 @@ string GetDynamicallyAccessedMemberKindsDescription (DynamicallyAccessedMemberKi

return string.Join (" | ", results.Select (r => r.ToString ()));
}

void MarkPropertiesFromReflectionCall (ref ReflectionPatternContext reflectionContext, TypeDefinition declaringType, string name, bool staticOnly = false)
{
bool foundMatch = false;
foreach (var property in declaringType.Properties) {
if (property.Name != name)
continue;

bool markedAny = false;

// It is not easy to reliably detect in the IL code whether the getter or setter (or both) are used.
// Be conservative and mark everything for the property.
var getter = property.GetMethod;
if (getter != null && (!staticOnly || staticOnly && getter.IsStatic)) {
reflectionContext.RecordRecognizedPattern (getter, () => _markStep.MarkIndirectlyCalledMethod (getter));
markedAny = true;
}

var setter = property.SetMethod;
if (setter != null && (!staticOnly || staticOnly && setter.IsStatic)) {
reflectionContext.RecordRecognizedPattern (setter, () => _markStep.MarkIndirectlyCalledMethod (setter));
markedAny = true;
}

if (markedAny) {
foundMatch = true;
reflectionContext.RecordRecognizedPattern (property, () => _markStep.MarkProperty (property));
}
}

if (!foundMatch)
reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{reflectionContext.MethodCalled.FullName}' inside '{reflectionContext.MethodCalling.FullName}' could not resolve property `{name}` on type `{declaringType.FullName}`.");
}

void MarkFieldsFromReflectionCall (ref ReflectionPatternContext reflectionContext, TypeDefinition declaringType, string name, bool staticOnly = false)
{
bool foundMatch = false;
foreach (var field in declaringType.Fields) {
if (field.Name != name)
continue;

if (staticOnly && !field.IsStatic)
continue;

foundMatch = true;
reflectionContext.RecordRecognizedPattern (field, () => _markStep.MarkField (field));
break;
}

if (!foundMatch)
reflectionContext.RecordUnrecognizedPattern ($"Reflection call '{reflectionContext.MethodCalled.FullName}' inside '{reflectionContext.MethodCalling.FullName}' could not resolve field `{name}` on type `{declaringType.FullName}`.");
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public RecognizedReflectionAccessPatternAttribute (Type reflectionMethodType, st
}

public RecognizedReflectionAccessPatternAttribute (Type reflectionMethodType, string reflectionMethodName, Type [] reflectionMethodParameters,
Type accessedItemType, string accessedItemName, string [] accessedItemParameters)
Type accessedItemType, string accessedItemName, string [] accessedItemParameters = null)
{
if (reflectionMethodType == null)
throw new ArgumentException ("Value cannot be null or empty.", nameof (reflectionMethodType));
Expand Down
68 changes: 37 additions & 31 deletions test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,26 @@ static void Branch_SystemTypeValueNode_KnownStringValue ()
TestNonExistingName ();
}

[Kept]
static void Branch_SystemTypeValueNode_NullValueNode ()
{
Expression.Call (typeof (ExpressionCallString), null, Type.EmptyTypes);
}

[Kept]
static void Branch_NullValueNode ()
{
Expression.Call ((Type)null, "OnlyCalledViaExpression", Type.EmptyTypes);
}

[Kept]
static void Branch_MethodParameterValueNode (Type T, string s)
{
TestNonExistingTypeParameter (T);
TestNonExistingNameParameter (s);
}

#region RecognizedReflectionAccessPatterns
[RecognizedReflectionAccessPattern (
typeof (Expression), nameof (Expression.Call), new Type [] { typeof (Type), typeof (string), typeof (Type []), typeof (Expression []) },
typeof (ExpressionCallString), nameof (A), new Type [0])]
Expand All @@ -39,7 +59,7 @@ static void Branch_SystemTypeValueNode_KnownStringValue ()
[Kept]
static void TestByName (int i)
{
string MethodName = string.Empty;
string MethodName = null;
switch (i) {
case 0:
MethodName = "A";
Expand All @@ -51,7 +71,7 @@ static void TestByName (int i)
break;
}

Expression.Call (typeof (ExpressionCallString), MethodName, Type.EmptyTypes);
Expression.Call (typeof (ExpressionCallString), MethodName, null);
}

[RecognizedReflectionAccessPattern (
Expand Down Expand Up @@ -87,33 +107,9 @@ static void TestByNameWithParameters ()
IQueryable source = null;
Expression.Call (typeof (ExpressionCallString), "Count", new Type [] { source.ElementType }, source.Expression);
}
#endregion

[Kept]
static void Branch_SystemTypeValueNode_NullValueNode ()
{
Expression.Call (typeof (ExpressionCallString), null, Type.EmptyTypes);
}

[UnrecognizedReflectionAccessPattern (
typeof (Expression), nameof (Expression.Call), new Type [] { typeof (Type), typeof (string), typeof (Type []), typeof (Expression []) })]
[Kept]
static void TestNonExistingName ()
{
Expression.Call (typeof (ExpressionCallString), "NonExisting", Type.EmptyTypes);
}

[Kept]
static void Branch_NullValueNode ()
{
Expression.Call ((Type)null, "OnlyCalledViaExpression", Type.EmptyTypes);
}

[Kept]
static Type FindType ()
{
return typeof (ExpressionCallString);
}

#region UnrecognizedReflectionAccessPatterns
[UnrecognizedReflectionAccessPattern (
typeof (Expression), nameof (Expression.Call), new Type [] { typeof (Type), typeof (string), typeof (Type []), typeof (Expression []) })]
[Kept]
Expand All @@ -122,11 +118,12 @@ static void Branch_UnrecognizedPatterns ()
Expression.Call (FindType (), "OnlyCalledViaExpression", Type.EmptyTypes);
}

[UnrecognizedReflectionAccessPattern (
typeof (Expression), nameof (Expression.Call), new Type [] { typeof (Type), typeof (string), typeof (Type []), typeof (Expression []) })]
[Kept]
static void Branch_MethodParameterValueNode (Type T, string s)
static void TestNonExistingName ()
{
TestNonExistingTypeParameter (T);
TestNonExistingNameParameter (s);
Expression.Call (typeof (ExpressionCallString), "NonExisting", Type.EmptyTypes);
}

[UnrecognizedReflectionAccessPattern (
Expand All @@ -144,6 +141,14 @@ static void TestNonExistingNameParameter (string s)
{
Expression.Call (typeof (ExpressionCallString), s, Type.EmptyTypes);
}
#endregion

#region Helpers
[Kept]
static Type FindType ()
{
return typeof (ExpressionCallString);
}

[Kept]
static void A () { }
Expand All @@ -170,5 +175,6 @@ protected static T Count<T> (T t)
{
return default (T);
}
#endregion
}
}
Loading

0 comments on commit 94da209

Please sign in to comment.