Skip to content
This repository was archived by the owner on Apr 14, 2022. It is now read-only.

Commit 2e3c1c9

Browse files
Fix 1091: NRE in TyCreateWalkingGraph (#1105)
1 parent 09dc4a0 commit 2e3c1c9

3 files changed

Lines changed: 115 additions & 9 deletions

File tree

src/Analysis/Ast/Impl/Dependencies/DependencyResolver.cs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,33 @@ public int Remove(TKey key) {
6868
return _version;
6969
}
7070

71-
Interlocked.Increment(ref _version);
71+
var version = Interlocked.Increment(ref _version);
7272

73+
var vertex = _vertices[index];
7374
_vertices[index] = default;
74-
return _version;
75+
if (vertex == null) {
76+
return version;
77+
}
78+
79+
foreach (var incomingIndex in vertex.Incoming) {
80+
var incoming = _vertices[incomingIndex];
81+
if (incoming != null && incoming.IsSealed) {
82+
_vertices[incomingIndex] = new DependencyVertex<TKey, TValue>(incoming, version, false);
83+
}
84+
}
85+
86+
if (!vertex.IsSealed) {
87+
return version;
88+
}
89+
90+
foreach (var outgoingIndex in vertex.Outgoing) {
91+
var outgoing = _vertices[outgoingIndex];
92+
if (outgoing != null && !outgoing.IsNew) {
93+
_vertices[outgoingIndex] = new DependencyVertex<TKey, TValue>(outgoing, version, true);
94+
}
95+
}
96+
97+
return version;
7598
}
7699
}
77100

@@ -135,7 +158,7 @@ private ImmutableArray<int> EnsureKeys(int index, ImmutableArray<TKey> keys, int
135158
} else {
136159
var vertex = _vertices[keyIndex];
137160
if (vertex != default && vertex.IsSealed && !vertex.ContainsOutgoing(index)) {
138-
_vertices[keyIndex] = new DependencyVertex<TKey, TValue>(vertex, version);
161+
_vertices[keyIndex] = new DependencyVertex<TKey, TValue>(vertex, version, false);
139162
}
140163
}
141164

