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