Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/linker.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Koritzinsky <jekoritz@microsoft.com>2021-04-19 23:01:44 +0300
committerGitHub <noreply@github.com>2021-04-19 23:01:44 +0300
commitf6ccab59b40c959c10a28f696b3ec8ba0569fe25 (patch)
tree620c068309c376300451ef6bc56838018887d91f
parent76bcd90110b1c4d65a53b99761ac00e0f6b06b21 (diff)
Support running a basic analysis of generic instantiations created through reflection. (#1955)
Enable a basic analysis for arrays of `System.Type` passed to `Type.MakeGenericType` and `MethodInfo.MakeGenericMethod`. This analysis works for arrays created within the method with a constant size where all array elements are known. This allows for common usages of `Type.MakeGenericType` and `MethodInfo.MakeGenericMethod` that let the compiler implicitly create an array for them (since the parameter is `params`) to get basic analysis. Also enable analysis for ldelem when it's a known array with a known value at a given known index. In combination with #1930, we could also enable this validation for `Expression.Call` on generic types.
-rw-r--r--src/linker/Linker.Dataflow/MethodBodyScanner.cs152
-rw-r--r--src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs102
-rw-r--r--src/linker/Linker.Dataflow/ValueNode.cs42
-rw-r--r--test/Mono.Linker.Tests.Cases/DataFlow/FieldDataFlow.cs5
-rw-r--r--test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs190
-rw-r--r--test/Mono.Linker.Tests.Cases/DataFlow/MethodParametersDataFlow.cs5
-rw-r--r--test/Mono.Linker.Tests.Cases/DataFlow/MethodReturnParameterDataFlow.cs5
-rw-r--r--test/Mono.Linker.Tests.Cases/DataFlow/MethodThisDataFlow.cs5
-rw-r--r--test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs25
9 files changed, 424 insertions, 107 deletions
diff --git a/src/linker/Linker.Dataflow/MethodBodyScanner.cs b/src/linker/Linker.Dataflow/MethodBodyScanner.cs
index 5ad2a17db..a13773f3f 100644
--- a/src/linker/Linker.Dataflow/MethodBodyScanner.cs
+++ b/src/linker/Linker.Dataflow/MethodBodyScanner.cs
@@ -166,33 +166,33 @@ namespace Mono.Linker.Dataflow
}
}
- public struct ValueBasicBlockPair
- {
- public ValueNode Value;
- public int BasicBlockIndex;
- }
-
private static void StoreMethodLocalValue<KeyType> (
Dictionary<KeyType, ValueBasicBlockPair> valueCollection,
ValueNode valueToStore,
KeyType collectionKey,
- int curBasicBlock)
+ int curBasicBlock,
+ int? maxTrackedValues = null)
{
ValueBasicBlockPair newValue = new ValueBasicBlockPair { BasicBlockIndex = curBasicBlock };
ValueBasicBlockPair existingValue;
- if (valueCollection.TryGetValue (collectionKey, out existingValue)
- && existingValue.BasicBlockIndex == curBasicBlock) {
- // If the previous value was stored in the current basic block, then we can safely
- // overwrite the previous value with the new one.
+ if (valueCollection.TryGetValue (collectionKey, out existingValue)) {
+ if (existingValue.BasicBlockIndex == curBasicBlock) {
+ // If the previous value was stored in the current basic block, then we can safely
+ // overwrite the previous value with the new one.
+ newValue.Value = valueToStore;
+ } else {
+ // If the previous value came from a previous basic block, then some other use of
+ // the local could see the previous value, so we must merge the new value with the
+ // old value.
+ newValue.Value = MergePointValue.MergeValues (existingValue.Value, valueToStore);
+ }
+ valueCollection[collectionKey] = newValue;
+ } else if (maxTrackedValues == null || valueCollection.Count < maxTrackedValues) {
+ // We're not currently tracking a value a this index, so store the value now.
newValue.Value = valueToStore;
- } else {
- // If the previous value came from a previous basic block, then some other use of
- // the local could see the previous value, so we must merge the new value with the
- // old value.
- newValue.Value = MergePointValue.MergeValues (existingValue.Value, valueToStore);
+ valueCollection[collectionKey] = newValue;
}
- valueCollection[collectionKey] = newValue;
}
public void Scan (MethodBody methodBody)
@@ -246,22 +246,9 @@ namespace Mono.Linker.Dataflow
case Code.Cgt_Un:
case Code.Clt:
case Code.Clt_Un:
- case Code.Ldelem_I:
- case Code.Ldelem_I1:
- case Code.Ldelem_I2:
- case Code.Ldelem_I4:
- case Code.Ldelem_I8:
- case Code.Ldelem_R4:
- case Code.Ldelem_R8:
- case Code.Ldelem_U1:
- case Code.Ldelem_U2:
- case Code.Ldelem_U4:
case Code.Shl:
case Code.Shr:
case Code.Shr_Un:
- case Code.Ldelem_Any:
- case Code.Ldelem_Ref:
- case Code.Ldelema:
case Code.Ceq:
PopUnknown (currentStack, 2, methodBody, operation.Offset);
PushUnknown (currentStack);
@@ -432,12 +419,10 @@ namespace Mono.Linker.Dataflow
case Code.Newarr: {
StackSlot count = PopUnknown (currentStack, 1, methodBody, operation.Offset);
- currentStack.Push (new StackSlot (new ArrayValue (count.Value)));
+ currentStack.Push (new StackSlot (new ArrayValue (count.Value, (TypeReference) operation.Operand)));
}
break;
- case Code.Cpblk:
- case Code.Initblk:
case Code.Stelem_I:
case Code.Stelem_I1:
case Code.Stelem_I2:
@@ -447,6 +432,27 @@ namespace Mono.Linker.Dataflow
case Code.Stelem_R8:
case Code.Stelem_Any:
case Code.Stelem_Ref:
+ ScanStelem (operation, currentStack, methodBody, curBasicBlock);
+ break;
+
+ case Code.Ldelem_I:
+ case Code.Ldelem_I1:
+ case Code.Ldelem_I2:
+ case Code.Ldelem_I4:
+ case Code.Ldelem_I8:
+ case Code.Ldelem_R4:
+ case Code.Ldelem_R8:
+ case Code.Ldelem_U1:
+ case Code.Ldelem_U2:
+ case Code.Ldelem_U4:
+ case Code.Ldelem_Any:
+ case Code.Ldelem_Ref:
+ case Code.Ldelema:
+ ScanLdelem (operation, currentStack, methodBody, curBasicBlock);
+ break;
+
+ case Code.Cpblk:
+ case Code.Initblk:
PopUnknown (currentStack, 3, methodBody, operation.Offset);
break;
@@ -527,7 +533,7 @@ namespace Mono.Linker.Dataflow
case Code.Call:
case Code.Callvirt:
case Code.Newobj:
- HandleCall (methodBody, operation, currentStack);
+ HandleCall (methodBody, operation, currentStack, curBasicBlock);
break;
case Code.Jmp:
@@ -852,7 +858,8 @@ namespace Mono.Linker.Dataflow
private void HandleCall (
MethodBody callingMethodBody,
Instruction operation,
- Stack<StackSlot> currentStack)
+ Stack<StackSlot> currentStack,
+ int curBasicBlock)
{
MethodReference calledMethod = (MethodReference) operation.Operand;
@@ -886,6 +893,12 @@ namespace Mono.Linker.Dataflow
if (methodReturnValue != null)
currentStack.Push (new StackSlot (methodReturnValue, calledMethod.ReturnType.IsByRefOrPointer ()));
+
+ foreach (var param in methodParams) {
+ if (param is ArrayValue arr) {
+ MarkArrayValuesAsUnknown (arr, curBasicBlock);
+ }
+ }
}
public abstract bool HandleCall (
@@ -894,5 +907,74 @@ namespace Mono.Linker.Dataflow
Instruction operation,
ValueNodeList methodParams,
out ValueNode methodReturnValue);
+
+ // Limit tracking array values to 32 values for performance reasons. There are many arrays much longer than 32 elements in .NET, but the interesting ones for the linker are nearly always less than 32 elements.
+ private const int MaxTrackedArrayValues = 32;
+
+ private static void MarkArrayValuesAsUnknown (ArrayValue arrValue, int curBasicBlock)
+ {
+ // Since we can't know the current index we're storing the value at, clear all indices.
+ // That way we won't accidentally think we know the value at a given index when we cannot.
+ foreach (var knownIndex in arrValue.IndexValues.Keys) {
+ // Don't pass MaxTrackedArrayValues since we are only looking at keys we've already seen.
+ StoreMethodLocalValue (arrValue.IndexValues, UnknownValue.Instance, knownIndex, curBasicBlock);
+ }
+ }
+
+ private void ScanStelem (
+ Instruction operation,
+ Stack<StackSlot> currentStack,
+ MethodBody methodBody,
+ int curBasicBlock)
+ {
+ StackSlot valueToStore = PopUnknown (currentStack, 1, methodBody, operation.Offset);
+ StackSlot indexToStoreAt = PopUnknown (currentStack, 1, methodBody, operation.Offset);
+ StackSlot arrayToStoreIn = PopUnknown (currentStack, 1, methodBody, operation.Offset);
+ int? indexToStoreAtInt = indexToStoreAt.Value.AsConstInt ();
+ foreach (var array in arrayToStoreIn.Value.UniqueValues ()) {
+ if (array is ArrayValue arrValue) {
+ if (indexToStoreAtInt == null) {
+ MarkArrayValuesAsUnknown (arrValue, curBasicBlock);
+ } else {
+ // When we know the index, we can record the value at that index.
+ StoreMethodLocalValue (arrValue.IndexValues, valueToStore.Value, indexToStoreAtInt.Value, curBasicBlock, MaxTrackedArrayValues);
+ }
+ }
+ }
+ }
+
+ private void ScanLdelem (
+ Instruction operation,
+ Stack<StackSlot> currentStack,
+ MethodBody methodBody,
+ int curBasicBlock)
+ {
+ StackSlot indexToLoadFrom = PopUnknown (currentStack, 1, methodBody, operation.Offset);
+ StackSlot arrayToLoadFrom = PopUnknown (currentStack, 1, methodBody, operation.Offset);
+ if (arrayToLoadFrom.Value is not ArrayValue arr) {
+ PushUnknown (currentStack);
+ return;
+ }
+ bool isByRef = operation.OpCode.Code == Code.Ldelema;
+
+ int? index = indexToLoadFrom.Value.AsConstInt ();
+ if (index == null) {
+ PushUnknown (currentStack);
+ if (isByRef) {
+ MarkArrayValuesAsUnknown (arr, curBasicBlock);
+ }
+ return;
+ }
+
+
+ ValueBasicBlockPair arrayIndexValue;
+ arr.IndexValues.TryGetValue (index.Value, out arrayIndexValue);
+ if (arrayIndexValue.Value != null) {
+ ValueNode valueToPush = arrayIndexValue.Value;
+ currentStack.Push (new StackSlot (valueToPush, isByRef));
+ } else {
+ currentStack.Push (new StackSlot (null, isByRef));
+ }
+ }
}
}
diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
index b52f1dd10..061a985f7 100644
--- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
+++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
@@ -192,7 +192,7 @@ namespace Mono.Linker.Dataflow
{
switch (field.Name) {
case "EmptyTypes" when field.DeclaringType.IsTypeOf ("System", "Type"): {
- return new ArrayValue (new ConstIntValue (0));
+ return new ArrayValue (new ConstIntValue (0), field.DeclaringType);
}
case "Empty" when field.DeclaringType.IsTypeOf ("System", "String"): {
return new KnownStringValue (string.Empty);
@@ -656,7 +656,7 @@ namespace Mono.Linker.Dataflow
break;
case IntrinsicId.Array_Empty: {
- methodReturnValue = new ArrayValue (new ConstIntValue (0));
+ methodReturnValue = new ArrayValue (new ConstIntValue (0), ((GenericInstanceMethod) calledMethod).GenericArguments[0]);
}
break;
@@ -702,20 +702,28 @@ namespace Mono.Linker.Dataflow
reflectionContext.AnalyzingPattern ();
foreach (var value in methodParams[0].UniqueValues ()) {
if (value is SystemTypeValue typeValue) {
- foreach (var genericParameter in typeValue.TypeRepresented.GenericParameters) {
- if (_context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (genericParameter) != DynamicallyAccessedMemberTypes.None ||
- (genericParameter.HasDefaultConstructorConstraint && !typeValue.TypeRepresented.IsTypeOf ("System", "Nullable`1"))) {
- // There is a generic parameter which has some requirements on the input types.
- // For now we don't support tracking actual array elements, so we can't validate that the requirements are fulfilled.
-
- // Special case: Nullable<T> where T : struct
- // The struct constraint in C# implies new() constraints, but Nullable doesn't make a use of that part.
- // There are several places even in the framework where typeof(Nullable<>).MakeGenericType would warn
- // without any good reason to do so.
+ if (AnalyzeGenericInstatiationTypeArray (methodParams[1], ref reflectionContext, calledMethodDefinition, typeValue.TypeRepresented.GenericParameters)) {
+ reflectionContext.RecordHandledPattern ();
+ } else {
+ bool hasUncheckedAnnotation = false;
+ foreach (var genericParameter in typeValue.TypeRepresented.GenericParameters) {
+ if (_context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (genericParameter) != DynamicallyAccessedMemberTypes.None ||
+ (genericParameter.HasDefaultConstructorConstraint && !typeValue.TypeRepresented.IsTypeOf ("System", "Nullable`1"))) {
+ // If we failed to analyze the array, we go through the analyses again
+ // and intentionally ignore one particular annotation:
+ // Special case: Nullable<T> where T : struct
+ // The struct constraint in C# implies new() constraints, but Nullable doesn't make a use of that part.
+ // There are several places even in the framework where typeof(Nullable<>).MakeGenericType would warn
+ // without any good reason to do so.
+ hasUncheckedAnnotation = true;
+ break;
+ }
+ }
+ if (hasUncheckedAnnotation) {
reflectionContext.RecordUnrecognizedPattern (
- 2055,
- $"Call to '{calledMethodDefinition.GetDisplayName ()}' can not be statically analyzed. " +
- $"It's not possible to guarantee the availability of requirements of the generic type.");
+ 2055,
+ $"Call to '{calledMethodDefinition.GetDisplayName ()}' can not be statically analyzed. " +
+ $"It's not possible to guarantee the availability of requirements of the generic type.");
}
}
@@ -811,7 +819,7 @@ namespace Mono.Linker.Dataflow
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)) {
- ValidateGenericMethodInstantiation (ref reflectionContext, method, calledMethod);
+ ValidateGenericMethodInstantiation (ref reflectionContext, method, methodParams[2], calledMethod);
MarkMethod (ref reflectionContext, method);
}
@@ -1630,8 +1638,7 @@ namespace Mono.Linker.Dataflow
foreach (var methodValue in methodParams[0].UniqueValues ()) {
if (methodValue is SystemReflectionMethodBaseValue methodBaseValue) {
- ValidateGenericMethodInstantiation (ref reflectionContext, methodBaseValue.MethodRepresented, calledMethod);
- reflectionContext.RecordHandledPattern ();
+ ValidateGenericMethodInstantiation (ref reflectionContext, methodBaseValue.MethodRepresented, methodParams[1], calledMethod);
} else if (methodValue == NullValue.Instance) {
reflectionContext.RecordHandledPattern ();
} else {
@@ -1712,6 +1719,42 @@ namespace Mono.Linker.Dataflow
return true;
}
+ private bool AnalyzeGenericInstatiationTypeArray (ValueNode arrayParam, ref ReflectionPatternContext reflectionContext, MethodReference calledMethod, IList<GenericParameter> genericParameters)
+ {
+ foreach (var typesValue in arrayParam.UniqueValues ()) {
+ if (typesValue.Kind != ValueNodeKind.Array) {
+ return false;
+ }
+ ArrayValue array = (ArrayValue) typesValue;
+ int? size = array.Size.AsConstInt ();
+ if (size == null || size != genericParameters.Count) {
+ return false;
+ }
+ bool allIndicesKnown = true;
+ for (int i = 0; i < size.Value; i++) {
+ if (!array.IndexValues.TryGetValue (i, out ValueBasicBlockPair value) || value.Value is null or { Kind: ValueNodeKind.Unknown }) {
+ allIndicesKnown = false;
+ break;
+ }
+ }
+
+ if (!allIndicesKnown) {
+ return false;
+ }
+
+ for (int i = 0; i < size.Value; i++) {
+ if (array.IndexValues.TryGetValue (i, out ValueBasicBlockPair value)) {
+ RequireDynamicallyAccessedMembers (
+ ref reflectionContext,
+ _context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (genericParameters[i]),
+ value.Value,
+ calledMethod.Resolve ());
+ }
+ }
+ }
+ return true;
+ }
+
void ProcessCreateInstanceByName (ref ReflectionPatternContext reflectionContext, MethodDefinition calledMethod, ValueNodeList methodParams)
{
reflectionContext.AnalyzingPattern ();
@@ -2176,20 +2219,19 @@ namespace Mono.Linker.Dataflow
void ValidateGenericMethodInstantiation (
ref ReflectionPatternContext reflectionContext,
MethodDefinition genericMethod,
+ ValueNode genericParametersArray,
MethodReference reflectionMethod)
{
- if (!genericMethod.HasGenericParameters)
- return;
-
- foreach (var genericParameter in genericMethod.GenericParameters) {
- if (_context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (genericParameter) != DynamicallyAccessedMemberTypes.None ||
- genericParameter.HasDefaultConstructorConstraint) {
- // There is a generic parameter which has some requirements on input types.
- // For now we don't support tracking actual array elements, so we can't validate that the requirements are fulfilled.
- reflectionContext.RecordUnrecognizedPattern (
- 2060, string.Format (Resources.Strings.IL2060,
- DiagnosticUtilities.GetMethodSignatureDisplayName (reflectionMethod)));
- }
+ if (!genericMethod.HasGenericParameters) {
+ reflectionContext.RecordHandledPattern ();
+ }
+
+ if (!AnalyzeGenericInstatiationTypeArray (genericParametersArray, ref reflectionContext, reflectionMethod, genericMethod.GenericParameters)) {
+ reflectionContext.RecordUnrecognizedPattern (
+ 2060,
+ string.Format (Resources.Strings.IL2060, DiagnosticUtilities.GetMethodSignatureDisplayName (reflectionMethod)));
+ } else {
+ reflectionContext.RecordHandledPattern ();
}
}
diff --git a/src/linker/Linker.Dataflow/ValueNode.cs b/src/linker/Linker.Dataflow/ValueNode.cs
index dd547b79b..f1b5b3b6d 100644
--- a/src/linker/Linker.Dataflow/ValueNode.cs
+++ b/src/linker/Linker.Dataflow/ValueNode.cs
@@ -414,6 +414,9 @@ namespace Mono.Linker.Dataflow
case ValueNodeKind.Array:
ArrayValue av = (ArrayValue) node;
foundCycle = av.Size.DetectCycle (seenNodes, allNodesSeen);
+ foreach (ValueBasicBlockPair pair in av.IndexValues.Values) {
+ foundCycle |= pair.Value.DetectCycle (seenNodes, allNodesSeen);
+ }
break;
default:
@@ -1211,12 +1214,12 @@ namespace Mono.Linker.Dataflow
class ArrayValue : ValueNode
{
- protected override int NumChildren => 1;
+ protected override int NumChildren => 1 + IndexValues.Count;
/// <summary>
/// Constructs an array value of the given size
/// </summary>
- public ArrayValue (ValueNode size)
+ public ArrayValue (ValueNode size, TypeReference elementType)
{
Kind = ValueNodeKind.Array;
@@ -1224,9 +1227,19 @@ namespace Mono.Linker.Dataflow
StaticType = null;
Size = size ?? UnknownValue.Instance;
+ ElementType = elementType.ResolveToMainTypeDefinition ();
+ IndexValues = new Dictionary<int, ValueBasicBlockPair> ();
+ }
+
+ private ArrayValue (ValueNode size, TypeReference elementType, Dictionary<int, ValueBasicBlockPair> indexValues)
+ : this (size, elementType)
+ {
+ IndexValues = indexValues;
}
public ValueNode Size { get; }
+ public TypeReference ElementType { get; }
+ public Dictionary<int, ValueBasicBlockPair> IndexValues { get; }
public override int GetHashCode ()
{
@@ -1239,23 +1252,36 @@ namespace Mono.Linker.Dataflow
return false;
ArrayValue otherArr = (ArrayValue) other;
- return Size.Equals (otherArr.Size);
+ bool equals = Size.Equals (otherArr.Size);
+ equals &= IndexValues.Count == otherArr.IndexValues.Count;
+ if (!equals)
+ return false;
+
+ // If both sets T and O are the same size and "T intersect O" is empty, then T == O.
+ HashSet<KeyValuePair<int, ValueBasicBlockPair>> thisValueSet = new (IndexValues);
+ HashSet<KeyValuePair<int, ValueBasicBlockPair>> otherValueSet = new (otherArr.IndexValues);
+ thisValueSet.ExceptWith (otherValueSet);
+ return thisValueSet.Count == 0;
}
protected override string NodeToString ()
{
- return ValueNodeDump.ValueNodeToString (this, Size);
+ // TODO: Use StringBuilder and remove Linq usage.
+ return $"(Array Size:{ValueNodeDump.ValueNodeToString (this, Size)}, Values:({string.Join (',', IndexValues.Select (v => $"({v.Key},{ValueNodeDump.ValueNodeToString (v.Value.Value)})"))})";
}
protected override IEnumerable<ValueNode> EvaluateUniqueValues ()
{
foreach (var sizeConst in Size.UniqueValuesInternal)
- yield return new ArrayValue (sizeConst);
+ yield return new ArrayValue (sizeConst, ElementType, IndexValues);
}
protected override ValueNode ChildAt (int index)
{
if (index == 0) return Size;
+ if (index - 1 <= IndexValues.Count)
+ return IndexValues.Values.ElementAt (index - 1).Value;
+
throw new InvalidOperationException ();
}
}
@@ -1337,4 +1363,10 @@ namespace Mono.Linker.Dataflow
return hashCode.ToHashCode ();
}
}
+
+ public struct ValueBasicBlockPair
+ {
+ public ValueNode Value;
+ public int BasicBlockIndex;
+ }
}
diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/FieldDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/FieldDataFlow.cs
index 6965f80ef..380b72a70 100644
--- a/test/Mono.Linker.Tests.Cases/DataFlow/FieldDataFlow.cs
+++ b/test/Mono.Linker.Tests.Cases/DataFlow/FieldDataFlow.cs
@@ -148,7 +148,12 @@ namespace Mono.Linker.Tests.Cases.DataFlow
{
var array = new object[1];
array[0] = this.GetType ();
+ MakeArrayValuesUnknown (array);
TypeStore._staticTypeWithPublicParameterlessConstructor = (Type) array[0];
+
+ static void MakeArrayValuesUnknown (object[] array)
+ {
+ }
}
private static void RequirePublicParameterlessConstructor (
diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs
index 08bb958cd..27d487a3e 100644
--- a/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs
+++ b/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs
@@ -735,6 +735,9 @@ namespace Mono.Linker.Tests.Cases.DataFlow
{
TestNullType ();
TestUnknownInput (null);
+ TestWithUnknownTypeArray (null);
+ TestWithArrayUnknownIndexSet (0);
+ TestWithArrayUnknownLengthSet (1);
TestNoArguments ();
TestWithRequirements ();
@@ -768,6 +771,31 @@ namespace Mono.Linker.Tests.Cases.DataFlow
inputType.MakeGenericType (typeof (TestType));
}
+ [UnrecognizedReflectionAccessPattern (typeof (Type), nameof (Type.MakeGenericType), new Type[] { typeof (Type[]) },
+ messageCode: "IL2055")]
+ static void TestWithUnknownTypeArray (Type[] types)
+ {
+ typeof (GenericWithPublicFieldsArgument<>).MakeGenericType (types);
+ }
+
+ [UnrecognizedReflectionAccessPattern (typeof (Type), nameof (Type.MakeGenericType), new Type[] { typeof (Type[]) },
+ messageCode: "IL2055")]
+ static void TestWithArrayUnknownIndexSet (int indexToSet)
+ {
+ Type[] types = new Type[1];
+ types[indexToSet] = typeof (TestType);
+ typeof (GenericWithPublicFieldsArgument<>).MakeGenericType (types);
+ }
+
+ [UnrecognizedReflectionAccessPattern (typeof (Type), nameof (Type.MakeGenericType), new Type[] { typeof (Type[]) },
+ messageCode: "IL2055")]
+ static void TestWithArrayUnknownLengthSet (int arrayLen)
+ {
+ Type[] types = new Type[arrayLen];
+ types[0] = typeof (TestType);
+ typeof (GenericWithPublicFieldsArgument<>).MakeGenericType (types);
+ }
+
[RecognizedReflectionAccessPattern]
static void TestNoArguments ()
{
@@ -778,8 +806,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow
{
}
- [UnrecognizedReflectionAccessPattern (typeof (Type), nameof (Type.MakeGenericType), new Type[] { typeof (Type[]) },
- messageCode: "IL2055")]
+ [RecognizedReflectionAccessPattern]
static void TestWithRequirements ()
{
// Currently this is not analyzable since we don't track array elements.
@@ -787,16 +814,14 @@ namespace Mono.Linker.Tests.Cases.DataFlow
typeof (GenericWithPublicFieldsArgument<>).MakeGenericType (typeof (TestType));
}
- [UnrecognizedReflectionAccessPattern (typeof (Type), nameof (Type.MakeGenericType), new Type[] { typeof (Type[]) },
- messageCode: "IL2055")]
+ [RecognizedReflectionAccessPattern]
static void TestWithRequirementsFromParam (
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] Type type)
{
typeof (GenericWithPublicFieldsArgument<>).MakeGenericType (type);
}
- [UnrecognizedReflectionAccessPattern (typeof (Type), nameof (Type.MakeGenericType), new Type[] { typeof (Type[]) },
- messageCode: "IL2055")]
+ [RecognizedReflectionAccessPattern]
static void TestWithRequirementsFromGenericParam<
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> ()
{
@@ -823,8 +848,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow
{
}
- [UnrecognizedReflectionAccessPattern (typeof (Type), nameof (Type.MakeGenericType), new Type[] { typeof (Type[]) },
- messageCode: "IL2055")]
+ [RecognizedReflectionAccessPattern]
static void TestWithMultipleArgumentsWithRequirements ()
{
typeof (GenericWithMultipleArgumentsWithRequirements<,>).MakeGenericType (typeof (TestType), typeof (TestType));
@@ -836,8 +860,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow
{
}
- [UnrecognizedReflectionAccessPattern (typeof (Type), nameof (Type.MakeGenericType), new Type[] { typeof (Type[]) },
- messageCode: "IL2055")]
+ [RecognizedReflectionAccessPattern]
static void TestWithNewConstraint ()
{
typeof (GenericWithNewConstraint<>).MakeGenericType (typeof (TestType));
@@ -847,8 +870,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow
{
}
- [UnrecognizedReflectionAccessPattern (typeof (Type), nameof (Type.MakeGenericType), new Type[] { typeof (Type[]) },
- messageCode: "IL2055")]
+ [RecognizedReflectionAccessPattern]
static void TestWithStructConstraint ()
{
typeof (GenericWithStructConstraint<>).MakeGenericType (typeof (TestType));
@@ -858,8 +880,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow
{
}
- [UnrecognizedReflectionAccessPattern (typeof (Type), nameof (Type.MakeGenericType), new Type[] { typeof (Type[]) },
- messageCode: "IL2055")]
+ [RecognizedReflectionAccessPattern]
static void TestWithUnmanagedConstraint ()
{
typeof (GenericWithUnmanagedConstraint<>).MakeGenericType (typeof (TestType));
@@ -881,8 +902,13 @@ namespace Mono.Linker.Tests.Cases.DataFlow
public static void Test ()
{
TestNullMethod ();
- TestUnknownInput (null);
+ TestUnknownMethod (null);
TestUnknownMethodButNoTypeArguments (null);
+ TestWithUnknownTypeArray (null);
+ TestWithArrayUnknownIndexSet (0);
+ TestWithArrayUnknownIndexSetByRef (0);
+ TestWithArrayUnknownLengthSet (1);
+ TestWithArrayPassedToAnotherMethod ();
TestWithNoArguments ();
TestWithRequirements ();
@@ -891,6 +917,11 @@ namespace Mono.Linker.Tests.Cases.DataFlow
TestWithRequirementsViaRuntimeMethod ();
TestWithRequirementsButNoTypeArguments ();
+ TestWithMultipleKnownGenericParameters ();
+ TestWithOneUnknownGenericParameter (null);
+ TestWithPartiallyInitializedGenericTypeArray ();
+ TestWithConditionalGenericTypeSet (true);
+
TestWithNoRequirements ();
TestWithNoRequirementsFromParam (null);
TestWithNoRequirementsViaRuntimeMethod ();
@@ -911,7 +942,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow
[UnrecognizedReflectionAccessPattern (typeof (MethodInfo), nameof (MethodInfo.MakeGenericMethod), new Type[] { typeof (Type[]) },
messageCode: "IL2060")]
- static void TestUnknownInput (MethodInfo mi)
+ static void TestUnknownMethod (MethodInfo mi)
{
mi.MakeGenericMethod (typeof (TestType));
}
@@ -924,6 +955,61 @@ namespace Mono.Linker.Tests.Cases.DataFlow
mi.MakeGenericMethod (Type.EmptyTypes);
}
+ [UnrecognizedReflectionAccessPattern (typeof (MethodInfo), nameof (MethodInfo.MakeGenericMethod), new Type[] { typeof (Type[]) },
+ messageCode: "IL2060")]
+ static void TestWithUnknownTypeArray (Type[] types)
+ {
+ typeof (MakeGenericMethod).GetMethod (nameof (GenericWithRequirements), BindingFlags.Static)
+ .MakeGenericMethod (types);
+ }
+
+ [UnrecognizedReflectionAccessPattern (typeof (MethodInfo), nameof (MethodInfo.MakeGenericMethod), new Type[] { typeof (Type[]) },
+ messageCode: "IL2060")]
+ static void TestWithArrayUnknownIndexSet (int indexToSet)
+ {
+ Type[] types = new Type[1];
+ types[indexToSet] = typeof (TestType);
+ typeof (MakeGenericMethod).GetMethod (nameof (GenericWithRequirements), BindingFlags.Static)
+ .MakeGenericMethod (types);
+ }
+
+ [UnrecognizedReflectionAccessPattern (typeof (MethodInfo), nameof (MethodInfo.MakeGenericMethod), new Type[] { typeof (Type[]) },
+ messageCode: "IL2060")]
+ static void TestWithArrayUnknownIndexSetByRef (int indexToSet)
+ {
+ Type[] types = new Type[1];
+ types[0] = typeof (TestType);
+ ref Type t = ref types[indexToSet];
+ t = typeof (TestType);
+ typeof (MakeGenericMethod).GetMethod (nameof (GenericWithRequirements), BindingFlags.Static)
+ .MakeGenericMethod (types);
+ }
+
+ [UnrecognizedReflectionAccessPattern (typeof (MethodInfo), nameof (MethodInfo.MakeGenericMethod), new Type[] { typeof (Type[]) },
+ messageCode: "IL2060")]
+ static void TestWithArrayUnknownLengthSet (int arrayLen)
+ {
+ Type[] types = new Type[arrayLen];
+ types[0] = typeof (TestType);
+ typeof (MakeGenericMethod).GetMethod (nameof (GenericWithRequirements), BindingFlags.Static)
+ .MakeGenericMethod (types);
+ }
+
+ static void MethodThatTakesArrayParameter (Type[] types)
+ {
+ }
+
+ [UnrecognizedReflectionAccessPattern (typeof (MethodInfo), nameof (MethodInfo.MakeGenericMethod), new Type[] { typeof (Type[]) },
+ messageCode: "IL2060")]
+ static void TestWithArrayPassedToAnotherMethod ()
+ {
+ Type[] types = new Type[1];
+ types[0] = typeof (TestType);
+ MethodThatTakesArrayParameter (types);
+ typeof (MakeGenericMethod).GetMethod (nameof (GenericWithRequirements), BindingFlags.Static)
+ .MakeGenericMethod (types);
+ }
+
[RecognizedReflectionAccessPattern]
static void TestWithNoArguments ()
{
@@ -935,16 +1021,14 @@ namespace Mono.Linker.Tests.Cases.DataFlow
{
}
- [UnrecognizedReflectionAccessPattern (typeof (MethodInfo), nameof (MethodInfo.MakeGenericMethod), new Type[] { typeof (Type[]) },
- messageCode: "IL2060")]
+ [RecognizedReflectionAccessPattern]
static void TestWithRequirements ()
{
typeof (MakeGenericMethod).GetMethod (nameof (GenericWithRequirements), BindingFlags.Static)
.MakeGenericMethod (typeof (TestType));
}
- [UnrecognizedReflectionAccessPattern (typeof (MethodInfo), nameof (MethodInfo.MakeGenericMethod), new Type[] { typeof (Type[]) },
- messageCode: "IL2060")]
+ [RecognizedReflectionAccessPattern]
static void TestWithRequirementsFromParam (
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] Type type)
{
@@ -959,8 +1043,8 @@ namespace Mono.Linker.Tests.Cases.DataFlow
.MakeGenericMethod (typeof (T));
}
- [UnrecognizedReflectionAccessPattern (typeof (MethodInfo), nameof (MethodInfo.MakeGenericMethod), new Type[] { typeof (Type[]) },
- messageCode: "IL2060")]
+
+ [RecognizedReflectionAccessPattern]
static void TestWithRequirementsViaRuntimeMethod ()
{
typeof (MakeGenericMethod).GetRuntimeMethod (nameof (GenericWithRequirements), Type.EmptyTypes)
@@ -971,7 +1055,6 @@ namespace Mono.Linker.Tests.Cases.DataFlow
messageCode: "IL2060")]
static void TestWithRequirementsButNoTypeArguments ()
{
- // Linker could figure out that this is not a problem, but it's not worth the complexity, since this will always throw at runtime
typeof (MakeGenericMethod).GetMethod (nameof (GenericWithRequirements), BindingFlags.Static)
.MakeGenericMethod (Type.EmptyTypes);
}
@@ -981,6 +1064,54 @@ namespace Mono.Linker.Tests.Cases.DataFlow
}
[RecognizedReflectionAccessPattern]
+ static void TestWithMultipleKnownGenericParameters ()
+ {
+ typeof (MakeGenericMethod).GetMethod (nameof (GenericMultipleParameters), BindingFlags.Static)
+ .MakeGenericMethod (typeof (TestType), typeof (TestType), typeof (TestType));
+ }
+
+ [UnrecognizedReflectionAccessPattern (typeof (MethodInfo), nameof (MethodInfo.MakeGenericMethod), new Type[] { typeof (Type[]) },
+ messageCode: "IL2060")]
+ static void TestWithOneUnknownGenericParameter (Type[] types)
+ {
+ typeof (MakeGenericMethod).GetMethod (nameof (GenericMultipleParameters), BindingFlags.Static)
+ .MakeGenericMethod (typeof (TestType), typeof (TestStruct), types[0]);
+ }
+
+ [UnrecognizedReflectionAccessPattern (typeof (MethodInfo), nameof (MethodInfo.MakeGenericMethod), new Type[] { typeof (Type[]) },
+ messageCode: "IL2060")]
+ static void TestWithPartiallyInitializedGenericTypeArray ()
+ {
+ Type[] types = new Type[3];
+ types[0] = typeof (TestType);
+ types[1] = typeof (TestStruct);
+ typeof (MakeGenericMethod).GetMethod (nameof (GenericMultipleParameters), BindingFlags.Static)
+ .MakeGenericMethod (types);
+ }
+
+ [RecognizedReflectionAccessPattern]
+ static void TestWithConditionalGenericTypeSet (bool thirdParameterIsStruct)
+ {
+ Type[] types = new Type[3];
+ types[0] = typeof (TestType);
+ types[1] = typeof (TestStruct);
+ if (thirdParameterIsStruct) {
+ types[2] = typeof (TestStruct);
+ } else {
+ types[2] = typeof (TestType);
+ }
+ typeof (MakeGenericMethod).GetMethod (nameof (GenericMultipleParameters), BindingFlags.Static)
+ .MakeGenericMethod (types);
+ }
+
+ public static void GenericMultipleParameters<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] U,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] V> ()
+ {
+ }
+
+ [RecognizedReflectionAccessPattern]
static void TestWithNoRequirements ()
{
typeof (MakeGenericMethod).GetMethod (nameof (GenericWithNoRequirements), BindingFlags.Static)
@@ -1005,8 +1136,8 @@ namespace Mono.Linker.Tests.Cases.DataFlow
{
}
- [UnrecognizedReflectionAccessPattern (typeof (MethodInfo), nameof (MethodInfo.MakeGenericMethod), new Type[] { typeof (Type[]) },
- messageCode: "IL2060")]
+
+ [RecognizedReflectionAccessPattern]
static void TestWithMultipleArgumentsWithRequirements ()
{
typeof (MakeGenericMethod).GetMethod (nameof (GenericWithMultipleArgumentsWithRequirements), BindingFlags.Static | BindingFlags.NonPublic)
@@ -1019,8 +1150,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow
{
}
- [UnrecognizedReflectionAccessPattern (typeof (MethodInfo), nameof (MethodInfo.MakeGenericMethod), new Type[] { typeof (Type[]) },
- messageCode: "IL2060")]
+ [RecognizedReflectionAccessPattern]
static void TestWithNewConstraint ()
{
typeof (MakeGenericMethod).GetMethod (nameof (GenericWithNewConstraint), BindingFlags.Static | BindingFlags.NonPublic)
@@ -1032,8 +1162,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow
var t = new T ();
}
- [UnrecognizedReflectionAccessPattern (typeof (MethodInfo), nameof (MethodInfo.MakeGenericMethod), new Type[] { typeof (Type[]) },
- messageCode: "IL2060")]
+ [RecognizedReflectionAccessPattern]
static void TestWithStructConstraint ()
{
typeof (MakeGenericMethod).GetMethod (nameof (GenericWithStructConstraint), BindingFlags.Static | BindingFlags.NonPublic)
@@ -1045,8 +1174,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow
var t = new T ();
}
- [UnrecognizedReflectionAccessPattern (typeof (MethodInfo), nameof (MethodInfo.MakeGenericMethod), new Type[] { typeof (Type[]) },
- messageCode: "IL2060")]
+ [RecognizedReflectionAccessPattern]
static void TestWithUnmanagedConstraint ()
{
typeof (MakeGenericMethod).GetMethod (nameof (GenericWithUnmanagedConstraint), BindingFlags.Static | BindingFlags.NonPublic)
diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/MethodParametersDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/MethodParametersDataFlow.cs
index 2897c4705..fe11b6b03 100644
--- a/test/Mono.Linker.Tests.Cases/DataFlow/MethodParametersDataFlow.cs
+++ b/test/Mono.Linker.Tests.Cases/DataFlow/MethodParametersDataFlow.cs
@@ -161,7 +161,12 @@ namespace Mono.Linker.Tests.Cases.DataFlow
{
var array = new object[1];
array[0] = this.GetType ();
+ MakeArrayValuesUnknown (array);
RequirePublicParameterlessConstructor ((Type) array[0]);
+
+ static void MakeArrayValuesUnknown (object[] array)
+ {
+ }
}
[RecognizedReflectionAccessPattern]
diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/MethodReturnParameterDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/MethodReturnParameterDataFlow.cs
index f66a25e74..c0925a264 100644
--- a/test/Mono.Linker.Tests.Cases/DataFlow/MethodReturnParameterDataFlow.cs
+++ b/test/Mono.Linker.Tests.Cases/DataFlow/MethodReturnParameterDataFlow.cs
@@ -126,7 +126,12 @@ namespace Mono.Linker.Tests.Cases.DataFlow
{
var array = new object[1];
array[0] = this.GetType ();
+ MakeArrayValuesUnknown (array);
return (Type) array[0];
+
+ static void MakeArrayValuesUnknown (object[] array)
+ {
+ }
}
[UnrecognizedReflectionAccessPattern (typeof (MethodReturnParameterDataFlow), nameof (RequirePublicConstructors), new Type[] { typeof (Type) }, messageCode: "IL2072")]
diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/MethodThisDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/MethodThisDataFlow.cs
index 9dc4813b8..a8bfbed1f 100644
--- a/test/Mono.Linker.Tests.Cases/DataFlow/MethodThisDataFlow.cs
+++ b/test/Mono.Linker.Tests.Cases/DataFlow/MethodThisDataFlow.cs
@@ -100,7 +100,12 @@ namespace Mono.Linker.Tests.Cases.DataFlow
{
var array = new object[1];
array[0] = array.GetType ();
+ MakeArrayValuesUnknown (array);
((MethodThisDataFlowTypeTest) array[0]).RequireThisNonPublicMethods ();
+
+ static void MakeArrayValuesUnknown (object[] array)
+ {
+ }
}
[UnrecognizedReflectionAccessPattern (typeof (MethodThisDataFlowTypeTest), nameof (MethodThisDataFlowTypeTest.RequireThisPublicMethods), new Type[] { },
diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs
index 866fab24b..7f47ff4a3 100644
--- a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs
+++ b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs
@@ -238,10 +238,11 @@ namespace Mono.Linker.Tests.Cases.Reflection
{ }
[Kept]
+ [ExpectedWarning ("IL2060", "Expression::Call")]
static void TestWithNoTypeParameters ()
{
- // Linker doesn't check if it's valid to call a generic method without generic parameters, it looks like a non-generic call
- // so it will preserve the target method.
+ // Linker warns since this is a call to a generic method with a mismatching number of generic parameters
+ // and provided type values for the generic instantiation.
Expression.Call (typeof (TestGenericMethods), nameof (GenericMethodCalledAsNonGeneric), Type.EmptyTypes);
}
@@ -253,18 +254,24 @@ namespace Mono.Linker.Tests.Cases.Reflection
}
[Kept]
- [ExpectedWarning ("IL2060", "Expression::Call")]
static void TestMethodWithRequirements ()
{
- // This must warn - as this is dangerous
- Expression.Call (typeof (TestGenericMethods), nameof (GenericMethodWithRequirements), new Type[] { GetUnknownType () });
+ // This may not warn - as it's safe
+ Expression.Call (typeof (TestGenericMethods), nameof (GenericMethodWithRequirements), new Type[] { GetUnknownTypeWithRequrements () });
+ }
+
+ [Kept]
+ [ExpectedWarning ("IL2060", "Expression::Call")]
+ static void TestMethodWithRequirementsUnknownTypeArray (Type[] types)
+ {
+ // The passed in types array cannot be analyzed, so a warning is produced.
+ Expression.Call (typeof (TestGenericMethods), nameof (GenericMethodWithRequirements), types);
}
[Kept]
[ExpectedWarning ("IL2060", "Expression::Call")]
static void TestMethodWithRequirementsButNoTypeArguments ()
{
- // Linker could figure out that this is not a problem, but it's not worth the complexity, since this will always throw at runtime
Expression.Call (typeof (TestGenericMethods), nameof (GenericMethodWithRequirementsNoArguments), Type.EmptyTypes);
}
@@ -333,6 +340,7 @@ namespace Mono.Linker.Tests.Cases.Reflection
TestWithNoTypeParameters ();
TestMethodWithoutRequirements ();
TestMethodWithRequirements ();
+ TestMethodWithRequirementsUnknownTypeArray (null);
TestMethodWithRequirementsButNoTypeArguments ();
UnknownMethodWithRequirements.TestWithTypeParameters ();
UnknownMethodWithRequirements.TestWithoutTypeParameters ();
@@ -342,6 +350,11 @@ namespace Mono.Linker.Tests.Cases.Reflection
[Kept]
static Type GetUnknownType () { return null; }
+
+ [Kept]
+ [return: KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))]
+ [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)]
+ static Type GetUnknownTypeWithRequrements () { return null; }
}
}
}