/// <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 SourceFieldSymbol field, ArrayBuilder <FieldInfo> order, bool earlyDecodingWellKnownAttributes) { Debug.Assert(order.Count == 0); var graph = PooledDictionary <SourceFieldSymbol, Node <SourceFieldSymbol> > .GetInstance(); CreateGraph(graph, field, earlyDecodingWellKnownAttributes); Debug.Assert(graph.Count >= 1); CheckGraph(graph); #if DEBUG var fields = ArrayBuilder <SourceFieldSymbol> .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 <SourceFieldSymbol>(order.Select(o => o.Field).Distinct()); Debug.Assert(fields.All(f => map.Contains(f))); fields.Free(); #endif graph.Free(); }
public static ConstantValue EvaluateFieldConstant( SourceFieldSymbol symbol, EqualsValueClauseSyntax equalsValueNode, HashSet <SourceFieldSymbolWithSyntaxReference> dependencies, bool earlyDecodingWellKnownAttributes, BindingDiagnosticBag diagnostics) { var compilation = symbol.DeclaringCompilation; var binderFactory = compilation.GetBinderFactory((SyntaxTree)symbol.Locations[0].SourceTree); var binder = binderFactory.GetBinder(equalsValueNode); if (earlyDecodingWellKnownAttributes) { binder = new EarlyWellKnownAttributeBinder(binder); } var inProgressBinder = new ConstantFieldsInProgressBinder(new ConstantFieldsInProgress(symbol, dependencies), binder); BoundFieldEqualsValue boundValue = BindFieldOrEnumInitializer(inProgressBinder, symbol, equalsValueNode, diagnostics); var initValueNodeLocation = equalsValueNode.Value.Location; var value = GetAndValidateConstantValue(boundValue.Value, symbol, symbol.Type, initValueNodeLocation, diagnostics); Debug.Assert(value != null); return(value); }
private static void GetAllReachable( Dictionary <SourceFieldSymbol, Node <SourceFieldSymbol> > graph, ArrayBuilder <SourceFieldSymbol> fields, SourceFieldSymbol field) { Debug.Assert(fields.Count == 0); var set = PooledHashSet <SourceFieldSymbol> .GetInstance(); var stack = ArrayBuilder <SourceFieldSymbol> .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(); }
internal ConstantFieldsInProgress( SourceFieldSymbol fieldOpt, HashSet<SourceFieldSymbol> dependencies) { this.fieldOpt = fieldOpt; this.dependencies = dependencies; }
internal ConstantFieldsInProgress( SourceFieldSymbol fieldOpt, HashSet<SourceFieldSymbolWithSyntaxReference> dependencies) { _fieldOpt = fieldOpt; _dependencies = dependencies; }
/// <summary> /// Build a dependency graph (a map from /// field to dependencies). /// </summary> private static void CreateGraph( Dictionary <SourceFieldSymbol, Node <SourceFieldSymbol> > graph, SourceFieldSymbol field, bool earlyDecodingWellKnownAttributes) { var pending = ArrayBuilder <SourceFieldSymbol> .GetInstance(); pending.Push(field); while (pending.Count > 0) { field = pending.Pop(); Node <SourceFieldSymbol> node; if (graph.TryGetValue(field, out node)) { if (node.Dependencies != null) { // Already visited node. continue; } } else { node = new Node <SourceFieldSymbol>(); node.DependedOnBy = ImmutableHashSet <SourceFieldSymbol> .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 <SourceFieldSymbol>(); node.DependedOnBy = ImmutableHashSet <SourceFieldSymbol> .Empty; } node.DependedOnBy = node.DependedOnBy.Add(field); graph[dependency] = node; } } pending.Free(); }
public static ConstantValue EvaluateFieldConstant( SourceFieldSymbol symbol, EqualsValueClauseSyntax equalsValueNode, HashSet<SourceFieldSymbolWithSyntaxReference> dependencies, bool earlyDecodingWellKnownAttributes, DiagnosticBag diagnostics) { var compilation = symbol.DeclaringCompilation; var binderFactory = compilation.GetBinderFactory((SyntaxTree)symbol.Locations[0].SourceTree); var binder = binderFactory.GetBinder(equalsValueNode); if (earlyDecodingWellKnownAttributes) { binder = new EarlyWellKnownAttributeBinder(binder); } var inProgressBinder = new ConstantFieldsInProgressBinder(new ConstantFieldsInProgress(symbol, dependencies), binder); var boundValue = BindFieldOrEnumInitializer(inProgressBinder, symbol, equalsValueNode, diagnostics); var initValueNodeLocation = equalsValueNode.Value.Location; var value = GetAndValidateConstantValue(boundValue, symbol, symbol.Type, initValueNodeLocation, diagnostics); Debug.Assert(value != null); return value; }
public FieldInfo(SourceFieldSymbol field, bool startsCycle) { this.Field = field; this.StartsCycle = startsCycle; }
internal void AddDependency(SourceFieldSymbol field) { this.dependencies.Add(field); }