From caec149950722f9190787bfa910eb50b4be035c8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 May 2026 15:24:26 +0000 Subject: [PATCH 1/4] Initial plan From 1f600150f7d928c8b3affd4443a81895f36a884e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 May 2026 15:36:12 +0000 Subject: [PATCH 2/4] Add flat expression order normalization Agent-Logs-Url: https://github.com/dadhi/FastExpressionCompiler/sessions/bc8ce881-a5e5-4b7b-ad10-dac6ff73fa64 Co-authored-by: dadhi <39516+dadhi@users.noreply.github.com> --- .../FlatExpression.cs | 196 ++++++++++++++++++ .../LightExpressionTests.cs | 49 ++++- 2 files changed, 244 insertions(+), 1 deletion(-) diff --git a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs index ad07b3fe..6d8f8d13 100644 --- a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs +++ b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs @@ -709,6 +709,96 @@ public SysExpr ToExpression() => [RequiresUnreferencedCode(FastExpressionCompiler.LightExpression.Trimming.Message)] public LightExpression ToLightExpression() => FastExpressionCompiler.LightExpression.FromSysExpressionConverter.ToLightExpression(ToExpression()); + /// Returns true when all reachable nodes are compacted into a canonical post-order layout. + /// + /// Canonical post-order means that each child is placed before its parent, the root is the last node, + /// and there are no unreachable nodes left in . + /// + public bool IsInOrder() + { + if (Nodes.Count == 0) + return true; + if ((uint)RootIdx >= (uint)Nodes.Count) + return false; + + SmallList, NoArrayPool> ordered = default; + var visitStates = new byte[Nodes.Count]; + if (!TryCollectReachablePostOrder(RootIdx, visitStates, ref ordered) || ordered.Count != Nodes.Count) + return false; + + for (var i = 0; i < ordered.Count; ++i) + if (ordered[i] != i) + return false; + + return true; + } + + /// Compacts the current tree into the canonical post-order layout and drops unreachable nodes. + /// The number of removed unreachable nodes. + public int PutInOrder() + { + if (Nodes.Count == 0) + return 0; + if ((uint)RootIdx >= (uint)Nodes.Count) + throw new InvalidOperationException($"Root idx {RootIdx} is outside of the node range."); + + SmallList, NoArrayPool> ordered = default; + var visitStates = new byte[Nodes.Count]; + if (!TryCollectReachablePostOrder(RootIdx, visitStates, ref ordered)) + throw new InvalidOperationException("The flat expression contains an invalid or cyclic child link."); + + var removedCount = Nodes.Count - ordered.Count; + var alreadyInOrder = removedCount == 0; + if (alreadyInOrder) + for (var i = 0; i < ordered.Count; ++i) + if (ordered[i] != i) + { + alreadyInOrder = false; + break; + } + + if (alreadyInOrder) + return 0; + + var remap = new int[Nodes.Count]; + Array.Fill(remap, -1); + for (var i = 0; i < ordered.Count; ++i) + remap[ordered[i]] = i; + + SmallList, NoArrayPool> reorderedNodes = default; + for (var i = 0; i < ordered.Count; ++i) + { + ref var oldNode = ref Nodes.GetSurePresentRef(ordered[i]); + ref var newNode = ref reorderedNodes.AddDefaultAndGetRef(); + newNode = oldNode; + newNode.SetNextIdx(0); + } + + for (var i = 0; i < ordered.Count; ++i) + { + ref var oldNode = ref Nodes.GetSurePresentRef(ordered[i]); + if (!HasStructuralChildren(in oldNode)) + continue; + + var oldChildIdx = oldNode.ChildIdx; + ref var newNode = ref reorderedNodes.GetSurePresentRef(i); + newNode.SetChildInfo(remap[oldChildIdx], oldNode.ChildCount); + + for (var childNumber = 1; childNumber < oldNode.ChildCount; ++childNumber) + { + var nextOldChildIdx = Nodes.GetSurePresentRef(oldChildIdx).NextIdx; + reorderedNodes.GetSurePresentRef(remap[oldChildIdx]).SetNextIdx(remap[nextOldChildIdx]); + oldChildIdx = nextOldChildIdx; + } + } + + Nodes = reorderedNodes; + RootIdx = remap[RootIdx]; + ClosureConstants = CompactClosureConstants(); + RebuildMetadata(); + return removedCount; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, int child) => AddNode(type, obj, nodeType, ExprNodeKind.Expression, 0, CloneChild(child)); @@ -1408,6 +1498,12 @@ private static Type GetArrayElementType(Type arrayType, int depth) return elementType ?? typeof(object); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool HasStructuralChildren(in ExprNode node) => + node.ChildCount != 0 && + !ReferenceEquals(node.Obj, ExprNode.InlineValueMarker) && + node.Kind != ExprNodeKind.UInt16Pair; + [MethodImpl(MethodImplOptions.AggressiveInlining)] private int CloneChild(int idx) { @@ -1606,6 +1702,106 @@ private static bool Contains(ref SmallList [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ushort ToStoredUShortIdx(int idx) => checked((ushort)idx); + private bool TryCollectReachablePostOrder( + int idx, + byte[] visitStates, + ref SmallList, NoArrayPool> ordered) + { + if ((uint)idx >= (uint)Nodes.Count) + return false; + + var visitState = visitStates[idx]; + if (visitState == 2) + return true; + if (visitState == 1) + return false; + + visitStates[idx] = 1; + + ref var node = ref Nodes.GetSurePresentRef(idx); + if (HasStructuralChildren(in node)) + { + var childIdx = node.ChildIdx; + for (var i = 0; i < node.ChildCount; ++i) + { + var currentChildIdx = childIdx; + if (!TryCollectReachablePostOrder(currentChildIdx, visitStates, ref ordered)) + return false; + childIdx = Nodes.GetSurePresentRef(currentChildIdx).NextIdx; + } + } + + visitStates[idx] = 2; + ordered.Add(idx); + return true; + } + + private SmallList, NoArrayPool> CompactClosureConstants() + { + SmallList, NoArrayPool> compacted = default; + if (ClosureConstants.Count == 0) + return compacted; + + var remap = new int[ClosureConstants.Count]; + Array.Fill(remap, -1); + for (var i = 0; i < Nodes.Count; ++i) + { + ref var node = ref Nodes.GetSurePresentRef(i); + if (node.NodeType != ExpressionType.Constant || !ReferenceEquals(node.Obj, ClosureConstantMarker)) + continue; + + var newConstIdx = remap[node.ChildIdx]; + if (newConstIdx < 0) + { + newConstIdx = compacted.Count; + remap[node.ChildIdx] = newConstIdx; + compacted.Add(ClosureConstants[node.ChildIdx]); + } + + node.SetChildInfo(newConstIdx, 0); + } + + return compacted; + } + + private void RebuildMetadata() + { + LambdaNodes = default; + BlocksWithVariables = default; + GotoNodes = default; + LabelNodes = default; + TryCatchNodes = default; + LambdaClosureParameterUsages = default; + + for (var i = 0; i < Nodes.Count; ++i) + { + ref var node = ref Nodes.GetSurePresentRef(i); + if (!node.IsExpression()) + continue; + + switch (node.NodeType) + { + case ExpressionType.Lambda: + LambdaNodes.Add(i); + CollectLambdaClosureParameterUsages(i); + break; + case ExpressionType.Block: + if (HasStructuralChildren(in node) && node.ChildCount == 2) + BlocksWithVariables.Add(i); + break; + case ExpressionType.Goto: + GotoNodes.Add(i); + break; + case ExpressionType.Label: + LabelNodes.Add(i); + break; + case ExpressionType.Try: + TryCatchNodes.Add(i); + break; + } + } + } + /// Reconstructs System.Linq nodes from the flat representation while reusing parameter and label identities. private struct Reader { diff --git a/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs b/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs index 3e3ff694..ba9055ad 100644 --- a/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs +++ b/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs @@ -55,7 +55,9 @@ public int Run() 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 38; + Flat_expression_order_check_accepts_canonical_post_order_layout(); + Flat_expression_put_in_order_compacts_reachable_nodes_and_rebuilds_metadata(); + return 40; } @@ -1023,5 +1025,50 @@ public void Flat_try_catch_nodes_tracked_from_expression_conversion() Asserts.AreEqual(1, fe.TryCatchNodes.Count); } + + public void Flat_expression_order_check_accepts_canonical_post_order_layout() + { + var fe = CreateComplexLightExpression("state").ToFlatExpression(); + + Asserts.IsTrue(fe.IsInOrder()); + } + + public void Flat_expression_put_in_order_compacts_reachable_nodes_and_rebuilds_metadata() + { + var valueHolder = new S { Value = "ordered" }; + + var fe = default(ExprTree); + var parameter = fe.ParameterOf("p"); + _ = fe.Constant(new S { Value = "unused" }, typeof(S)); + _ = fe.Label(typeof(int), "unused"); + var returnTarget = fe.Label(typeof(string), "done"); + var closureValue = fe.Field(fe.Constant(valueHolder, typeof(S)), typeof(S).GetField(nameof(S.Value))); + var tryCatch = fe.TryCatch( + fe.Add(parameter, fe.ConstantInt(1)), + fe.Catch(typeof(Exception), fe.ConstantInt(-1))); + fe.RootIdx = fe.Lambda>( + fe.Block(typeof(string), new[] { fe.Variable(typeof(int), "temp") }, + tryCatch, + fe.Label(returnTarget, closureValue)), + parameter); + + Asserts.IsFalse(fe.IsInOrder()); + Asserts.AreEqual(2, fe.ClosureConstants.Count); + + var removedNodes = fe.PutInOrder(); + + Asserts.IsTrue(removedNodes >= 2); + Asserts.IsTrue(fe.IsInOrder()); + Asserts.AreEqual(fe.Nodes.Count - 1, fe.RootIdx); + Asserts.AreEqual(1, fe.ClosureConstants.Count); + Asserts.AreSame(valueHolder, fe.ClosureConstants[0]); + Asserts.AreEqual(1, fe.LambdaNodes.Count); + Asserts.AreEqual(fe.RootIdx, fe.LambdaNodes[0]); + Asserts.AreEqual(1, fe.BlocksWithVariables.Count); + Asserts.AreEqual(1, fe.LabelNodes.Count); + Asserts.AreEqual(0, fe.GotoNodes.Count); + Asserts.AreEqual(1, fe.TryCatchNodes.Count); + Asserts.AreEqual("ordered", ((LambdaExpression)fe.ToLightExpression()).CompileFast>(true)(41)); + } } } From ded86b8f0cf47b473b334d564420daa9ec5842d1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 May 2026 15:40:18 +0000 Subject: [PATCH 3/4] Refine flat expression ordering tests and docs Agent-Logs-Url: https://github.com/dadhi/FastExpressionCompiler/sessions/bc8ce881-a5e5-4b7b-ad10-dac6ff73fa64 Co-authored-by: dadhi <39516+dadhi@users.noreply.github.com> --- .../FlatExpression.cs | 3 ++- .../LightExpressionTests.cs | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs index 6d8f8d13..3bf7d129 100644 --- a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs +++ b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs @@ -740,7 +740,7 @@ public int PutInOrder() if (Nodes.Count == 0) return 0; if ((uint)RootIdx >= (uint)Nodes.Count) - throw new InvalidOperationException($"Root idx {RootIdx} is outside of the node range."); + throw new InvalidOperationException($"Root index {RootIdx} is outside of the node range."); SmallList, NoArrayPool> ordered = default; var visitStates = new byte[Nodes.Count]; @@ -771,6 +771,7 @@ public int PutInOrder() ref var oldNode = ref Nodes.GetSurePresentRef(ordered[i]); ref var newNode = ref reorderedNodes.AddDefaultAndGetRef(); newNode = oldNode; + // Clear all sibling links first; the next loop rewires them using the remapped child order. newNode.SetNextIdx(0); } diff --git a/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs b/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs index ba9055ad..005554cb 100644 --- a/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs +++ b/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs @@ -55,7 +55,7 @@ public int Run() 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(); - Flat_expression_order_check_accepts_canonical_post_order_layout(); + Flat_expression_created_from_conversion_is_in_canonical_order(); Flat_expression_put_in_order_compacts_reachable_nodes_and_rebuilds_metadata(); return 40; } @@ -1026,7 +1026,7 @@ public void Flat_try_catch_nodes_tracked_from_expression_conversion() Asserts.AreEqual(1, fe.TryCatchNodes.Count); } - public void Flat_expression_order_check_accepts_canonical_post_order_layout() + public void Flat_expression_created_from_conversion_is_in_canonical_order() { var fe = CreateComplexLightExpression("state").ToFlatExpression(); @@ -1039,8 +1039,8 @@ public void Flat_expression_put_in_order_compacts_reachable_nodes_and_rebuilds_m var fe = default(ExprTree); var parameter = fe.ParameterOf("p"); - _ = fe.Constant(new S { Value = "unused" }, typeof(S)); - _ = fe.Label(typeof(int), "unused"); + _ = fe.Constant(new S { Value = "unreachable_constant" }, typeof(S)); + _ = fe.Label(typeof(int), "unreachable_label"); var returnTarget = fe.Label(typeof(string), "done"); var closureValue = fe.Field(fe.Constant(valueHolder, typeof(S)), typeof(S).GetField(nameof(S.Value))); var tryCatch = fe.TryCatch( From fdf7b03ff978fa8b48e4ddf4e445bf9a6b25f8e7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 05:15:35 +0000 Subject: [PATCH 4/4] Simplify flat expression ordering implementation Agent-Logs-Url: https://github.com/dadhi/FastExpressionCompiler/sessions/a59f1ceb-a4eb-40cd-9e5a-9ced5ca388e6 Co-authored-by: dadhi <39516+dadhi@users.noreply.github.com> --- .../FlatExpression.cs | 148 ++---------------- .../LightExpressionTests.cs | 54 ++++--- 2 files changed, 40 insertions(+), 162 deletions(-) diff --git a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs index 3bf7d129..f6821ab5 100644 --- a/src/FastExpressionCompiler.LightExpression/FlatExpression.cs +++ b/src/FastExpressionCompiler.LightExpression/FlatExpression.cs @@ -721,83 +721,26 @@ public bool IsInOrder() if ((uint)RootIdx >= (uint)Nodes.Count) return false; - SmallList, NoArrayPool> ordered = default; var visitStates = new byte[Nodes.Count]; - if (!TryCollectReachablePostOrder(RootIdx, visitStates, ref ordered) || ordered.Count != Nodes.Count) + var expectedIdx = 0; + if (!TryValidateCanonicalPostOrder(RootIdx, visitStates, ref expectedIdx)) return false; - - for (var i = 0; i < ordered.Count; ++i) - if (ordered[i] != i) - return false; - - return true; + return expectedIdx == Nodes.Count; } /// Compacts the current tree into the canonical post-order layout and drops unreachable nodes. /// The number of removed unreachable nodes. + [RequiresUnreferencedCode(FastExpressionCompiler.LightExpression.Trimming.Message)] public int PutInOrder() { if (Nodes.Count == 0) return 0; - if ((uint)RootIdx >= (uint)Nodes.Count) - throw new InvalidOperationException($"Root index {RootIdx} is outside of the node range."); - - SmallList, NoArrayPool> ordered = default; - var visitStates = new byte[Nodes.Count]; - if (!TryCollectReachablePostOrder(RootIdx, visitStates, ref ordered)) - throw new InvalidOperationException("The flat expression contains an invalid or cyclic child link."); - - var removedCount = Nodes.Count - ordered.Count; - var alreadyInOrder = removedCount == 0; - if (alreadyInOrder) - for (var i = 0; i < ordered.Count; ++i) - if (ordered[i] != i) - { - alreadyInOrder = false; - break; - } - - if (alreadyInOrder) + if (IsInOrder()) return 0; - var remap = new int[Nodes.Count]; - Array.Fill(remap, -1); - for (var i = 0; i < ordered.Count; ++i) - remap[ordered[i]] = i; - - SmallList, NoArrayPool> reorderedNodes = default; - for (var i = 0; i < ordered.Count; ++i) - { - ref var oldNode = ref Nodes.GetSurePresentRef(ordered[i]); - ref var newNode = ref reorderedNodes.AddDefaultAndGetRef(); - newNode = oldNode; - // Clear all sibling links first; the next loop rewires them using the remapped child order. - newNode.SetNextIdx(0); - } - - for (var i = 0; i < ordered.Count; ++i) - { - ref var oldNode = ref Nodes.GetSurePresentRef(ordered[i]); - if (!HasStructuralChildren(in oldNode)) - continue; - - var oldChildIdx = oldNode.ChildIdx; - ref var newNode = ref reorderedNodes.GetSurePresentRef(i); - newNode.SetChildInfo(remap[oldChildIdx], oldNode.ChildCount); - - for (var childNumber = 1; childNumber < oldNode.ChildCount; ++childNumber) - { - var nextOldChildIdx = Nodes.GetSurePresentRef(oldChildIdx).NextIdx; - reorderedNodes.GetSurePresentRef(remap[oldChildIdx]).SetNextIdx(remap[nextOldChildIdx]); - oldChildIdx = nextOldChildIdx; - } - } - - Nodes = reorderedNodes; - RootIdx = remap[RootIdx]; - ClosureConstants = CompactClosureConstants(); - RebuildMetadata(); - return removedCount; + var originalNodeCount = Nodes.Count; + this = FromExpression(ToExpression()); + return originalNodeCount - Nodes.Count; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -1703,10 +1646,10 @@ private static bool Contains(ref SmallList [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ushort ToStoredUShortIdx(int idx) => checked((ushort)idx); - private bool TryCollectReachablePostOrder( + private bool TryValidateCanonicalPostOrder( int idx, byte[] visitStates, - ref SmallList, NoArrayPool> ordered) + ref int expectedIdx) { if ((uint)idx >= (uint)Nodes.Count) return false; @@ -1726,81 +1669,14 @@ private bool TryCollectReachablePostOrder( for (var i = 0; i < node.ChildCount; ++i) { var currentChildIdx = childIdx; - if (!TryCollectReachablePostOrder(currentChildIdx, visitStates, ref ordered)) + if (!TryValidateCanonicalPostOrder(currentChildIdx, visitStates, ref expectedIdx)) return false; childIdx = Nodes.GetSurePresentRef(currentChildIdx).NextIdx; } } visitStates[idx] = 2; - ordered.Add(idx); - return true; - } - - private SmallList, NoArrayPool> CompactClosureConstants() - { - SmallList, NoArrayPool> compacted = default; - if (ClosureConstants.Count == 0) - return compacted; - - var remap = new int[ClosureConstants.Count]; - Array.Fill(remap, -1); - for (var i = 0; i < Nodes.Count; ++i) - { - ref var node = ref Nodes.GetSurePresentRef(i); - if (node.NodeType != ExpressionType.Constant || !ReferenceEquals(node.Obj, ClosureConstantMarker)) - continue; - - var newConstIdx = remap[node.ChildIdx]; - if (newConstIdx < 0) - { - newConstIdx = compacted.Count; - remap[node.ChildIdx] = newConstIdx; - compacted.Add(ClosureConstants[node.ChildIdx]); - } - - node.SetChildInfo(newConstIdx, 0); - } - - return compacted; - } - - private void RebuildMetadata() - { - LambdaNodes = default; - BlocksWithVariables = default; - GotoNodes = default; - LabelNodes = default; - TryCatchNodes = default; - LambdaClosureParameterUsages = default; - - for (var i = 0; i < Nodes.Count; ++i) - { - ref var node = ref Nodes.GetSurePresentRef(i); - if (!node.IsExpression()) - continue; - - switch (node.NodeType) - { - case ExpressionType.Lambda: - LambdaNodes.Add(i); - CollectLambdaClosureParameterUsages(i); - break; - case ExpressionType.Block: - if (HasStructuralChildren(in node) && node.ChildCount == 2) - BlocksWithVariables.Add(i); - break; - case ExpressionType.Goto: - GotoNodes.Add(i); - break; - case ExpressionType.Label: - LabelNodes.Add(i); - break; - case ExpressionType.Try: - TryCatchNodes.Add(i); - break; - } - } + return idx == expectedIdx++; } /// Reconstructs System.Linq nodes from the flat representation while reusing parameter and label identities. diff --git a/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs b/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs index 005554cb..a7ad89ca 100644 --- a/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs +++ b/test/FastExpressionCompiler.LightExpression.UnitTests/LightExpressionTests.cs @@ -56,7 +56,7 @@ public int Run() Flat_goto_and_label_nodes_tracked_from_expression_conversion(); Flat_try_catch_nodes_tracked_from_expression_conversion(); Flat_expression_created_from_conversion_is_in_canonical_order(); - Flat_expression_put_in_order_compacts_reachable_nodes_and_rebuilds_metadata(); + Flat_expression_put_in_order_rebuilds_direct_helper_tree_with_compact_indexes(); return 40; } @@ -1033,42 +1033,44 @@ public void Flat_expression_created_from_conversion_is_in_canonical_order() Asserts.IsTrue(fe.IsInOrder()); } - public void Flat_expression_put_in_order_compacts_reachable_nodes_and_rebuilds_metadata() + public void Flat_expression_put_in_order_rebuilds_direct_helper_tree_with_compact_indexes() { - var valueHolder = new S { Value = "ordered" }; - var fe = default(ExprTree); var parameter = fe.ParameterOf("p"); - _ = fe.Constant(new S { Value = "unreachable_constant" }, typeof(S)); - _ = fe.Label(typeof(int), "unreachable_label"); - var returnTarget = fe.Label(typeof(string), "done"); - var closureValue = fe.Field(fe.Constant(valueHolder, typeof(S)), typeof(S).GetField(nameof(S.Value))); - var tryCatch = fe.TryCatch( - fe.Add(parameter, fe.ConstantInt(1)), - fe.Catch(typeof(Exception), fe.ConstantInt(-1))); - fe.RootIdx = fe.Lambda>( - fe.Block(typeof(string), new[] { fe.Variable(typeof(int), "temp") }, - tryCatch, - fe.Label(returnTarget, closureValue)), - parameter); - + var one = fe.ConstantInt(1); + var add = fe.Add(parameter, one); + fe.RootIdx = fe.Lambda>(add, parameter); + + Asserts.AreEqual(0, parameter); + Asserts.AreEqual(1, one); + Asserts.AreEqual(4, add); + Asserts.AreEqual(6, fe.RootIdx); + Asserts.AreEqual(7, fe.Nodes.Count); Asserts.IsFalse(fe.IsInOrder()); - Asserts.AreEqual(2, fe.ClosureConstants.Count); var removedNodes = fe.PutInOrder(); - Asserts.IsTrue(removedNodes >= 2); + Asserts.AreEqual(2, removedNodes); Asserts.IsTrue(fe.IsInOrder()); - Asserts.AreEqual(fe.Nodes.Count - 1, fe.RootIdx); - Asserts.AreEqual(1, fe.ClosureConstants.Count); - Asserts.AreSame(valueHolder, fe.ClosureConstants[0]); + Asserts.AreEqual(5, fe.Nodes.Count); + Asserts.AreEqual(4, fe.RootIdx); Asserts.AreEqual(1, fe.LambdaNodes.Count); Asserts.AreEqual(fe.RootIdx, fe.LambdaNodes[0]); - Asserts.AreEqual(1, fe.BlocksWithVariables.Count); - Asserts.AreEqual(1, fe.LabelNodes.Count); Asserts.AreEqual(0, fe.GotoNodes.Count); - Asserts.AreEqual(1, fe.TryCatchNodes.Count); - Asserts.AreEqual("ordered", ((LambdaExpression)fe.ToLightExpression()).CompileFast>(true)(41)); + Asserts.AreEqual(0, fe.LabelNodes.Count); + Asserts.AreEqual(0, fe.TryCatchNodes.Count); + Asserts.AreEqual(ExpressionType.Parameter, fe.Nodes[0].NodeType); + Asserts.AreEqual(ExpressionType.Constant, fe.Nodes[1].NodeType); + Asserts.AreEqual(ExpressionType.Add, fe.Nodes[2].NodeType); + Asserts.AreEqual(ExpressionType.Parameter, fe.Nodes[3].NodeType); + Asserts.AreEqual(ExpressionType.Lambda, fe.Nodes[4].NodeType); + Asserts.AreEqual(0, fe.Nodes[2].ChildIdx); + Asserts.AreEqual(2, fe.Nodes[2].ChildCount); + Asserts.AreEqual(1, fe.Nodes[0].NextIdx); + Asserts.AreEqual(2, fe.Nodes[4].ChildIdx); + Asserts.AreEqual(2, fe.Nodes[4].ChildCount); + Asserts.AreEqual(3, fe.Nodes[2].NextIdx); + Asserts.AreEqual(42, ((LambdaExpression)fe.ToLightExpression()).CompileFast>(true)(41)); } } }