@@ -173,6 +173,30 @@ public struct ExprTree
173173 /// enabling callers to discover nested lambdas without a full tree traversal.</summary>
174174 public SmallList < int , Stack16 < int > , NoArrayPool < int > > LambdaNodes ;
175175
176+ /// <summary>Gets or sets the indices of all block nodes that carry explicit variable declarations.
177+ /// These are the block nodes where <c>children.Count == 2</c> (variable list + expression list).
178+ /// Populated automatically by <see cref="Block(Type,IEnumerable{int},int[])"/> and <see cref="ExprTree.FromExpression"/>,
179+ /// enabling callers to enumerate block-scoped variables without a full tree traversal.</summary>
180+ public SmallList < int , Stack16 < int > , NoArrayPool < int > > BlocksWithVariables ;
181+
182+ /// <summary>Gets or sets the indices of all <see cref="ExpressionType.Goto"/> nodes
183+ /// (including <c>return</c> and <c>break</c>/<c>continue</c> goto-family nodes).
184+ /// Populated automatically by <see cref="MakeGoto"/> and <see cref="ExprTree.FromExpression"/>,
185+ /// enabling callers to link gotos to their label targets without a full tree traversal.</summary>
186+ public SmallList < int , Stack16 < int > , NoArrayPool < int > > GotoNodes ;
187+
188+ /// <summary>Gets or sets the indices of all <see cref="ExpressionType.Label"/> expression nodes.
189+ /// Populated automatically by <see cref="Label(int,int?)"/> and <see cref="ExprTree.FromExpression"/>,
190+ /// enabling callers to link label expressions to their targets without a full tree traversal.</summary>
191+ public SmallList < int , Stack16 < int > , NoArrayPool < int > > LabelNodes ;
192+
193+ /// <summary>Gets or sets the indices of all <see cref="ExpressionType.Try"/> nodes
194+ /// (try/catch, try/finally, try/fault, and combined forms).
195+ /// Populated automatically by <see cref="TryCatch"/>, <see cref="TryFinally"/>,
196+ /// <see cref="TryFault"/>, <see cref="TryCatchFinally"/> and <see cref="ExprTree.FromExpression"/>,
197+ /// enabling callers to locate all try regions without a full tree traversal.</summary>
198+ public SmallList < int , Stack16 < int > , NoArrayPool < int > > TryCatchNodes ;
199+
176200 /// <summary>Adds a parameter node and returns its index.</summary>
177201 public int Parameter ( Type type , string name = null )
178202 {
@@ -380,26 +404,36 @@ public int Block(params int[] expressions) =>
380404 /// Variable parameter nodes share the same id-slot as the refs used inside the body
381405 /// (out-of-order: the variable decl nodes appear in children[0] before the body expressions
382406 /// that reference them in children[1]).
407+ /// <para>When the block has explicit variable declarations its node index is recorded in
408+ /// <see cref="BlocksWithVariables"/>, enabling callers to enumerate block-scoped variables
409+ /// without a full tree traversal.</para>
383410 /// </remarks>
384411 public int Block ( Type type , IEnumerable < int > variables , params int [ ] expressions )
385412 {
386413 if ( expressions == null || expressions . Length == 0 )
387414 throw new ArgumentException ( "Block should contain at least one expression." , nameof ( expressions ) ) ;
388415
389416 ChildList children = default ;
417+ var hasVariables = false ;
390418 if ( variables != null )
391419 {
392420 ChildList variableChildren = default ;
393421 foreach ( var variable in variables )
394422 variableChildren . Add ( variable ) ;
395423 if ( variableChildren . Count != 0 )
424+ {
396425 children . Add ( AddChildListNode ( in variableChildren ) ) ;
426+ hasVariables = true ;
427+ }
397428 }
398429 ChildList bodyChildren = default ;
399430 for ( var i = 0 ; i < expressions . Length ; ++ i )
400431 bodyChildren . Add ( expressions [ i ] ) ;
401432 children . Add ( AddChildListNode ( in bodyChildren ) ) ;
402- return AddFactoryExpressionNode ( type ?? Nodes [ expressions [ expressions . Length - 1 ] ] . Type , null , ExpressionType . Block , in children ) ;
433+ var index = AddFactoryExpressionNode ( type ?? Nodes [ expressions [ expressions . Length - 1 ] ] . Type , null , ExpressionType . Block , in children ) ;
434+ if ( hasVariables )
435+ BlocksWithVariables . Add ( index ) ;
436+ return index ;
403437 }
404438
405439 /// <summary>Adds a typed lambda node.</summary>
@@ -468,18 +502,26 @@ public int Label(Type type = null, string name = null)
468502 }
469503
470504 /// <summary>Adds a label-expression node.</summary>
471- public int Label ( int target , int ? defaultValue = null ) =>
472- defaultValue . HasValue
505+ /// <remarks>The node index is recorded in <see cref="LabelNodes"/>.</remarks>
506+ public int Label ( int target , int ? defaultValue = null )
507+ {
508+ var index = defaultValue . HasValue
473509 ? AddFactoryExpressionNode ( Nodes [ target ] . Type , null , ExpressionType . Label , 0 , target , defaultValue . Value )
474510 : AddFactoryExpressionNode ( Nodes [ target ] . Type , null , ExpressionType . Label , 0 , target ) ;
511+ LabelNodes . Add ( index ) ;
512+ return index ;
513+ }
475514
476515 /// <summary>Adds a goto-family node.</summary>
516+ /// <remarks>The node index is recorded in <see cref="GotoNodes"/>.</remarks>
477517 public int MakeGoto ( GotoExpressionKind kind , int target , int ? value = null , Type type = null )
478518 {
479519 var resultType = type ?? ( value . HasValue ? Nodes [ value . Value ] . Type : typeof ( void ) ) ;
480- return value . HasValue
520+ var index = value . HasValue
481521 ? AddFactoryExpressionNode ( resultType , kind , ExpressionType . Goto , 0 , target , value . Value )
482522 : AddFactoryExpressionNode ( resultType , kind , ExpressionType . Goto , 0 , target ) ;
523+ GotoNodes . Add ( index ) ;
524+ return index ;
483525 }
484526
485527 /// <summary>Adds a goto node.</summary>
@@ -557,29 +599,48 @@ public int MakeCatchBlock(Type test, int? variable, int body, int? filter)
557599 }
558600
559601 /// <summary>Adds a try/catch node.</summary>
602+ /// <remarks>The node index is recorded in <see cref="TryCatchNodes"/>.</remarks>
560603 public int TryCatch ( int body , params int [ ] handlers )
561604 {
605+ int index ;
562606 if ( handlers == null || handlers . Length == 0 )
563- return AddFactoryExpressionNode ( Nodes [ body ] . Type , null , ExpressionType . Try , 0 , body ) ;
564-
565- ChildList handlerChildren = default ;
566- for ( var i = 0 ; i < handlers . Length ; ++ i )
567- handlerChildren . Add ( handlers [ i ] ) ;
568- ChildList children = default ;
569- children . Add ( body ) ;
570- children . Add ( AddChildListNode ( in handlerChildren ) ) ;
571- return AddFactoryExpressionNode ( Nodes [ body ] . Type , null , ExpressionType . Try , in children ) ;
607+ {
608+ index = AddFactoryExpressionNode ( Nodes [ body ] . Type , null , ExpressionType . Try , 0 , body ) ;
609+ }
610+ else
611+ {
612+ ChildList handlerChildren = default ;
613+ for ( var i = 0 ; i < handlers . Length ; ++ i )
614+ handlerChildren . Add ( handlers [ i ] ) ;
615+ ChildList children = default ;
616+ children . Add ( body ) ;
617+ children . Add ( AddChildListNode ( in handlerChildren ) ) ;
618+ index = AddFactoryExpressionNode ( Nodes [ body ] . Type , null , ExpressionType . Try , in children ) ;
619+ }
620+ TryCatchNodes . Add ( index ) ;
621+ return index ;
572622 }
573623
574624 /// <summary>Adds a try/finally node.</summary>
575- public int TryFinally ( int body , int @finally ) =>
576- AddFactoryExpressionNode ( Nodes [ body ] . Type , null , ExpressionType . Try , 0 , body , @finally ) ;
625+ /// <remarks>The node index is recorded in <see cref="TryCatchNodes"/>.</remarks>
626+ public int TryFinally ( int body , int @finally )
627+ {
628+ var index = AddFactoryExpressionNode ( Nodes [ body ] . Type , null , ExpressionType . Try , 0 , body , @finally ) ;
629+ TryCatchNodes . Add ( index ) ;
630+ return index ;
631+ }
577632
578633 /// <summary>Adds a try/fault node.</summary>
579- public int TryFault ( int body , int fault ) =>
580- AddFactoryExpressionNode ( Nodes [ body ] . Type , null , ExpressionType . Try , TryFaultFlag , body , fault ) ;
634+ /// <remarks>The node index is recorded in <see cref="TryCatchNodes"/>.</remarks>
635+ public int TryFault ( int body , int fault )
636+ {
637+ var index = AddFactoryExpressionNode ( Nodes [ body ] . Type , null , ExpressionType . Try , TryFaultFlag , body , fault ) ;
638+ TryCatchNodes . Add ( index ) ;
639+ return index ;
640+ }
581641
582642 /// <summary>Adds a try node with optional finally block and catch handlers.</summary>
643+ /// <remarks>The node index is recorded in <see cref="TryCatchNodes"/>.</remarks>
583644 public int TryCatchFinally ( int body , int ? @finally , params int [ ] handlers )
584645 {
585646 ChildList children = default ;
@@ -593,7 +654,9 @@ public int TryCatchFinally(int body, int? @finally, params int[] handlers)
593654 handlerChildren . Add ( handlers [ i ] ) ;
594655 children . Add ( AddChildListNode ( in handlerChildren ) ) ;
595656 }
596- return AddFactoryExpressionNode ( Nodes [ body ] . Type , null , ExpressionType . Try , 0 , in children ) ;
657+ var index = AddFactoryExpressionNode ( Nodes [ body ] . Type , null , ExpressionType . Try , 0 , in children ) ;
658+ TryCatchNodes . Add ( index ) ;
659+ return index ;
597660 }
598661
599662 /// <summary>Adds a type-test node.</summary>
@@ -847,7 +910,8 @@ private int AddExpression(SysExpr expression)
847910 // children.Count == 2 is the canonical test for the presence of variables.
848911 var block = ( System . Linq . Expressions . BlockExpression ) expression ;
849912 ChildList children = default ;
850- if ( block . Variables . Count != 0 )
913+ var hasVariables = block . Variables . Count != 0 ;
914+ if ( hasVariables )
851915 {
852916 ChildList variables = default ;
853917 for ( var i = 0 ; i < block . Variables . Count ; ++ i )
@@ -858,7 +922,10 @@ private int AddExpression(SysExpr expression)
858922 for ( var i = 0 ; i < block . Expressions . Count ; ++ i )
859923 expressions . Add ( AddExpression ( block . Expressions [ i ] ) ) ;
860924 children . Add ( _tree . AddChildListNode ( in expressions ) ) ;
861- return _tree . AddRawExpressionNode ( expression . Type , null , expression . NodeType , in children ) ;
925+ var blockIndex = _tree . AddRawExpressionNode ( expression . Type , null , expression . NodeType , in children ) ;
926+ if ( hasVariables )
927+ _tree . BlocksWithVariables . Add ( blockIndex ) ;
928+ return blockIndex ;
862929 }
863930 case ExpressionType . MemberAccess :
864931 {
@@ -944,7 +1011,9 @@ private int AddExpression(SysExpr expression)
9441011 children . Add ( AddLabelTarget ( @goto . Target ) ) ;
9451012 if ( @goto . Value != null )
9461013 children . Add ( AddExpression ( @goto . Value ) ) ;
947- return _tree . AddRawExpressionNode ( expression . Type , @goto . Kind , expression . NodeType , children ) ;
1014+ var gotoIndex = _tree . AddRawExpressionNode ( expression . Type , @goto . Kind , expression . NodeType , children ) ;
1015+ _tree . GotoNodes . Add ( gotoIndex ) ;
1016+ return gotoIndex ;
9481017 }
9491018 case ExpressionType . Label :
9501019 {
@@ -953,7 +1022,9 @@ private int AddExpression(SysExpr expression)
9531022 children . Add ( AddLabelTarget ( label . Target ) ) ;
9541023 if ( label . DefaultValue != null )
9551024 children . Add ( AddExpression ( label . DefaultValue ) ) ;
956- return _tree . AddRawExpressionNode ( expression . Type , null , expression . NodeType , children ) ;
1025+ var labelIndex = _tree . AddRawExpressionNode ( expression . Type , null , expression . NodeType , children ) ;
1026+ _tree . LabelNodes . Add ( labelIndex ) ;
1027+ return labelIndex ;
9571028 }
9581029 case ExpressionType . Switch :
9591030 {
@@ -991,7 +1062,9 @@ private int AddExpression(SysExpr expression)
9911062 handlers . Add ( AddCatchBlock ( @try . Handlers [ i ] ) ) ;
9921063 children . Add ( _tree . AddChildListNode ( in handlers ) ) ;
9931064 }
994- return _tree . AddNode ( expression . Type , null , expression . NodeType , ExprNodeKind . Expression , flags , in children ) ;
1065+ var tryIndex = _tree . AddNode ( expression . Type , null , expression . NodeType , ExprNodeKind . Expression , flags , in children ) ;
1066+ _tree . TryCatchNodes . Add ( tryIndex ) ;
1067+ return tryIndex ;
9951068 }
9961069 case ExpressionType . MemberInit :
9971070 {
0 commit comments