Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -592,17 +592,22 @@ protected virtual void Scan(MethodIL methodBody, ref InterproceduralState interp
case ILOpcode.conv_r8:
case ILOpcode.ldind_ref:
case ILOpcode.ldobj:
case ILOpcode.mkrefany:
case ILOpcode.unbox:
case ILOpcode.unbox_any:
case ILOpcode.box:
case ILOpcode.neg:
case ILOpcode.not:
PopUnknown(currentStack, 1, methodBody, offset);
PushUnknown(currentStack);
reader.Skip(opcode);
break;

case ILOpcode.box:
case ILOpcode.mkrefany:
HandleTypeTokenAccess(methodBody, offset, (TypeDesc)methodBody.GetObject(reader.ReadILToken()));
PopUnknown(currentStack, 1, methodBody, offset);
PushUnknown(currentStack);
break;

case ILOpcode.isinst:
case ILOpcode.castclass:
// We can consider a NOP because the value doesn't change.
Expand All @@ -622,6 +627,7 @@ protected virtual void Scan(MethodIL methodBody, ref InterproceduralState interp
{
StackSlot count = PopUnknown(currentStack, 1, methodBody, offset);
var arrayElement = (TypeDesc)methodBody.GetObject(reader.ReadILToken());
HandleTypeTokenAccess(methodBody, offset, arrayElement);
currentStack.Push(new StackSlot(ArrayValue.Create(count.Value, arrayElement)));
}
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ public static bool RequiresReflectionMethodBodyScannerForAccess(FlowAnnotations
field.DoesFieldRequire(DiagnosticUtilities.RequiresDynamicCodeAttribute, out _);
}

public static bool RequiresReflectionMethodBodyScannerForAccess(FlowAnnotations flowAnnotations, TypeDesc type)
{
return GenericArgumentDataFlow.RequiresGenericArgumentDataFlow(flowAnnotations, type) ||
type.DoesTypeRequire(DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _) ||
type.DoesTypeRequire(DiagnosticUtilities.RequiresAssemblyFilesAttribute, out _) ||
type.DoesTypeRequire(DiagnosticUtilities.RequiresDynamicCodeAttribute, out _);
}

internal static void CheckAndReportAllRequires(in DiagnosticContext diagnosticContext, TypeSystemEntity calledMember)
{
CheckAndReportRequires(diagnosticContext, calledMember, DiagnosticUtilities.RequiresUnreferencedCodeAttribute);
Expand Down Expand Up @@ -429,10 +437,10 @@ private static bool IsComInterop(MarshalAsDescriptor? marshalInfoProvider, TypeD

private void ProcessGenericArgumentDataFlow(MethodDesc method)
{
// We only need to validate static methods and then all generic methods
// Instance non-generic methods don't need validation because the creation of the instance
// is the place where the validation will happen.
if (!method.Signature.IsStatic && !method.HasInstantiation && !method.IsConstructor)
// We mostly need to validate static methods and generic methods
// Instance non-generic methods on reference types don't need validation
// because the creation of the instance is the place where the validation will happen.
if (!method.Signature.IsStatic && !method.HasInstantiation && !method.IsConstructor && !method.OwningType.IsValueType)
return;

if (GenericArgumentDataFlow.RequiresGenericArgumentDataFlow(_annotations, method))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1240,6 +1240,10 @@ public bool CanGenerateMetadata(FieldDesc field)
protected abstract MetadataCategory GetMetadataCategory(TypeDesc type);
protected abstract MetadataCategory GetMetadataCategory(FieldDesc field);

public virtual void GetDependenciesDueToAccess(ref DependencyList dependencies, NodeFactory factory, MethodIL methodIL, TypeDesc accessedType)
{
}

public virtual void GetDependenciesDueToAccess(ref DependencyList dependencies, NodeFactory factory, MethodIL methodIL, MethodDesc calledMethod)
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,15 @@ public override void GetDependenciesDueToAccess(ref DependencyList dependencies,
}
}

public override void GetDependenciesDueToAccess(ref DependencyList dependencies, NodeFactory factory, MethodIL methodIL, TypeDesc accessedType)
{
bool scanReflection = (_generationOptions & UsageBasedMetadataGenerationOptions.ReflectionILScanning) != 0;
if (scanReflection && Dataflow.ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForAccess(FlowAnnotations, accessedType))
{
AddDataflowDependency(ref dependencies, factory, methodIL, "Access to interesting type");
}
}

public override void GetDependenciesDueToAccess(ref DependencyList dependencies, NodeFactory factory, MethodIL methodIL, MethodDesc calledMethod)
{
bool scanReflection = (_generationOptions & UsageBasedMetadataGenerationOptions.ReflectionILScanning) != 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,7 @@ private void ImportMkRefAny(int token)
{
_dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.TypeHandleToRuntimeType), "mkrefany");
_dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.TypeHandleToRuntimeTypeHandle), "mkrefany");
_factory.MetadataManager.GetDependenciesDueToAccess(ref _dependencies, _factory, _methodIL, (TypeDesc)_canonMethodIL.GetObject(token));
ImportTypedRefOperationDependencies(token, "mkrefany");
}

Expand Down Expand Up @@ -960,6 +961,8 @@ private void ImportLdToken(int token)
}
}

