From 67e525587e649bc13abc846094ed736e988ed91b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Apr 2026 13:43:41 +0000 Subject: [PATCH 1/3] Initial plan From c480c7ee2e061263ea244c02b005e742262c70c1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Apr 2026 13:59:07 +0000 Subject: [PATCH 2/3] FlatExpression: track lambda node indices during construction to reduce TryCollectInfo work Agent-Logs-Url: https://github.com/dadhi/FastExpressionCompiler/sessions/9e120fbc-6ec4-46c7-9cc8-46e269e91f1b Co-authored-by: dadhi <39516+dadhi@users.noreply.github.com> --- .../FlatExpression.cs | 20 +++- .../LightExpressionTests.cs | 112 +++++++++++++++++- 2 files changed, 128 insertions(+), 4 deletions(-) diff --git a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs index 01338164..b8107204 100644 --- a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs +++ b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs @@ -167,6 +167,12 @@ public struct ExprTree /// Gets or sets closure constants that are referenced from constant nodes. public SmallList, NoArrayPool> ClosureConstants; + /// Gets or sets the indices of all lambda nodes added during construction. + /// The root lambda index is stored in ; all other entries are nested lambdas. + /// Populated automatically by and , + /// enabling callers to discover nested lambdas without a full tree traversal. + public SmallList, NoArrayPool> LambdaNodes; + /// Adds a parameter node and returns its index. public int Parameter(Type type, string name = null) { @@ -414,11 +420,17 @@ public int Lambda(int body, params int[] parameters) where TDelegate /// out-of-order decl pattern. The Reader resolves identity through a shared id map /// so that all refs and the single decl resolve to the same /// object. + /// The lambda node index is recorded in so callers can discover + /// nested lambdas (all entries except ) without a full tree traversal. /// - public int Lambda(Type delegateType, int body, params int[] parameters) => - parameters == null || parameters.Length == 0 + public int Lambda(Type delegateType, int body, params int[] parameters) + { + var index = parameters == null || parameters.Length == 0 ? AddFactoryExpressionNode(delegateType, null, ExpressionType.Lambda, 0, body) : AddFactoryExpressionNode(delegateType, null, ExpressionType.Lambda, PrependToChildList(body, parameters)); + LambdaNodes.Add(index); + return index; + } /// Adds a member-assignment binding node. public int Bind(System.Reflection.MemberInfo member, int expression) => @@ -823,7 +835,9 @@ private int AddExpression(SysExpr expression) children.Add(AddExpression(lambda.Body)); for (var i = 0; i < lambda.Parameters.Count; ++i) children.Add(AddExpression(lambda.Parameters[i])); - return _tree.AddRawExpressionNode(expression.Type, null, expression.NodeType, children); + var lambdaIndex = _tree.AddRawExpressionNode(expression.Type, null, expression.NodeType, children); + _tree.LambdaNodes.Add(lambdaIndex); + return lambdaIndex; } case ExpressionType.Block: { diff --git a/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs b/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs index eea42063..ce1195b6 100644 --- a/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs +++ b/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs @@ -40,7 +40,11 @@ public int Run() Flat_nested_lambda_captures_outer_parameter_identity(); Flat_out_of_order_decl_block_in_lambda_compiles_correctly(); Flat_enum_constant_stored_inline_roundtrip(); - return 23; + Flat_lambda_nodes_tracks_all_lambdas_during_direct_construction(); + Flat_lambda_nodes_tracks_deeply_nested_lambdas_during_direct_construction(); + Flat_lambda_nodes_tracks_lambdas_from_expression_conversion(); + Flat_lambda_nodes_has_single_entry_for_root_only_lambda(); + return 27; } @@ -686,5 +690,111 @@ void Check(TEnum enumValue) where TEnum : Enum Check(UIntEnum.A); Check(UIntEnum.B); } + + /// + /// When building a flat expression directly, calling Lambda() for a nested lambda + /// and then for the root lambda should result in both indices recorded in LambdaNodes. + /// The root is identified by RootIndex; all others are nested. + /// + public void Flat_lambda_nodes_tracks_all_lambdas_during_direct_construction() + { + var fe = default(ExprTree); + var x = fe.ParameterOf("x"); + + // Build: outer: x => () => x + var inner = fe.Lambda>(x); + fe.RootIndex = fe.Lambda>>(inner, x); + + // Both the root and nested lambda indices should be recorded + Asserts.AreEqual(2, fe.LambdaNodes.Count); + + // Check that inner and root are both in LambdaNodes + var foundInner = false; + var foundRoot = false; + for (var i = 0; i < fe.LambdaNodes.Count; i++) + { + if (fe.LambdaNodes[i] == inner) foundInner = true; + if (fe.LambdaNodes[i] == fe.RootIndex) foundRoot = true; + } + Asserts.IsTrue(foundInner); + Asserts.IsTrue(foundRoot); + + // Nested lambdas are all LambdaNodes entries that are not the root + var nestedCount = 0; + for (var i = 0; i < fe.LambdaNodes.Count; i++) + if (fe.LambdaNodes[i] != fe.RootIndex) + ++nestedCount; + Asserts.AreEqual(1, nestedCount); + } + + /// + /// When building a flat expression with multiple levels of nesting, + /// all lambda node indices are captured in LambdaNodes. + /// + public void Flat_lambda_nodes_tracks_deeply_nested_lambdas_during_direct_construction() + { + var fe = default(ExprTree); + var x = fe.ParameterOf("x"); + + // Build: outer: x => (() => (() => x)) + var innermost = fe.Lambda>(x); + var middle = fe.Lambda>>(innermost); + fe.RootIndex = fe.Lambda>>>(middle, x); + + // All three lambda nodes should be recorded + Asserts.AreEqual(3, fe.LambdaNodes.Count); + + // Count nested (non-root) lambdas + var nestedCount = 0; + for (var i = 0; i < fe.LambdaNodes.Count; i++) + if (fe.LambdaNodes[i] != fe.RootIndex) + ++nestedCount; + Asserts.AreEqual(2, nestedCount); + } + + /// + /// When converting a System.Linq expression tree with nested lambdas via FromExpression, + /// the resulting ExprTree should have all lambda indices populated in LambdaNodes. + /// + public void Flat_lambda_nodes_tracks_lambdas_from_expression_conversion() + { + var p = SysExpr.Parameter(typeof(int), "p"); + // Build: p => () => p using System.Linq.Expressions + var sysLambda = SysExpr.Lambda>>( + SysExpr.Lambda>(p), + p); + + var fe = sysLambda.ToFlatExpression(); + + // Both root and nested lambda indices should be recorded + Asserts.AreEqual(2, fe.LambdaNodes.Count); + + // The root lambda must be in the list + var foundRoot = false; + for (var i = 0; i < fe.LambdaNodes.Count; i++) + if (fe.LambdaNodes[i] == fe.RootIndex) { foundRoot = true; break; } + Asserts.IsTrue(foundRoot); + + // Exactly one nested lambda + var nestedCount = 0; + for (var i = 0; i < fe.LambdaNodes.Count; i++) + if (fe.LambdaNodes[i] != fe.RootIndex) + ++nestedCount; + Asserts.AreEqual(1, nestedCount); + } + + /// + /// A flat expression with no nested lambdas (root-only) should have exactly one + /// entry in LambdaNodes (the root itself). + /// + public void Flat_lambda_nodes_has_single_entry_for_root_only_lambda() + { + var fe = default(ExprTree); + var p = fe.ParameterOf("p"); + fe.RootIndex = fe.Lambda>(fe.Add(p, fe.ConstantInt(1)), p); + + Asserts.AreEqual(1, fe.LambdaNodes.Count); + Asserts.AreEqual(fe.RootIndex, fe.LambdaNodes[0]); + } } } From 60a4f9336cbbf8efe185b04e78d7a4e5ed4ee984 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Apr 2026 18:46:20 +0000 Subject: [PATCH 3/3] FlatExpression: also track BlocksWithVariables, GotoNodes, LabelNodes and TryCatchNodes during construction Agent-Logs-Url: https://github.com/dadhi/FastExpressionCompiler/sessions/fbbd4fe6-1e27-488e-b48b-b5ff5dbc8626 Co-authored-by: dadhi <39516+dadhi@users.noreply.github.com> --- .../FlatExpression.cs | 119 ++++++++++++--- .../LightExpressionTests.cs | 137 +++++++++++++++++- 2 files changed, 232 insertions(+), 24 deletions(-) diff --git a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs index b8107204..11c12e74 100644 --- a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs +++ b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs @@ -173,6 +173,30 @@ public struct ExprTree /// enabling callers to discover nested lambdas without a full tree traversal. public SmallList, NoArrayPool> LambdaNodes; + /// Gets or sets the indices of all block nodes that carry explicit variable declarations. + /// These are the block nodes where children.Count == 2 (variable list + expression list). + /// Populated automatically by and , + /// enabling callers to enumerate block-scoped variables without a full tree traversal. + public SmallList, NoArrayPool> BlocksWithVariables; + + /// Gets or sets the indices of all nodes + /// (including return and break/continue goto-family nodes). + /// Populated automatically by and , + /// enabling callers to link gotos to their label targets without a full tree traversal. + public SmallList, NoArrayPool> GotoNodes; + + /// Gets or sets the indices of all expression nodes. + /// Populated automatically by and , + /// enabling callers to link label expressions to their targets without a full tree traversal. + public SmallList, NoArrayPool> LabelNodes; + + /// Gets or sets the indices of all nodes + /// (try/catch, try/finally, try/fault, and combined forms). + /// Populated automatically by , , + /// , and , + /// enabling callers to locate all try regions without a full tree traversal. + public SmallList, NoArrayPool> TryCatchNodes; + /// Adds a parameter node and returns its index. public int Parameter(Type type, string name = null) { @@ -380,6 +404,9 @@ public int Block(params int[] expressions) => /// Variable parameter nodes share the same id-slot as the refs used inside the body /// (out-of-order: the variable decl nodes appear in children[0] before the body expressions /// that reference them in children[1]). + /// When the block has explicit variable declarations its node index is recorded in + /// , enabling callers to enumerate block-scoped variables + /// without a full tree traversal. /// public int Block(Type type, IEnumerable variables, params int[] expressions) { @@ -387,19 +414,26 @@ public int Block(Type type, IEnumerable variables, params int[] expressions throw new ArgumentException("Block should contain at least one expression.", nameof(expressions)); ChildList children = default; + var hasVariables = false; if (variables != null) { ChildList variableChildren = default; foreach (var variable in variables) variableChildren.Add(variable); if (variableChildren.Count != 0) + { children.Add(AddChildListNode(in variableChildren)); + hasVariables = true; + } } ChildList bodyChildren = default; for (var i = 0; i < expressions.Length; ++i) bodyChildren.Add(expressions[i]); children.Add(AddChildListNode(in bodyChildren)); - return AddFactoryExpressionNode(type ?? Nodes[expressions[expressions.Length - 1]].Type, null, ExpressionType.Block, in children); + var index = AddFactoryExpressionNode(type ?? Nodes[expressions[expressions.Length - 1]].Type, null, ExpressionType.Block, in children); + if (hasVariables) + BlocksWithVariables.Add(index); + return index; } /// Adds a typed lambda node. @@ -468,18 +502,26 @@ public int Label(Type type = null, string name = null) } /// Adds a label-expression node. - public int Label(int target, int? defaultValue = null) => - defaultValue.HasValue + /// The node index is recorded in . + public int Label(int target, int? defaultValue = null) + { + var index = defaultValue.HasValue ? AddFactoryExpressionNode(Nodes[target].Type, null, ExpressionType.Label, 0, target, defaultValue.Value) : AddFactoryExpressionNode(Nodes[target].Type, null, ExpressionType.Label, 0, target); + LabelNodes.Add(index); + return index; + } /// Adds a goto-family node. + /// The node index is recorded in . public int MakeGoto(GotoExpressionKind kind, int target, int? value = null, Type type = null) { var resultType = type ?? (value.HasValue ? Nodes[value.Value].Type : typeof(void)); - return value.HasValue + var index = value.HasValue ? AddFactoryExpressionNode(resultType, kind, ExpressionType.Goto, 0, target, value.Value) : AddFactoryExpressionNode(resultType, kind, ExpressionType.Goto, 0, target); + GotoNodes.Add(index); + return index; } /// Adds a goto node. @@ -557,29 +599,48 @@ public int MakeCatchBlock(Type test, int? variable, int body, int? filter) } /// Adds a try/catch node. + /// The node index is recorded in . public int TryCatch(int body, params int[] handlers) { + int index; if (handlers == null || handlers.Length == 0) - return AddFactoryExpressionNode(Nodes[body].Type, null, ExpressionType.Try, 0, body); - - ChildList handlerChildren = default; - for (var i = 0; i < handlers.Length; ++i) - handlerChildren.Add(handlers[i]); - ChildList children = default; - children.Add(body); - children.Add(AddChildListNode(in handlerChildren)); - return AddFactoryExpressionNode(Nodes[body].Type, null, ExpressionType.Try, in children); + { + index = AddFactoryExpressionNode(Nodes[body].Type, null, ExpressionType.Try, 0, body); + } + else + { + ChildList handlerChildren = default; + for (var i = 0; i < handlers.Length; ++i) + handlerChildren.Add(handlers[i]); + ChildList children = default; + children.Add(body); + children.Add(AddChildListNode(in handlerChildren)); + index = AddFactoryExpressionNode(Nodes[body].Type, null, ExpressionType.Try, in children); + } + TryCatchNodes.Add(index); + return index; } /// Adds a try/finally node. - public int TryFinally(int body, int @finally) => - AddFactoryExpressionNode(Nodes[body].Type, null, ExpressionType.Try, 0, body, @finally); + /// The node index is recorded in . + public int TryFinally(int body, int @finally) + { + var index = AddFactoryExpressionNode(Nodes[body].Type, null, ExpressionType.Try, 0, body, @finally); + TryCatchNodes.Add(index); + return index; + } /// Adds a try/fault node. - public int TryFault(int body, int fault) => - AddFactoryExpressionNode(Nodes[body].Type, null, ExpressionType.Try, TryFaultFlag, body, fault); + /// The node index is recorded in . + public int TryFault(int body, int fault) + { + var index = AddFactoryExpressionNode(Nodes[body].Type, null, ExpressionType.Try, TryFaultFlag, body, fault); + TryCatchNodes.Add(index); + return index; + } /// Adds a try node with optional finally block and catch handlers. + /// The node index is recorded in . public int TryCatchFinally(int body, int? @finally, params int[] handlers) { ChildList children = default; @@ -593,7 +654,9 @@ public int TryCatchFinally(int body, int? @finally, params int[] handlers) handlerChildren.Add(handlers[i]); children.Add(AddChildListNode(in handlerChildren)); } - return AddFactoryExpressionNode(Nodes[body].Type, null, ExpressionType.Try, 0, in children); + var index = AddFactoryExpressionNode(Nodes[body].Type, null, ExpressionType.Try, 0, in children); + TryCatchNodes.Add(index); + return index; } /// Adds a type-test node. @@ -847,7 +910,8 @@ private int AddExpression(SysExpr expression) // children.Count == 2 is the canonical test for the presence of variables. var block = (System.Linq.Expressions.BlockExpression)expression; ChildList children = default; - if (block.Variables.Count != 0) + var hasVariables = block.Variables.Count != 0; + if (hasVariables) { ChildList variables = default; for (var i = 0; i < block.Variables.Count; ++i) @@ -858,7 +922,10 @@ private int AddExpression(SysExpr expression) for (var i = 0; i < block.Expressions.Count; ++i) expressions.Add(AddExpression(block.Expressions[i])); children.Add(_tree.AddChildListNode(in expressions)); - return _tree.AddRawExpressionNode(expression.Type, null, expression.NodeType, in children); + var blockIndex = _tree.AddRawExpressionNode(expression.Type, null, expression.NodeType, in children); + if (hasVariables) + _tree.BlocksWithVariables.Add(blockIndex); + return blockIndex; } case ExpressionType.MemberAccess: { @@ -944,7 +1011,9 @@ private int AddExpression(SysExpr expression) children.Add(AddLabelTarget(@goto.Target)); if (@goto.Value != null) children.Add(AddExpression(@goto.Value)); - return _tree.AddRawExpressionNode(expression.Type, @goto.Kind, expression.NodeType, children); + var gotoIndex = _tree.AddRawExpressionNode(expression.Type, @goto.Kind, expression.NodeType, children); + _tree.GotoNodes.Add(gotoIndex); + return gotoIndex; } case ExpressionType.Label: { @@ -953,7 +1022,9 @@ private int AddExpression(SysExpr expression) children.Add(AddLabelTarget(label.Target)); if (label.DefaultValue != null) children.Add(AddExpression(label.DefaultValue)); - return _tree.AddRawExpressionNode(expression.Type, null, expression.NodeType, children); + var labelIndex = _tree.AddRawExpressionNode(expression.Type, null, expression.NodeType, children); + _tree.LabelNodes.Add(labelIndex); + return labelIndex; } case ExpressionType.Switch: { @@ -991,7 +1062,9 @@ private int AddExpression(SysExpr expression) handlers.Add(AddCatchBlock(@try.Handlers[i])); children.Add(_tree.AddChildListNode(in handlers)); } - return _tree.AddNode(expression.Type, null, expression.NodeType, ExprNodeKind.Expression, flags, in children); + var tryIndex = _tree.AddNode(expression.Type, null, expression.NodeType, ExprNodeKind.Expression, flags, in children); + _tree.TryCatchNodes.Add(tryIndex); + return tryIndex; } case ExpressionType.MemberInit: { diff --git a/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs b/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs index ce1195b6..edf89600 100644 --- a/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs +++ b/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs @@ -44,7 +44,13 @@ public int Run() Flat_lambda_nodes_tracks_deeply_nested_lambdas_during_direct_construction(); Flat_lambda_nodes_tracks_lambdas_from_expression_conversion(); Flat_lambda_nodes_has_single_entry_for_root_only_lambda(); - return 27; + Flat_blocks_with_variables_tracked_during_direct_construction(); + Flat_goto_and_label_nodes_tracked_during_direct_construction(); + Flat_try_catch_nodes_tracked_during_direct_construction(); + Flat_blocks_with_variables_tracked_from_expression_conversion(); + Flat_goto_and_label_nodes_tracked_from_expression_conversion(); + Flat_try_catch_nodes_tracked_from_expression_conversion(); + return 33; } @@ -796,5 +802,134 @@ public void Flat_lambda_nodes_has_single_entry_for_root_only_lambda() Asserts.AreEqual(1, fe.LambdaNodes.Count); Asserts.AreEqual(fe.RootIndex, fe.LambdaNodes[0]); } + + /// + /// Block nodes with explicit variable declarations are recorded in BlocksWithVariables; + /// blocks without variables produce no entry. + /// + public void Flat_blocks_with_variables_tracked_during_direct_construction() + { + var fe = default(ExprTree); + var p = fe.ParameterOf("p"); + var v = fe.Variable(typeof(int), "v"); + + // Block with one variable: should be tracked + var blockWithVar = fe.Block(typeof(int), new[] { v }, fe.Assign(v, p), v); + // Block without variables: should NOT be tracked + var blockNoVar = fe.Block(fe.Add(p, fe.ConstantInt(1))); + + fe.RootIndex = fe.Lambda>(fe.Block(blockWithVar, blockNoVar), p); + + Asserts.AreEqual(1, fe.BlocksWithVariables.Count); + Asserts.AreEqual(blockWithVar, fe.BlocksWithVariables[0]); + } + + /// + /// Goto and label expression nodes are recorded in GotoNodes and LabelNodes respectively. + /// + public void Flat_goto_and_label_nodes_tracked_during_direct_construction() + { + var fe = default(ExprTree); + var p = fe.ParameterOf("p"); + var target = fe.Label(typeof(int), "done"); + + var gotoNode = fe.Goto(target, p, typeof(int)); + var labelNode = fe.Label(target, fe.ConstantInt(0)); + + fe.RootIndex = fe.Lambda>(fe.Block(gotoNode, labelNode), p); + + Asserts.AreEqual(1, fe.GotoNodes.Count); + Asserts.AreEqual(gotoNode, fe.GotoNodes[0]); + + Asserts.AreEqual(1, fe.LabelNodes.Count); + Asserts.AreEqual(labelNode, fe.LabelNodes[0]); + } + + /// + /// Try/catch, try/finally and try/fault node indices are all recorded in TryCatchNodes. + /// + public void Flat_try_catch_nodes_tracked_during_direct_construction() + { + var fe = default(ExprTree); + var p = fe.ParameterOf("p"); + + var tryCatchNode = fe.TryCatch( + fe.Add(p, fe.ConstantInt(1)), + fe.Catch(typeof(Exception), fe.ConstantInt(-1))); + + var tryFinallyNode = fe.TryFinally( + fe.Add(p, fe.ConstantInt(2)), + fe.Default(typeof(void))); + + fe.RootIndex = fe.Lambda>( + fe.Block(tryCatchNode, tryFinallyNode), p); + + Asserts.AreEqual(2, fe.TryCatchNodes.Count); + + var foundTryCatch = false; + var foundTryFinally = false; + for (var i = 0; i < fe.TryCatchNodes.Count; i++) + { + if (fe.TryCatchNodes[i] == tryCatchNode) foundTryCatch = true; + if (fe.TryCatchNodes[i] == tryFinallyNode) foundTryFinally = true; + } + Asserts.IsTrue(foundTryCatch); + Asserts.IsTrue(foundTryFinally); + } + + /// + /// When converting a System.Linq expression tree, blocks with variables are + /// recorded in BlocksWithVariables; plain blocks are not. + /// + public void Flat_blocks_with_variables_tracked_from_expression_conversion() + { + var p = SysExpr.Parameter(typeof(int), "p"); + var v = SysExpr.Variable(typeof(int), "v"); + // block with variable + var sysBlock = SysExpr.Block(new[] { v }, SysExpr.Assign(v, p), v); + var sysLambda = SysExpr.Lambda>(sysBlock, p); + + var fe = sysLambda.ToFlatExpression(); + + Asserts.AreEqual(1, fe.BlocksWithVariables.Count); + } + + /// + /// When converting a System.Linq expression tree with goto/label, both + /// GotoNodes and LabelNodes are populated. + /// + public void Flat_goto_and_label_nodes_tracked_from_expression_conversion() + { + var p = SysExpr.Parameter(typeof(int), "p"); + var target = SysExpr.Label(typeof(int), "done"); + var sysLambda = SysExpr.Lambda>( + SysExpr.Block( + SysExpr.Goto(target, p, typeof(int)), + SysExpr.Label(target, SysExpr.Constant(0))), + p); + + var fe = sysLambda.ToFlatExpression(); + + Asserts.AreEqual(1, fe.GotoNodes.Count); + Asserts.AreEqual(1, fe.LabelNodes.Count); + } + + /// + /// When converting a System.Linq expression tree with a try/catch, + /// TryCatchNodes is populated. + /// + public void Flat_try_catch_nodes_tracked_from_expression_conversion() + { + var p = SysExpr.Parameter(typeof(int), "p"); + var sysLambda = SysExpr.Lambda>( + SysExpr.TryCatch( + SysExpr.Add(p, SysExpr.Constant(1)), + SysExpr.Catch(typeof(Exception), SysExpr.Constant(-1))), + p); + + var fe = sysLambda.ToFlatExpression(); + + Asserts.AreEqual(1, fe.TryCatchNodes.Count); + } } }