SymbolTreeInfo_Source.cs 7.3 KB
Newer Older
1 2 3
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System;
4
using System.Collections.Generic;
C
CyrusNajmabadi 已提交
5
using System.Collections.Immutable;
6
using System.Linq;
7 8
using System.Threading;
using System.Threading.Tasks;
9
using Microsoft.CodeAnalysis.Collections;
10
using Microsoft.CodeAnalysis.Serialization;
11 12 13 14 15 16
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.FindSymbols
{
    internal partial class SymbolTreeInfo
    {
17 18 19 20
        private static SimplePool<MultiDictionary<string, ISymbol>> s_symbolMapPool =
            new SimplePool<MultiDictionary<string, ISymbol>>(() => new MultiDictionary<string, ISymbol>());

        private static MultiDictionary<string, ISymbol> AllocateSymbolMap()
21
            => s_symbolMapPool.Allocate();
22 23 24 25 26 27 28

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

29
        public static Task<SymbolTreeInfo> GetInfoForSourceAssemblyAsync(
30
            Project project, Checksum checksum, CancellationToken cancellationToken)
31
        {
32
            var result = LoadOrCreateSourceSymbolTreeInfoAsync(
33
                project, checksum, loadOnly: false, cancellationToken: cancellationToken);
34 35
            Contract.ThrowIfNull(result);
            return result;
36 37 38 39
        }

        public static async Task<Checksum> GetSourceSymbolsChecksumAsync(Project project, CancellationToken cancellationToken)
        {
C
CyrusNajmabadi 已提交
40 41 42 43 44 45
            // 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.
46
            var serializer = new Serializer(project.Solution.Workspace);
47
            var projectStateChecksums = await project.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false);
48

C
CyrusNajmabadi 已提交
49 50
            // Order the documents by FilePath.  Default ordering in the RemoteWorkspace is
            // to be ordered by Guid (which is not consistent across VS sessions).
51
            var textChecksumsTasks = project.Documents.OrderBy(d => d.FilePath).Select(async d =>
52
            {
53 54
                var documentStateChecksum = await d.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false);
                return documentStateChecksum.Text;
55 56
            });

57 58
            var compilationOptionsChecksum = projectStateChecksums.CompilationOptions;
            var parseOptionsChecksum = projectStateChecksums.ParseOptions;
59
            var textChecksums = await Task.WhenAll(textChecksumsTasks).ConfigureAwait(false);
60

C
CyrusNajmabadi 已提交
61 62 63 64 65 66
            var allChecksums = ArrayBuilder<Checksum>.GetInstance();
            try
            {
                allChecksums.AddRange(textChecksums);
                allChecksums.Add(compilationOptionsChecksum);
                allChecksums.Add(parseOptionsChecksum);
67

C
CyrusNajmabadi 已提交
68
                var checksum = Checksum.Create(WellKnownSynchronizationKind.SymbolTreeInfo, allChecksums);
C
CyrusNajmabadi 已提交
69 70 71 72 73 74
                return checksum;
            }
            finally
            {
                allChecksums.Free();
            }
75 76
        }

77 78
        internal static async Task<SymbolTreeInfo> CreateSourceSymbolTreeInfoAsync(
            Project project, Checksum checksum, CancellationToken cancellationToken)
79
        {
80 81
            var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
            var assembly = compilation.Assembly;
82 83
            if (assembly == null)
            {
84
                return CreateEmpty(checksum);
85 86
            }

87
            var unsortedNodes = ArrayBuilder<BuilderNode>.GetInstance();
88
            unsortedNodes.Add(new BuilderNode(assembly.GlobalNamespace.Name, RootNodeParentIndex));
89

C
CyrusNajmabadi 已提交
90
            GenerateSourceNodes(assembly.GlobalNamespace, unsortedNodes, s_getMembersNoPrivate);
91

C
CyrusNajmabadi 已提交
92
            return CreateSymbolTreeInfo(
93
                project.Solution, checksum, project.FilePath, unsortedNodes.ToImmutableAndFree(), 
C
CyrusNajmabadi 已提交
94
                inheritanceMap: new OrderPreservingMultiDictionary<string, string>());
95 96 97 98 99
        }

        // generate nodes for the global namespace an all descendants
        private static void GenerateSourceNodes(
            INamespaceSymbol globalNamespace,
100
            ArrayBuilder<BuilderNode> list,
101
            Action<ISymbol, MultiDictionary<string, ISymbol>> lookup)
102 103
        {
            // Add all child members
104 105 106 107
            var symbolMap = AllocateSymbolMap();
            try
            {
                lookup(globalNamespace, symbolMap);
108

109 110 111 112 113 114
                foreach (var kvp in symbolMap)
                {
                    GenerateSourceNodes(kvp.Key, 0 /*index of root node*/, kvp.Value, list, lookup);
                }
            }
            finally
115
            {
116
                FreeSymbolMap(symbolMap);
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
            }
        }

        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,
132
            MultiDictionary<string, ISymbol>.ValueSet symbolsWithSameName,
133
            ArrayBuilder<BuilderNode> list,
134
            Action<ISymbol, MultiDictionary<string, ISymbol>> lookup)
135
        {
136
            var node = new BuilderNode(name, parentIndex);
137 138 139
            var nodeIndex = list.Count;
            list.Add(node);

140 141 142 143 144 145 146 147
            var symbolMap = AllocateSymbolMap();
            try
            {
                // Add all child members
                foreach (var symbol in symbolsWithSameName)
                {
                    lookup(symbol, symbolMap);
                }
148

149 150 151 152 153 154
                foreach (var kvp in symbolMap)
                {
                    GenerateSourceNodes(kvp.Key, nodeIndex, kvp.Value, list, lookup);
                }
            }
            finally
155
            {
156
                FreeSymbolMap(symbolMap);
157 158 159
            }
        }

160 161
        private static Action<ISymbol, MultiDictionary<string, ISymbol>> s_getMembersNoPrivate =
            (symbol, symbolMap) => AddSymbol(symbol, symbolMap, s_useSymbolNoPrivate);
162

163
        private static void AddSymbol(ISymbol symbol, MultiDictionary<string, ISymbol> symbolMap, Func<ISymbol, bool> useSymbol)
164 165
        {
            var nt = symbol as INamespaceOrTypeSymbol;
166 167 168 169 170 171 172 173 174 175 176
            if (nt != null)
            {
                foreach (var member in nt.GetMembers())
                {
                    if (useSymbol(member))
                    {
                        symbolMap.Add(member.Name, member);
                    }
                }
            }
        }
177 178
    }
}