@@ -887,7 +887,7 @@ internal static class CurryClosureActions
887
887
888
888
#region Collect Bound Constants
889
889
890
- private static bool IsClosureBoundConstant ( object value , TypeInfo type ) =>
890
+ private static bool IsClosureBoundConstant ( object value , TypeInfo type ) =>
891
891
value is Delegate ||
892
892
! type . IsPrimitive && ! type . IsEnum && ! ( value is string ) && ! ( value is Type ) ;
893
893
@@ -1310,13 +1310,7 @@ public static bool TryEmit(object exprObj, ExpressionType exprNodeType, Type exp
1310
1310
case ExpressionType . NotEqual :
1311
1311
return TryEmitComparison ( exprObj , exprNodeType , paramExprs , il , ref closure ) ;
1312
1312
1313
- case ExpressionType . Add :
1314
- case ExpressionType . AddChecked :
1315
- case ExpressionType . Subtract :
1316
- case ExpressionType . SubtractChecked :
1317
- case ExpressionType . Multiply :
1318
- case ExpressionType . MultiplyChecked :
1319
- case ExpressionType . Divide :
1313
+ case ExpressionType arithmetic when Tools . IsArithmetic ( arithmetic ) :
1320
1314
return TryEmitArithmeticOperation ( exprObj , exprType , exprNodeType , paramExprs , il , ref closure ) ;
1321
1315
1322
1316
case ExpressionType . AndAlso :
@@ -1329,7 +1323,8 @@ public static bool TryEmit(object exprObj, ExpressionType exprNodeType, Type exp
1329
1323
case ExpressionType . Conditional :
1330
1324
return TryEmitConditional ( ( ConditionalExpression ) exprObj , paramExprs , il , ref closure ) ;
1331
1325
1332
- case ExpressionType . Assign :
1326
+ case ExpressionType arithmeticAssign when Tools . GetArithmeticAssignOrSelf ( arithmeticAssign ) != arithmeticAssign :
1327
+ case ExpressionType . Assign :
1333
1328
return TryEmitAssign ( exprObj , exprType , paramExprs , il , ref closure ) ;
1334
1329
1335
1330
case ExpressionType . Block :
@@ -1655,9 +1650,12 @@ private static bool TryEmitParameter(object paramExprObj, Type paramType,
1655
1650
{
1656
1651
if ( closure . HasClosure )
1657
1652
paramIndex += 1 ; // shift parameter indices by one, because the first one will be closure
1658
-
1659
- var asAddress = parent == ExpressionType . Call && paramType . GetTypeInfo ( ) . IsValueType && ! Tools . IsByRefParameter ( paramExprObj ) ;
1653
+ var isByRef = Tools . IsByRefParameter ( paramExprObj ) ;
1654
+ var asAddress = parent == ExpressionType . Call && paramType . GetTypeInfo ( ) . IsValueType && ! isByRef ;
1660
1655
EmitLoadParamArg ( il , paramIndex , asAddress ) ;
1656
+ if ( isByRef && Tools . IsArithmetic ( parent ) )
1657
+ EmitDereference ( il , paramType ) ;
1658
+
1661
1659
return true ;
1662
1660
}
1663
1661
@@ -1692,6 +1690,35 @@ private static bool TryEmitParameter(object paramExprObj, Type paramType,
1692
1690
return true ;
1693
1691
}
1694
1692
1693
+ private static void EmitDereference ( ILGenerator il , Type type )
1694
+ {
1695
+ if ( type == typeof ( Int32 ) )
1696
+ il . Emit ( OpCodes . Ldind_I4 ) ;
1697
+ else if ( type == typeof ( Int64 ) )
1698
+ il . Emit ( OpCodes . Ldind_I8 ) ;
1699
+ else if ( type == typeof ( Int16 ) )
1700
+ il . Emit ( OpCodes . Ldind_I2 ) ;
1701
+ else if ( type == typeof ( SByte ) )
1702
+ il . Emit ( OpCodes . Ldind_I1 ) ;
1703
+ else if ( type == typeof ( Single ) )
1704
+ il . Emit ( OpCodes . Ldind_R4 ) ;
1705
+ else if ( type == typeof ( Double ) )
1706
+ il . Emit ( OpCodes . Ldind_R8 ) ;
1707
+ else if ( type == typeof ( IntPtr ) )
1708
+ il . Emit ( OpCodes . Ldind_I ) ;
1709
+ else if ( type == typeof ( UIntPtr ) )
1710
+ il . Emit ( OpCodes . Ldind_I ) ;
1711
+ else if ( type == typeof ( Byte ) )
1712
+ il . Emit ( OpCodes . Ldind_U1 ) ;
1713
+ else if ( type == typeof ( UInt16 ) )
1714
+ il . Emit ( OpCodes . Ldind_U2 ) ;
1715
+ else if ( type == typeof ( UInt32 ) )
1716
+ il . Emit ( OpCodes . Ldind_U4 ) ;
1717
+ else
1718
+ il . Emit ( OpCodes . Ldobj , type ) ;
1719
+ //TODO: UInt64 as there is no OpCodes? Ldind_Ref?
1720
+ }
1721
+
1695
1722
// loads argument at paramIndex onto evaluation stack
1696
1723
private static void EmitLoadParamArg ( ILGenerator il , int paramIndex , bool asAddress )
1697
1724
{
@@ -1774,6 +1801,11 @@ private static bool TryEmitConvert(object exprObj, Type targetType,
1774
1801
object [ ] paramExprs , ILGenerator il , ref ClosureInfo closure )
1775
1802
{
1776
1803
var e = exprObj . GetOperandExprInfo ( ) ;
1804
+
1805
+ var m = exprObj is UnaryExpression ue ? ue . Method : ( ( UnaryExpressionInfo ) exprObj ) . Method ;
1806
+ if ( m != null && m . Name [ 2 ] != '_' && m . Name != "op_Implicit" && m . Name != "op_Explicit" )
1807
+ return TryEmit ( e . Expr , e . NodeType , e . Type , paramExprs , il , ref closure , ExpressionType . Call , 0 ) && EmitMethodCall ( il , m ) ;
1808
+
1777
1809
if ( ! TryEmit ( e . Expr , e . NodeType , e . Type , paramExprs , il , ref closure , ExpressionType . Convert ) )
1778
1810
return false ;
1779
1811
@@ -1812,30 +1844,36 @@ private static bool TryEmitConvert(object exprObj, Type targetType,
1812
1844
// Conversion to Nullable: new Nullable<T>(T val);
1813
1845
else if ( targetTypeInfo . IsGenericType && targetTypeInfo . GetGenericTypeDefinition ( ) == typeof ( Nullable < > ) )
1814
1846
il . Emit ( OpCodes . Newobj , targetType . GetConstructorByArgs ( targetTypeInfo . GenericTypeArguments [ 0 ] ) ) ;
1815
-
1816
- else if ( targetType == typeof ( int ) )
1817
- il . Emit ( OpCodes . Conv_I4 ) ;
1818
- else if ( targetType == typeof ( float ) )
1819
- il . Emit ( OpCodes . Conv_R4 ) ;
1820
- else if ( targetType == typeof ( uint ) )
1821
- il . Emit ( OpCodes . Conv_U4 ) ;
1822
- else if ( targetType == typeof ( sbyte ) )
1823
- il . Emit ( OpCodes . Conv_I1 ) ;
1824
- else if ( targetType == typeof ( byte ) )
1825
- il . Emit ( OpCodes . Conv_U1 ) ;
1826
- else if ( targetType == typeof ( short ) )
1827
- il . Emit ( OpCodes . Conv_I2 ) ;
1828
- else if ( targetType == typeof ( ushort ) )
1829
- il . Emit ( OpCodes . Conv_U2 ) ;
1830
- else if ( targetType == typeof ( long ) )
1831
- il . Emit ( OpCodes . Conv_I8 ) ;
1832
- else if ( targetType == typeof ( ulong ) )
1833
- il . Emit ( OpCodes . Conv_U8 ) ;
1834
- else if ( targetType == typeof ( double ) )
1835
- il . Emit ( OpCodes . Conv_R8 ) ;
1847
+ else
1848
+ {
1849
+ if ( targetType . GetTypeInfo ( ) . IsEnum )
1850
+ targetType = Enum . GetUnderlyingType ( targetType ) ;
1851
+
1852
+ if ( targetType == typeof ( int ) )
1853
+ il . Emit ( OpCodes . Conv_I4 ) ;
1854
+ else if ( targetType == typeof ( float ) )
1855
+ il . Emit ( OpCodes . Conv_R4 ) ;
1856
+ else if ( targetType == typeof ( uint ) )
1857
+ il . Emit ( OpCodes . Conv_U4 ) ;
1858
+ else if ( targetType == typeof ( sbyte ) )
1859
+ il . Emit ( OpCodes . Conv_I1 ) ;
1860
+ else if ( targetType == typeof ( byte ) )
1861
+ il . Emit ( OpCodes . Conv_U1 ) ;
1862
+ else if ( targetType == typeof ( short ) )
1863
+ il . Emit ( OpCodes . Conv_I2 ) ;
1864
+ else if ( targetType == typeof ( ushort ) )
1865
+ il . Emit ( OpCodes . Conv_U2 ) ;
1866
+ else if ( targetType == typeof ( long ) )
1867
+ il . Emit ( OpCodes . Conv_I8 ) ;
1868
+ else if ( targetType == typeof ( ulong ) )
1869
+ il . Emit ( OpCodes . Conv_U8 ) ;
1870
+ else if ( targetType == typeof ( double ) )
1871
+ il . Emit ( OpCodes . Conv_R8 ) ;
1872
+
1873
+ else // cast as the last resort and let's it fail if unlucky
1874
+ il . Emit ( OpCodes . Castclass , targetType ) ;
1875
+ }
1836
1876
1837
- else // cast as the last resort and let's it fail if unlucky
1838
- il . Emit ( OpCodes . Castclass , targetType ) ;
1839
1877
return true ;
1840
1878
}
1841
1879
@@ -2234,7 +2272,7 @@ private static bool TryEmitAssign(object exprObj, Type exprType,
2234
2272
object [ ] paramExprs , ILGenerator il , ref ClosureInfo closure )
2235
2273
{
2236
2274
object left , right ;
2237
- ExpressionType leftNodeType , rightNodeType ;
2275
+ ExpressionType leftNodeType , rightNodeType , nodeType ;
2238
2276
2239
2277
var expr = exprObj as BinaryExpression ;
2240
2278
if ( expr != null )
@@ -2243,6 +2281,7 @@ private static bool TryEmitAssign(object exprObj, Type exprType,
2243
2281
right = expr . Right ;
2244
2282
leftNodeType = expr . Left . NodeType ;
2245
2283
rightNodeType = expr . Right . NodeType ;
2284
+ nodeType = expr . NodeType ;
2246
2285
}
2247
2286
else
2248
2287
{
@@ -2251,6 +2290,7 @@ private static bool TryEmitAssign(object exprObj, Type exprType,
2251
2290
right = info . Right ;
2252
2291
leftNodeType = left . GetNodeType ( ) ;
2253
2292
rightNodeType = right . GetNodeType ( ) ;
2293
+ nodeType = info . NodeType ;
2254
2294
}
2255
2295
2256
2296
// if this assignment is part of a single body-less expression or the result of a block
@@ -2275,8 +2315,16 @@ private static bool TryEmitAssign(object exprObj, Type exprType,
2275
2315
if ( Tools . IsByRefParameter ( left ) )
2276
2316
{
2277
2317
EmitLoadParamArg ( il , paramIndex , false ) ;
2278
- if ( ! TryEmit ( right , rightNodeType , exprType , paramExprs , il , ref closure , ExpressionType . Assign ) )
2318
+
2319
+ var newExprType = Tools . GetArithmeticAssignOrSelf ( nodeType ) ;
2320
+ if ( newExprType != nodeType )
2321
+ {
2322
+ if ( ! TryEmit ( exprObj , newExprType , exprType , paramExprs , il , ref closure , ExpressionType . Assign ) )
2323
+ return false ;
2324
+ }
2325
+ else if ( ! TryEmit ( right , rightNodeType , exprType , paramExprs , il , ref closure , ExpressionType . Assign ) )
2279
2326
return false ;
2327
+
2280
2328
EmitByRefStore ( il , left . ToExpression ( ) . Type ) ;
2281
2329
}
2282
2330
else
@@ -2466,8 +2514,8 @@ private static void EmitByRefStore(ILGenerator il, Type type)
2466
2514
il . Emit ( OpCodes . Stind_Ref ) ;
2467
2515
else if ( type == typeof ( IntPtr ) || type == typeof ( UIntPtr ) )
2468
2516
il . Emit ( OpCodes . Stind_I ) ;
2469
- else
2470
- throw new NotImplementedException ( ) ;
2517
+ else
2518
+ il . Emit ( OpCodes . Stobj , type ) ;
2471
2519
}
2472
2520
2473
2521
private static bool TryEmitIndexAssign ( IndexExpression indexExpr , Type instType , Type elementType , ILGenerator il )
@@ -2539,7 +2587,6 @@ private static bool TryEmitMethodCall(object exprObj, object[] paramExprs, ILGen
2539
2587
return EmitMethodCall ( il , method ) ;
2540
2588
}
2541
2589
2542
-
2543
2590
// if call is done into byref method parameters there is no indicators in tree, so grab that from method
2544
2591
// current approach is to copy into new list only if there are by ref with by ref parameters,
2545
2592
// possible approach to store hit map of small size (possible 256 bit #89) to check if parameter is by ref
@@ -2845,6 +2892,9 @@ private static bool TryEmitComparison(object exprObj, ExpressionType exprNodeTyp
2845
2892
{
2846
2893
leftOpType = expr . Left . Type ;
2847
2894
rightOpType = expr . Right . Type ;
2895
+
2896
+ if ( expr . Right is ConstantExpression c && c . Value == null && expr . Right . Type == typeof ( object ) )
2897
+ rightOpType = leftOpType ;
2848
2898
}
2849
2899
else
2850
2900
{
@@ -2930,7 +2980,7 @@ var methodName
2930
2980
private static bool TryEmitArithmeticOperation ( object exprObj , Type exprType , ExpressionType exprNodeType ,
2931
2981
object [ ] paramExprs , ILGenerator il , ref ClosureInfo closure )
2932
2982
{
2933
- if ( ! EmitBinary ( exprObj , paramExprs , il , ref closure , ExpressionType . Default ) )
2983
+ if ( ! EmitBinary ( exprObj , paramExprs , il , ref closure , exprNodeType ) )
2934
2984
return false ;
2935
2985
2936
2986
var exprTypeInfo = exprType . GetTypeInfo ( ) ;
@@ -2942,7 +2992,9 @@ var methodName
2942
2992
: exprNodeType == ExpressionType . Subtract ? "op_Subtraction"
2943
2993
: exprNodeType == ExpressionType . SubtractChecked ? "op_Subtraction"
2944
2994
: exprNodeType == ExpressionType . Multiply ? "op_Multiply"
2995
+ : exprNodeType == ExpressionType . MultiplyChecked ? "op_Multiply"
2945
2996
: exprNodeType == ExpressionType . Divide ? "op_Division"
2997
+ : exprNodeType == ExpressionType . Modulo ? "op_Modulus"
2946
2998
: null ;
2947
2999
2948
3000
var method = methodName != null ? exprTypeInfo . GetDeclaredMethod ( methodName ) : null ;
@@ -2952,30 +3004,37 @@ var methodName
2952
3004
switch ( exprNodeType )
2953
3005
{
2954
3006
case ExpressionType . Add :
3007
+ case ExpressionType . AddAssign :
2955
3008
il . Emit ( OpCodes . Add ) ;
2956
3009
return true ;
2957
3010
2958
3011
case ExpressionType . AddChecked :
3012
+ case ExpressionType . AddAssignChecked :
2959
3013
il . Emit ( IsUnsigned ( exprType ) ? OpCodes . Add_Ovf_Un : OpCodes . Add_Ovf ) ;
2960
3014
return true ;
2961
3015
2962
3016
case ExpressionType . Subtract :
3017
+ case ExpressionType . SubtractAssign :
2963
3018
il . Emit ( OpCodes . Sub ) ;
2964
3019
return true ;
2965
3020
2966
3021
case ExpressionType . SubtractChecked :
3022
+ case ExpressionType . SubtractAssignChecked :
2967
3023
il . Emit ( IsUnsigned ( exprType ) ? OpCodes . Sub_Ovf_Un : OpCodes . Sub_Ovf ) ;
2968
3024
return true ;
2969
3025
2970
3026
case ExpressionType . Multiply :
3027
+ case ExpressionType . MultiplyAssign :
2971
3028
il . Emit ( OpCodes . Mul ) ;
2972
3029
return true ;
2973
3030
2974
3031
case ExpressionType . MultiplyChecked :
3032
+ case ExpressionType . MultiplyAssignChecked :
2975
3033
il . Emit ( IsUnsigned ( exprType ) ? OpCodes . Mul_Ovf_Un : OpCodes . Mul_Ovf ) ;
2976
3034
return true ;
2977
3035
2978
3036
case ExpressionType . Divide :
3037
+ case ExpressionType . DivideAssign :
2979
3038
il . Emit ( OpCodes . Div ) ;
2980
3039
return true ;
2981
3040
}
@@ -3128,16 +3187,64 @@ internal static object GetLastBlockExpression(object exprObj)
3128
3187
return null ;
3129
3188
}
3130
3189
3131
- internal static bool IsByRefAssign ( object exprObj )
3190
+ //TODO: test what is faster? Copy and inline switch? Switch in method? Ors in method?
3191
+ [ System . Runtime . CompilerServices . MethodImpl ( System . Runtime . CompilerServices . MethodImplOptions . AggressiveInlining ) ]
3192
+ internal static bool IsArithmetic ( ExpressionType arithmetic ) =>
3193
+ arithmetic == ExpressionType . Add
3194
+ || arithmetic == ExpressionType . AddChecked
3195
+ || arithmetic == ExpressionType . Subtract
3196
+ || arithmetic == ExpressionType . SubtractChecked
3197
+ || arithmetic == ExpressionType . Multiply
3198
+ || arithmetic == ExpressionType . MultiplyChecked
3199
+ || arithmetic == ExpressionType . Divide
3200
+ || arithmetic == ExpressionType . Modulo ;
3201
+
3202
+ [ System . Runtime . CompilerServices . MethodImpl ( System . Runtime . CompilerServices . MethodImplOptions . AggressiveInlining ) ]
3203
+ internal static ExpressionType GetArithmeticAssignOrSelf ( ExpressionType arithmetic )
3132
3204
{
3133
- var exprInfo = exprObj as BinaryExpressionInfo ;
3134
- if ( exprInfo != null && exprInfo . NodeType == ExpressionType . Assign )
3135
- return IsByRefParameter ( exprInfo . Left ) ;
3205
+ switch ( arithmetic )
3206
+ {
3207
+ case ExpressionType . AddAssign : return ExpressionType . Add ;
3208
+ case ExpressionType . AddAssignChecked : return ExpressionType . AddChecked ;
3209
+ case ExpressionType . SubtractAssign : return ExpressionType . Subtract ;
3210
+ case ExpressionType . SubtractAssignChecked : return ExpressionType . SubtractChecked ;
3211
+ case ExpressionType . MultiplyAssign : return ExpressionType . Multiply ;
3212
+ case ExpressionType . MultiplyAssignChecked : return ExpressionType . MultiplyChecked ;
3213
+ case ExpressionType . DivideAssign : return ExpressionType . Divide ;
3214
+ case ExpressionType . ModuloAssign : return ExpressionType . Modulo ;
3215
+ }
3136
3216
3137
- var expr = exprObj as BinaryExpression ;
3138
- if ( expr != null && expr . NodeType == ExpressionType . Assign )
3139
- return IsByRefParameter ( expr . Left ) ;
3217
+ return arithmetic ;
3218
+ }
3219
+
3220
+ internal static bool IsByRefAssign ( object exprObj )
3221
+ {
3222
+ ExpressionType ? nodeType = null ;
3223
+ object left = null ;
3224
+ if ( exprObj is BinaryExpression expr )
3225
+ {
3226
+ nodeType = expr . NodeType ;
3227
+ left = expr . Left ;
3228
+ }
3229
+ else if ( exprObj is BinaryExpressionInfo exprInfo )
3230
+ {
3231
+ nodeType = exprInfo . NodeType ;
3232
+ left = exprInfo . Left ;
3233
+ }
3140
3234
3235
+ if ( nodeType . HasValue && ( nodeType == ExpressionType . Assign
3236
+ || ( GetArithmeticAssignOrSelf ( nodeType . Value ) != nodeType . Value )
3237
+ || nodeType == ExpressionType . AndAssign
3238
+ || nodeType == ExpressionType . ExclusiveOrAssign
3239
+ || nodeType == ExpressionType . LeftShiftAssign
3240
+ || nodeType == ExpressionType . OrAssign
3241
+ || nodeType == ExpressionType . PostDecrementAssign
3242
+ || nodeType == ExpressionType . PostIncrementAssign
3243
+ || nodeType == ExpressionType . PowerAssign
3244
+ || nodeType == ExpressionType . PreDecrementAssign
3245
+ || nodeType == ExpressionType . PreIncrementAssign
3246
+ || nodeType == ExpressionType . RightShiftAssign ) )
3247
+ return IsByRefParameter ( left ) ;
3141
3248
return false ;
3142
3249
}
3143
3250
@@ -3503,6 +3610,8 @@ public class UnaryExpressionInfo : ExpressionInfo
3503
3610
3504
3611
public readonly ExpressionInfo Operand ;
3505
3612
3613
+ public readonly MethodInfo Method ;
3614
+
3506
3615
public override Expression ToExpression ( )
3507
3616
{
3508
3617
if ( NodeType == ExpressionType . Convert )
@@ -3516,6 +3625,13 @@ public UnaryExpressionInfo(ExpressionType nodeType, ExpressionInfo operand, Type
3516
3625
Operand = operand ;
3517
3626
Type = type ;
3518
3627
}
3628
+
3629
+ public UnaryExpressionInfo ( ExpressionType nodeType , ExpressionInfo operand , MethodInfo method )
3630
+ {
3631
+ NodeType = nodeType ;
3632
+ Operand = operand ;
3633
+ Method = method ;
3634
+ }
3519
3635
}
3520
3636
3521
3637
public abstract class BinaryExpressionInfo : ExpressionInfo
0 commit comments