@@ -1679,6 +1679,13 @@ internal enum ParentFlags : ushort
16791679
16801680 internal static bool IgnoresResult( this ParentFlags parent) => ( parent & ParentFlags . IgnoreResult ) != 0 ;
16811681
1682+ internal static bool EmitPopIfIgnoreResult ( this ILGenerator il , ParentFlags parent )
1683+ {
1684+ if ( ( parent & ParentFlags . IgnoreResult ) != 0 )
1685+ il . Emit ( OpCodes . Pop ) ;
1686+ return true ;
1687+ }
1688+
16821689 /// <summary>Supports emitting of selected expressions, e.g. lambdaExpr are not supported yet.
16831690 /// When emitter find not supported expression it will return false from <see cref="TryEmit"/>, so I could fallback
16841691 /// to normal and slow Expression.Compile.</summary>
@@ -2226,11 +2233,7 @@ private static bool TryEmitCoalesceOperator(BinaryExpression exprObj, IReadOnlyL
22262233 il . MarkLabel ( labelDone ) ;
22272234 }
22282235 }
2229-
2230- if ( ( parent & ParentFlags . IgnoreResult ) != 0 )
2231- il . Emit ( OpCodes . Pop ) ;
2232-
2233- return true ;
2236+ return il . EmitPopIfIgnoreResult ( parent ) ;
22342237 }
22352238
22362239 private static void EmitDefault ( Type type , ILGenerator il )
@@ -2556,22 +2559,13 @@ private static bool TryEmitSimpleUnaryExpression(UnaryExpression expr, IReadOnly
25562559 }
25572560 }
25582561 else if ( expr . NodeType == ExpressionType . OnesComplement )
2559- {
25602562 il . Emit ( OpCodes . Not ) ;
2561- }
25622563 else if ( expr . NodeType == ExpressionType . Unbox )
2563- {
25642564 il . Emit ( OpCodes . Unbox_Any , exprType ) ;
2565- }
2566- else if ( expr . NodeType == ExpressionType . IsTrue )
2567- { }
2568- else if ( expr . NodeType == ExpressionType . UnaryPlus )
2569- { }
2570-
2571- if ( ( parent & ParentFlags . IgnoreResult ) != 0 )
2572- il . Emit ( OpCodes . Pop ) ;
2565+ // else if (expr.NodeType == ExpressionType.IsTrue) { }
2566+ // else if (expr.NodeType == ExpressionType.UnaryPlus) { }
25732567
2574- return true ;
2568+ return il . EmitPopIfIgnoreResult ( parent ) ;
25752569 }
25762570
25772571#if LIGHT_EXPRESSION
@@ -2651,11 +2645,7 @@ private static bool TryEmitConvert(UnaryExpression expr, IReadOnlyList<PE> param
26512645 if ( ! TryEmit ( opExpr , paramExprs , il , ref closure , setup ,
26522646 parent & ~ ParentFlags . IgnoreResult | ParentFlags . InstanceCall , - 1 ) )
26532647 return false ;
2654-
2655- il . Emit ( method . IsVirtual ? OpCodes . Callvirt : OpCodes . Call , method ) ;
2656- if ( ( parent & ParentFlags . IgnoreResult ) != 0 && method . ReturnType != typeof ( void ) )
2657- il . Emit ( OpCodes . Pop ) ;
2658- return true ;
2648+ return EmitMethodCall ( il , method ) ;
26592649 }
26602650
26612651 var sourceType = opExpr . Type ;
@@ -2673,10 +2663,7 @@ private static bool TryEmitConvert(UnaryExpression expr, IReadOnlyList<PE> param
26732663 EmitStoreLocalVariableAndLoadItsAddress ( il , sourceType ) ;
26742664
26752665 il . Emit ( OpCodes . Call , sourceType . FindValueGetterMethod ( ) ) ;
2676-
2677- if ( ( parent & ParentFlags . IgnoreResult ) != 0 )
2678- il . Emit ( OpCodes . Pop ) ;
2679- return true ;
2666+ return il . EmitPopIfIgnoreResult ( parent ) ;
26802667 }
26812668
26822669 if ( ! TryEmit ( opExpr , paramExprs , il , ref closure , setup , parent & ~ ParentFlags . IgnoreResult & ~ ParentFlags . InstanceAccess ) )
@@ -2694,9 +2681,7 @@ private static bool TryEmitConvert(UnaryExpression expr, IReadOnlyList<PE> param
26942681 {
26952682 if ( targetType == typeof ( object ) && sourceType . IsValueType )
26962683 il . Emit ( OpCodes . Box , sourceType ) ;
2697- if ( IgnoresResult ( parent ) )
2698- il . Emit ( OpCodes . Pop ) ;
2699- return true ;
2684+ return il . EmitPopIfIgnoreResult ( parent ) ;
27002685 }
27012686
27022687 // check implicit / explicit conversion operators on source and target types
@@ -2708,14 +2693,9 @@ private static bool TryEmitConvert(UnaryExpression expr, IReadOnlyList<PE> param
27082693 if ( convertOpMethod != null )
27092694 {
27102695 il . Emit ( OpCodes . Call , convertOpMethod ) ;
2711-
27122696 if ( targetTypeIsNullable )
27132697 il . Emit ( OpCodes . Newobj , targetType . GetTypeInfo ( ) . DeclaredConstructors . GetFirst ( ) ) ;
2714-
2715- if ( ( parent & ParentFlags . IgnoreResult ) != 0 ) // todo: @simplify move this thing into the method and refactor the usages
2716- il . Emit ( OpCodes . Pop ) ;
2717-
2718- return true ;
2698+ return il . EmitPopIfIgnoreResult ( parent ) ;
27192699 }
27202700 }
27212701 else if ( ! targetTypeIsNullable )
@@ -2739,9 +2719,7 @@ private static bool TryEmitConvert(UnaryExpression expr, IReadOnlyList<PE> param
27392719 }
27402720
27412721 il . Emit ( OpCodes . Call , convertOpMethod ) ;
2742- if ( ( parent & ParentFlags . IgnoreResult ) != 0 )
2743- il . Emit ( OpCodes . Pop ) ;
2744- return true ;
2722+ return il . EmitPopIfIgnoreResult ( parent ) ;
27452723 }
27462724 }
27472725
@@ -2750,9 +2728,7 @@ private static bool TryEmitConvert(UnaryExpression expr, IReadOnlyList<PE> param
27502728 if ( method != null && method . DeclaringType == targetType && method . GetParameters ( ) [ 0 ] . ParameterType == sourceType )
27512729 {
27522730 il . Emit ( OpCodes . Call , method ) ;
2753- if ( ( parent & ParentFlags . IgnoreResult ) != 0 )
2754- il . Emit ( OpCodes . Pop ) ;
2755- return true ;
2731+ return il . EmitPopIfIgnoreResult ( parent ) ;
27562732 }
27572733
27582734 var actualSourceType = sourceTypeIsNullable ? underlyingNullableSourceType : sourceType ;
@@ -2767,10 +2743,7 @@ private static bool TryEmitConvert(UnaryExpression expr, IReadOnlyList<PE> param
27672743 }
27682744
27692745 il . Emit ( OpCodes . Call , convertOpMethod ) ;
2770- if ( ( parent & ParentFlags . IgnoreResult ) != 0 )
2771- il . Emit ( OpCodes . Pop ) ;
2772-
2773- return true ;
2746+ return il . EmitPopIfIgnoreResult ( parent ) ;
27742747 }
27752748 }
27762749 else if ( ! sourceTypeIsNullable )
@@ -2780,14 +2753,10 @@ private static bool TryEmitConvert(UnaryExpression expr, IReadOnlyList<PE> param
27802753 if ( convertOpMethod != null )
27812754 {
27822755 il . Emit ( OpCodes . Call , convertOpMethod ) ;
2783-
27842756 if ( targetTypeIsNullable )
27852757 il . Emit ( OpCodes . Newobj , targetType . GetTypeInfo ( ) . DeclaredConstructors . GetFirst ( ) ) ;
27862758
2787- if ( ( parent & ParentFlags . IgnoreResult ) != 0 )
2788- il . Emit ( OpCodes . Pop ) ;
2789-
2790- return true ;
2759+ return il . EmitPopIfIgnoreResult ( parent ) ;
27912760 }
27922761 }
27932762
@@ -2859,10 +2828,7 @@ private static bool TryEmitConvert(UnaryExpression expr, IReadOnlyList<PE> param
28592828 }
28602829 }
28612830
2862- if ( ( parent & ParentFlags . IgnoreResult ) != 0 )
2863- il . Emit ( OpCodes . Pop ) ;
2864-
2865- return true ;
2831+ return il . EmitPopIfIgnoreResult ( parent ) ;
28662832 }
28672833
28682834 private static bool TryEmitValueConvert ( Type targetType , ILGenerator il , bool isChecked )
@@ -2884,7 +2850,7 @@ private static bool TryEmitValueConvert(Type targetType, ILGenerator il, bool is
28842850 else if ( targetType == typeof ( long ) )
28852851 il . Emit ( isChecked ? OpCodes . Conv_Ovf_I8 : OpCodes . Conv_I8 ) ;
28862852 else if ( targetType == typeof ( ulong ) )
2887- il . Emit ( isChecked ? OpCodes . Conv_Ovf_U8 : OpCodes . Conv_U8 ) ;
2853+ il . Emit ( isChecked ? OpCodes . Conv_Ovf_U8 : OpCodes . Conv_U8 ) ; // should we consider if sourceType.IsUnsigned == false and using the OpCodes.Conv_I8 (seems like the System.Compile does it)
28882854 else if ( targetType == typeof ( double ) )
28892855 il . Emit ( OpCodes . Conv_R8 ) ;
28902856 else
@@ -3987,7 +3953,7 @@ private static bool TryEmitMemberAccess(MemberExpression expr, IReadOnlyList<PE>
39873953 var p = ( parent | ParentFlags . MemberAccess | ParentFlags . InstanceAccess )
39883954 & ~ ParentFlags . IgnoreResult & ~ ParentFlags . DupMemberOwner ;
39893955
3990- if ( ! TryEmit ( instanceExpr , paramExprs , il , ref closure , setup , p , - 1 /*pi*/ ) )
3956+ if ( ! TryEmit ( instanceExpr , paramExprs , il , ref closure , setup , p , - 1 ) )
39913957 return false ;
39923958
39933959 if ( ( parent & ParentFlags . DupMemberOwner ) != 0 )
@@ -4322,24 +4288,17 @@ private static bool TryEmitComparison(Expression exprLeft, Expression exprRight,
43224288 ( leftOpType == typeof ( object ) || rightOpType == typeof ( object ) ) )
43234289 {
43244290 if ( expressionType == ExpressionType . Equal )
4325- {
43264291 il . Emit ( OpCodes . Ceq ) ;
4327- if ( ( parent & ParentFlags . IgnoreResult ) != 0 )
4328- il . Emit ( OpCodes . Pop ) ;
4329- }
43304292 else if ( expressionType == ExpressionType . NotEqual )
43314293 {
43324294 il . Emit ( OpCodes . Ceq ) ;
4333- il . Emit ( OpCodes . Ldc_I4_0 ) ; // todo: @perf may there is optimization for comparison with IL, like the `Bne_Un`
4295+ il . Emit ( OpCodes . Ldc_I4_0 ) ; // todo: @perf Currently it produces the same code a System Compile but I wonder if we can use OpCodes.Not
43344296 il . Emit ( OpCodes . Ceq ) ;
43354297 }
43364298 else
43374299 return false ;
43384300
4339- if ( ( parent & ParentFlags . IgnoreResult ) != 0 )
4340- il . Emit ( OpCodes . Pop ) ;
4341-
4342- return true ;
4301+ return il . EmitPopIfIgnoreResult ( parent ) ;
43434302 }
43444303 }
43454304
@@ -4395,10 +4354,7 @@ var methodName
43954354 if ( leftIsNullable )
43964355 goto nullCheck ;
43974356
4398- if ( ( parent & ParentFlags . IgnoreResult ) != 0 )
4399- il . Emit ( OpCodes . Pop ) ;
4400-
4401- return true ;
4357+ return il . EmitPopIfIgnoreResult ( parent ) ;
44024358 }
44034359
44044360 // handle primitives comparison
@@ -4409,7 +4365,7 @@ var methodName
44094365 break ;
44104366
44114367 case ExpressionType . NotEqual :
4412- il . Emit ( OpCodes . Ceq ) ; // todo: @perf optimize the IL chain
4368+ il . Emit ( OpCodes . Ceq ) ;
44134369 il . Emit ( OpCodes . Ldc_I4_0 ) ;
44144370 il . Emit ( OpCodes . Ceq ) ;
44154371 break ;
@@ -4423,22 +4379,23 @@ var methodName
44234379 break ;
44244380
44254381 case ExpressionType . GreaterThanOrEqual :
4426- case ExpressionType . LessThanOrEqual :
4427- var ifTrueLabel = il . DefineLabel ( ) ;
4428- if ( rightOpType == typeof ( uint ) || rightOpType == typeof ( ulong ) ||
4429- rightOpType == typeof ( ushort ) || rightOpType == typeof ( byte ) )
4430- il . Emit ( expressionType == ExpressionType . GreaterThanOrEqual ? OpCodes . Bge_Un_S : OpCodes . Ble_Un_S , ifTrueLabel ) ;
4431- else
4432- il . Emit ( expressionType == ExpressionType . GreaterThanOrEqual ? OpCodes . Bge_S : OpCodes . Ble_S , ifTrueLabel ) ;
4433-
4382+ // simplifying by using the LessThen (Clt) and comparing with negative outcome (Ceq 0)
4383+ if ( leftOpType . IsUnsigned ( ) && rightOpType . IsUnsigned ( ) )
4384+ il . Emit ( OpCodes . Clt_Un ) ;
4385+ else
4386+ il . Emit ( OpCodes . Clt ) ;
44344387 il . Emit ( OpCodes . Ldc_I4_0 ) ;
4435- var doneLabel = il . DefineLabel ( ) ;
4436- il . Emit ( OpCodes . Br_S , doneLabel ) ;
4437-
4438- il . MarkLabel ( ifTrueLabel ) ;
4439- il . Emit ( OpCodes . Ldc_I4_1 ) ;
4388+ il . Emit ( OpCodes . Ceq ) ;
4389+ break ;
44404390
4441- il . MarkLabel ( doneLabel ) ;
4391+ case ExpressionType . LessThanOrEqual :
4392+ // simplifying by using the GreaterThen (Cgt) and comparing with negative outcome (Ceq 0)
4393+ if ( leftOpType . IsUnsigned ( ) && rightOpType . IsUnsigned ( ) )
4394+ il . Emit ( OpCodes . Cgt_Un ) ;
4395+ else
4396+ il . Emit ( OpCodes . Cgt ) ;
4397+ il . Emit ( OpCodes . Ldc_I4_0 ) ;
4398+ il . Emit ( OpCodes . Ceq ) ;
44424399 break ;
44434400
44444401 default :
@@ -4486,10 +4443,7 @@ var methodName
44864443 }
44874444 }
44884445
4489- if ( ( parent & ParentFlags . IgnoreResult ) != 0 )
4490- il . Emit ( OpCodes . Pop ) ;
4491-
4492- return true ;
4446+ return il . EmitPopIfIgnoreResult ( parent ) ;
44934447 }
44944448
44954449#if LIGHT_EXPRESSION
@@ -5048,7 +5002,10 @@ private static int EmitStoreLocalVariableAndLoadItsAddress(ILGenerator il, Type
50485002 internal static class Tools
50495003 {
50505004 internal static bool IsUnsigned ( this Type type ) =>
5051- type == typeof ( byte ) || type == typeof ( ushort ) || type == typeof ( uint ) || type == typeof ( ulong ) ;
5005+ type == typeof ( byte ) ||
5006+ type == typeof ( ushort ) ||
5007+ type == typeof ( uint ) ||
5008+ type == typeof ( ulong ) ;
50525009
50535010 internal static bool IsNullable ( this Type type ) =>
50545011 type . IsGenericType && type . GetGenericTypeDefinition ( ) == typeof ( Nullable < > ) ;
@@ -5352,11 +5309,6 @@ static ILGeneratorHacks()
53525309 // our own helper - always available
53535310 var postIncMethod = typeof ( ILGeneratorHacks ) . GetTypeInfo ( ) . GetDeclaredMethod ( nameof ( PostInc ) ) ;
53545311
5355- // now let's compile the following method without allocating the LocalBuilder class:
5356- /*
5357- il.m_localSignature.AddArgument(type);
5358- return PostInc(ref il.LocalCount);
5359- */
53605312 var efficientMethod = new DynamicMethod ( string . Empty ,
53615313 typeof ( int ) , new [ ] { typeof ( ExpressionCompiler . ArrayClosure ) , typeof ( ILGenerator ) , typeof ( Type ) } ,
53625314 typeof ( ExpressionCompiler . ArrayClosure ) , skipVisibility : true ) ;
@@ -5498,13 +5450,15 @@ internal static StringBuilder ToExpressionString(this ParameterExpression pe, St
54985450 }
54995451
55005452 internal static StringBuilder ToExpressionString ( this LabelTarget lt , StringBuilder sb , List < LabelTarget > labelTargets ,
5501- bool stripNamespace = false , Func < Type , string , string > printType = null )
5453+ int lineIdent = 0 , bool stripNamespace = false , Func < Type , string , string > printType = null )
55025454 {
55035455 var i = labelTargets . Count - 1 ;
55045456 while ( i != - 1 && ! ReferenceEquals ( labelTargets [ i ] , lt ) ) -- i ;
55055457 if ( i != - 1 )
5506- return sb . Append ( "l[" ) . Append ( i ) . Append ( "]/* " ) . AppendName ( lt . Name , lt . Type , lt ) . Append ( " */" ) ;
5507-
5458+ return sb . Append ( "l[" ) . Append ( i )
5459+ . Append ( " // (" ) . AppendName ( lt . Name , lt . Type , lt ) . Append ( ')' )
5460+ . NewLineIdent ( lineIdent ) . Append ( ']' ) ;
5461+
55085462 labelTargets . Add ( lt ) ;
55095463 sb . Append ( "l[" ) . Append ( labelTargets . Count - 1 ) . Append ( "]=Label(" ) ;
55105464 sb . AppendTypeof ( lt . Type , stripNamespace , printType ) ;
@@ -5666,7 +5620,7 @@ internal static StringBuilder CreateExpressionString(this Expression e, StringBu
56665620 if ( args . Count == 0 && e . Type . IsValueType )
56675621 return sb . Append ( "New(" ) . AppendTypeof ( e . Type , stripNamespace , printType ) . Append ( ')' ) ;
56685622
5669- sb . Append ( "New(/* " ) . Append ( args . Count ) . Append ( " args*/ " ) ;
5623+ sb . Append ( "New( // " ) . Append ( args . Count ) . Append ( " args" ) ;
56705624 var ctorIndex = x . Constructor . DeclaringType . GetTypeInfo ( ) . DeclaredConstructors . ToArray ( ) . GetFirstIndex ( x . Constructor ) ;
56715625 sb . NewLineIdent ( lineIdent ) . AppendTypeof ( x . Type , stripNamespace , printType )
56725626 . Append ( ".GetTypeInfo().DeclaredConstructors.ToArray()[" ) . Append ( ctorIndex ) . Append ( "]," ) ;
@@ -5785,10 +5739,10 @@ internal static StringBuilder CreateExpressionString(this Expression e, StringBu
57855739 sb . NewLineIdentExpr ( x . Body , paramsExprs , uniqueExprs , lts , lineIdent , stripNamespace , printType , identSpaces ) ;
57865740
57875741 if ( x . BreakLabel != null )
5788- x . BreakLabel . ToExpressionString ( sb . Append ( ',' ) . NewLineIdent ( lineIdent ) , lts , stripNamespace , printType ) ;
5742+ x . BreakLabel . ToExpressionString ( sb . Append ( ',' ) . NewLineIdent ( lineIdent ) , lts , lineIdent , stripNamespace , printType ) ;
57895743
57905744 if ( x . ContinueLabel != null )
5791- x . ContinueLabel . ToExpressionString ( sb . Append ( ',' ) . NewLineIdent ( lineIdent ) , lts , stripNamespace , printType ) ;
5745+ x . ContinueLabel . ToExpressionString ( sb . Append ( ',' ) . NewLineIdent ( lineIdent ) , lts , lineIdent , stripNamespace , printType ) ;
57925746
57935747 return sb . Append ( ')' ) ;
57945748 }
@@ -5836,7 +5790,7 @@ internal static StringBuilder CreateExpressionString(this Expression e, StringBu
58365790 {
58375791 var x = ( LabelExpression ) e ;
58385792 sb . Append ( "Label(" ) ;
5839- x . Target . ToExpressionString ( sb , lts , stripNamespace , printType ) ;
5793+ x . Target . ToExpressionString ( sb , lts , lineIdent , stripNamespace , printType ) ;
58405794 if ( x . DefaultValue != null )
58415795 sb . Append ( ',' ) . NewLineIdentExpr ( x . DefaultValue , paramsExprs , uniqueExprs , lts , lineIdent , stripNamespace , printType , identSpaces ) ;
58425796 return sb . Append ( ')' ) ;
@@ -5847,7 +5801,7 @@ internal static StringBuilder CreateExpressionString(this Expression e, StringBu
58475801 sb . Append ( "MakeGoto(" ) . AppendEnum ( x . Kind , stripNamespace , printType ) . Append ( ',' ) ;
58485802
58495803 sb . NewLineIdent ( lineIdent ) ;
5850- x . Target . ToExpressionString ( sb , lts , stripNamespace , printType ) . Append ( ',' ) ;
5804+ x . Target . ToExpressionString ( sb , lts , lineIdent , stripNamespace , printType ) . Append ( ',' ) ;
58515805
58525806 sb . NewLineIdentExpr ( x . Value , paramsExprs , uniqueExprs , lts , lineIdent , stripNamespace , printType , identSpaces ) . Append ( ',' ) ;
58535807 sb . NewLineIdent ( lineIdent ) . AppendTypeof ( x . Type , stripNamespace , printType ) ;
0 commit comments