提交 da4e06fc 编写于 作者: K kayleh

Fix some O(n^2) codepaths to allow faster MetadataAsSource processing of large enums.

AbstractCodeGenerationService.AddMembers was using an O(n^2) algorithm to insert new members.  This change allows bulk inserting members if the AutoInsertionLocation flag is false.  For now, metadata as source only sets AutoInsertionLocation to false for enum symbols which can commonly have thousands of members.

SeparatedSyntaxList.InsertRange was also calling Insert for each node.  It now adds the nodes in a single step. (changeset 1344073)
上级 5ec2e9a8
......@@ -367,19 +367,18 @@ public SeparatedSyntaxList<TNode> InsertRange(int index, IEnumerable<TNode> node
}
}
var nodesToInsertWithSeparators = new List<SyntaxNodeOrToken>();
foreach (var item in nodes)
{
if (item != null)
{
// if item before insertion point is a node, add a separator
if (insertionIndex > 0 && nodesWithSeps[insertionIndex - 1].IsNode)
if (nodesToInsertWithSeparators.Count > 0 || (insertionIndex > 0 && nodesWithSeps[insertionIndex - 1].IsNode))
{
nodesWithSeps = nodesWithSeps.Insert(insertionIndex, item.Green.CreateSeparator<TNode>(item)); // separator
insertionIndex++;
nodesToInsertWithSeparators.Add(item.Green.CreateSeparator<TNode>(item));
}
nodesWithSeps = nodesWithSeps.Insert(insertionIndex, item);
insertionIndex++;
nodesToInsertWithSeparators.Add(item);
}
}
......@@ -387,10 +386,10 @@ public SeparatedSyntaxList<TNode> InsertRange(int index, IEnumerable<TNode> node
if (insertionIndex < nodesWithSeps.Count && nodesWithSeps[insertionIndex].IsNode)
{
var node = nodesWithSeps[insertionIndex].AsNode();
nodesWithSeps = nodesWithSeps.Insert(insertionIndex, node.Green.CreateSeparator<TNode>(node)); // separator
nodesToInsertWithSeparators.Add(node.Green.CreateSeparator<TNode>(node)); // separator
}
return new SeparatedSyntaxList<TNode>(nodesWithSeps);
return new SeparatedSyntaxList<TNode>(nodesWithSeps.InsertRange(insertionIndex, nodesToInsertWithSeparators));
}
private static bool KeepSeparatorWithPreviousNode(SyntaxToken separator)
......
......@@ -295,7 +295,7 @@ public SyntaxNodeOrTokenList Insert(int index, SyntaxNodeOrToken nodeOrToken)
throw new ArgumentException("nodeOrToken");
}
return InsertRange(index, new[] { nodeOrToken });
return InsertRange(index, SpecializedCollections.SingletonEnumerable(nodeOrToken));
}
/// <summary>
......@@ -315,22 +315,14 @@ public SyntaxNodeOrTokenList InsertRange(int index, IEnumerable<SyntaxNodeOrToke
throw new ArgumentNullException("nodesAndTokens");
}
var items = nodesAndTokens.ToList();
if (items.Count == 0)
if (nodesAndTokens.IsEmpty())
{
return this;
}
var nodes = this.ToList();
nodes.InsertRange(index, nodesAndTokens);
if (nodes.Count == 0)
{
return this;
}
else
{
return CreateList(nodes[0].UnderlyingNode, nodes);
}
return CreateList(nodes[0].UnderlyingNode, nodes);
}
private static SyntaxNodeOrTokenList CreateList(GreenNode creator, List<SyntaxNodeOrToken> items)
......
......@@ -329,7 +329,7 @@ public static MemberDeclarationSyntax LastOperator(SyntaxList<MemberDeclarationS
return SyntaxFactory.ExplicitInterfaceSpecifier(name);
}
public static CodeGenerationDestination GetDestination(TypeDeclarationSyntax destination)
public static CodeGenerationDestination GetDestination(SyntaxNode destination)
{
if (destination != null)
{
......@@ -337,10 +337,18 @@ public static CodeGenerationDestination GetDestination(TypeDeclarationSyntax des
{
case SyntaxKind.ClassDeclaration:
return CodeGenerationDestination.ClassType;
case SyntaxKind.CompilationUnit:
return CodeGenerationDestination.CompilationUnit;
case SyntaxKind.EnumDeclaration:
return CodeGenerationDestination.EnumType;
case SyntaxKind.InterfaceDeclaration:
return CodeGenerationDestination.InterfaceType;
case SyntaxKind.NamespaceDeclaration:
return CodeGenerationDestination.Namespace;
case SyntaxKind.StructDeclaration:
return CodeGenerationDestination.StructType;
default:
return CodeGenerationDestination.Unspecified;
}
}
......
......@@ -24,6 +24,11 @@ public CSharpCodeGenerationService(HostLanguageServices languageServices)
{
}
public override CodeGenerationDestination GetDestination(SyntaxNode node)
{
return CSharpCodeGenerationHelpers.GetDestination(node);
}
protected override AbstractImportsAdder CreateImportsAdder(
Document document)
{
......@@ -317,6 +322,32 @@ protected override TDeclarationNode AddNamespace<TDeclarationNode>(TDeclarationN
return destination;
}
protected override TDeclarationNode AddMembers<TDeclarationNode>(TDeclarationNode destination, IEnumerable<SyntaxNode> members)
{
CheckDeclarationNode<EnumDeclarationSyntax, TypeDeclarationSyntax, NamespaceDeclarationSyntax, CompilationUnitSyntax>(destination);
if (destination is EnumDeclarationSyntax)
{
return Cast<TDeclarationNode>(Cast<EnumDeclarationSyntax>(destination)
.AddMembers(members.Cast<EnumMemberDeclarationSyntax>().ToArray()));
}
else if (destination is TypeDeclarationSyntax)
{
return Cast<TDeclarationNode>(Cast<TypeDeclarationSyntax>(destination)
.AddMembers(members.Cast<MemberDeclarationSyntax>().ToArray()));
}
else if (destination is NamespaceDeclarationSyntax)
{
return Cast<TDeclarationNode>(Cast<NamespaceDeclarationSyntax>(destination)
.AddMembers(members.Cast<MemberDeclarationSyntax>().ToArray()));
}
else
{
return Cast<TDeclarationNode>(Cast<CompilationUnitSyntax>(destination)
.AddMembers(members.Cast<MemberDeclarationSyntax>().ToArray()));
}
}
public override TDeclarationNode RemoveAttribute<TDeclarationNode>(
TDeclarationNode destination,
AttributeData attributeToRemove,
......
......@@ -66,6 +66,7 @@ public TDeclarationNode AddMembers<TDeclarationNode>(TDeclarationNode destinatio
protected abstract TDeclarationNode AddProperty<TDeclarationNode>(TDeclarationNode destination, IPropertySymbol property, CodeGenerationOptions options, IList<bool> availableIndices) where TDeclarationNode : SyntaxNode;
protected abstract TDeclarationNode AddNamedType<TDeclarationNode>(TDeclarationNode destination, INamedTypeSymbol namedType, CodeGenerationOptions options, IList<bool> availableIndices) where TDeclarationNode : SyntaxNode;
protected abstract TDeclarationNode AddNamespace<TDeclarationNode>(TDeclarationNode destination, INamespaceSymbol @namespace, CodeGenerationOptions options, IList<bool> availableIndices) where TDeclarationNode : SyntaxNode;
protected abstract TDeclarationNode AddMembers<TDeclarationNode>(TDeclarationNode destination, IEnumerable<SyntaxNode> members) where TDeclarationNode : SyntaxNode;
public abstract TDeclarationNode AddParameters<TDeclarationNode>(TDeclarationNode destinationMember, IEnumerable<IParameterSymbol> parameters, CodeGenerationOptions options, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
public abstract TDeclarationNode AddAttributes<TDeclarationNode>(TDeclarationNode destination, IEnumerable<AttributeData> attributes, SyntaxToken? target, CodeGenerationOptions options, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
......@@ -78,6 +79,7 @@ public TDeclarationNode AddMembers<TDeclarationNode>(TDeclarationNode destinatio
public abstract TDeclarationNode UpdateDeclarationType<TDeclarationNode>(TDeclarationNode declaration, ITypeSymbol newType, CodeGenerationOptions options, CancellationToken cancellationToken) where TDeclarationNode : SyntaxNode;
public abstract TDeclarationNode UpdateDeclarationMembers<TDeclarationNode>(TDeclarationNode declaration, IList<ISymbol> newMembers, CodeGenerationOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) where TDeclarationNode : SyntaxNode;
public abstract CodeGenerationDestination GetDestination(SyntaxNode node);
public abstract SyntaxNode CreateEventDeclaration(IEventSymbol @event, CodeGenerationDestination destination, CodeGenerationOptions options);
public abstract SyntaxNode CreateFieldDeclaration(IFieldSymbol field, CodeGenerationDestination destination, CodeGenerationOptions options);
public abstract SyntaxNode CreateMethodDeclaration(IMethodSymbol method, CodeGenerationDestination destination, CodeGenerationOptions options);
......@@ -132,6 +134,24 @@ protected static T Cast<T>(object value)
}
}
protected static void CheckDeclarationNode<TDeclarationNode1, TDeclarationNode2, TDeclarationNode3, TDeclarationNode4>(SyntaxNode destination)
where TDeclarationNode1 : SyntaxNode
where TDeclarationNode2 : SyntaxNode
where TDeclarationNode3 : SyntaxNode
where TDeclarationNode4 : SyntaxNode
{
if (!(destination is TDeclarationNode1) &&
!(destination is TDeclarationNode2) &&
!(destination is TDeclarationNode3) &&
!(destination is TDeclarationNode4))
{
throw new ArgumentException(
string.Format(WorkspacesResources.InvalidDestinationNode3,
typeof(TDeclarationNode1).Name, typeof(TDeclarationNode2).Name, typeof(TDeclarationNode3).Name, typeof(TDeclarationNode4).Name),
"destination");
}
}
private async Task<Document> GetEditAsync(
Solution solution,
INamespaceOrTypeSymbol destination,
......@@ -189,16 +209,42 @@ protected static T Cast<T>(object value)
// not want an explicit declaration.
var filteredMembers = membersList.Where(m => !m.IsImplicitlyDeclared);
foreach (var member in filteredMembers)
if (options.AutoInsertionLocation)
{
foreach (var member in filteredMembers)
{
currentDestination = member.TypeSwitch(
(IEventSymbol @event) => this.AddEvent(currentDestination, @event, options, availableIndices),
(IFieldSymbol field) => this.AddField(currentDestination, field, options, availableIndices),
(IPropertySymbol property) => this.AddProperty(currentDestination, property, options, availableIndices),
(IMethodSymbol method) => this.AddMethod(currentDestination, method, options, availableIndices),
(INamedTypeSymbol namedType) => this.AddNamedType(currentDestination, namedType, options, availableIndices),
(INamespaceSymbol @namespace) => this.AddNamespace(currentDestination, @namespace, options, availableIndices),
_ => currentDestination);
}
}
else
{
currentDestination = member.TypeSwitch(
(IEventSymbol @event) => this.AddEvent(currentDestination, @event, options, availableIndices),
(IFieldSymbol field) => this.AddField(currentDestination, field, options, availableIndices),
(IPropertySymbol property) => this.AddProperty(currentDestination, property, options, availableIndices),
(IMethodSymbol method) => this.AddMethod(currentDestination, method, options, availableIndices),
(INamedTypeSymbol namedType) => this.AddNamedType(currentDestination, namedType, options, availableIndices),
(INamespaceSymbol @namespace) => this.AddNamespace(currentDestination, @namespace, options, availableIndices),
_ => currentDestination);
var newMembers = new List<SyntaxNode>();
var codeGenerationDestination = GetDestination(destination);
foreach (var member in filteredMembers)
{
var newMember = member.TypeSwitch(
(IEventSymbol @event) => this.CreateEventDeclaration(@event, codeGenerationDestination, options),
(IFieldSymbol field) => this.CreateFieldDeclaration(field, codeGenerationDestination, options),
(IPropertySymbol property) => this.CreatePropertyDeclaration(property, codeGenerationDestination, options),
(IMethodSymbol method) => this.CreateMethodDeclaration(method, codeGenerationDestination, options),
(INamedTypeSymbol namedType) => this.CreateNamedTypeDeclaration(namedType, codeGenerationDestination, options),
(INamespaceSymbol @namespace) => this.CreateNamespaceDeclaration(@namespace, codeGenerationDestination, options),
_ => null);
if (newMember != null)
{
newMembers.Add(newMember);
}
}
currentDestination = this.AddMembers(currentDestination, newMembers);
}
return currentDestination;
......
......@@ -13,15 +13,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration
enumMember As IFieldSymbol,
options As CodeGenerationOptions) As EnumBlockSyntax
' We never generate the special enum backing field.
If enumMember.Name = WellKnownMemberNames.EnumBackingFieldName Then
Dim member = GenerateEnumMemberDeclaration(enumMember, destination, options)
If member Is Nothing Then
Return destination
End If
Dim members = New List(Of StatementSyntax)()
members.AddRange(destination.Members)
Dim member = GenerateEnumMemberDeclaration(enumMember, destination, options)
members.Add(member)
Dim leadingTrivia = destination.EndEnumStatement.GetLeadingTrivia()
......@@ -32,6 +30,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration
Public Function GenerateEnumMemberDeclaration(enumMember As IFieldSymbol,
enumDeclarationOpt As EnumBlockSyntax,
options As CodeGenerationOptions) As EnumMemberDeclarationSyntax
' We never generate the special enum backing field.
If enumMember.Name = WellKnownMemberNames.EnumBackingFieldName Then
Return Nothing
End If
Dim reusableSyntax = GetReuseableSyntaxNodeForSymbol(Of EnumMemberDeclarationSyntax)(enumMember, options)
If reusableSyntax IsNot Nothing Then
Return reusableSyntax
......
......@@ -236,17 +236,25 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration
Return declarationList.Count
End Function
Public Function GetDestination(destination As TypeBlockSyntax) As CodeGenerationDestination
Public Function GetDestination(destination As SyntaxNode) As CodeGenerationDestination
If destination IsNot Nothing Then
Select Case destination.VisualBasicKind
Case SyntaxKind.ClassBlock
Return CodeGenerationDestination.ClassType
Case SyntaxKind.CompilationUnit
Return CodeGenerationDestination.CompilationUnit
Case SyntaxKind.EnumBlock
Return CodeGenerationDestination.EnumType
Case SyntaxKind.InterfaceBlock
Return CodeGenerationDestination.InterfaceType
Case SyntaxKind.ModuleBlock
Return CodeGenerationDestination.ModuleType
Case SyntaxKind.NamespaceBlock
Return CodeGenerationDestination.Namespace
Case SyntaxKind.StructureBlock
Return CodeGenerationDestination.StructType
Case Else
Return CodeGenerationDestination.Unspecified
End Select
End If
......
......@@ -18,6 +18,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration
MyBase.New(provider.GetService(Of ISymbolDeclarationService)())
End Sub
Public Overloads Overrides Function GetDestination(containerNode As SyntaxNode) As CodeGenerationDestination
Return VisualBasicCodeGenerationHelpers.GetDestination(containerNode)
End Function
Protected Overrides Function CreateImportsAdder(document As Document) As AbstractImportsAdder
Return New ImportsStatementsAdder(document)
End Function
......@@ -204,6 +208,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration
Return destinationMember
End Function
Protected Overrides Function AddMembers(Of TDeclarationNode As SyntaxNode)(destination As TDeclarationNode, members As IEnumerable(Of SyntaxNode)) As TDeclarationNode
CheckDeclarationNode(Of EnumBlockSyntax, TypeBlockSyntax, NamespaceBlockSyntax, CompilationUnitSyntax)(destination)
If TypeOf destination Is EnumBlockSyntax Then
Return Cast(Of TDeclarationNode)(Cast(Of EnumBlockSyntax)(destination).AddMembers(members.Cast(Of EnumMemberDeclarationSyntax).ToArray()))
ElseIf TypeOf destination Is TypeBlockSyntax Then
Return Cast(Of TDeclarationNode)(Cast(Of TypeBlockSyntax)(destination).AddMembers(members.Cast(Of StatementSyntax).ToArray()))
ElseIf TypeOf destination Is NamespaceBlockSyntax Then
Return Cast(Of TDeclarationNode)(Cast(Of NamespaceBlockSyntax)(destination).AddMembers(members.Cast(Of StatementSyntax).ToArray()))
Else
Return Cast(Of TDeclarationNode)(Cast(Of CompilationUnitSyntax)(destination).AddMembers(members.Cast(Of StatementSyntax).ToArray()))
End If
End Function
Private Overloads Shared Function AddParametersToMethod(Of TDeclarationNode As SyntaxNode)(methodStatement As MethodBaseSyntax,
methodBlock As MethodBlockBaseSyntax,
parameters As IEnumerable(Of IParameterSymbol),
......
......@@ -39,6 +39,22 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
Throw Contract.Unreachable
End Function
<Extension>
Public Function AddMembers(node As TypeBlockSyntax, ParamArray members As StatementSyntax()) As TypeBlockSyntax
Select Case node.VisualBasicKind
Case SyntaxKind.ModuleBlock
Return DirectCast(node, ModuleBlockSyntax).AddMembers(members)
Case SyntaxKind.InterfaceBlock
Return DirectCast(node, InterfaceBlockSyntax).AddMembers(members)
Case SyntaxKind.StructureBlock
Return DirectCast(node, StructureBlockSyntax).AddMembers(members)
Case SyntaxKind.ClassBlock
Return DirectCast(node, ClassBlockSyntax).AddMembers(members)
End Select
Throw Contract.Unreachable
End Function
<Extension>
Public Function WithMembers(node As TypeBlockSyntax, members As SyntaxList(Of StatementSyntax)) As TypeBlockSyntax
Select Case node.VisualBasicKind
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册