diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs index 77de311145e890..6b0753cfef025e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs @@ -592,10 +592,8 @@ 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); @@ -603,6 +601,13 @@ protected virtual void Scan(MethodIL methodBody, ref InterproceduralState interp 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. @@ -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; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs index 4738e0755bf08b..3483348d90155b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs @@ -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); @@ -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)) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index 515321b37e4974..ead8231abb9543 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -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) { } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 18fe157a668b3e..13c2e2ff9d713c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -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; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs index 362af202beb4ef..31c9035787ee48 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs @@ -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"); } @@ -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"); @@ -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); @@ -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"); diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs index 1a39904824fde1..51baf8335050f4 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs @@ -49,6 +49,11 @@ public static void Main() TestNoWarningsInRUCMethod(); TestNoWarningsInRUCType(); TestGenericParameterFlowsToNestedType.Test(); + + TestInstanceMethodOnValueType(); + TestValueTypeBox(); + TestMkrefAny(); + TestInArray(); } static void TestSingleGenericParameterOnType() @@ -852,6 +857,58 @@ static void TestNoWarningsInRUCType() rucType.VirtualMethodRequiresPublicMethods(); } + [ExpectedWarning("IL2091", "RequiresParameterlessCtor", Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning("IL2091", "RequiresParameterlessCtor", Tool.Trimmer, "")] + [ExpectedWarning("IL2091", "RequiresParameterlessCtor", Tool.Trimmer, "")] + static void TestInstanceMethodOnValueType() + { + default(RequiresParameterlessCtor).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() + { + if (default(RequiresParameterlessCtor) is IRequireParameterlessCtor i) + { + i.Do(); + } + } + + [ExpectedWarning("IL2091", "RequiresParameterlessCtor", Tool.Trimmer | Tool.NativeAot, "")] + [ExpectedWarning("IL2091", "RequiresParameterlessCtor", Tool.Trimmer, "")] + [ExpectedWarning("IL2091", "RequiresParameterlessCtor", Tool.Trimmer, "")] + static void TestMkrefAny() + { + RequiresParameterlessCtor 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() + { + var arr = new RequiresParameterlessCtor[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 + { + public T Do() + { + return Activator.CreateInstance(); + } + } + class TestGenericParameterFlowsToNestedType { class Generic