Skip to content

Commit 0310abf

Browse files
Port the check for illegal type recursion to the EnsureLoadableType implementation (#91707)
- Algorithm described in ECMA 335 II.9.2 Generics and recursive inheritance graphs) - Reuse the Tarjan algorithm logic for this
1 parent 5235a5d commit 0310abf

File tree

3 files changed

+168
-1
lines changed

3 files changed

+168
-1
lines changed

src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Validation.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using Internal.TypeSystem;
7+
using Internal.TypeSystem.Ecma;
78

89
using Debug = System.Diagnostics.Debug;
910

@@ -301,11 +302,20 @@ private static TypeDesc EnsureLoadableTypeUncached(TypeDesc type)
301302
// Validate classes, structs, enums, interfaces, and delegates
302303
Debug.Assert(type.IsDefType);
303304

304-
// Don't validate generic definitions
305+
// Don't validate generic definitions much other than by checking for illegal recursion.
305306
if (type.IsGenericDefinition)
306307
{
308+
// Check for illegal recursion
309+
if (type is EcmaType ecmaType && ILCompiler.LazyGenericsSupport.CheckForECMAIllegalGenericRecursion(ecmaType))
310+
{
311+
ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type);
312+
}
307313
return type;
308314
}
315+
else if (type.HasInstantiation)
316+
{
317+
((CompilerTypeSystemContext)type.Context).EnsureLoadableType(type.GetTypeDefinition());
318+
}
309319

310320
// System.__Canon or System.__UniversalCanon
311321
if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any))

src/coreclr/tools/Common/Compiler/GenericCycleDetection/GraphBuilder.cs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Collections.Generic;
45
using System.Reflection.Metadata;
56
using System.Reflection.Metadata.Ecma335;
67

