SymbolTreeInfo_Source.cs 6.1 KB
Newer Older
1 2
using System;
using System.Collections.Generic;
C
CyrusNajmabadi 已提交
3
using System.Collections.Immutable;
4 5
using System.Threading;
using System.Threading.Tasks;
6
using Microsoft.CodeAnalysis.Collections;
7
using Microsoft.CodeAnalysis.Serialization;
8 9 10 11 12 13
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.FindSymbols
{
    internal partial class SymbolTreeInfo
    {
14 15 16 17 18 19 20 21 22 23 24 25 26 27
        private static SimplePool<MultiDictionary<string, ISymbol>> s_symbolMapPool =
            new SimplePool<MultiDictionary<string, ISymbol>>(() => new MultiDictionary<string, ISymbol>());

        private static MultiDictionary<string, ISymbol> AllocateSymbolMap()
        {
            return s_symbolMapPool.Allocate();
        }

        private static void FreeSymbolMap(MultiDictionary<string, ISymbol> symbolMap)
        {
            symbolMap.Clear();
            s_symbolMapPool.Free(symbolMap);
        }

28
        public static async Task<SymbolTreeInfo> GetInfoForSourceAssemblyAsync(
29
            Project project, Checksum checksum, CancellationToken cancellationToken)
30 31
        {
            var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
32

33 34 35 36 37 38 39
            return await LoadOrCreateSourceSymbolTreeInfoAsync(
                project.Solution, compilation.Assembly, checksum, project.FilePath,
                loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false);
        }

        public static async Task<Checksum> GetSourceSymbolsChecksumAsync(Project project, CancellationToken cancellationToken)
        {
C
CyrusNajmabadi 已提交
40 41 42 43 44 45 46
            // The SymbolTree for source is built from the source-symbols from the project's compilation's
            // assembly.  Specifically, we only get the name, kind and parent/child relationship of all the
            // child symbols.  So we want to be able to reuse the index as long as none of these have 
            // changed.  The only thing that can make those source-symbols change in that manner are if
            // the text of any document changes, or if options for the project change.  So we build our
            // checksum out of that data.
            var stateChecksums = await project.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false);
47
            var checksum = Checksum.Create(nameof(SymbolTreeInfo),
48
                new Checksum[] { stateChecksums.Documents.Checksum, stateChecksums.CompilationOptions, stateChecksums.ParseOptions });
49
            return checksum;
50 51 52
        }

        internal static SymbolTreeInfo CreateSourceSymbolTreeInfo(
53
            Solution solution, Checksum checksum, IAssemblySymbol assembly,
C
CyrusNajmabadi 已提交
54
            string filePath, CancellationToken cancellationToken)
55 56 57 58 59 60
        {
            if (assembly == null)
            {
                return null;
            }

61
            var unsortedNodes = ArrayBuilder<BuilderNode>.GetInstance();
62
            unsortedNodes.Add(new BuilderNode(assembly.GlobalNamespace.Name, RootNodeParentIndex));
63

C
CyrusNajmabadi 已提交
64
            GenerateSourceNodes(assembly.GlobalNamespace, unsortedNodes, s_getMembersNoPrivate);
65

C
CyrusNajmabadi 已提交
66
            return CreateSymbolTreeInfo(
67
                solution, checksum, filePath, unsortedNodes.ToImmutableAndFree(), 
C
CyrusNajmabadi 已提交
68
                inheritanceMap: new OrderPreservingMultiDictionary<string, string>());
69 70 71 72 73
        }

        // generate nodes for the global namespace an all descendants
        private static void GenerateSourceNodes(
            INamespaceSymbol globalNamespace,
74
            ArrayBuilder<BuilderNode> list,
75
            Action<ISymbol, MultiDictionary<string, ISymbol>> lookup)
76 77
        {
            // Add all child members
78 79 80 81
            var symbolMap = AllocateSymbolMap();
            try
            {
                lookup(globalNamespace, symbolMap);
82

83 84 85 86 87 88
                foreach (var kvp in symbolMap)
                {
                    GenerateSourceNodes(kvp.Key, 0 /*index of root node*/, kvp.Value, list, lookup);
                }
            }
            finally
89
            {
90
                FreeSymbolMap(symbolMap);
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
            }
        }

        private static readonly Func<ISymbol, bool> s_useSymbolNoPrivate =
            s => s.CanBeReferencedByName && s.DeclaredAccessibility != Accessibility.Private;

        private static readonly Func<ISymbol, bool> s_useSymbolNoPrivateOrInternal =
            s => s.CanBeReferencedByName &&
            s.DeclaredAccessibility != Accessibility.Private &&
            s.DeclaredAccessibility != Accessibility.Internal;

        // generate nodes for symbols that share the same name, and all their descendants
        private static void GenerateSourceNodes(
            string name,
            int parentIndex,
106
            MultiDictionary<string, ISymbol>.ValueSet symbolsWithSameName,
107
            ArrayBuilder<BuilderNode> list,
108
            Action<ISymbol, MultiDictionary<string, ISymbol>> lookup)
109
        {
110
            var node = new BuilderNode(name, parentIndex);
111 112 113
            var nodeIndex = list.Count;
            list.Add(node);

114 115 116 117 118 119 120 121
            var symbolMap = AllocateSymbolMap();
            try
            {
                // Add all child members
                foreach (var symbol in symbolsWithSameName)
                {
                    lookup(symbol, symbolMap);
                }
122

123 124 125 126 127 128
                foreach (var kvp in symbolMap)
                {
                    GenerateSourceNodes(kvp.Key, nodeIndex, kvp.Value, list, lookup);
                }
            }
            finally
129
            {
130
                FreeSymbolMap(symbolMap);
131 132 133
            }
        }

134 135
        private static Action<ISymbol, MultiDictionary<string, ISymbol>> s_getMembersNoPrivate =
            (symbol, symbolMap) => AddSymbol(symbol, symbolMap, s_useSymbolNoPrivate);
136

137
        private static void AddSymbol(ISymbol symbol, MultiDictionary<string, ISymbol> symbolMap, Func<ISymbol, bool> useSymbol)
138 139
        {
            var nt = symbol as INamespaceOrTypeSymbol;
140 141 142 143 144 145 146 147 148 149 150
            if (nt != null)
            {
                foreach (var member in nt.GetMembers())
                {
                    if (useSymbol(member))
                    {
                        symbolMap.Add(member.Name, member);
                    }
                }
            }
        }
151 152
    }
}