Fix CustomAttributeData in the presence of generic attributes (#56879)
authorDavid Wrighton <davidwr@microsoft.com>
Thu, 5 Aug 2021 19:34:21 +0000 (12:34 -0700)
committerGitHub <noreply@github.com>
Thu, 5 Aug 2021 19:34:21 +0000 (12:34 -0700)
* Fix CustomAttributeData in the presence of generic attributes
- Generic attributes need to force the actual exact method to be loaded not just the canonical scenario
- GenericAttribute testing had been disabled due to dotnet/msbuild#6734
- Move GenericAttribute test project to Pri0 as its the only generic custom attribute runtime testing that will occur before .NET 6.0 ships
- Test disabled on Mono as Mono currently doesn't support this feature.

Fixes #56492

src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs
src/tests/Common/dirs.proj
src/tests/issues.targets
src/tests/reflection/GenericAttribute/GenericAttributeMetadata.il
src/tests/reflection/GenericAttribute/GenericAttributeTests.cs
src/tests/reflection/GenericAttribute/GenericAttributeTests.csproj

index 4bdc413..edf7aeb 100644 (file)
@@ -273,6 +273,13 @@ namespace System.Reflection
             m_scope = scope;
             m_ctor = (RuntimeConstructorInfo)RuntimeType.GetMethodBase(scope, caCtorToken)!;
 
+            if (m_ctor!.DeclaringType!.IsGenericType)
+            {
+                MetadataImport metadataScope = scope.MetadataImport;
+                var attributeType = scope.ResolveType(metadataScope.GetParentToken(caCtorToken), null, null)!;
+                m_ctor = (RuntimeConstructorInfo)scope.ResolveMethod(caCtorToken, attributeType.GenericTypeArguments, null)!.MethodHandle.GetMethodInfo();
+            }
+
             ParameterInfo[] parameters = m_ctor.GetParametersNoCopy();
             m_ctorParams = new CustomAttributeCtorParameter[parameters.Length];
             for (int i = 0; i < parameters.Length; i++)
index 2dd8be1..6766e6b 100644 (file)
@@ -16,7 +16,6 @@
       <DisabledProjects Include="$(TestRoot)Loader\classloader\generics\GenericMethods\VSW491668.csproj" /> <!-- issue 5501 -->
       <DisabledProjects Include="$(TestRoot)Loader\classloader\StaticVirtualMethods\**\generatetest.csproj" /> <!-- test generators -->
       <DisabledProjects Include="$(TestRoot)Performance\Scenario\JitBench\unofficial_dotnet\JitBench.csproj" /> <!-- no official build support for SDK-style netcoreapp2.0 projects -->
-      <DisabledProjects Include="$(TestRoot)reflection\GenericAttribute\GenericAttributeTests.csproj" />
       <DisabledProjects Include="$(TestRoot)TestWrappers*\**\*.csproj" />
     </ItemGroup>
 
index afeb0dd..828fb48 100644 (file)
 
     <!-- Known failures for mono runtime on *all* architectures/operating systems in *all* runtime modes -->
     <ItemGroup Condition="'$(RuntimeFlavor)' == 'mono'" >
+        <ExcludeList Include = "$(XunitTestBinBase)/reflection/GenericAttribute/**">
+            <Issue>https://github.com/dotnet/runtime/issues/56887</Issue>
+        </ExcludeList>
         <ExcludeList Include = "$(XunitTestBinBase)/JIT/HardwareIntrinsics/X86/Sse2.X64/StoreNonTemporal_r/**">
             <Issue>https://github.com/dotnet/runtime/issues/54176</Issue>
         </ExcludeList>
index 71d5b16..ca83e92 100644 (file)
 
   // --- The following custom attribute is added automatically, do not uncomment -------
   //  .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) 
-
+/*  Re-enable once the fix to https://github.com/dotnet/msbuild/issues/6734 propagates to this repo.
   .custom instance void class SingleAttribute`1<int32>::.ctor() = ( 01 00 00 00 ) 
   .custom instance void class SingleAttribute`1<bool>::.ctor() = ( 01 00 00 00 ) 
   .custom instance void class MultiAttribute`1<int32>::.ctor() = ( 01 00 00 00 ) 
   .custom instance void class MultiAttribute`1<int32>::.ctor(!0) = ( 01 00 01 00 00 00 00 00 ) 
   .custom instance void class MultiAttribute`1<int32>::.ctor() = ( 01 00 01 00 54 08 05 56 61 6C 75 65 02 00 00 00 ) // ....T..Value....
   .custom instance void class MultiAttribute`1<bool>::.ctor() = ( 01 00 00 00 ) 
-  .custom instance void class MultiAttribute`1<bool>::.ctor(!0) = ( 01 00 01 00 00 ) 
+  .custom instance void class MultiAttribute`1<bool>::.ctor(!0) = ( 01 00 01 00 00 ) */
   .hash algorithm 0x00008004
   .ver 0:0:0:0
 }