_factory.MetadataManager.GetDependenciesDueToAccess(ref _dependencies, _factory, _methodIL, (TypeDesc)_canonMethodIL.GetObject(token));

_dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GetRuntimeTypeHandle), "ldtoken");
_dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.GetRuntimeType), "ldtoken");

Expand Down Expand Up @@ -1190,6 +1193,9 @@ private void AddBoxingDependencies(TypeDesc type, string reason)
if (!type.IsValueType)
return;

TypeDesc typeForAccessCheck = type.IsRuntimeDeterminedSubtype ? type.ConvertToCanonForm(CanonicalFormKind.Specific) : type;
_factory.MetadataManager.GetDependenciesDueToAccess(ref _dependencies, _factory, _methodIL, typeForAccessCheck);

if (type.IsRuntimeDeterminedSubtype)
{
_dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, type), reason);
Expand Down Expand Up @@ -1217,6 +1223,7 @@ private void ImportLeave(BasicBlock target)
private void ImportNewArray(int token)
{
var elementType = (TypeDesc)_methodIL.GetObject(token);
_factory.MetadataManager.GetDependenciesDueToAccess(ref _dependencies, _factory, _methodIL, (TypeDesc)_canonMethodIL.GetObject(token));
if (elementType.IsRuntimeDeterminedSubtype)
{
_dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.TypeHandle, elementType.MakeArrayType()), "newarr");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ public static void Main()
TestNoWarningsInRUCMethod<TestType>();
TestNoWarningsInRUCType<TestType, TestType>();
TestGenericParameterFlowsToNestedType.Test();

TestInstanceMethodOnValueType<object>();
TestValueTypeBox<object>();
TestMkrefAny<object>();
TestInArray<object>();
}

static void TestSingleGenericParameterOnType()
Expand Down Expand Up @@ -852,6 +857,58 @@ static void TestNoWarningsInRUCType<T, U>()
rucType.VirtualMethodRequiresPublicMethods<T>();
}

[ExpectedWarning("IL2091", "RequiresParameterlessCtor", Tool.Trimmer | Tool.NativeAot, "")]
[ExpectedWarning("IL2091", "RequiresParameterlessCtor", Tool.Trimmer, "")]
[ExpectedWarning("IL2091", "RequiresParameterlessCtor", Tool.Trimmer, "")]
static void TestInstanceMethodOnValueType<T>()
{
default(RequiresParameterlessCtor<T>).Do();
}

[ExpectedWarning("IL2091", "RequiresParameterlessCtor", Tool.Trimmer | Tool.NativeAot, "")]
[ExpectedWarning("IL2091", "RequiresParameterlessCtor", Tool.Trimmer, "")]
[ExpectedWarning("IL2091", "RequiresParameterlessCtor", Tool.Trimmer, "")]
[ExpectedWarning("IL2091", "IRequireParameterlessCtor", Tool.Trimmer, "")]
[ExpectedWarning("IL2091", "IRequireParameterlessCtor", Tool.Trimmer, "")]
static void TestValueTypeBox<T>()
{
if (default(RequiresParameterlessCtor<T>) is IRequireParameterlessCtor<T> i)
{
i.Do();
}
}

[ExpectedWarning("IL2091", "RequiresParameterlessCtor", Tool.Trimmer | Tool.NativeAot, "")]
[ExpectedWarning("IL2091", "RequiresParameterlessCtor", Tool.Trimmer, "")]
[ExpectedWarning("IL2091", "RequiresParameterlessCtor", Tool.Trimmer, "")]
static void TestMkrefAny<T>()
{
RequiresParameterlessCtor<T> val = default;
TypedReference tr = __makeref(val);
// This is a potential box operation, e.g. TypedReference.ToObject(tr);
}

[ExpectedWarning("IL2091", "RequiresParameterlessCtor", Tool.Trimmer | Tool.NativeAot, "")]
[ExpectedWarning("IL2091", "RequiresParameterlessCtor", Tool.Trimmer, "")]
static void TestInArray<T>()
{
var arr = new RequiresParameterlessCtor<T>[1];
// This is a potential box operation, e.g. arr.GetValue(0)
}

interface IRequireParameterlessCtor<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T>
{
T Do();
}

struct RequiresParameterlessCtor<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T> : IRequireParameterlessCtor<T>
{
public T Do()
{
return Activator.CreateInstance<T>();
}
}

class TestGenericParameterFlowsToNestedType
{
class Generic<T>
Expand Down
Loading