Skip to content

Commit e47acb2

Browse files
committed
start on #478
1 parent 5d1f87d commit e47acb2

4 files changed

Lines changed: 35 additions & 20 deletions

File tree

src/Directory.Build.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
</ItemGroup>
3434

3535
<PropertyGroup>
36+
<Features>strict</Features>
3637
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
3738
</PropertyGroup>
3839

src/FastExpressionCompiler/FastExpressionCompiler.cs

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,15 @@ namespace FastExpressionCompiler.LightExpression
4242
using static FastExpressionCompiler.LightExpression.Expression;
4343
using PE = FastExpressionCompiler.LightExpression.ParameterExpression;
4444
using FastExpressionCompiler.LightExpression.ImTools;
45+
using FastExpressionCompiler.LightExpression.ILDecoder;
4546
using static FastExpressionCompiler.LightExpression.ImTools.SmallMap4;
4647
#else
4748
namespace FastExpressionCompiler
4849
{
4950
using static System.Linq.Expressions.Expression;
5051
using PE = System.Linq.Expressions.ParameterExpression;
5152
using FastExpressionCompiler.ImTools;
53+
using FastExpressionCompiler.ILDecoder;
5254
using static FastExpressionCompiler.ImTools.SmallMap4;
5355
#endif
5456
using System;
@@ -104,6 +106,8 @@ public interface IDelegateDebugInfo
104106
string ExpressionString { get; }
105107
/// <summary>The equivalent C# code of the lambda expression</summary>
106108
string CSharpString { get; }
109+
/// <summary>Delegate IL op-codes and tokens</summary>
110+
string ILString { get; }
107111

108112
// todo: @feature add the debug info to the nested lambdas
109113
// /// <summary>Total nested lambda counting</summary>
@@ -514,19 +518,17 @@ internal static object TryCompileBoundToFirstClosureParam(Type delegateType, Exp
514518
var collectResult = TryCollectInfo(ref closureInfo, bodyExpr, paramExprs, null, ref closureInfo.NestedLambdas, flags);
515519
if (collectResult == Result.OK)
516520
{
521+
var constantsAndNestedLambdas = (closureInfo.Status & ClosureStatus.HasClosure) != 0
522+
? closureInfo.GetArrayOfConstantsAndNestedLambdas()
523+
: null;
524+
517525
ArrayClosure closure;
518-
if ((flags & CompilerFlags.EnableDelegateDebugInfo) == 0)
519-
{
520-
closure = (closureInfo.Status & ClosureStatus.HasClosure) == 0
521-
? EmptyArrayClosure
522-
: new ArrayClosure(closureInfo.GetArrayOfConstantsAndNestedLambdas());
523-
}
526+
var hasDebugInfo = (flags & CompilerFlags.EnableDelegateDebugInfo) != 0;
527+
if (!hasDebugInfo)
528+
closure = constantsAndNestedLambdas == null ? EmptyArrayClosure : new ArrayClosure(constantsAndNestedLambdas);
524529
else
525-
{ // todo: @feature add the debug info to the nested lambdas!
530+
{
526531
var debugExpr = Lambda(delegateType, bodyExpr, paramExprs?.ToReadOnlyList() ?? Tools.Empty<PE>());
527-
var constantsAndNestedLambdas = (closureInfo.Status & ClosureStatus.HasClosure) == 0
528-
? null
529-
: closureInfo.GetArrayOfConstantsAndNestedLambdas();
530532
closure = new DebugArrayClosure(constantsAndNestedLambdas, debugExpr);
531533
}
532534

@@ -548,6 +550,8 @@ internal static object TryCompileBoundToFirstClosureParam(Type delegateType, Exp
548550
{
549551
il.Demit(OpCodes.Ret);
550552
compiledDelegate = dynMethod.CreateDelegate(delegateType, closure);
553+
if (hasDebugInfo)
554+
((DebugArrayClosure)closure).ILString = compiledDelegate.Method.ToILString().ToString();
551555
}
552556

553557
DynamicMethodHacks.FreePooledILGenerator(dynMethod, il);
@@ -1007,6 +1011,9 @@ public sealed class DebugArrayClosure : ArrayClosure, IDelegateDebugInfo
10071011

10081012
private readonly Lazy<string> _csharpString;
10091013
public string CSharpString => _csharpString.Value;
1014+
1015+
public string ILString { get; internal set; }
1016+
10101017
public DebugArrayClosure(object[] constantsAndNestedLambdas, LambdaExpression expr)
10111018
: base(constantsAndNestedLambdas)
10121019
{
@@ -1804,7 +1811,7 @@ private static bool FindAlreadyCompiledNestedLambdaInfoInLambdas(
18041811
return false;
18051812
}
18061813

1807-
private static bool TryCompileNestedLambda(ref ClosureInfo nestedClosureInfo, NestedLambdaInfo nestedLambdaInfo, CompilerFlags setup)
1814+
private static bool TryCompileNestedLambda(ref ClosureInfo nestedClosureInfo, NestedLambdaInfo nestedLambdaInfo, CompilerFlags flags)
18081815
{
18091816
// 1. Try to compile nested lambda in place
18101817
// 2. Check that parameters used in compiled lambda are passed or closed by outer lambda
@@ -1829,28 +1836,34 @@ private static bool TryCompileNestedLambda(ref ClosureInfo nestedClosureInfo, Ne
18291836
nestedClosureInfo.NestedLambdas = nestedLambdaInfo.NestedLambdas;
18301837
nestedClosureInfo.NonPassedParameters = nestedLambdaInfo.NonPassedParameters;
18311838

1832-
var nestedConstsAndLambdas = nestedClosureInfo.GetArrayOfConstantsAndNestedLambdas();
1839+
var constantsAndNestedLambdas = (nestedClosureInfo.Status & ClosureStatus.HasClosure) != 0
1840+
? nestedClosureInfo.GetArrayOfConstantsAndNestedLambdas()
1841+
: null;
18331842

18341843
ArrayClosure nestedLambdaClosure = null;
18351844
var hasNonPassedParameters = nestedLambdaInfo.NonPassedParameters.Count != 0;
18361845
if (!hasNonPassedParameters)
1837-
nestedLambdaClosure = (nestedClosureInfo.Status & ClosureStatus.HasClosure) == 0
1838-
? EmptyArrayClosure
1839-
: new ArrayClosure(nestedConstsAndLambdas);
1846+
{
1847+
var hasDebugInfo = (flags & CompilerFlags.EnableDelegateDebugInfo) != 0;
1848+
if (!hasDebugInfo)
1849+
nestedLambdaClosure = constantsAndNestedLambdas == null ? EmptyArrayClosure : new ArrayClosure(constantsAndNestedLambdas);
1850+
else
1851+
nestedLambdaClosure = new DebugArrayClosure(constantsAndNestedLambdas, nestedLambdaExpr);
1852+
}
18401853

18411854
var closurePlusParamTypes = RentPooledOrNewClosureTypeToParamTypes(nestedLambdaParamExprs);
18421855

18431856
var method = new DynamicMethod(string.Empty, nestedReturnType, closurePlusParamTypes, typeof(ArrayClosure), true);
18441857
var il = DynamicMethodHacks.RentPooledOrNewILGenerator(method, nestedReturnType, closurePlusParamTypes);
18451858

1846-
if (nestedConstsAndLambdas != null)
1859+
if (constantsAndNestedLambdas != null)
18471860
EmittingVisitor.EmitLoadConstantsAndNestedLambdasIntoVars(il, ref nestedClosureInfo);
18481861

18491862
var parent = nestedReturnType == typeof(void) ? ParentFlags.IgnoreResult : ParentFlags.LambdaCall;
18501863
if (nestedReturnType.IsByRef)
18511864
parent |= ParentFlags.ReturnByRef;
18521865

1853-
var emitOk = EmittingVisitor.TryEmit(nestedLambdaBody, nestedLambdaParamExprs, il, ref nestedClosureInfo, setup, parent);
1866+
var emitOk = EmittingVisitor.TryEmit(nestedLambdaBody, nestedLambdaParamExprs, il, ref nestedClosureInfo, flags, parent);
18541867
if (emitOk)
18551868
{
18561869
il.Demit(OpCodes.Ret);
@@ -1862,8 +1875,8 @@ private static bool TryCompileNestedLambda(ref ClosureInfo nestedClosureInfo, Ne
18621875
: method.CreateDelegate(Tools.GetFuncOrActionType(closurePlusParamTypes, nestedReturnType), null);
18631876

18641877
nestedLambdaInfo.Lambda = !hasNonPassedParameters ? nestedLambda
1865-
: nestedConstsAndLambdas == null ? new NestedLambdaForNonPassedParams(nestedLambda)
1866-
: new NestedLambdaForNonPassedParamsWithConstants(nestedLambda, nestedConstsAndLambdas);
1878+
: constantsAndNestedLambdas == null ? new NestedLambdaForNonPassedParams(nestedLambda)
1879+
: new NestedLambdaForNonPassedParamsWithConstants(nestedLambda, constantsAndNestedLambdas);
18671880
}
18681881
DynamicMethodHacks.FreePooledILGenerator(method, il);
18691882
FreePooledClosureTypeAndParamTypes(closurePlusParamTypes);

src/FastExpressionCompiler/TestTools.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ namespace FastExpressionCompiler.LightExpression;
1717
using FastExpressionCompiler.LightExpression.ImTools;
1818
#else
1919
namespace FastExpressionCompiler;
20+
2021
using FastExpressionCompiler.ILDecoder;
2122
using FastExpressionCompiler.ImTools;
2223
using System.Linq.Expressions;

test/FastExpressionCompiler.TestsRunner/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public static void Main()
1515
// LightExpression.ILGeneratorTools.DisableILGeneratorPooling = true;
1616

1717
// new Issue461_InvalidProgramException_when_null_checking_type_by_ref().Run();
18-
// new LightExpression.UnitTests.NestedLambdasSharedToExpressionCodeStringTest().Run();
18+
new LightExpression.UnitTests.NestedLambdasSharedToExpressionCodeStringTest().Run();
1919
// new Issue55_CompileFast_crash_with_ref_parameter().Run();
2020

2121
var t = new LightExpression.TestRun(LightExpression.TestFlags.RethrowException);

0 commit comments

Comments
 (0)