diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionInvokeSupportDependencyAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionInvokeSupportDependencyAlgorithm.cs new file mode 100644 index 00000000000000..4e8ffb80febb79 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionInvokeSupportDependencyAlgorithm.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection.Metadata; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; + +namespace ILCompiler.DependencyAnalysis +{ + internal static class ReflectionInvokeSupportDependencyAlgorithm + { + // Inserts dependencies to make the following corner case work (we need MethodTable for `MyStruct[]`): + // + // struct MyStruct + // { + // public static int Count(params MyStruct[] myStructs) + // { + // return myStructs.Length; + // } + // + // public static void Main() + // { + // typeof(MyStruct).InvokeMember(nameof(Count), BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, new object[] { default(MyStruct) }); + // } + // } + public static void GetDependenciesFromParamsArray(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) + { + MethodSignature sig = method.Signature; + if (sig.Length < 1 || !sig[sig.Length - 1].IsArray) + return; + + if (method.GetTypicalMethodDefinition() is not EcmaMethod ecmaMethod) + return; + + MetadataReader reader = ecmaMethod.MetadataReader; + MethodDefinition methodDef = reader.GetMethodDefinition(ecmaMethod.Handle); + + foreach (ParameterHandle paramHandle in methodDef.GetParameters()) + { + Parameter param = reader.GetParameter(paramHandle); + if (param.SequenceNumber == sig.Length /* SequenceNumber is 1-based */) + { + if (!reader.GetCustomAttributeHandle(param.GetCustomAttributes(), "System", "ParamArrayAttribute").IsNil) + { + dependencies ??= new DependencyList(); + dependencies.Add( + factory.ConstructedTypeSymbol(sig[sig.Length - 1].NormalizeInstantiation()), + "Reflection invoke"); + } + + break; + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index 837171d9059c1c..d908623c91c4ef 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -332,6 +332,8 @@ public void GetDependenciesDueToReflectability(ref DependencyList dependencies, { // We're going to generate a mapping table entry for this. Collect dependencies. ReflectionInvokeMapNode.AddDependenciesDueToReflectability(ref dependencies, factory, method); + + ReflectionInvokeSupportDependencyAlgorithm.GetDependenciesFromParamsArray(ref dependencies, factory, method); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index af1e7859356933..c37b562a1383a2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -204,6 +204,7 @@ + diff --git a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs index bf82a2657252d2..ee88635a8b65d1 100644 --- a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs +++ b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs @@ -59,6 +59,7 @@ private static int Main() TestGetUninitializedObject.Run(); TestInstanceFields.Run(); TestReflectionInvoke.Run(); + TestInvokeMemberParamsCornerCase.Run(); TestDefaultInterfaceInvoke.Run(); TestCovariantReturnInvoke.Run(); #if !CODEGEN_CPP @@ -264,6 +265,26 @@ public static unsafe void Run() } } + class TestInvokeMemberParamsCornerCase + { + public struct MyStruct { } + + public static int Count(params MyStruct[] myStructs) + { + return myStructs.Length; + } + + public static void Run() + { + Console.WriteLine(nameof(TestInvokeMemberParamsCornerCase)); + + // Needs MethodTable for MyStruct[] and the compiler should have created it. + typeof(TestInvokeMemberParamsCornerCase).InvokeMember(nameof(Count), + BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, + null, null, new object[] { default(MyStruct) }); + } + } + class TestDefaultInterfaceInvoke { interface IFoo