@@ -263,13 +286,14 @@ private bool TryBuildReverseGraph(ImmutableArray<DependencyVertex<TKey, TValue>>
263286
private bool TryCreateWalkingGraph(in ImmutableArray<DependencyVertex<TKey, TValue>> vertices, int version, out ImmutableArray<WalkingVertex<TKey, TValue>> analysisGraph) {
264287
var nodesByVertexIndex = new Dictionary<int, WalkingVertex<TKey, TValue>>();
265288

266-
foreach (var vertex in vertices) {
289+
for (var index = 0; index < vertices.Count; index++) {
290+
var vertex = vertices[index];
267291
if (vertex == null || vertex.IsWalked) {
268292
continue;
269293
}
270294

271-
var node = new WalkingVertex<TKey, TValue>(vertices[vertex.Index]);
272-
nodesByVertexIndex[vertex.Index] = node;
295+
var node = new WalkingVertex<TKey, TValue>(vertices[index]);
296+
nodesByVertexIndex[index] = node;
273297
}
274298

275299
if (nodesByVertexIndex.Count == 0) {
@@ -288,9 +312,12 @@ private bool TryCreateWalkingGraph(in ImmutableArray<DependencyVertex<TKey, TVal
288312
foreach (var outgoingIndex in node.DependencyVertex.Outgoing) {
289313
if (!nodesByVertexIndex.TryGetValue(outgoingIndex, out var outgoingNode)) {
290314
var vertex = vertices[outgoingIndex];
315+
if (vertex == null) {
316+
continue;
317+
}
318+
291319
outgoingNode = new WalkingVertex<TKey, TValue>(vertex);
292320
nodesByVertexIndex[outgoingIndex] = outgoingNode;
293-
294321
queue.Enqueue(outgoingNode);
295322
}
296323

src/Analysis/Ast/Impl/Dependencies/DependencyVertex.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ internal sealed class DependencyVertex<TKey, TValue> {
2929
public int Index { get; }
3030
public string DebuggerDisplay => $"{Key}:{Value}";
3131

32+
public bool IsNew => _state == (int)State.New;
3233
public bool IsSealed => _state >= (int)State.Sealed;
3334
public bool IsWalked => _state == (int)State.Walked;
3435

@@ -38,7 +39,7 @@ internal sealed class DependencyVertex<TKey, TValue> {
3839
private HashSet<int> _outgoing;
3940
private static HashSet<int> _empty = new HashSet<int>();
4041

41-
public DependencyVertex(DependencyVertex<TKey, TValue> oldVertex, int version) {
42+
public DependencyVertex(DependencyVertex<TKey, TValue> oldVertex, int version, bool isNew) {
4243
Key = oldVertex.Key;
4344
Value = oldVertex.Value;
4445
IsRoot = oldVertex.IsRoot;
@@ -48,7 +49,7 @@ public DependencyVertex(DependencyVertex<TKey, TValue> oldVertex, int version) {
4849
Version = version;
4950

5051
_outgoing = oldVertex.Outgoing;
51-
_state = oldVertex.IsWalked ? (int)State.ChangedOutgoing : (int)State.New;
52+
_state = !isNew && oldVertex.IsWalked ? (int)State.ChangedOutgoing : (int)State.New;
5253
}
5354

5455
public DependencyVertex(TKey key, TValue value, bool isRoot, ImmutableArray<int> incoming, int version, int index) {

src/Analysis/Ast/Test/DependencyResolverTests.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,84 @@ public async Task ChangeValue_MissingKeys() {
177177
result.ToString().Should().Be("AD");
178178
}
179179

180+
[TestMethod]
181+
public async Task ChangeValue_Add() {
182+
var resolver = new DependencyResolver<string, string>();
183+
resolver.ChangeValue("A", "A:BD", true, "B", "D");
184+
resolver.ChangeValue("C", "C", false);
185+
186+
var walker = resolver.CreateWalker();
187+
walker.MissingKeys.Should().Equal("B", "D");
188+
var node1 = await walker.GetNextAsync(default);
189+
var node2 = await walker.GetNextAsync(default);
190+
node1.Value.Should().Be("A:BD");
191+
node2.Value.Should().Be("C");
192+
node1.Commit();
193+
node2.Commit();
194+
195+
walker.Remaining.Should().Be(0);
196+
197+
resolver.ChangeValue("B", "B", false);
198+
walker = resolver.CreateWalker();
199+
walker.MissingKeys.Should().Equal("D");
200+
201+
var node = await walker.GetNextAsync(default);
202+
node.Value.Should().Be("B");
203+
node.Commit();
204+
205+
node = await walker.GetNextAsync(default);
206+
node.Value.Should().Be("A:BD");
207+
node.Commit();
208+
209+
walker.Remaining.Should().Be(0);
210+
211+
resolver.ChangeValue("D", "D:C", false);
212+
walker = resolver.CreateWalker();
213+
walker.MissingKeys.Should().BeEmpty();
214+
215+
node = await walker.GetNextAsync(default);
216+
node.Value.Should().Be("D:C");
217+
node.Commit();
218+
219+
node = await walker.GetNextAsync(default);
220+
node.Value.Should().Be("A:BD");
221+
node.Commit();
222+
223+
walker.Remaining.Should().Be(0);
224+
}
225+
226+
[TestMethod]
227+
public async Task ChangeValue_Remove() {
228+
var resolver = new DependencyResolver<string, string>();
229+
resolver.ChangeValue("A", "A:BC", true, "B", "C");
230+
resolver.ChangeValue("B", "B:C", false, "C");
231+
resolver.ChangeValue("C", "C", false);
232+
233+
var walker = resolver.CreateWalker();
234+
walker.MissingKeys.Should().BeEmpty();
235+
var node = await walker.GetNextAsync(default);
236+
node.Value.Should().Be("C");
237+
node.Commit();
238+
239+
node = await walker.GetNextAsync(default);
240+
node.Value.Should().Be("B:C");
241+
node.Commit();
242+
243+
node = await walker.GetNextAsync(default);
244+
node.Value.Should().Be("A:BC");
245+
node.Commit();
246+
247+
resolver.Remove("B");
248+
walker = resolver.CreateWalker();
249+
walker.MissingKeys.Should().Equal("B");
250+
251+
node = await walker.GetNextAsync(default);
252+
node.Value.Should().Be("A:BC");
253+
node.Commit();
254+
255+
walker.Remaining.Should().Be(0);
256+
}
257+
180258
[TestMethod]
181259
public async Task ChangeValue_RemoveKeys() {
182260
var resolver = new DependencyResolver<string, string>();

0 commit comments

Comments
 (0)