@@ -14,6 +15,146 @@ internal static partial class LazyGenericsSupport
1415
{
1516
private sealed partial class GraphBuilder
1617
{
18+
// Generate the illegal recursion graph as specified in ECMA 335 II.9.2 Generics and recursive inheritance graphs
19+
public GraphBuilder(EcmaType type)
20+
{
21+
_graph = new Graph<EcmaGenericParameter>();
22+
23+
if (type.HasInstantiation)
24+
{
25+
HashSet<TypeDesc> types = new HashSet<TypeDesc>();
26+
List<TypeDesc> typesToProcess = new List<TypeDesc>();
27+
typesToProcess.Add(type);
28+
while (typesToProcess.Count > 0)
29+
{
30+
var processType = typesToProcess[typesToProcess.Count - 1];
31+
typesToProcess.RemoveAt(typesToProcess.Count - 1);
32+
33+
if (processType.IsParameterizedType)
34+
{
35+
if (processType.GetParameterType() is InstantiatedType instantiatedType)
36+
{
37+
AddProcessType(instantiatedType.GetTypeDefinition());
38+
WalkFormals(instantiatedType, expanded: true);
39+
}
40+
}
41+
else if (processType.GetTypeDefinition() == processType)
42+
{
43+
if (processType.HasInstantiation && processType is EcmaType ecmaProcessType)
44+
{
45+
AddSpecializedType(ecmaProcessType.BaseType);
46+
foreach (var t in ecmaProcessType.ExplicitlyImplementedInterfaces)
47+
{
48+
AddSpecializedType(t);
49+
}
50+
51+
void AddSpecializedType(TypeDesc typeToSpecialize)
52+
{
53+
if (typeToSpecialize != null)
54+
{
55+
var specializedType = typeToSpecialize.InstantiateSignature(processType.Instantiation, default(Instantiation));
56+
AddProcessType(specializedType);
57+
}
58+
}
59+
}
60+
}
61+
else if (processType is InstantiatedType instantiatedType)
62+
{
63+
AddProcessType(instantiatedType.GetTypeDefinition());
64+
WalkFormals(instantiatedType, expanded: false);
65+
}
66+
67+
void WalkFormals(InstantiatedType instantiatedType, bool expanded)
68+
{
69+
for (int i = 0; i < instantiatedType.Instantiation.Length; i++)
70+
{
71+
var formal = instantiatedType.GetTypeDefinition().Instantiation[i] as GenericParameterDesc;
72+
if (instantiatedType.Instantiation[i] is GenericParameterDesc genParam)
73+
{
74+
AddEdge(genParam, formal, expanded);
75+
}
76+
else
77+
{
78+
foreach (var genParameter in GetGenericParameters(instantiatedType.Instantiation[i]))
79+
{
80+
AddEdge(genParameter, formal, true);
81+
}
82+
}
83+
}
84+
}
85+
86+
void AddProcessType(TypeDesc type)
87+
{
88+
if (type == null)
89+
return;
90+
91+
if (types.Add(type))
92+
{
93+
typesToProcess.Add(type);
94+
}
95+
96+
if (type is ParameterizedType paramType)
97+
{
98+
AddProcessType(paramType.GetParameterType());
99+
}
100+
101+
foreach (var instType in type.Instantiation)
102+
{
103+
AddProcessType(instType);
104+
}
105+
}
106+
107+
void AddEdge(GenericParameterDesc from, GenericParameterDesc to, bool flagged)
108+
{
109+
EcmaGenericParameter fromEcma = from as EcmaGenericParameter;
110+
EcmaGenericParameter toEcma = to as EcmaGenericParameter;
111+
if (fromEcma == null || toEcma == null)
112+
return;
113+
_graph.AddEdge(fromEcma, toEcma, flagged);
114+
}
115+
116+
IEnumerable<GenericParameterDesc> GetGenericParameters(TypeDesc t)
117+
{
118+
if (t is GenericParameterDesc genParamDesc)
119+
{
120+
yield return genParamDesc;
121+
}
122+
else if (t is ParameterizedType paramType)
123+
{
124+
foreach (var genParamType in GetGenericParameters(paramType.GetParameterType()))
125+
{
126+
yield return genParamType;
127+
}
128+
}
129+
else if (t.HasInstantiation)
130+
{
131+
foreach (var instType in t.Instantiation)
132+
{
133+
foreach (var genParamType in GetGenericParameters(instType))
134+
{
135+
yield return genParamType;
136+
}
137+
}
138+
}
139+
else if (t is FunctionPointerType fptrType)
140+
{
141+
foreach (var genParamType in GetGenericParameters(fptrType.Signature.ReturnType))
142+
{
143+
yield return genParamType;
144+
}
145+
foreach (var parameterType in fptrType.Signature)
146+
{
147+
foreach (var genParamType in GetGenericParameters(parameterType))
148+
{
149+
yield return genParamType;
150+
}
151+
}
152+
}
153+
}
154+
}
155+
}
156+
}
157+
17158
public GraphBuilder(EcmaModule assembly)
18159
{
19160
_graph = new Graph<EcmaGenericParameter>();

src/coreclr/tools/Common/Compiler/GenericCycleDetection/ModuleCycleInfo.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,22 @@ protected override ModuleCycleInfo CreateValueFromKey(EcmaModule key)
7272
}
7373
}
7474

75+
internal static bool CheckForECMAIllegalGenericRecursion(EcmaType type)
76+
{
77+
GraphBuilder gb = new GraphBuilder(type);
78+
Graph<EcmaGenericParameter> graph = gb.Graph;
79+
80+
var flaggedCycleData = graph.ComputeVerticesInvolvedInAFlaggedCycle();
81+
82+
foreach (var _ in flaggedCycleData)
83+
{
84+
// If the list isn't empty, there is an illegal generic recursion
85+
return true;
86+
}
87+
88+
return false;
89+
}
90+
7591
internal sealed class GenericCycleDetector
7692
{
7793
private readonly CycleInfoHashtable _hashtable = new CycleInfoHashtable();

0 commit comments

Comments
 (0)