@@ -20,6 +20,9 @@ namespace System.Linq.Dynamic.Core.Parser;
20
20
/// </summary>
21
21
public class ExpressionParser
22
22
{
23
+ private static readonly string [ ] OutKeywords = { "out" , "$out" } ;
24
+ private const string DiscardVariable = "_" ;
25
+
23
26
private const string MethodOrderBy = nameof ( Queryable . OrderBy ) ;
24
27
private const string MethodOrderByDescending = nameof ( Queryable . OrderByDescending ) ;
25
28
private const string MethodThenBy = nameof ( Queryable . ThenBy ) ;
@@ -165,7 +168,31 @@ public Expression Parse(Type? resultType, bool createParameterCtor = true)
165
168
return expr ;
166
169
}
167
170
168
- #pragma warning disable 0219
171
+ // out keyword
172
+ private Expression ParseOutKeyword ( )
173
+ {
174
+ if ( _textParser . CurrentToken . Id == TokenId . Identifier && OutKeywords . Contains ( _textParser . CurrentToken . Text ) )
175
+ {
176
+ // Go to next token (which should be a '_')
177
+ _textParser . NextToken ( ) ;
178
+
179
+ var variableName = _textParser . CurrentToken . Text ;
180
+ if ( variableName != DiscardVariable )
181
+ {
182
+ throw ParseError ( _textParser . CurrentToken . Pos , Res . OutKeywordRequiresDiscard ) ;
183
+ }
184
+
185
+ // Advance to next token
186
+ _textParser . NextToken ( ) ;
187
+
188
+ // Use MakeByRefType() to indicate that it's a by-reference type because C# uses this for both 'ref' and 'out' parameters.
189
+ // The "typeof(object).MakeByRefType()" is used, this will be changed later in the flow to the real type.
190
+ return Expression . Parameter ( typeof ( object ) . MakeByRefType ( ) , variableName ) ;
191
+ }
192
+
193
+ return ParseConditionalOperator ( ) ;
194
+ }
195
+
169
196
internal IList < DynamicOrdering > ParseOrdering ( bool forceThenBy = false )
170
197
{
171
198
var orderings = new List < DynamicOrdering > ( ) ;
@@ -206,7 +233,6 @@ internal IList<DynamicOrdering> ParseOrdering(bool forceThenBy = false)
206
233
_textParser . ValidateToken ( TokenId . End , Res . SyntaxError ) ;
207
234
return orderings ;
208
235
}
209
- #pragma warning restore 0219
210
236
211
237
// ?: operator
212
238
private Expression ParseConditionalOperator ( )
@@ -931,9 +957,9 @@ private Expression ParseIdentifier()
931
957
932
958
var isValidKeyWord = _keywordsHelper . TryGetValue ( _textParser . CurrentToken . Text , out var value ) ;
933
959
934
-
960
+
935
961
bool shouldPrioritizeType = true ;
936
-
962
+
937
963
if ( _parsingConfig . PrioritizePropertyOrFieldOverTheType && value is Type )
938
964
{
939
965
bool isPropertyOrField = _it != null && FindPropertyOrField ( _it . Type , _textParser . CurrentToken . Text , false ) != null ;
@@ -1774,16 +1800,19 @@ private Expression ParseMemberAccess(Type? type, Expression? expression)
1774
1800
throw ParseError ( errorPos , Res . MethodsAreInaccessible , TypeHelper . GetTypeName ( method . DeclaringType ! ) ) ;
1775
1801
}
1776
1802
1777
- if ( method . IsGenericMethod )
1803
+ MethodInfo methodToCall ;
1804
+ if ( ! method . IsGenericMethod )
1805
+ {
1806
+ methodToCall = method ;
1807
+ }
1808
+ else
1778
1809
{
1779
1810
var genericParameters = method . GetParameters ( ) . Where ( p => p . ParameterType . IsGenericParameter ) ;
1780
1811
var typeArguments = genericParameters . Select ( a => args [ a . Position ] . Type ) ;
1781
- var constructedMethod = method . MakeGenericMethod ( typeArguments . ToArray ( ) ) ;
1782
-
1783
- return Expression . Call ( expression , constructedMethod , args ) ;
1812
+ methodToCall = method . MakeGenericMethod ( typeArguments . ToArray ( ) ) ;
1784
1813
}
1785
-
1786
- return Expression . Call ( expression , method , args ) ;
1814
+
1815
+ return CallMethod ( expression , methodToCall , args ) ;
1787
1816
1788
1817
default :
1789
1818
throw ParseError ( errorPos , Res . AmbiguousMethodInvocation , id , TypeHelper . GetTypeName ( type ) ) ;
@@ -1848,6 +1877,59 @@ private Expression ParseMemberAccess(Type? type, Expression? expression)
1848
1877
throw ParseError ( errorPos , Res . UnknownPropertyOrField , id , TypeHelper . GetTypeName ( type ) ) ;
1849
1878
}
1850
1879
1880
+ private static Expression CallMethod ( Expression ? expression , MethodInfo methodToCall , Expression [ ] args )
1881
+ {
1882
+ #if NET35
1883
+ return Expression . Call ( expression , methodToCall , args ) ;
1884
+ #else
1885
+ if ( ! args . OfType < ParameterExpression > ( ) . Any ( p => p . IsByRef ) )
1886
+ {
1887
+ return Expression . Call ( expression , methodToCall , args ) ;
1888
+ }
1889
+
1890
+ // A list which is used to store all method arguments.
1891
+ var newList = new List < Expression > ( ) ;
1892
+
1893
+ // A list which contains the variable expression for the 'out' parameter, and also contains the returnValue variable.
1894
+ var blockList = new List < ParameterExpression > ( ) ;
1895
+
1896
+ foreach ( var arg in args )
1897
+ {
1898
+ if ( arg is ParameterExpression { IsByRef : true } parameterExpression )
1899
+ {
1900
+ // Create a variable expression to hold the 'out' parameter.
1901
+ var variable = Expression . Variable ( parameterExpression . Type , parameterExpression . Name ) ;
1902
+
1903
+ newList . Add ( variable ) ;
1904
+ blockList . Add ( variable ) ;
1905
+ }
1906
+ else
1907
+ {
1908
+ newList . Add ( arg ) ;
1909
+ }
1910
+ }
1911
+
1912
+ // Create a method call expression to call the method
1913
+ var methodCall = Expression . Call ( expression , methodToCall , newList ) ;
1914
+
1915
+ // Create a variable to hold the return value
1916
+ var returnValue = Expression . Variable ( methodToCall . ReturnType ) ;
1917
+
1918
+ // Add this return variable to the blockList
1919
+ blockList . Add ( returnValue ) ;
1920
+
1921
+ // Create the block to return the boolean value.
1922
+ var block = Expression . Block (
1923
+ blockList . ToArray ( ) ,
1924
+ Expression . Assign ( returnValue , methodCall ) ,
1925
+ returnValue
1926
+ ) ;
1927
+
1928
+ // Create the lambda expression (note that expression must be a ParameterExpression).
1929
+ return Expression . Lambda ( block , ( ParameterExpression ) expression ! ) ;
1930
+ #endif
1931
+ }
1932
+
1851
1933
private Expression ParseAsLambda ( string id )
1852
1934
{
1853
1935
// This might be an internal variable for use within a lambda expression, so store it as such
@@ -2021,7 +2103,7 @@ private Expression ParseEnumerable(Expression instance, Type elementType, string
2021
2103
2022
2104
private Type ResolveTypeFromArgumentExpression ( string functionName , Expression argumentExpression , int ? arguments = null )
2023
2105
{
2024
- string argument = arguments == null ? string . Empty : arguments == 1 ? "first " : "second " ;
2106
+ var argument = arguments == null ? string . Empty : arguments == 1 ? "first " : "second " ;
2025
2107
2026
2108
switch ( argumentExpression )
2027
2109
{
@@ -2088,7 +2170,7 @@ private Expression[] ParseArguments()
2088
2170
var argList = new List < Expression > ( ) ;
2089
2171
while ( true )
2090
2172
{
2091
- var argumentExpression = ParseConditionalOperator ( ) ;
2173
+ var argumentExpression = ParseOutKeyword ( ) ;
2092
2174
2093
2175
_expressionHelper . WrapConstantExpression ( ref argumentExpression ) ;
2094
2176
@@ -2102,6 +2184,11 @@ private Expression[] ParseArguments()
2102
2184
_textParser . NextToken ( ) ;
2103
2185
}
2104
2186
2187
+ //if (argList.OfType<ParameterExpression>().Count() > 1)
2188
+ //{
2189
+ // throw ParseError(_textParser.CurrentToken.Pos, Res.OutVariableSingleRequired);
2190
+ //}
2191
+
2105
2192
return argList . ToArray ( ) ;
2106
2193
}
2107
2194
0 commit comments