Skip to content

Commit

Permalink
Create local function (a bit eagerly and with a static name)
Browse files Browse the repository at this point in the history
  • Loading branch information
GrahamTheCoder committed Apr 26, 2020
1 parent af1c601 commit 1372eec
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 117 deletions.
6 changes: 3 additions & 3 deletions CodeConverter/CSharp/AdditionalDeclaration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public AdditionalDeclaration(string prefix, ExpressionSyntax initializer, TypeSy
Type = type;
}

public IdentifierNameSyntax IdentifierName => SyntaxFactory.IdentifierName(Id).WithAdditionalAnnotations(AdditionalLocals.Annotation);
public IdentifierNameSyntax IdentifierName => SyntaxFactory.IdentifierName(Id).WithAdditionalAnnotations(HoistedNodeState.Annotation);


public static IEnumerable<StatementSyntax> ReplaceNames(IEnumerable<StatementSyntax> csNodes, Dictionary<string, string> newNames)
Expand All @@ -33,10 +33,10 @@ public static IEnumerable<StatementSyntax> ReplaceNames(IEnumerable<StatementSyn

public static T ReplaceNames<T>(T csNode, Dictionary<string, string> newNames) where T: SyntaxNode
{
return csNode.ReplaceNodes(csNode.GetAnnotatedNodes(AdditionalLocals.Annotation), (_, withReplaced) => {
return csNode.ReplaceNodes(csNode.GetAnnotatedNodes(HoistedNodeState.Annotation), (_, withReplaced) => {
var idns = (IdentifierNameSyntax)withReplaced;
if (newNames.TryGetValue(idns.Identifier.ValueText, out var newName)) {
return idns.WithoutAnnotations(AdditionalLocals.Annotation).WithIdentifier(SyntaxFactory.Identifier(newName));
return idns.WithoutAnnotations(HoistedNodeState.Annotation).WithIdentifier(SyntaxFactory.Identifier(newName));
}
return idns;
});
Expand Down
45 changes: 0 additions & 45 deletions CodeConverter/CSharp/AdditionalLocals.cs

This file was deleted.

5 changes: 2 additions & 3 deletions CodeConverter/CSharp/DeclarationNodeVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ internal class DeclarationNodeVisitor : VBasic.VisualBasicSyntaxVisitor<Task<CSh
private readonly MethodsWithHandles _methodsWithHandles = new MethodsWithHandles();
private readonly Dictionary<VBSyntax.StatementSyntax, MemberDeclarationSyntax[]> _additionalDeclarations = new Dictionary<VBSyntax.StatementSyntax, MemberDeclarationSyntax[]>();
private readonly AdditionalInitializers _additionalInitializers;
private readonly AdditionalLocals _additionalLocals = new AdditionalLocals();
private readonly HoistedNodeState _additionalLocals = new HoistedNodeState();
private uint _failedMemberConversionMarkerCount;
private readonly HashSet<string> _extraUsingDirectives = new HashSet<string>();
private readonly VisualBasicEqualityComparison _visualBasicEqualityComparison;
Expand Down Expand Up @@ -490,8 +490,7 @@ private IEnumerable<MemberDeclarationSyntax> CreateMemberDeclarations(IReadOnlyC
foreach (var f in fieldDecls) yield return f;
} else
{
var additionalDeclarationInfo = _additionalLocals.GetDeclarations();
if (additionalDeclarationInfo.Count() > 0) {
if (_additionalLocals.GetDeclarations().Count() > 0) {
foreach (var additionalDecl in CreateAdditionalLocalMembers(convertedModifiers, attributes, decl)) {
yield return additionalDecl;
}
Expand Down
53 changes: 26 additions & 27 deletions CodeConverter/CSharp/ExpressionNodeVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal class ExpressionNodeVisitor : VBasic.VisualBasicSyntaxVisitor<Task<CSha
private readonly bool _optionCompareText = false;
private readonly VisualBasicEqualityComparison _visualBasicEqualityComparison;
private readonly Stack<ExpressionSyntax> _withBlockLhs = new Stack<ExpressionSyntax>();
private readonly AdditionalLocals _additionalLocals;
private readonly HoistedNodeState _additionalLocals;
private readonly MethodsWithHandles _methodsWithHandles;
private readonly QueryConverter _queryConverter;
private readonly Lazy<IDictionary<ITypeSymbol, string>> _convertMethodsLookupByReturnType;
Expand All @@ -43,7 +43,7 @@ internal class ExpressionNodeVisitor : VBasic.VisualBasicSyntaxVisitor<Task<CSha
private INamedTypeSymbol _vbBooleanTypeSymbol;

public ExpressionNodeVisitor(SemanticModel semanticModel,
VisualBasicEqualityComparison visualBasicEqualityComparison, AdditionalLocals additionalLocals,
VisualBasicEqualityComparison visualBasicEqualityComparison, HoistedNodeState additionalLocals,
Compilation csCompilation, MethodsWithHandles methodsWithHandles, CommonConversions commonConversions,
HashSet<string> extraUsingDirectives)
{
Expand Down Expand Up @@ -817,20 +817,20 @@ private async Task<CSharpSyntaxNode> WithRemovedRedundantConversionOrNull(VBSynt
public override async Task<CSharpSyntaxNode> VisitInvocationExpression(
VBasic.Syntax.InvocationExpressionSyntax node)
{
var invocationSymbol = _semanticModel.GetSymbolInfo(node).ExtractBestMatch<ISymbol>();
var withinLocalFunction = RequiresLocalFunction(node, invocationSymbol as IMethodSymbol);
var invocationSymbol = _semanticModel.GetSymbolInfo(node).ExtractBestMatch<IMethodSymbol>();
var withinLocalFunction = RequiresLocalFunction(node, invocationSymbol);
if (withinLocalFunction) {
_additionalLocals.PushScope();
}
try {
var convertedInvocation = await ConvertInvocation(node, invocationSymbol);
if (withinLocalFunction) {

return await HoistAndCallLocalFunction(node, invocationSymbol, (ExpressionSyntax)convertedInvocation);
}
return await ConvertInvocation(node, invocationSymbol);
} finally {
if (withinLocalFunction) {
_additionalLocals.PopScope();
_additionalLocals.PopExpressionScope();

This comment has been minimized.

Copy link
@GrahamTheCoder

GrahamTheCoder Sep 7, 2020

Author Member

I had to distinguish which bits of scope I was popping here. Arguably, there should be separate stacks for MemberDeclarations, MethodStatements and Expressions. That'd mean you could add to the MemberDeclarations stack without worrying about it being popped in other scopes.

}
}
}
Expand Down Expand Up @@ -919,27 +919,26 @@ async Task<CSharpSyntaxNode> CreateElementAccess()
/// <param name="invocation"></param>
/// <param name="invocationSymbol"></param>
/// <returns></returns>
//private async Task<(string Id, StatementSyntax FunctionDeclaration)> CreateLocalByRefFunction(VBSyntax.InvocationExpressionSyntax invocation, IMethodSymbol invocationSymbol)
//{
// RequiresLocalFunction(invocation, invocationSymbol);

// var localFuncName = $"local{invocationSymbol.Name}";
// const string retVariableName = "ret";
// var localFuncId = SyntaxFactory.IdentifierName(localFuncName);
// // Need essentially the original invocation with any byref args swapped out for temporaries
// var callAndStoreResult = CommonConversions.CreateLocalVariableDeclarationAndAssignment(retVariableName,
// SyntaxFactory.InvocationExpression(expression,
// );

// var block = SyntaxFactory.Block(
// callAndStoreResult,
// AssignStmt(expression, tempArg),
// SyntaxFactory.ReturnStatement(SyntaxFactory.IdentifierName(retVariableName))
// );
// var localfunction = SyntaxFactory.LocalFunctionStatement(CommonConversions.GetTypeSyntax(invocationSymbol.ReturnType),
// localFuncId.Identifier).WithBody(block);
// return (localFuncName, localfunction);
//}
private async Task<InvocationExpressionSyntax> HoistAndCallLocalFunction(VBSyntax.InvocationExpressionSyntax invocation, IMethodSymbol invocationSymbol, ExpressionSyntax csExpression)
{
const string retVariableName = "ret";
var localFuncName = $"local{invocationSymbol.Name}";
var generatedNames = new HashSet<string>();//TODO: Populate from local scope

var callAndStoreResult = CommonConversions.CreateLocalVariableDeclarationAndAssignment(retVariableName, csExpression);

var statements = await _additionalLocals.CreateLocals(invocation, new[] { callAndStoreResult }, generatedNames, _semanticModel);

var localFuncId = SyntaxFactory.IdentifierName(localFuncName);

var block = SyntaxFactory.Block(
statements.Concat(SyntaxFactory.ReturnStatement(SyntaxFactory.IdentifierName(retVariableName)).Yield())
);
var localFunction = SyntaxFactory.LocalFunctionStatement(CommonConversions.GetTypeSyntax(invocationSymbol.ReturnType),
localFuncId.Identifier).WithBody(block);
_additionalLocals.Hoist(new HoistedStatement(localFunction));
return SyntaxFactory.InvocationExpression(localFuncId, SyntaxFactory.ArgumentList());
}

private bool RequiresLocalFunction(VBSyntax.InvocationExpressionSyntax invocation, IMethodSymbol invocationSymbol)
{
Expand Down
90 changes: 90 additions & 0 deletions CodeConverter/CSharp/HoistedNodeState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax;
using VBasic = Microsoft.CodeAnalysis.VisualBasic;
using CS = Microsoft.CodeAnalysis.CSharp;
using ICSharpCode.CodeConverter.Shared;
using System;

namespace ICSharpCode.CodeConverter.CSharp
{
internal class HoistedNodeState
{
public static SyntaxAnnotation Annotation = new SyntaxAnnotation("CodeconverterAdditionalLocal");

private readonly Stack<List<IHoistedNode>> _hoistedNodesPerScope;

public HoistedNodeState()
{
_hoistedNodesPerScope = new Stack<List<IHoistedNode>>();
}

public void PushScope()
{
_hoistedNodesPerScope.Push(new List<IHoistedNode>());
}

public void PopScope()
{
_hoistedNodesPerScope.Pop();
}

public void PopExpressionScope()
{
var statements = GetStatements();
PopScope();
foreach (var statement in statements) {
Hoist(statement);
}
}

public T Hoist<T>(T additionalLocal) where T: IHoistedNode
{
_hoistedNodesPerScope.Peek().Add(additionalLocal);
return additionalLocal;
}

public IReadOnlyCollection<AdditionalDeclaration> GetDeclarations()
{
return _hoistedNodesPerScope.Peek().OfType<AdditionalDeclaration>().ToArray();
}

public IReadOnlyCollection<AdditionalAssignment> GetPostAssignments()
{
return _hoistedNodesPerScope.Peek().OfType<AdditionalAssignment>().ToArray();
}

public IReadOnlyCollection<HoistedStatement> GetStatements()
{
return _hoistedNodesPerScope.Peek().OfType<HoistedStatement>().ToArray();
}

public async Task<SyntaxList<CS.Syntax.StatementSyntax>> CreateLocals(VBasic.VisualBasicSyntaxNode vbNode, IEnumerable<CS.Syntax.StatementSyntax> csNodes, HashSet<string> generatedNames, SemanticModel semanticModel)
{
var preDeclarations = new List<CS.Syntax.StatementSyntax>();
var postAssignments = new List<CS.Syntax.StatementSyntax>();

var additionalDeclarationInfo = GetDeclarations();
var newNames = additionalDeclarationInfo.ToDictionary(l => l.Id, l =>
NameGenerator.GetUniqueVariableNameInScope(semanticModel, generatedNames, vbNode, l.Prefix)
);
foreach (var additionalLocal in additionalDeclarationInfo) {
var decl = CommonConversions.CreateVariableDeclarationAndAssignment(newNames[additionalLocal.Id],
additionalLocal.Initializer, additionalLocal.Type);
preDeclarations.Add(CS.SyntaxFactory.LocalDeclarationStatement(decl));
}

foreach (var additionalAssignment in GetPostAssignments()) {
var assign = CS.SyntaxFactory.AssignmentExpression(CS.SyntaxKind.SimpleAssignmentExpression, additionalAssignment.Expression, additionalAssignment.IdentifierName);
postAssignments.Add(CS.SyntaxFactory.ExpressionStatement(assign));
}

var statementsWithUpdatedIds = AdditionalDeclaration.ReplaceNames(preDeclarations.Concat(csNodes).Concat(postAssignments), newNames);

return CS.SyntaxFactory.List(statementsWithUpdatedIds);
}
}
}
42 changes: 5 additions & 37 deletions CodeConverter/CSharp/HoistedNodeStateVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ namespace ICSharpCode.CodeConverter.CSharp
internal class HoistedNodeStateVisitor : VBasic.VisualBasicSyntaxVisitor<Task<SyntaxList<StatementSyntax>>>
{
private readonly VBasic.VisualBasicSyntaxVisitor<Task<SyntaxList<StatementSyntax>>> _wrappedVisitor;
private readonly AdditionalLocals _additionalLocals;
private readonly HoistedNodeState _additionalLocals;
private readonly SemanticModel _semanticModel;
private readonly HashSet<string> _generatedNames;

public HoistedNodeStateVisitor(VBasic.VisualBasicSyntaxVisitor<Task<SyntaxList<StatementSyntax>>> wrappedVisitor, AdditionalLocals additionalLocals,
public HoistedNodeStateVisitor(VBasic.VisualBasicSyntaxVisitor<Task<SyntaxList<StatementSyntax>>> wrappedVisitor, HoistedNodeState additionalLocals,
SemanticModel semanticModel, HashSet<string> generatedNames)
{
_wrappedVisitor = wrappedVisitor;
Expand All @@ -44,46 +44,14 @@ private async Task<SyntaxList<StatementSyntax>> AddLocalVariables(VBasic.VisualB
{
_additionalLocals.PushScope();
try {
return await CreateLocals(node);
var csNodes = await _wrappedVisitor.Visit(node);
var statements = await _additionalLocals.CreateLocals(node, csNodes, _generatedNames, _semanticModel);
return statements.InsertRange(0, _additionalLocals.GetStatements().Select(s => s.Statement));
} finally {
_additionalLocals.PopScope();
}
}

private async Task<SyntaxList<StatementSyntax>> CreateLocals(VBasic.VisualBasicSyntaxNode node)
{
IEnumerable<StatementSyntax> csNodes = await _wrappedVisitor.Visit(node);

var preDeclarations = new List<StatementSyntax>();
var postAssignments = new List<StatementSyntax>();

var additionalDeclarationInfo = _additionalLocals.GetDeclarations();
var newNames = additionalDeclarationInfo.ToDictionary(l => l.Id, l =>
NameGenerator.GetUniqueVariableNameInScope(_semanticModel, _generatedNames, node, l.Prefix)
);
if (additionalDeclarationInfo.Count() > 0) {
foreach (var additionalLocal in additionalDeclarationInfo) {
var decl = CommonConversions.CreateVariableDeclarationAndAssignment(newNames[additionalLocal.Id],
additionalLocal.Initializer, additionalLocal.Type);
preDeclarations.Add(SyntaxFactory.LocalDeclarationStatement(decl));
}
}
var additionalAssignmentInfo = _additionalLocals.GetPostAssignments();
if (additionalAssignmentInfo.Count() > 0)
{

foreach (var additionalAssignment in additionalAssignmentInfo)
{
var assign = SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, additionalAssignment.Expression, additionalAssignment.IdentifierName);
postAssignments.Add(SyntaxFactory.ExpressionStatement(assign));
}
}

var statementsWithUpdatedIds = AdditionalDeclaration.ReplaceNames(preDeclarations.Concat(csNodes).Concat(postAssignments), newNames);

return SyntaxFactory.List(statementsWithUpdatedIds);
}

public override Task<SyntaxList<StatementSyntax>> VisitAddRemoveHandlerStatement(VBSyntax.AddRemoveHandlerStatementSyntax node) => AddLocalVariables(node);
public override Task<SyntaxList<StatementSyntax>> VisitAssignmentStatement(VBSyntax.AssignmentStatementSyntax node) => AddLocalVariables(node);
public override Task<SyntaxList<StatementSyntax>> VisitCallStatement(VBSyntax.CallStatementSyntax node) => AddLocalVariables(node);
Expand Down
14 changes: 14 additions & 0 deletions CodeConverter/CSharp/HoistedStatement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace ICSharpCode.CodeConverter.CSharp
{
internal class HoistedStatement : IHoistedNode
{
public StatementSyntax Statement { get; }

public HoistedStatement(StatementSyntax statementSyntax)
{
Statement = statementSyntax;
}
}
}
File renamed without changes.
4 changes: 2 additions & 2 deletions CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ internal class MethodBodyExecutableStatementVisitor : VBasic.VisualBasicSyntaxVi

private CommonConversions CommonConversions { get; }

public static async Task<MethodBodyExecutableStatementVisitor> CreateAsync(VBasic.VisualBasicSyntaxNode node, SemanticModel semanticModel, CommentConvertingVisitorWrapper triviaConvertingExpressionVisitor, CommonConversions commonConversions, Stack<ExpressionSyntax> withBlockLhs, HashSet<string> extraUsingDirectives, AdditionalLocals additionalLocals, MethodsWithHandles methodsWithHandles, bool isIterator, IdentifierNameSyntax csReturnVariable)
public static async Task<MethodBodyExecutableStatementVisitor> CreateAsync(VBasic.VisualBasicSyntaxNode node, SemanticModel semanticModel, CommentConvertingVisitorWrapper triviaConvertingExpressionVisitor, CommonConversions commonConversions, Stack<ExpressionSyntax> withBlockLhs, HashSet<string> extraUsingDirectives, HoistedNodeState additionalLocals, MethodsWithHandles methodsWithHandles, bool isIterator, IdentifierNameSyntax csReturnVariable)
{
var solution = commonConversions.Document.Project.Solution;
var declarationsToInlineInLoop = await solution.GetDescendantsToInlineInLoopAsync(semanticModel, node);
Expand All @@ -54,7 +54,7 @@ public static async Task<MethodBodyExecutableStatementVisitor> CreateAsync(VBasi
private MethodBodyExecutableStatementVisitor(VBasic.VisualBasicSyntaxNode methodNode, SemanticModel semanticModel,
CommentConvertingVisitorWrapper expressionVisitor, CommonConversions commonConversions,
Stack<ExpressionSyntax> withBlockLhs, HashSet<string> extraUsingDirectives,
AdditionalLocals additionalLocals, MethodsWithHandles methodsWithHandles, HashSet<ILocalSymbol> localsToInlineInLoop)
HoistedNodeState additionalLocals, MethodsWithHandles methodsWithHandles, HashSet<ILocalSymbol> localsToInlineInLoop)
{
_methodNode = methodNode;
_semanticModel = semanticModel;
Expand Down

0 comments on commit 1372eec

Please sign in to comment.