Skip to content

Commit 64d4cac

Browse files
Extract class
1 parent cde4d4d commit 64d4cac

File tree

3 files changed

+193
-162
lines changed

3 files changed

+193
-162
lines changed

CodeConverter/CSharp/DeclarationNodeVisitor.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ public DeclarationNodeVisitor(Document document, Compilation compilation, Semant
6060
_csSyntaxGenerator = csSyntaxGenerator;
6161
_visualBasicEqualityComparison = new VisualBasicEqualityComparison(_semanticModel, _extraUsingDirectives);
6262
TriviaConvertingDeclarationVisitor = new CommentConvertingVisitorWrapper(this, _semanticModel.SyntaxTree);
63-
var typeConversionAnalyzer = new TypeConversionAnalyzer(semanticModel, csCompilation, _extraUsingDirectives, _csSyntaxGenerator, _visualBasicEqualityComparison);
63+
var expressionEvaluator = new ExpressionEvaluator(semanticModel, _visualBasicEqualityComparison);
64+
var typeConversionAnalyzer = new TypeConversionAnalyzer(semanticModel, csCompilation, _extraUsingDirectives, _csSyntaxGenerator, expressionEvaluator);
6465
CommonConversions = new CommonConversions(document, semanticModel, typeConversionAnalyzer, csSyntaxGenerator, csCompilation);
6566
_additionalInitializers = new AdditionalInitializers();
6667
var expressionNodeVisitor = new ExpressionNodeVisitor(semanticModel, _visualBasicEqualityComparison, _additionalLocals, csCompilation, _methodsWithHandles, CommonConversions, _extraUsingDirectives);
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using ICSharpCode.CodeConverter.Util;
6+
using ICSharpCode.CodeConverter.Util.FromRoslyn;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.CSharp;
9+
using Microsoft.CodeAnalysis.Editing;
10+
using Microsoft.CodeAnalysis.FindSymbols;
11+
using Microsoft.CodeAnalysis.Operations;
12+
using Microsoft.CodeAnalysis.VisualBasic;
13+
using Microsoft.VisualBasic;
14+
using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax;
15+
using Microsoft.VisualBasic.CompilerServices;
16+
using Microsoft.CodeAnalysis.CSharp.Syntax;
17+
using System.Linq.Expressions;
18+
19+
namespace ICSharpCode.CodeConverter.CSharp
20+
{
21+
internal class ExpressionEvaluator
22+
{
23+
public static IReadOnlyDictionary<string, MethodInfo> ConversionsTypeFullNames { get; } = GetConversionsMethodsByTypeFullName();
24+
public static IReadOnlyDictionary<string, MethodInfo> ConversionsMethodNames { get; } = ConversionsTypeFullNames.Values.Concat(GetStringsMethods()).ToDictionary(m => m.Name, m => m);
25+
private readonly VisualBasicEqualityComparison _visualBasicEqualityComparison;
26+
private readonly SemanticModel _semanticModel;
27+
28+
public ExpressionEvaluator(SemanticModel semanticModel, VisualBasicEqualityComparison visualBasicEqualityComparison)
29+
{
30+
_semanticModel = semanticModel;
31+
_visualBasicEqualityComparison = visualBasicEqualityComparison;
32+
}
33+
34+
private static Dictionary<string, MethodInfo> GetConversionsMethodsByTypeFullName()
35+
{
36+
return typeof(Conversions).GetMethods(BindingFlags.Public | BindingFlags.Static)
37+
.Where(m => m.Name.StartsWith("To") && m.GetParameters().Length == 1 && m.ReturnType?.FullName != null)
38+
.ToLookup(m => m.ReturnType.FullName, m => m)
39+
.ToDictionary(kvp => kvp.Key, GetMostGeneralOverload);
40+
}
41+
private static MethodInfo GetMostGeneralOverload(IGrouping<string, MethodInfo> kvp)
42+
{
43+
return kvp.OrderByDescending(mi => mi.GetParameters().First().ParameterType == typeof(object)).First();
44+
}
45+
46+
private static IEnumerable<MethodInfo> GetStringsMethods()
47+
{
48+
return typeof(Strings).GetMethods(BindingFlags.Public | BindingFlags.Static)
49+
.ToLookup(m => m.Name, m => m)
50+
.Where(g => g.Count() == 1).SelectMany(ms => ms);
51+
}
52+
53+
public ExpressionSyntax GetConstantOrNull(VBSyntax.ExpressionSyntax vbNode, ITypeSymbol type, ExpressionSyntax csNode)
54+
{
55+
var vbOperation = _semanticModel.GetOperation(vbNode).SkipParens(true);
56+
57+
// Guideline tradeoff: Usually would aim for erring on the side of correct runtime behaviour. But making lots of simple constants unreadable for the sake of an edge case that will turn into an easily fixed compile error seems overkill.
58+
// See https://github.com/icsharpcode/CodeConverter/blob/master/.github/CONTRIBUTING.md#deciding-what-the-output-should-be
59+
if (Equals(vbOperation.Type, type) && IsProbablyConstExpression(vbOperation)) return csNode;
60+
61+
if (TryCompileTimeEvaluate(vbOperation, out var result) && ConversionsTypeFullNames.TryGetValue(type.GetFullMetadataName(), out var method)) {
62+
result = method.Invoke(null, new[] { result });
63+
return LiteralConversions.GetLiteralExpression(result, convertedType: type);
64+
}
65+
66+
return null;
67+
}
68+
69+
/// <remarks>Deal with cases like "2*PI" without inlining the const</remarks>
70+
private bool IsProbablyConstExpression(IOperation op)
71+
{
72+
op = op.SkipParens(true);
73+
74+
if (op is IFieldReferenceOperation fro && fro.Field.IsConst || op is ILocalReferenceOperation lro && lro.Local.IsConst || op is ILiteralOperation) {
75+
return true;
76+
}
77+
78+
if (op is IBinaryOperation bo && IsProbablyConstExpression(bo.LeftOperand) && IsProbablyConstExpression(bo.RightOperand)) {
79+
return true;
80+
}
81+
82+
return false;
83+
}
84+
85+
private bool TryCompileTimeEvaluate(IOperation vbOperation, out object result)
86+
{
87+
result = null;
88+
if (vbOperation.ConstantValue.HasValue) {
89+
result = vbOperation.ConstantValue.Value;
90+
return true;
91+
}
92+
return TryCompileTimeEvaluateInvocation(vbOperation, out result) || TryCompileTimeEvaluateBinaryExpression(vbOperation, out result);
93+
}
94+
95+
private bool TryCompileTimeEvaluateInvocation(IOperation vbOperation, out object result)
96+
{
97+
if (vbOperation is IInvocationOperation invocationOperation &&
98+
ConversionsMethodNames.TryGetValue(invocationOperation.TargetMethod.Name,
99+
out var conversionMethodInfo) && invocationOperation.Arguments.Length == 1 &&
100+
invocationOperation.Arguments.Single().Value.ConstantValue.HasValue) {
101+
result = conversionMethodInfo.Invoke(null,
102+
new[] { invocationOperation.Arguments.Single().Value.ConstantValue.Value });
103+
104+
return true;
105+
}
106+
107+
result = null;
108+
return false;
109+
}
110+
111+
private bool TryCompileTimeEvaluateBinaryExpression(IOperation vbOperation, out object result)
112+
{
113+
result = null;
114+
if (vbOperation is IBinaryOperation binaryOperation && TryCompileTimeEvaluate(binaryOperation.LeftOperand, out var leftResult) && TryCompileTimeEvaluate(binaryOperation.RightOperand, out var rightResult)) {
115+
var textComparisonCaseInsensitive = _visualBasicEqualityComparison.OptionCompareTextCaseInsensitive;
116+
switch (binaryOperation.OperatorKind) {
117+
case BinaryOperatorKind.Add:
118+
result = Operators.AddObject(leftResult, rightResult);
119+
break;
120+
case BinaryOperatorKind.Subtract:
121+
result = Operators.SubtractObject(leftResult, rightResult);
122+
break;
123+
case BinaryOperatorKind.Multiply:
124+
result = Operators.MultiplyObject(leftResult, rightResult);
125+
break;
126+
case BinaryOperatorKind.Divide:
127+
result = Operators.DivideObject(leftResult, rightResult);
128+
break;
129+
case BinaryOperatorKind.IntegerDivide:
130+
result = Operators.IntDivideObject(leftResult, rightResult);
131+
break;
132+
case BinaryOperatorKind.Remainder:
133+
result = Operators.ModObject(leftResult, rightResult);
134+
break;
135+
case BinaryOperatorKind.Power:
136+
result = Operators.ExponentObject(leftResult, rightResult);
137+
break;
138+
case BinaryOperatorKind.LeftShift:
139+
result = Operators.LeftShiftObject(leftResult, rightResult);
140+
break;
141+
case BinaryOperatorKind.RightShift:
142+
result = Operators.RightShiftObject(leftResult, rightResult);
143+
break;
144+
case BinaryOperatorKind.And:
145+
result = Operators.AndObject(leftResult, rightResult);
146+
break;
147+
case BinaryOperatorKind.Or:
148+
result = Operators.OrObject(leftResult, rightResult);
149+
break;
150+
case BinaryOperatorKind.ExclusiveOr:
151+
result = Operators.XorObject(leftResult, rightResult);
152+
break;
153+
case BinaryOperatorKind.Concatenate:
154+
result = Operators.ConcatenateObject(leftResult, rightResult);
155+
break;
156+
case BinaryOperatorKind.Equals:
157+
case BinaryOperatorKind.ObjectValueEquals:
158+
result = Operators.CompareObjectEqual(leftResult, rightResult, textComparisonCaseInsensitive);
159+
break;
160+
case BinaryOperatorKind.NotEquals:
161+
case BinaryOperatorKind.ObjectValueNotEquals:
162+
result = Operators.CompareObjectNotEqual(leftResult, rightResult, textComparisonCaseInsensitive);
163+
break;
164+
case BinaryOperatorKind.LessThan:
165+
result = Operators.CompareObjectLess(leftResult, rightResult, textComparisonCaseInsensitive);
166+
break;
167+
case BinaryOperatorKind.LessThanOrEqual:
168+
result = Operators.CompareObjectLessEqual(leftResult, rightResult, textComparisonCaseInsensitive);
169+
break;
170+
case BinaryOperatorKind.GreaterThanOrEqual:
171+
result = Operators.CompareObjectGreaterEqual(leftResult, rightResult, textComparisonCaseInsensitive);
172+
break;
173+
case BinaryOperatorKind.GreaterThan:
174+
result = Operators.CompareObjectGreater(leftResult, rightResult, textComparisonCaseInsensitive);
175+
break;
176+
default:
177+
return false;
178+
179+
}
180+
181+
return true;
182+
}
183+
return false;
184+
}
185+
}
186+
}

0 commit comments

Comments
 (0)