index f930570..e7661d2 100644 (file)
@@ -8,6 +8,7 @@ class Program
 {
     static int Main(string[] args)
     {
+/* Re-enable once the fix to https://github.com/dotnet/msbuild/issues/6734 propagates to this repo.
         Assembly assembly = typeof(Class).GetTypeInfo().Assembly;
         Assert(CustomAttributeExtensions.GetCustomAttribute<SingleAttribute<int>>(assembly) != null);
         Assert(((ICustomAttributeProvider)assembly).GetCustomAttributes(typeof(SingleAttribute<int>), true) != null);
@@ -18,6 +19,8 @@ class Program
         Assert(CustomAttributeExtensions.IsDefined(assembly, typeof(SingleAttribute<bool>)));
         Assert(((ICustomAttributeProvider)assembly).IsDefined(typeof(SingleAttribute<bool>), true));
 
+*/
+
         TypeInfo programTypeInfo = typeof(Class).GetTypeInfo();
         Assert(CustomAttributeExtensions.GetCustomAttribute<SingleAttribute<int>>(programTypeInfo) != null);
         Assert(((ICustomAttributeProvider)programTypeInfo).GetCustomAttributes(typeof(SingleAttribute<int>), true) != null);
@@ -153,6 +156,24 @@ class Program
         Assert(CustomAttributeExtensions.GetCustomAttributes(programTypeInfo, typeof(MultiAttribute<>), true) == null);
         Assert(!((ICustomAttributeProvider)programTypeInfo).GetCustomAttributes(typeof(MultiAttribute<>), true).GetEnumerator().MoveNext());
 
+        // Test coverage for CustomAttributeData api surface
+        var a1_data = CustomAttributeData.GetCustomAttributes(programTypeInfo);
+        AssertAny(a1_data, a => a.AttributeType == typeof(SingleAttribute<int>));
+        AssertAny(a1_data, a => a.AttributeType == typeof(SingleAttribute<bool>));
+
+        AssertAny(a1_data, a => a.AttributeType == typeof(MultiAttribute<int>) && a.ConstructorArguments.Count == 0 && a.NamedArguments.Count == 0);
+        AssertAny(a1_data, a => a.AttributeType == typeof(MultiAttribute<int>) && a.ConstructorArguments.Count == 1 && a.NamedArguments.Count == 0 && a.ConstructorArguments[0].ArgumentType == typeof(int) &&  ((int)a.ConstructorArguments[0].Value) == 1);
+        AssertAny(a1_data, a => a.AttributeType == typeof(MultiAttribute<int>) && a.ConstructorArguments.Count == 0 && a.NamedArguments.Count == 1 && a.NamedArguments[0].TypedValue.ArgumentType == typeof(int) &&  ((int)a.NamedArguments[0].TypedValue.Value) == 2);
+
+        AssertAny(a1_data, a => a.AttributeType == typeof(MultiAttribute<bool>) && a.ConstructorArguments.Count == 0 && a.NamedArguments.Count == 0);
+        AssertAny(a1_data, a => a.AttributeType == typeof(MultiAttribute<bool>) && a.ConstructorArguments.Count == 1 && a.NamedArguments.Count == 0 && a.ConstructorArguments[0].ArgumentType == typeof(bool) &&  ((bool)a.ConstructorArguments[0].Value) == true);
+        AssertAny(a1_data, a => a.AttributeType == typeof(MultiAttribute<bool>) && a.ConstructorArguments.Count == 0 && a.NamedArguments.Count == 1 && a.NamedArguments[0].TypedValue.ArgumentType == typeof(bool) &&  ((bool)a.NamedArguments[0].TypedValue.Value) == true);
+
+        AssertAny(a1_data, a => a.AttributeType == typeof(MultiAttribute<bool?>) && a.ConstructorArguments.Count == 0 && a.NamedArguments.Count == 0);
+
+        AssertAny(a1_data, a => a.AttributeType == typeof(MultiAttribute<Type>) && a.ConstructorArguments.Count == 1 && a.NamedArguments.Count == 0 && a.ConstructorArguments[0].ArgumentType == typeof(Type) &&  ((Type)a.ConstructorArguments[0].Value) == typeof(Class));
+        AssertAny(a1_data, a => a.AttributeType == typeof(MultiAttribute<Type>) && a.ConstructorArguments.Count == 0 && a.NamedArguments.Count == 1 && a.NamedArguments[0].TypedValue.ArgumentType == typeof(Type) &&  ((Type)a.NamedArguments[0].TypedValue.Value) == typeof(Class.Derive));
+
         return 100;
     }
 
@@ -176,4 +197,17 @@ class Program
         }
         throw new Exception($"Error in line: {line}");
     }
+
+    static void AssertAny(IEnumerable<CustomAttributeData> source, Func<CustomAttributeData, bool> condition, int count = 1, [CallerLineNumberAttribute]int line = 0)
+    {
+        var enumerator = source.GetEnumerator();
+        while (enumerator.MoveNext())
+        {
+            if(condition(enumerator.Current) && --count == 0)
+            {
+                return;
+            }
+        }
+        throw new Exception($"Error in line: {line}");
+    }
 }
index 79cbc4a..4ca4f7b 100644 (file)
@@ -3,7 +3,6 @@
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <OutputType>Exe</OutputType>
     <CLRTestKind>BuildAndRun</CLRTestKind>
-    <CLRTestPriority>1</CLRTestPriority>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="GenericAttributeTests.cs" />