/// <summary> /// Generate a list containing the given field and all dependencies /// of that field that require evaluation. The list is ordered by /// dependencies, with fields with no dependencies first. Cycles are /// broken at the first field lexically in the cycle. If multiple threads /// call this method with the same field, the order of the fields /// returned should be the same, although some fields may be missing /// from the lists in some threads as other threads evaluate fields. /// </summary> internal static void OrderAllDependencies( this SourceFieldSymbolWithSyntaxReference field, ArrayBuilder <FieldInfo> order, bool earlyDecodingWellKnownAttributes) { Debug.Assert(order.Count == 0); var graph = PooledDictionary <SourceFieldSymbolWithSyntaxReference, Node <SourceFieldSymbolWithSyntaxReference> > .GetInstance(); CreateGraph(graph, field, earlyDecodingWellKnownAttributes); Debug.Assert(graph.Count >= 1); CheckGraph(graph); #if DEBUG var fields = ArrayBuilder <SourceFieldSymbolWithSyntaxReference> .GetInstance(); fields.AddRange(graph.Keys); #endif OrderGraph(graph, order); #if DEBUG // Verify all entries in the graph are in the ordered list. var map = new HashSet <SourceFieldSymbolWithSyntaxReference>(order.Select(o => o.Field).Distinct()); Debug.Assert(fields.All(f => map.Contains(f))); fields.Free(); #endif graph.Free(); }
private static void GetAllReachable( Dictionary <SourceFieldSymbolWithSyntaxReference, Node <SourceFieldSymbolWithSyntaxReference> > graph, ArrayBuilder <SourceFieldSymbolWithSyntaxReference> fields, SourceFieldSymbolWithSyntaxReference field) { Debug.Assert(fields.Count == 0); var set = PooledHashSet <SourceFieldSymbolWithSyntaxReference> .GetInstance(); var stack = ArrayBuilder <SourceFieldSymbolWithSyntaxReference> .GetInstance(); set.Add(field); stack.Push(field); while (stack.Count > 0) { field = stack.Pop(); var node = graph[field]; foreach (var dependency in node.Dependencies) { if (!set.Contains(dependency)) { set.Add(dependency); stack.Push(dependency); } } } fields.AddRange(set); stack.Free(); set.Free(); }
/// <summary> /// Build a dependency graph (a map from /// field to dependencies). /// </summary> private static void CreateGraph( Dictionary <SourceFieldSymbolWithSyntaxReference, Node <SourceFieldSymbolWithSyntaxReference> > graph, SourceFieldSymbolWithSyntaxReference field, bool earlyDecodingWellKnownAttributes) { var pending = ArrayBuilder <SourceFieldSymbolWithSyntaxReference> .GetInstance(); pending.Push(field); while (pending.Count > 0) { field = pending.Pop(); Node <SourceFieldSymbolWithSyntaxReference> node; if (graph.TryGetValue(field, out node)) { if (node.Dependencies != null) { // Already visited node. continue; } } else { node = new Node <SourceFieldSymbolWithSyntaxReference>(); node.DependedOnBy = ImmutableHashSet <SourceFieldSymbolWithSyntaxReference> .Empty; } var dependencies = field.GetConstantValueDependencies(earlyDecodingWellKnownAttributes); // GetConstantValueDependencies will return an empty set if // the constant value has already been calculated. That avoids // calculating the full graph repeatedly. For instance with // "enum E { M0 = 0, M1 = M0 + 1, ..., Mn = Mn-1 + 1 }", we'll calculate // the graph M0, ..., Mi for the first field we evaluate, Mi. But for // the next field, Mj, we should only calculate the graph Mi, ..., Mj. node.Dependencies = dependencies; graph[field] = node; foreach (var dependency in dependencies) { pending.Push(dependency); if (!graph.TryGetValue(dependency, out node)) { node = new Node <SourceFieldSymbolWithSyntaxReference>(); node.DependedOnBy = ImmutableHashSet <SourceFieldSymbolWithSyntaxReference> .Empty; } node.DependedOnBy = node.DependedOnBy.Add(field); graph[dependency] = node; } } pending.Free(); }
public FieldInfo(SourceFieldSymbolWithSyntaxReference field, bool startsCycle) { this.Field = field; this.StartsCycle = startsCycle; }
internal void AddDependency(SourceFieldSymbolWithSyntaxReference field) { _dependencies.Add(field); }