/// <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;
 }
Esempio n. 5
0
 internal void AddDependency(SourceFieldSymbolWithSyntaxReference field)
 {
     _dependencies.Add(field);
 }