Skip to content

Commit

Permalink
Passing null to args for Activator.CreateInstance means no args (dotn…
Browse files Browse the repository at this point in the history
…et/linker#2111)

The core already recognized empty array as no args, but null means the same thing.

This change also:
* Fixes a bug when PublicConstructors was required but only PublicParameterlessConstructors were provided the warning message would not say what the requirement was.
* Simplifies some of the code
* Better matching for UnrecognizedReflectionAccessPattern and warnings in tests
* Added tests

Commit migrated from dotnet/linker@c3d69fb
  • Loading branch information
vitek-karas committed Jul 12, 2021
1 parent b321abd commit fcf09b3
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1558,10 +1558,12 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c
// Overload that has the parameters as the second or fourth argument
int argsParam = parameters.Count == 2 || parameters.Count == 3 ? 1 : 3;

if (methodParams.Count > argsParam &&
methodParams[argsParam] is ArrayValue arrayValue &&
arrayValue.Size.AsConstInt () != null) {
ctorParameterCount = arrayValue.Size.AsConstInt ();
if (methodParams.Count > argsParam) {
if (methodParams[argsParam] is ArrayValue arrayValue &&
arrayValue.Size.AsConstInt () != null)
ctorParameterCount = arrayValue.Size.AsConstInt ();
else if (methodParams[argsParam] is NullValue)
ctorParameterCount = 0;
}

if (parameters.Count > 3) {
Expand All @@ -1588,9 +1590,14 @@ methodParams[argsParam] is ArrayValue arrayValue &&
reflectionContext.RecordHandledPattern ();
} else {
// Otherwise fall back to the bitfield requirements
var requiredMemberTypes = ctorParameterCount == 0
? DynamicallyAccessedMemberTypes.PublicParameterlessConstructor
: GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors (bindingFlags);
var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors (bindingFlags);

// Special case the public parameterless constructor if we know that there are 0 args passed in
if (ctorParameterCount == 0 &&
requiredMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicConstructors) &&
!requiredMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.NonPublicConstructors))
requiredMemberTypes = DynamicallyAccessedMemberTypes.PublicParameterlessConstructor;

RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethod.Parameters[0]);
}
}
Expand Down Expand Up @@ -1997,17 +2004,24 @@ void RequireDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionC
// We allow a new() constraint on a generic parameter to satisfy DynamicallyAccessedMemberTypes.PublicParameterlessConstructor
reflectionContext.RecordHandledPattern ();
} else if (uniqueValue is LeafValueWithDynamicallyAccessedMemberNode valueWithDynamicallyAccessedMember) {
if (!valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes.HasFlag (requiredMemberTypes)) {
var availableMemberTypes = valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes;
if (!availableMemberTypes.HasFlag (requiredMemberTypes)) {
string missingMemberTypes = $"'{nameof (DynamicallyAccessedMemberTypes)}.{nameof (DynamicallyAccessedMemberTypes.All)}'";
if (requiredMemberTypes != DynamicallyAccessedMemberTypes.All) {
var missingMemberTypesList = AllDynamicallyAccessedMemberTypes
.Where (damt => (requiredMemberTypes & ~valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes & damt) == damt && damt != DynamicallyAccessedMemberTypes.None)
.Where (damt => (requiredMemberTypes & ~availableMemberTypes & damt) == damt && damt != DynamicallyAccessedMemberTypes.None)
.ToList ();

// PublicConstructors is a special case since its value is 3 - so PublicParameterlessConstructor (1) | _PublicConstructor_WithMoreThanOneParameter_ (2)
// The above bit logic only works for value with single bit set.
if (requiredMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicConstructors) &&
availableMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor) &&
!availableMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicConstructors))
missingMemberTypesList.Add (DynamicallyAccessedMemberTypes.PublicConstructors);

if (missingMemberTypesList.Contains (DynamicallyAccessedMemberTypes.PublicConstructors) &&
missingMemberTypesList.SingleOrDefault (x => x == DynamicallyAccessedMemberTypes.PublicParameterlessConstructor) is var ppc &&
ppc != DynamicallyAccessedMemberTypes.None)
missingMemberTypesList.Remove (ppc);
missingMemberTypesList.Contains (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor))
missingMemberTypesList.Remove (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor);

missingMemberTypes = string.Join (", ", missingMemberTypesList.Select (mmt => {
string mmtName = mmt == DynamicallyAccessedMemberTypesOverlay.Interfaces
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ class DerivedTypeWithInstantiatedGenericOnBase : GenericBaseTypeWithRequirements
[UnrecognizedReflectionAccessPattern (typeof (GenericBaseTypeWithRequirements<>), "T", messageCode: "IL2091")]
class DerivedTypeWithOpenGenericOnBase<T> : GenericBaseTypeWithRequirements<T>
{
[UnrecognizedReflectionAccessPattern (typeof (GenericBaseTypeWithRequirements<>), "T", messageCode: "IL2091")]
public DerivedTypeWithOpenGenericOnBase () { }
}

[RecognizedReflectionAccessPattern]
Expand Down Expand Up @@ -363,11 +365,12 @@ public TypeRequiresPublicFields<TOuter> PublicFieldsProperty {
[RecognizedReflectionAccessPattern]
set;
}

public TypeRequiresPublicMethods<TOuter> PublicMethodsProperty {
[UnrecognizedReflectionAccessPattern (typeof (TypeRequiresPublicMethods<>), "T", messageCode: "IL2091")]
get;
get => null;
[UnrecognizedReflectionAccessPattern (typeof (TypeRequiresPublicMethods<>), "T", messageCode: "IL2091")]
set;
set { }
}

[RecognizedReflectionAccessPattern]
Expand Down Expand Up @@ -424,6 +427,8 @@ class FullyInstantiatedOverPartiallyInstantiatedFields
class PartialyInstantiatedMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TOuter>
: BaseForPartialInstantiation<TestType, TOuter>
{
[UnrecognizedReflectionAccessPattern (typeof (BaseForPartialInstantiation<,>), "TMethods", messageCode: "IL2091")]
public PartialyInstantiatedMethods () { }
}

[RecognizedReflectionAccessPattern]
Expand Down
Loading

0 comments on commit fcf09b3

Please sign in to comment.