1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
using Xunit;
using CustomAttributeValue = System.Reflection.Metadata.CustomAttributeValue<Internal.TypeSystem.TypeDesc>;
namespace ILCompiler.Compiler.Tests
{
//
// This test uses IL scanner to scan a dependency graph, starting with a
// single method from the test assembly.
// It then checks various invariants about the resulting dependency graph.
// The test method declares these invariants using custom attributes.
//
// The invariants to check for are:
// * Whether an EEType was/was not generated
// * Whether a method body was/was not generated
// * Etc.
//
// The most valuable tests are the ones that check that something was not
// generated. These let us create unit tests for size on disk regressions.
//
public class DependencyGraphTests
{
public static IEnumerable<object[]> GetTestMethods()
{
var target = new TargetDetails(TargetArchitecture.X64, TargetOS.Windows, TargetAbi.CoreRT);
var context = new CompilerTypeSystemContext(target, SharedGenericsMode.CanonicalReferenceTypes);
context.InputFilePaths = new Dictionary<string, string> {
{ "Test.CoreLib", @"Test.CoreLib.dll" },
{ "ILCompiler.Compiler.Tests.Assets", @"ILCompiler.Compiler.Tests.Assets.dll" },
};
context.ReferenceFilePaths = new Dictionary<string, string>();
context.SetSystemModule(context.GetModuleForSimpleName("Test.CoreLib"));
var testModule = context.GetModuleForSimpleName("ILCompiler.Compiler.Tests.Assets");
bool foundSomethingToCheck = false;
foreach (var type in testModule.GetType("ILCompiler.Compiler.Tests.Assets", "DependencyGraph").GetNestedTypes())
{
foundSomethingToCheck = true;
yield return new object[] { type.GetMethod("Entrypoint", null) };
}
Assert.True(foundSomethingToCheck, "No methods to check?");
}
[Theory]
[MemberData(nameof(GetTestMethods))]
public void TestDependencyGraphInvariants(EcmaMethod method)
{
//
// Scan the input method
//
var context = (CompilerTypeSystemContext)method.Context;
CompilationModuleGroup compilationGroup = new SingleFileCompilationModuleGroup();
CompilationBuilder builder = new RyuJitCompilationBuilder(context, compilationGroup);
IILScanner scanner = builder.GetILScannerBuilder()
.UseCompilationRoots(new ICompilationRootProvider[] { new SingleMethodRootProvider(method) })
.ToILScanner();
ILScanResults results = scanner.Scan();
//
// Check invariants
//
const string assetsNamespace = "ILCompiler.Compiler.Tests.Assets";
bool foundSomethingToCheck = false;
foreach (var attr in method.GetDecodedCustomAttributes(assetsNamespace, "GeneratesConstructedEETypeAttribute"))
{
foundSomethingToCheck = true;
Assert.Contains((TypeDesc)attr.FixedArguments[0].Value, results.ConstructedEETypes);
}
foreach (var attr in method.GetDecodedCustomAttributes(assetsNamespace, "NoConstructedEETypeAttribute"))
{
foundSomethingToCheck = true;
Assert.DoesNotContain((TypeDesc)attr.FixedArguments[0].Value, results.ConstructedEETypes);
}
foreach (var attr in method.GetDecodedCustomAttributes(assetsNamespace, "GeneratesMethodBodyAttribute"))
{
foundSomethingToCheck = true;
MethodDesc methodToCheck = GetMethodFromAttribute(attr);
Assert.Contains(methodToCheck.GetCanonMethodTarget(CanonicalFormKind.Specific), results.CompiledMethodBodies);
}
foreach (var attr in method.GetDecodedCustomAttributes(assetsNamespace, "NoMethodBodyAttribute"))
{
foundSomethingToCheck = true;
MethodDesc methodToCheck = GetMethodFromAttribute(attr);
Assert.DoesNotContain(methodToCheck.GetCanonMethodTarget(CanonicalFormKind.Specific), results.CompiledMethodBodies);
}
//
// Make sure we checked something
//
Assert.True(foundSomethingToCheck, "No invariants to check?");
}
private static MethodDesc GetMethodFromAttribute(CustomAttributeValue attr)
{
if (attr.NamedArguments.Length > 0)
throw new NotImplementedException(); // TODO: parse sig and instantiation
return ((TypeDesc)attr.FixedArguments[0].Value).GetMethod((string)attr.FixedArguments[1].Value, null);
}
}
}
|