Skip to content

Commit 1372eec

Browse files
Create local function (a bit eagerly and with a static name)
1 parent af1c601 commit 1372eec

9 files changed

Lines changed: 142 additions & 117 deletions

CodeConverter/CSharp/AdditionalDeclaration.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public AdditionalDeclaration(string prefix, ExpressionSyntax initializer, TypeSy
2222
Type = type;
2323
}
2424

25-
public IdentifierNameSyntax IdentifierName => SyntaxFactory.IdentifierName(Id).WithAdditionalAnnotations(AdditionalLocals.Annotation);
25+
public IdentifierNameSyntax IdentifierName => SyntaxFactory.IdentifierName(Id).WithAdditionalAnnotations(HoistedNodeState.Annotation);
2626

2727

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

3434
public static T ReplaceNames<T>(T csNode, Dictionary<string, string> newNames) where T: SyntaxNode
3535
{
36-
return csNode.ReplaceNodes(csNode.GetAnnotatedNodes(AdditionalLocals.Annotation), (_, withReplaced) => {
36+
return csNode.ReplaceNodes(csNode.GetAnnotatedNodes(HoistedNodeState.Annotation), (_, withReplaced) => {
3737
var idns = (IdentifierNameSyntax)withReplaced;
3838
if (newNames.TryGetValue(idns.Identifier.ValueText, out var newName)) {
39-
return idns.WithoutAnnotations(AdditionalLocals.Annotation).WithIdentifier(SyntaxFactory.Identifier(newName));
39+
return idns.WithoutAnnotations(HoistedNodeState.Annotation).WithIdentifier(SyntaxFactory.Identifier(newName));
4040
}
4141
return idns;
4242
});

CodeConverter/CSharp/AdditionalLocals.cs

Lines changed: 0 additions & 45 deletions
This file was deleted.

CodeConverter/CSharp/DeclarationNodeVisitor.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ internal class DeclarationNodeVisitor : VBasic.VisualBasicSyntaxVisitor<Task<CSh
3737
private readonly MethodsWithHandles _methodsWithHandles = new MethodsWithHandles();
3838
private readonly Dictionary<VBSyntax.StatementSyntax, MemberDeclarationSyntax[]> _additionalDeclarations = new Dictionary<VBSyntax.StatementSyntax, MemberDeclarationSyntax[]>();
3939
private readonly AdditionalInitializers _additionalInitializers;
40-
private readonly AdditionalLocals _additionalLocals = new AdditionalLocals();
40+
private readonly HoistedNodeState _additionalLocals = new HoistedNodeState();
4141
private uint _failedMemberConversionMarkerCount;
4242
private readonly HashSet<string> _extraUsingDirectives = new HashSet<string>();
4343
private readonly VisualBasicEqualityComparison _visualBasicEqualityComparison;
@@ -490,8 +490,7 @@ private IEnumerable<MemberDeclarationSyntax> CreateMemberDeclarations(IReadOnlyC
490490
foreach (var f in fieldDecls) yield return f;
491491
} else
492492
{
493-
var additionalDeclarationInfo = _additionalLocals.GetDeclarations();
494-
if (additionalDeclarationInfo.Count() > 0) {
493+
if (_additionalLocals.GetDeclarations().Count() > 0) {
495494
foreach (var additionalDecl in CreateAdditionalLocalMembers(convertedModifiers, attributes, decl)) {
496495
yield return additionalDecl;
497496
}

CodeConverter/CSharp/ExpressionNodeVisitor.cs

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ internal class ExpressionNodeVisitor : VBasic.VisualBasicSyntaxVisitor<Task<CSha
3434
private readonly bool _optionCompareText = false;
3535
private readonly VisualBasicEqualityComparison _visualBasicEqualityComparison;
3636
private readonly Stack<ExpressionSyntax> _withBlockLhs = new Stack<ExpressionSyntax>();
37-
private readonly AdditionalLocals _additionalLocals;
37+
private readonly HoistedNodeState _additionalLocals;
3838
private readonly MethodsWithHandles _methodsWithHandles;
3939
private readonly QueryConverter _queryConverter;
4040
private readonly Lazy<IDictionary<ITypeSymbol, string>> _convertMethodsLookupByReturnType;
@@ -43,7 +43,7 @@ internal class ExpressionNodeVisitor : VBasic.VisualBasicSyntaxVisitor<Task<CSha
4343
private INamedTypeSymbol _vbBooleanTypeSymbol;
4444

4545
public ExpressionNodeVisitor(SemanticModel semanticModel,
46-
VisualBasicEqualityComparison visualBasicEqualityComparison, AdditionalLocals additionalLocals,
46+
VisualBasicEqualityComparison visualBasicEqualityComparison, HoistedNodeState additionalLocals,
4747
Compilation csCompilation, MethodsWithHandles methodsWithHandles, CommonConversions commonConversions,
4848
HashSet<string> extraUsingDirectives)
4949
{
@@ -817,20 +817,20 @@ private async Task<CSharpSyntaxNode> WithRemovedRedundantConversionOrNull(VBSynt
817817
public override async Task<CSharpSyntaxNode> VisitInvocationExpression(
818818
VBasic.Syntax.InvocationExpressionSyntax node)
819819
{
820-
var invocationSymbol = _semanticModel.GetSymbolInfo(node).ExtractBestMatch<ISymbol>();
821-
var withinLocalFunction = RequiresLocalFunction(node, invocationSymbol as IMethodSymbol);
820+
var invocationSymbol = _semanticModel.GetSymbolInfo(node).ExtractBestMatch<IMethodSymbol>();
821+
var withinLocalFunction = RequiresLocalFunction(node, invocationSymbol);
822822
if (withinLocalFunction) {
823823
_additionalLocals.PushScope();
824824
}
825825
try {
826826
var convertedInvocation = await ConvertInvocation(node, invocationSymbol);
827827
if (withinLocalFunction) {
828-
828+
return await HoistAndCallLocalFunction(node, invocationSymbol, (ExpressionSyntax)convertedInvocation);
829829
}
830830
return await ConvertInvocation(node, invocationSymbol);
831831
} finally {
832832
if (withinLocalFunction) {
833-
_additionalLocals.PopScope();
833+
_additionalLocals.PopExpressionScope();
834834
}
835835
}
836836
}
@@ -919,27 +919,26 @@ async Task<CSharpSyntaxNode> CreateElementAccess()
919919
/// <param name="invocation"></param>
920920
/// <param name="invocationSymbol"></param>
921921
/// <returns></returns>
922-
//private async Task<(string Id, StatementSyntax FunctionDeclaration)> CreateLocalByRefFunction(VBSyntax.InvocationExpressionSyntax invocation, IMethodSymbol invocationSymbol)
923-
//{
924-
// RequiresLocalFunction(invocation, invocationSymbol);
925-
926-
// var localFuncName = $"local{invocationSymbol.Name}";
927-
// const string retVariableName = "ret";
928-
// var localFuncId = SyntaxFactory.IdentifierName(localFuncName);
929-
// // Need essentially the original invocation with any byref args swapped out for temporaries
930-
// var callAndStoreResult = CommonConversions.CreateLocalVariableDeclarationAndAssignment(retVariableName,
931-
// SyntaxFactory.InvocationExpression(expression,
932-
// );
933-
934-
// var block = SyntaxFactory.Block(
935-
// callAndStoreResult,
936-
// AssignStmt(expression, tempArg),
937-
// SyntaxFactory.ReturnStatement(SyntaxFactory.IdentifierName(retVariableName))
938-
// );
939-
// var localfunction = SyntaxFactory.LocalFunctionStatement(CommonConversions.GetTypeSyntax(invocationSymbol.ReturnType),
940-
// localFuncId.Identifier).WithBody(block);
941-
// return (localFuncName, localfunction);
942-
//}
922+
private async Task<InvocationExpressionSyntax> HoistAndCallLocalFunction(VBSyntax.InvocationExpressionSyntax invocation, IMethodSymbol invocationSymbol, ExpressionSyntax csExpression)
923+
{
924+
const string retVariableName = "ret";
925+
var localFuncName = $"local{invocationSymbol.Name}";
926+
var generatedNames = new HashSet<string>();//TODO: Populate from local scope
927+
928+
var callAndStoreResult = CommonConversions.CreateLocalVariableDeclarationAndAssignment(retVariableName, csExpression);
929+
930+
var statements = await _additionalLocals.CreateLocals(invocation, new[] { callAndStoreResult }, generatedNames, _semanticModel);
931+
932+
var localFuncId = SyntaxFactory.IdentifierName(localFuncName);
933+
934+
var block = SyntaxFactory.Block(
935+
statements.Concat(SyntaxFactory.ReturnStatement(SyntaxFactory.IdentifierName(retVariableName)).Yield())
936+
);
937+
var localFunction = SyntaxFactory.LocalFunctionStatement(CommonConversions.GetTypeSyntax(invocationSymbol.ReturnType),
938+
localFuncId.Identifier).WithBody(block);
939+
_additionalLocals.Hoist(new HoistedStatement(localFunction));
940+
return SyntaxFactory.InvocationExpression(localFuncId, SyntaxFactory.ArgumentList());
941+
}
943942

944943
private bool RequiresLocalFunction(VBSyntax.InvocationExpressionSyntax invocation, IMethodSymbol invocationSymbol)
945944
{
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
using Microsoft.CodeAnalysis;
6+
using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax;
7+
using VBasic = Microsoft.CodeAnalysis.VisualBasic;
8+
using CS = Microsoft.CodeAnalysis.CSharp;
9+
using ICSharpCode.CodeConverter.Shared;
10+
using System;
11+
12+
namespace ICSharpCode.CodeConverter.CSharp
13+
{
14+
internal class HoistedNodeState
15+
{
16+
public static SyntaxAnnotation Annotation = new SyntaxAnnotation("CodeconverterAdditionalLocal");
17+
18+
private readonly Stack<List<IHoistedNode>> _hoistedNodesPerScope;
19+
20+
public HoistedNodeState()
21+
{
22+
_hoistedNodesPerScope = new Stack<List<IHoistedNode>>();
23+
}
24+
25+
public void PushScope()
26+
{
27+
_hoistedNodesPerScope.Push(new List<IHoistedNode>());
28+
}
29+
30+
public void PopScope()
31+
{
32+
_hoistedNodesPerScope.Pop();
33+
}
34+
35+
public void PopExpressionScope()
36+
{
37+
var statements = GetStatements();
38+
PopScope();
39+
foreach (var statement in statements) {
40+
Hoist(statement);
41+
}
42+
}
43+
44+
public T Hoist<T>(T additionalLocal) where T: IHoistedNode
45+
{
46+
_hoistedNodesPerScope.Peek().Add(additionalLocal);
47+
return additionalLocal;
48+
}
49+
50+
public IReadOnlyCollection<AdditionalDeclaration> GetDeclarations()
51+
{
52+
return _hoistedNodesPerScope.Peek().OfType<AdditionalDeclaration>().ToArray();
53+
}
54+
55+
public IReadOnlyCollection<AdditionalAssignment> GetPostAssignments()
56+
{
57+
return _hoistedNodesPerScope.Peek().OfType<AdditionalAssignment>().ToArray();
58+
}
59+
60+
public IReadOnlyCollection<HoistedStatement> GetStatements()
61+
{
62+
return _hoistedNodesPerScope.Peek().OfType<HoistedStatement>().ToArray();
63+
}
64+
65+
public async Task<SyntaxList<CS.Syntax.StatementSyntax>> CreateLocals(VBasic.VisualBasicSyntaxNode vbNode, IEnumerable<CS.Syntax.StatementSyntax> csNodes, HashSet<string> generatedNames, SemanticModel semanticModel)
66+
{
67+
var preDeclarations = new List<CS.Syntax.StatementSyntax>();
68+
var postAssignments = new List<CS.Syntax.StatementSyntax>();
69+
70+
var additionalDeclarationInfo = GetDeclarations();
71+
var newNames = additionalDeclarationInfo.ToDictionary(l => l.Id, l =>
72+
NameGenerator.GetUniqueVariableNameInScope(semanticModel, generatedNames, vbNode, l.Prefix)
73+
);
74+
foreach (var additionalLocal in additionalDeclarationInfo) {
75+
var decl = CommonConversions.CreateVariableDeclarationAndAssignment(newNames[additionalLocal.Id],
76+
additionalLocal.Initializer, additionalLocal.Type);
77+
preDeclarations.Add(CS.SyntaxFactory.LocalDeclarationStatement(decl));
78+
}
79+
80+
foreach (var additionalAssignment in GetPostAssignments()) {
81+
var assign = CS.SyntaxFactory.AssignmentExpression(CS.SyntaxKind.SimpleAssignmentExpression, additionalAssignment.Expression, additionalAssignment.IdentifierName);
82+
postAssignments.Add(CS.SyntaxFactory.ExpressionStatement(assign));
83+
}
84+
85+
var statementsWithUpdatedIds = AdditionalDeclaration.ReplaceNames(preDeclarations.Concat(csNodes).Concat(postAssignments), newNames);
86+
87+
return CS.SyntaxFactory.List(statementsWithUpdatedIds);
88+
}
89+
}
90+
}

CodeConverter/CSharp/HoistedNodeStateVisitor.cs

Lines changed: 5 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ namespace ICSharpCode.CodeConverter.CSharp
2121
internal class HoistedNodeStateVisitor : VBasic.VisualBasicSyntaxVisitor<Task<SyntaxList<StatementSyntax>>>
2222
{
2323
private readonly VBasic.VisualBasicSyntaxVisitor<Task<SyntaxList<StatementSyntax>>> _wrappedVisitor;
24-
private readonly AdditionalLocals _additionalLocals;
24+
private readonly HoistedNodeState _additionalLocals;
2525
private readonly SemanticModel _semanticModel;
2626
private readonly HashSet<string> _generatedNames;
2727

28-
public HoistedNodeStateVisitor(VBasic.VisualBasicSyntaxVisitor<Task<SyntaxList<StatementSyntax>>> wrappedVisitor, AdditionalLocals additionalLocals,
28+
public HoistedNodeStateVisitor(VBasic.VisualBasicSyntaxVisitor<Task<SyntaxList<StatementSyntax>>> wrappedVisitor, HoistedNodeState additionalLocals,
2929
SemanticModel semanticModel, HashSet<string> generatedNames)
3030
{
3131
_wrappedVisitor = wrappedVisitor;
@@ -44,46 +44,14 @@ private async Task<SyntaxList<StatementSyntax>> AddLocalVariables(VBasic.VisualB
4444
{
4545
_additionalLocals.PushScope();
4646
try {
47-
return await CreateLocals(node);
47+
var csNodes = await _wrappedVisitor.Visit(node);
48+
var statements = await _additionalLocals.CreateLocals(node, csNodes, _generatedNames, _semanticModel);
49+
return statements.InsertRange(0, _additionalLocals.GetStatements().Select(s => s.Statement));
4850
} finally {
4951
_additionalLocals.PopScope();
5052
}
5153
}
5254

53-
private async Task<SyntaxList<StatementSyntax>> CreateLocals(VBasic.VisualBasicSyntaxNode node)
54-
{
55-
IEnumerable<StatementSyntax> csNodes = await _wrappedVisitor.Visit(node);
56-
57-
var preDeclarations = new List<StatementSyntax>();
58-
var postAssignments = new List<StatementSyntax>();
59-
60-
var additionalDeclarationInfo = _additionalLocals.GetDeclarations();
61-
var newNames = additionalDeclarationInfo.ToDictionary(l => l.Id, l =>
62-
NameGenerator.GetUniqueVariableNameInScope(_semanticModel, _generatedNames, node, l.Prefix)
63-
);
64-
if (additionalDeclarationInfo.Count() > 0) {
65-
foreach (var additionalLocal in additionalDeclarationInfo) {
66-
var decl = CommonConversions.CreateVariableDeclarationAndAssignment(newNames[additionalLocal.Id],
67-
additionalLocal.Initializer, additionalLocal.Type);
68-
preDeclarations.Add(SyntaxFactory.LocalDeclarationStatement(decl));
69-
}
70-
}
71-
var additionalAssignmentInfo = _additionalLocals.GetPostAssignments();
72-
if (additionalAssignmentInfo.Count() > 0)
73-
{
74-
75-
foreach (var additionalAssignment in additionalAssignmentInfo)
76-
{
77-
var assign = SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, additionalAssignment.Expression, additionalAssignment.IdentifierName);
78-
postAssignments.Add(SyntaxFactory.ExpressionStatement(assign));
79-
}
80-
}
81-
82-
var statementsWithUpdatedIds = AdditionalDeclaration.ReplaceNames(preDeclarations.Concat(csNodes).Concat(postAssignments), newNames);
83-
84-
return SyntaxFactory.List(statementsWithUpdatedIds);
85-
}
86-
8755
public override Task<SyntaxList<StatementSyntax>> VisitAddRemoveHandlerStatement(VBSyntax.AddRemoveHandlerStatementSyntax node) => AddLocalVariables(node);
8856
public override Task<SyntaxList<StatementSyntax>> VisitAssignmentStatement(VBSyntax.AssignmentStatementSyntax node) => AddLocalVariables(node);
8957
public override Task<SyntaxList<StatementSyntax>> VisitCallStatement(VBSyntax.CallStatementSyntax node) => AddLocalVariables(node);
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using Microsoft.CodeAnalysis.CSharp.Syntax;
2+
3+
namespace ICSharpCode.CodeConverter.CSharp
4+
{
5+
internal class HoistedStatement : IHoistedNode
6+
{
7+
public StatementSyntax Statement { get; }
8+
9+
public HoistedStatement(StatementSyntax statementSyntax)
10+
{
11+
Statement = statementSyntax;
12+
}
13+
}
14+
}

CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ internal class MethodBodyExecutableStatementVisitor : VBasic.VisualBasicSyntaxVi
4141

4242
private CommonConversions CommonConversions { get; }
4343

44-
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)
44+
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)
4545
{
4646
var solution = commonConversions.Document.Project.Solution;
4747
var declarationsToInlineInLoop = await solution.GetDescendantsToInlineInLoopAsync(semanticModel, node);
@@ -54,7 +54,7 @@ public static async Task<MethodBodyExecutableStatementVisitor> CreateAsync(VBasi
5454
private MethodBodyExecutableStatementVisitor(VBasic.VisualBasicSyntaxNode methodNode, SemanticModel semanticModel,
5555
CommentConvertingVisitorWrapper expressionVisitor, CommonConversions commonConversions,
5656
Stack<ExpressionSyntax> withBlockLhs, HashSet<string> extraUsingDirectives,
57-
AdditionalLocals additionalLocals, MethodsWithHandles methodsWithHandles, HashSet<ILocalSymbol> localsToInlineInLoop)
57+
HoistedNodeState additionalLocals, MethodsWithHandles methodsWithHandles, HashSet<ILocalSymbol> localsToInlineInLoop)
5858
{
5959
_methodNode = methodNode;
6060
_semanticModel = semanticModel;

0 commit comments

Comments
 (0)