-
-
Notifications
You must be signed in to change notification settings - Fork 94
Expand file tree
/
Copy pathFlatExpression.cs
More file actions
1992 lines (1772 loc) · 104 KB
/
FlatExpression.cs
File metadata and controls
1992 lines (1772 loc) · 104 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
namespace FastExpressionCompiler.FlatExpression;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using FastExpressionCompiler.LightExpression.ImTools;
using ChildList = FastExpressionCompiler.LightExpression.ImTools.SmallList<int, FastExpressionCompiler.LightExpression.ImTools.Stack16<int>, FastExpressionCompiler.LightExpression.ImTools.NoArrayPool<int>>;
using LightExpression = FastExpressionCompiler.LightExpression.Expression;
using SysCatchBlock = System.Linq.Expressions.CatchBlock;
using SysElementInit = System.Linq.Expressions.ElementInit;
using SysExpr = System.Linq.Expressions.Expression;
using SysLabelTarget = System.Linq.Expressions.LabelTarget;
using SysMemberBinding = System.Linq.Expressions.MemberBinding;
using SysParameterExpression = System.Linq.Expressions.ParameterExpression;
using SysSwitchCase = System.Linq.Expressions.SwitchCase;
/// <summary>Classifies the stored flat node payload.</summary>
public enum ExprNodeKind : byte
{
/// <summary>Represents a regular expression node.</summary>
Expression,
/// <summary>Represents a switch case payload.</summary>
SwitchCase,
/// <summary>Represents a catch block payload.</summary>
CatchBlock,
/// <summary>Represents a label target payload.</summary>
LabelTarget,
/// <summary>Represents a member-assignment binding payload.</summary>
MemberAssignment,
/// <summary>Represents a nested member-binding payload.</summary>
MemberMemberBinding,
/// <summary>Represents a list-binding payload.</summary>
MemberListBinding,
/// <summary>Represents an element initializer payload.</summary>
ElementInit,
/// <summary>Represents an internal object-reference metadata node.</summary>
ObjectReference,
/// <summary>Represents an internal child-list metadata node.</summary>
ChildList,
/// <summary>Represents an internal pair of UInt16 values.</summary>
UInt16Pair,
}
/// <summary>Maps a lambda node to a parameter identity used from an outer scope and therefore captured in closure.</summary>
[StructLayout(LayoutKind.Sequential, Pack = 2)]
public readonly struct LambdaClosureParameterUsage
{
/// <summary>The lambda node index containing the parameter usage.</summary>
public readonly short LambdaNodeIndex;
/// <summary>The parameter identity id (<see cref="ExprNode.ChildIdx"/>) referenced from outer scope.</summary>
public readonly short ParameterId;
public LambdaClosureParameterUsage(short lambdaNodeIndex, short parameterId)
{
LambdaNodeIndex = lambdaNodeIndex;
ParameterId = parameterId;
}
}
/// <summary>Stores one flat expression node plus its intrusive child-link metadata in 24 bytes on 64-bit runtimes.</summary>
/// <remarks>
/// Layout (64-bit): Type(8) | Obj(8) | _meta(4) | _data(4) = 24 bytes.
/// _meta bits: NodeType(8)|Tag(8)|NextIdx(16).
/// _data bits: ChildCount(16)|ChildIdx(16) for regular nodes,
/// or the raw 32-bit value for inline primitive constants (when <see cref="Obj"/> == <see cref="InlineValueMarker"/>).
/// </remarks>
[StructLayout(LayoutKind.Explicit, Size = 24)]
public struct ExprNode
{
// _meta layout: bits [31:24]=NodeType | [23:20]=Flags | [19:16]=Kind | [15:0]=NextIdx
private const int MetaNodeTypeShift = 24;
private const int MetaTagShift = 16;
private const uint MetaKeepWithoutNext = 0xFFFF0000u;
// _data layout: bits [31:16]=ChildCount | [15:0]=ChildIdx (or full uint for inline constants)
private const int DataCountShift = 16;
private const uint DataIdxMask = 0xFFFFu;
private const int FlagsShift = 4;
private const uint KindMask = 0x0Fu;
/// <summary>Sentinel placed in <see cref="Obj"/> to indicate the node holds a small primitive constant in <see cref="InlineValue"/>.</summary>
internal static readonly object InlineValueMarker = new();
/// <summary>Gets or sets the runtime type of the represented node.</summary>
[FieldOffset(0)]
public Type Type;
/// <summary>Gets or sets the runtime payload associated with the node.</summary>
[FieldOffset(8)]
public object Obj;
/// <summary>NodeType(8b) | Tag=(Flags:4b|Kind:4b)(8b) | NextIdx(16b)</summary>
[FieldOffset(16)]
private uint _meta;
/// <summary>ChildCount(16b) | ChildIdx(16b) —OR— raw 32-bit inline constant value.</summary>
[FieldOffset(20)]
private uint _data;
/// <summary>Gets the expression kind encoded for this node.</summary>
public ExpressionType NodeType => (ExpressionType)(_meta >> MetaNodeTypeShift);
/// <summary>Gets the payload classification for this node.</summary>
public ExprNodeKind Kind => (ExprNodeKind)((_meta >> MetaTagShift) & KindMask);
internal byte Flags => (byte)((_meta >> (MetaTagShift + FlagsShift)) & 0xFu);
/// <summary>Gets the next sibling node index in the intrusive child chain.</summary>
public int NextIdx => (int)(_meta & 0xFFFFu);
/// <summary>Gets the number of direct children linked from this node.</summary>
public int ChildCount => (int)(_data >> DataCountShift);
/// <summary>Gets the first child index or an auxiliary payload index.</summary>
public int ChildIdx => (int)(_data & DataIdxMask);
/// <summary>Gets the raw 32-bit value for inline primitive constants. Only valid when <see cref="Obj"/> == <see cref="InlineValueMarker"/>.</summary>
internal uint InlineValue => _data;
internal ExprNode(Type type, object obj, ExpressionType nodeType, ExprNodeKind kind, byte flags = 0, int childIdx = 0, int childCount = 0, int nextIdx = 0)
{
Type = type;
Obj = obj;
var tag = (byte)((flags << FlagsShift) | (byte)kind);
_meta = ((uint)(byte)nodeType << MetaNodeTypeShift) | ((uint)tag << MetaTagShift) | (ushort)nextIdx;
_data = ((uint)(ushort)childCount << DataCountShift) | (ushort)childIdx;
}
/// <summary>Constructs an inline primitive constant node; <see cref="Obj"/> is set to <see cref="InlineValueMarker"/>.</summary>
internal ExprNode(Type type, uint inlineValue)
{
Type = type;
Obj = InlineValueMarker;
_meta = (uint)(byte)ExpressionType.Constant << MetaNodeTypeShift;
_data = inlineValue;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void SetNextIdx(int nextIdx) =>
_meta = (_meta & MetaKeepWithoutNext) | (ushort)nextIdx;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void SetChildInfo(int childIdx, int childCount) =>
_data = ((uint)(ushort)childCount << DataCountShift) | (ushort)childIdx;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool Is(ExprNodeKind kind) => Kind == kind;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool IsExpression() => Kind == ExprNodeKind.Expression;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool HasFlag(byte flag) => (Flags & flag) != 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool ShouldCloneWhenLinked() =>
ReferenceEquals(Obj, InlineValueMarker) ||
Kind == ExprNodeKind.LabelTarget || NodeType == ExpressionType.Parameter ||
Kind == ExprNodeKind.ObjectReference || ChildCount == 0;
}
/// <summary>Stores an expression tree as a flat node array plus out-of-line closure constants.</summary>
public struct ExprTree
{
private static readonly object ClosureConstantMarker = new();
private const byte ParameterByRefFlag = 1;
private const byte BinaryLiftedToNullFlag = 1;
private const byte LoopHasBreakFlag = 1;
private const byte LoopHasContinueFlag = 2;
private const byte CatchHasVariableFlag = 1;
private const byte CatchHasFilterFlag = 2;
private const byte TryFaultFlag = 1;
/// <summary>Gets or sets the root node index.</summary>
public int RootIndex;
/// <summary>Gets or sets the flat node storage.</summary>
public SmallList<ExprNode, Stack16<ExprNode>, NoArrayPool<ExprNode>> Nodes;
/// <summary>Gets or sets closure constants that are referenced from constant nodes.</summary>
public SmallList<object, Stack16<object>, NoArrayPool<object>> ClosureConstants;
/// <summary>Gets or sets the indices of all lambda nodes added during construction.
/// The root lambda index is stored in <see cref="RootIndex"/>; all other entries are nested lambdas.
/// Populated automatically by <see cref="Lambda(Type,int,int[])"/> and <see cref="ExprTree.FromExpression"/>,
/// enabling callers to discover nested lambdas without a full tree traversal.</summary>
public SmallList<int, Stack16<int>, NoArrayPool<int>> LambdaNodes;
/// <summary>Gets or sets the indices of all block nodes that carry explicit variable declarations.
/// These are the block nodes where <c>children.Count == 2</c> (variable list + expression list).
/// Populated automatically by <see cref="Block(Type,IEnumerable{int},int[])"/> and <see cref="ExprTree.FromExpression"/>,
/// enabling callers to enumerate block-scoped variables without a full tree traversal.</summary>
public SmallList<int, Stack16<int>, NoArrayPool<int>> BlocksWithVariables;
/// <summary>Gets or sets the indices of all <see cref="ExpressionType.Goto"/> nodes
/// (including <c>return</c> and <c>break</c>/<c>continue</c> goto-family nodes).
/// Populated automatically by <see cref="MakeGoto"/> and <see cref="ExprTree.FromExpression"/>,
/// enabling callers to link gotos to their label targets without a full tree traversal.</summary>
public SmallList<int, Stack16<int>, NoArrayPool<int>> GotoNodes;
/// <summary>Gets or sets the indices of all <see cref="ExpressionType.Label"/> expression nodes.
/// Populated automatically by <see cref="Label(int,int?)"/> and <see cref="ExprTree.FromExpression"/>,
/// enabling callers to link label expressions to their targets without a full tree traversal.</summary>
public SmallList<int, Stack16<int>, NoArrayPool<int>> LabelNodes;
/// <summary>Gets or sets the indices of all <see cref="ExpressionType.Try"/> nodes
/// (try/catch, try/finally, try/fault, and combined forms).
/// Populated automatically by <see cref="TryCatch"/>, <see cref="TryFinally"/>,
/// <see cref="TryFault"/>, <see cref="TryCatchFinally"/> and <see cref="ExprTree.FromExpression"/>,
/// enabling callers to locate all try regions without a full tree traversal.</summary>
public SmallList<int, Stack16<int>, NoArrayPool<int>> TryCatchNodes;
/// <summary>Gets or sets mappings of lambda-node index to captured parameter id for nested-lambda closures.
/// Populated by <see cref="ExprTree.FromExpression"/> while flattening System.Linq lambdas;
/// each entry means that the lambda body references a parameter that is not declared as that lambda parameter
/// and not declared as a local block/catch variable in that lambda body scope.</summary>
public SmallList<LambdaClosureParameterUsage, Stack16<LambdaClosureParameterUsage>, NoArrayPool<LambdaClosureParameterUsage>> LambdaClosureParameterUsages;
/// <summary>Adds a parameter node and returns its index.</summary>
public int Parameter(Type type, string name = null)
{
var id = Nodes.Count + 1;
return AddRawLeafExpressionNode(type, name, ExpressionType.Parameter, type.IsByRef ? ParameterByRefFlag : (byte)0, childIdx: id);
}
/// <summary>Adds a typed parameter node and returns its index.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ParameterOf<T>(string name = null) => Parameter(typeof(T), name);
/// <summary>Adds a variable node and returns its index.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Variable(Type type, string name = null) => Parameter(type, name);
/// <summary>Adds a default-value node and returns its index.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Default(Type type) => AddRawExpressionNode(type, null, ExpressionType.Default);
/// <summary>Adds a constant node using the runtime type of the supplied value.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Constant(object value) =>
Constant(value, value?.GetType() ?? typeof(object));
/// <summary>Adds a constant node with an explicit constant type.</summary>
public int Constant(object value, Type type)
{
if (value == null || value is string || value is Type || value is decimal)
return AddRawExpressionNode(type, value, ExpressionType.Constant);
if (type.IsEnum)
{
var underlyingTc = Type.GetTypeCode(Enum.GetUnderlyingType(type));
if (IsSmallPrimitive(underlyingTc))
return AddInlineConstantNode(type, (uint)System.Convert.ToInt64(value));
// long/ulong-backed enum (extremely rare): store boxed in Obj
return AddRawExpressionNode(type, value, ExpressionType.Constant);
}
if (type.IsPrimitive)
{
var tc = Type.GetTypeCode(type);
if (IsSmallPrimitive(tc))
return AddInlineConstantNode(type, ToInlineValue(value, tc));
// long, ulong, double: primitive but too wide for _data, store boxed in Obj
return AddRawExpressionNode(type, value, ExpressionType.Constant);
}
// Delegate, array types, and user-defined reference/value types go to ClosureConstants
var constantIndex = ClosureConstants.Add(value);
return AddRawLeafExpressionNode(type, ClosureConstantMarker, ExpressionType.Constant, childIdx: constantIndex);
}
/// <summary>Adds a null constant node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ConstantNull(Type type = null) => AddRawExpressionNode(type ?? typeof(object), null, ExpressionType.Constant);
/// <summary>Adds an <see cref="int"/> constant node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ConstantInt(int value) => AddRawExpressionNode(typeof(int), value, ExpressionType.Constant);
/// <summary>Adds a typed constant node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ConstantOf<T>(T value) => Constant(value, typeof(T));
/// <summary>Adds a parameterless <c>new</c> node for the specified type.</summary>
[RequiresUnreferencedCode(FastExpressionCompiler.LightExpression.Trimming.Message)]
public int New(Type type)
{
if (type.IsValueType)
return AddRawExpressionNode(type, null, ExpressionType.New);
foreach (var ctor in type.GetConstructors())
if (ctor.GetParameters().Length == 0)
return New(ctor);
throw new ArgumentException($"The type {type} is missing the default constructor");
}
/// <summary>Adds a constructor call node.</summary>
public int New(System.Reflection.ConstructorInfo constructor, params int[] arguments) =>
AddFactoryExpressionNode(constructor.DeclaringType, constructor, ExpressionType.New, arguments);
/// <summary>Adds an array initialization node.</summary>
public int NewArrayInit(Type elementType, params int[] expressions) =>
AddFactoryExpressionNode(elementType.MakeArrayType(), null, ExpressionType.NewArrayInit, expressions);
/// <summary>Adds an array-bounds node.</summary>
public int NewArrayBounds(Type elementType, params int[] bounds) =>
AddFactoryExpressionNode(elementType.MakeArrayType(), null, ExpressionType.NewArrayBounds, bounds);
/// <summary>Adds an invocation node.</summary>
public int Invoke(int expression, params int[] arguments) =>
arguments == null || arguments.Length == 0
? AddFactoryExpressionNode(Nodes[expression].Type, null, ExpressionType.Invoke, expression)
: AddFactoryExpressionNode(Nodes[expression].Type, null, ExpressionType.Invoke, PrependToChildList(expression, arguments));
/// <summary>Adds a static-call node.</summary>
public int Call(System.Reflection.MethodInfo method, params int[] arguments) =>
AddFactoryExpressionNode(method.ReturnType, method, ExpressionType.Call, arguments);
/// <summary>Adds an instance-call node.</summary>
public int Call(int instance, System.Reflection.MethodInfo method, params int[] arguments) =>
arguments == null || arguments.Length == 0
? AddFactoryExpressionNode(method.ReturnType, method, ExpressionType.Call, instance)
: AddFactoryExpressionNode(method.ReturnType, method, ExpressionType.Call, PrependToChildList(instance, arguments));
/// <summary>Adds a field or property access node.</summary>
public int MakeMemberAccess(int? instance, System.Reflection.MemberInfo member) =>
instance.HasValue
? AddFactoryExpressionNode(GetMemberType(member), member, ExpressionType.MemberAccess, instance.Value)
: AddRawExpressionNode(GetMemberType(member), member, ExpressionType.MemberAccess);
/// <summary>Adds a field-access node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Field(int instance, System.Reflection.FieldInfo field) => MakeMemberAccess(instance, field);
/// <summary>Adds a property-access node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Property(int instance, System.Reflection.PropertyInfo property) => MakeMemberAccess(instance, property);
/// <summary>Adds a static property-access node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Property(System.Reflection.PropertyInfo property) => MakeMemberAccess(null, property);
/// <summary>Adds an indexed property-access node.</summary>
public int Property(int instance, System.Reflection.PropertyInfo property, params int[] arguments) =>
arguments == null || arguments.Length == 0
? Property(instance, property)
: AddFactoryExpressionNode(property.PropertyType, property, ExpressionType.Index, PrependToChildList(instance, arguments));
/// <summary>Adds a one-dimensional array index node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ArrayIndex(int array, int index) => MakeBinary(ExpressionType.ArrayIndex, array, index);
/// <summary>Adds an array access node.</summary>
public int ArrayAccess(int array, params int[] indexes) =>
indexes != null && indexes.Length == 1
? ArrayIndex(array, indexes[0])
: AddFactoryExpressionNode(GetArrayElementType(Nodes[array].Type, indexes?.Length ?? 0), null, ExpressionType.Index, PrependToChildList(array, indexes));
/// <summary>Adds a conversion node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Convert(int operand, Type type, System.Reflection.MethodInfo method = null) =>
AddFactoryExpressionNode(type, method, ExpressionType.Convert, operand);
/// <summary>Adds a type-as node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int TypeAs(int operand, Type type) =>
AddFactoryExpressionNode(type, null, ExpressionType.TypeAs, operand);
/// <summary>Adds a numeric negation node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Negate(int operand, System.Reflection.MethodInfo method = null) =>
MakeUnary(ExpressionType.Negate, operand, method: method);
/// <summary>Adds a logical or bitwise not node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Not(int operand, System.Reflection.MethodInfo method = null) =>
MakeUnary(ExpressionType.Not, operand, method: method);
/// <summary>Adds a unary node of the specified kind.</summary>
public int MakeUnary(ExpressionType nodeType, int operand, Type type = null, System.Reflection.MethodInfo method = null) =>
AddFactoryExpressionNode(type ?? GetUnaryResultType(nodeType, Nodes[operand].Type, method), method, nodeType, operand);
/// <summary>Adds an assignment node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Assign(int left, int right) => MakeBinary(ExpressionType.Assign, left, right);
/// <summary>Adds an addition node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Add(int left, int right, System.Reflection.MethodInfo method = null) => MakeBinary(ExpressionType.Add, left, right, method: method);
/// <summary>Adds an equality node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Equal(int left, int right, System.Reflection.MethodInfo method = null) => MakeBinary(ExpressionType.Equal, left, right, method: method);
/// <summary>Adds a binary node of the specified kind.</summary>
public int MakeBinary(ExpressionType nodeType, int left, int right, bool isLiftedToNull = false,
System.Reflection.MethodInfo method = null, int? conversion = null, Type type = null)
=> conversion.HasValue
? AddFactoryExpressionNode(type ?? GetBinaryResultType(nodeType, Nodes[left].Type, Nodes[right].Type, method),
method, nodeType, isLiftedToNull ? BinaryLiftedToNullFlag : (byte)0, left, right, conversion.Value)
: AddFactoryExpressionNode(type ?? GetBinaryResultType(nodeType, Nodes[left].Type, Nodes[right].Type, method),
method, nodeType, isLiftedToNull ? BinaryLiftedToNullFlag : (byte)0, left, right);
/// <summary>Adds a conditional node.</summary>
public int Condition(int test, int ifTrue, int ifFalse, Type type = null) =>
AddFactoryExpressionNode(type ?? Nodes[ifTrue].Type, null, ExpressionType.Conditional, 0, test, ifTrue, ifFalse);
/// <summary>Adds a block node without explicit variables.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Block(params int[] expressions) =>
Block(null, null, expressions);
/// <summary>Adds a block node with optional explicit result type and variables.</summary>
/// <remarks>
/// Child layout of the Block node depends on whether there are explicit variables:
/// <list type="bullet">
/// <item>With variables: children[0] = ChildList(variable₀, variable₁, …)
/// children[1] = ChildList(expr₀, expr₁, …)</item>
/// <item>Without variables: children[0] = ChildList(expr₀, expr₁, …)</item>
/// </list>
/// A <c>children.Count == 2</c> check is therefore the canonical way to detect variables.
/// 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]).
/// <para>When the block has explicit variable declarations its node index is recorded in
/// <see cref="BlocksWithVariables"/>, enabling callers to enumerate block-scoped variables
/// without a full tree traversal.</para>
/// </remarks>
public int Block(Type type, IEnumerable<int> variables, params int[] expressions)
{
if (expressions == null || expressions.Length == 0)
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));
var index = AddFactoryExpressionNode(type ?? Nodes[expressions[expressions.Length - 1]].Type, null, ExpressionType.Block, in children);
if (hasVariables)
BlocksWithVariables.Add(index);
return index;
}
/// <summary>Adds a typed lambda node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Lambda<TDelegate>(int body, params int[] parameters) where TDelegate : Delegate =>
Lambda(typeof(TDelegate), body, parameters);
/// <summary>Adds a lambda node.</summary>
/// <remarks>
/// Child layout of the Lambda node:
/// <list type="bullet">
/// <item>children[0] = body expression</item>
/// <item>children[1…n] = parameter decl nodes (parameter₀, parameter₁, …)</item>
/// </list>
/// The body is stored first; parameter decl nodes follow. This means that when the
/// body contains refs to those parameters, the ref nodes are encountered by the
/// <see cref="Reader"/> before the corresponding decl node — an intentional
/// 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
/// <see cref="System.Linq.Expressions.ParameterExpression"/> object.
/// <para>The lambda node index is recorded in <see cref="LambdaNodes"/> so callers can discover
/// nested lambdas (all entries except <see cref="RootIndex"/>) without a full tree traversal.</para>
/// </remarks>
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;
}
/// <summary>Adds a member-assignment binding node.</summary>
public int Bind(System.Reflection.MemberInfo member, int expression) =>
AddFactoryAuxNode(GetMemberType(member), member, ExprNodeKind.MemberAssignment, expression);
/// <summary>Adds a nested member-binding node.</summary>
public int MemberBind(System.Reflection.MemberInfo member, params int[] bindings) =>
AddFactoryAuxNode(GetMemberType(member), member, ExprNodeKind.MemberMemberBinding, bindings);
/// <summary>Adds an element-initializer node.</summary>
public int ElementInit(System.Reflection.MethodInfo addMethod, params int[] arguments) =>
AddFactoryAuxNode(addMethod.DeclaringType, addMethod, ExprNodeKind.ElementInit, arguments);
/// <summary>Adds a list-binding node.</summary>
public int ListBind(System.Reflection.MemberInfo member, params int[] initializers) =>
AddFactoryAuxNode(GetMemberType(member), member, ExprNodeKind.MemberListBinding, initializers);
/// <summary>Adds a member-init node.</summary>
public int MemberInit(int @new, params int[] bindings) =>
bindings == null || bindings.Length == 0
? AddFactoryExpressionNode(Nodes[@new].Type, null, ExpressionType.MemberInit, @new)
: AddFactoryExpressionNode(Nodes[@new].Type, null, ExpressionType.MemberInit, PrependToChildList(@new, bindings));
/// <summary>Adds a list-init node.</summary>
public int ListInit(int @new, params int[] initializers) =>
initializers == null || initializers.Length == 0
? AddFactoryExpressionNode(Nodes[@new].Type, null, ExpressionType.ListInit, @new)
: AddFactoryExpressionNode(Nodes[@new].Type, null, ExpressionType.ListInit, PrependToChildList(@new, initializers));
/// <summary>Adds a label-target node.</summary>
public int Label(Type type = null, string name = null)
{
var id = Nodes.Count + 1;
return AddRawLeafAuxNode(type ?? typeof(void), name, ExprNodeKind.LabelTarget, childIdx: id);
}
/// <summary>Adds a label-expression node.</summary>
/// <remarks>The node index is recorded in <see cref="LabelNodes"/>.</remarks>
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;
}
/// <summary>Adds a goto-family node.</summary>
/// <remarks>The node index is recorded in <see cref="GotoNodes"/>.</remarks>
public int MakeGoto(GotoExpressionKind kind, int target, int? value = null, Type type = null)
{
var resultType = type ?? (value.HasValue ? Nodes[value.Value].Type : typeof(void));
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;
}
/// <summary>Adds a goto node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Goto(int target, int? value = null, Type type = null) => MakeGoto(GotoExpressionKind.Goto, target, value, type);
/// <summary>Adds a return node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Return(int target, int value) => MakeGoto(GotoExpressionKind.Return, target, value, Nodes[value].Type);
/// <summary>Adds a loop node.</summary>
public int Loop(int body, int? @break = null, int? @continue = null)
{
ChildList children = default;
children.Add(body);
if (@break.HasValue)
children.Add(@break.Value);
if (@continue.HasValue)
children.Add(@continue.Value);
return AddFactoryExpressionNode(typeof(void), null, ExpressionType.Loop,
(byte)((@break.HasValue ? LoopHasBreakFlag : 0) | (@continue.HasValue ? LoopHasContinueFlag : 0)), in children);
}
/// <summary>Adds a switch-case node.</summary>
public int SwitchCase(int body, params int[] testValues)
{
ChildList children = default;
if (testValues != null && testValues.Length != 0)
for (var i = 0; i < testValues.Length; ++i)
children.Add(testValues[i]);
children.Add(body);
return AddFactoryAuxNode(Nodes[body].Type, null, ExprNodeKind.SwitchCase, children);
}
/// <summary>Adds a switch node without an explicit default case or comparer.</summary>
public int Switch(int switchValue, params int[] cases) =>
Switch(Nodes[switchValue].Type, switchValue, null, null, cases);
/// <summary>Adds a switch node.</summary>
public int Switch(Type type, int switchValue, int? defaultBody, System.Reflection.MethodInfo comparison, params int[] cases)
{
ChildList children = default;
children.Add(switchValue);
if (defaultBody.HasValue)
children.Add(defaultBody.Value);
if (cases != null && cases.Length != 0)
{
ChildList caseChildren = default;
for (var i = 0; i < cases.Length; ++i)
caseChildren.Add(cases[i]);
children.Add(AddChildListNode(in caseChildren));
}
return AddFactoryExpressionNode(type, comparison, ExpressionType.Switch, in children);
}
/// <summary>Adds a catch block with an exception variable.</summary>
public int Catch(int variable, int body) =>
AddFactoryAuxNode(Nodes[variable].Type, null, ExprNodeKind.CatchBlock, CatchHasVariableFlag, variable, body);
/// <summary>Adds a catch block without an exception variable.</summary>
public int Catch(Type test, int body) =>
AddFactoryAuxNode(test, null, ExprNodeKind.CatchBlock, 0, body);
/// <summary>Adds a catch block with optional exception variable and filter.</summary>
public int MakeCatchBlock(Type test, int? variable, int body, int? filter)
{
ChildList children = default;
if (variable.HasValue)
children.Add(variable.Value);
children.Add(body);
if (filter.HasValue)
children.Add(filter.Value);
return AddFactoryAuxNode(test, null, ExprNodeKind.CatchBlock,
(byte)((variable.HasValue ? CatchHasVariableFlag : 0) | (filter.HasValue ? CatchHasFilterFlag : 0)), in children);
}
/// <summary>Adds a try/catch node.</summary>
/// <remarks>The node index is recorded in <see cref="TryCatchNodes"/>.</remarks>
public int TryCatch(int body, params int[] handlers)
{
int index;
if (handlers == null || handlers.Length == 0)
{
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;
}
/// <summary>Adds a try/finally node.</summary>
/// <remarks>The node index is recorded in <see cref="TryCatchNodes"/>.</remarks>
public int TryFinally(int body, int @finally)
{
var index = AddFactoryExpressionNode(Nodes[body].Type, null, ExpressionType.Try, 0, body, @finally);
TryCatchNodes.Add(index);
return index;
}
/// <summary>Adds a try/fault node.</summary>
/// <remarks>The node index is recorded in <see cref="TryCatchNodes"/>.</remarks>
public int TryFault(int body, int fault)
{
var index = AddFactoryExpressionNode(Nodes[body].Type, null, ExpressionType.Try, TryFaultFlag, body, fault);
TryCatchNodes.Add(index);
return index;
}
/// <summary>Adds a try node with optional finally block and catch handlers.</summary>
/// <remarks>The node index is recorded in <see cref="TryCatchNodes"/>.</remarks>
public int TryCatchFinally(int body, int? @finally, params int[] handlers)
{
ChildList children = default;
children.Add(body);
if (@finally.HasValue)
children.Add(@finally.Value);
if (handlers != null && handlers.Length != 0)
{
ChildList handlerChildren = default;
for (var i = 0; i < handlers.Length; ++i)
handlerChildren.Add(handlers[i]);
children.Add(AddChildListNode(in handlerChildren));
}
var index = AddFactoryExpressionNode(Nodes[body].Type, null, ExpressionType.Try, 0, in children);
TryCatchNodes.Add(index);
return index;
}
/// <summary>Adds a type-test node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int TypeIs(int expression, Type type) =>
AddFactoryExpressionNode(typeof(bool), type, ExpressionType.TypeIs, expression);
/// <summary>Adds a type-equality test node.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int TypeEqual(int expression, Type type) =>
AddFactoryExpressionNode(typeof(bool), type, ExpressionType.TypeEqual, expression);
/// <summary>Adds a dynamic-expression node.</summary>
public int Dynamic(Type delegateType, CallSiteBinder binder, params int[] arguments)
{
ChildList children = default;
children.Add(AddObjectReferenceNode(typeof(Type), delegateType));
if (arguments != null && arguments.Length != 0)
for (var i = 0; i < arguments.Length; ++i)
children.Add(arguments[i]);
return AddFactoryExpressionNode(typeof(object), binder, ExpressionType.Dynamic, children);
}
/// <summary>Adds a runtime-variables node.</summary>
public int RuntimeVariables(params int[] variables) =>
AddFactoryExpressionNode(typeof(IRuntimeVariables), null, ExpressionType.RuntimeVariables, variables);
/// <summary>Adds a debug-info node.</summary>
public int DebugInfo(string fileName, int startLine, int startColumn, int endLine, int endColumn) =>
AddFactoryExpressionNode(typeof(void), fileName, ExpressionType.DebugInfo, CreateDebugInfoChildren(startLine, startColumn, endLine, endColumn));
/// <summary>Flattens a System.Linq expression tree.</summary>
public static ExprTree FromExpression(SysExpr expression) =>
new Builder().Build(expression ?? throw new ArgumentNullException(nameof(expression)));
/// <summary>Flattens a LightExpression tree.</summary>
public static ExprTree FromLightExpression(LightExpression expression) =>
FromExpression((expression ?? throw new ArgumentNullException(nameof(expression))).ToExpression());
/// <summary>Reconstructs the flat tree as a System.Linq expression tree.</summary>
[RequiresUnreferencedCode(FastExpressionCompiler.LightExpression.Trimming.Message)]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2077",
Justification = "Flat expression round-trip stores the runtime type metadata explicitly for reconstruction.")]
public SysExpr ToExpression() =>
Nodes.Count != 0
? new Reader(this).ReadExpression(RootIndex)
: throw new InvalidOperationException("Flat expression tree is empty.");
/// <summary>Reconstructs the flat tree as a LightExpression tree.</summary>
[RequiresUnreferencedCode(FastExpressionCompiler.LightExpression.Trimming.Message)]
public LightExpression ToLightExpression() => FastExpressionCompiler.LightExpression.FromSysExpressionConverter.ToLightExpression(ToExpression());
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, int child) =>
AddNode(type, obj, nodeType, ExprNodeKind.Expression, 0, CloneChild(child));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags, int child) =>
AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, CloneChild(child));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags, int c0, int c1) =>
AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, CloneChild(c0), CloneChild(c1));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags, int c0, int c1, int c2) =>
AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, CloneChild(c0), CloneChild(c1), CloneChild(c2));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags, int c0, int c1, int c2, int c3) =>
AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, CloneChild(c0), CloneChild(c1), CloneChild(c2), CloneChild(c3));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags, int c0, int c1, int c2, int c3, int c4) =>
AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, CloneChild(c0), CloneChild(c1), CloneChild(c2), CloneChild(c3), CloneChild(c4));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags, int c0, int c1, int c2, int c3, int c4, int c5) =>
AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, CloneChild(c0), CloneChild(c1), CloneChild(c2), CloneChild(c3), CloneChild(c4), CloneChild(c5));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags, int c0, int c1, int c2, int c3, int c4, int c5, int c6) =>
AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, CloneChild(c0), CloneChild(c1), CloneChild(c2), CloneChild(c3), CloneChild(c4), CloneChild(c5), CloneChild(c6));
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, int[] children)
{
if (children != null)
switch (children.Length)
{
case 1: return AddFactoryExpressionNode(type, obj, nodeType, 0, children[0]);
case 2: return AddFactoryExpressionNode(type, obj, nodeType, 0, children[0], children[1]);
case 3: return AddFactoryExpressionNode(type, obj, nodeType, 0, children[0], children[1], children[2]);
case 4: return AddFactoryExpressionNode(type, obj, nodeType, 0, children[0], children[1], children[2], children[3]);
case 5: return AddFactoryExpressionNode(type, obj, nodeType, 0, children[0], children[1], children[2], children[3], children[4]);
case 6: return AddFactoryExpressionNode(type, obj, nodeType, 0, children[0], children[1], children[2], children[3], children[4], children[5]);
case 7: return AddFactoryExpressionNode(type, obj, nodeType, 0, children[0], children[1], children[2], children[3], children[4], children[5], children[6]);
}
var cloned = CloneChildren(children);
return AddNode(type, obj, nodeType, ExprNodeKind.Expression, 0, in cloned);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, in ChildList children)
{
var cloned = CloneChildren(children);
return AddNode(type, obj, nodeType, ExprNodeKind.Expression, 0, in cloned);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddFactoryExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags, in ChildList children)
{
var cloned = CloneChildren(children);
return AddNode(type, obj, nodeType, ExprNodeKind.Expression, flags, in cloned);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddRawExpressionNode(Type type, object obj, ExpressionType nodeType) =>
AddLeafNode(type, obj, nodeType, ExprNodeKind.Expression, 0, 0, 0);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddRawExpressionNode(Type type, object obj, ExpressionType nodeType, in ChildList children) =>
AddNode(type, obj, nodeType, ExprNodeKind.Expression, 0, in children);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddRawExpressionNode(Type type, object obj, ExpressionType nodeType, int[] children) =>
AddNode(type, obj, nodeType, ExprNodeKind.Expression, 0, children);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddRawExpressionNode(Type type, object obj, ExpressionType nodeType, int child0, int child1, int child2) =>
AddNode(type, obj, nodeType, ExprNodeKind.Expression, 0, child0, child1, child2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddRawLeafExpressionNode(Type type, object obj, ExpressionType nodeType, byte flags = 0, int childIdx = 0, int childCount = 0) =>
AddLeafNode(type, obj, nodeType, ExprNodeKind.Expression, flags, childIdx, childCount);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddFactoryAuxNode(Type type, object obj, ExprNodeKind kind, byte flags, int child) =>
AddNode(type, obj, ExpressionType.Extension, kind, flags, CloneChild(child));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddFactoryAuxNode(Type type, object obj, ExprNodeKind kind, int child) =>
AddFactoryAuxNode(type, obj, kind, 0, child);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddFactoryAuxNode(Type type, object obj, ExprNodeKind kind, byte flags, int child0, int child1) =>
AddNode(type, obj, ExpressionType.Extension, kind, flags, CloneChild(child0), CloneChild(child1));
private int AddFactoryAuxNode(Type type, object obj, ExprNodeKind kind, int[] children)
{
var cloned = CloneChildren(children);
return AddNode(type, obj, ExpressionType.Extension, kind, 0, in cloned);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddFactoryAuxNode(Type type, object obj, ExprNodeKind kind, byte flags, in ChildList children)
{
var cloned = CloneChildren(children);
return AddNode(type, obj, ExpressionType.Extension, kind, flags, in cloned);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddFactoryAuxNode(Type type, object obj, ExprNodeKind kind, in ChildList children) =>
AddFactoryAuxNode(type, obj, kind, 0, in children);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddRawAuxNode(Type type, object obj, ExprNodeKind kind, in ChildList children) =>
AddNode(type, obj, ExpressionType.Extension, kind, 0, in children);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddRawLeafAuxNode(Type type, object obj, ExprNodeKind kind, byte flags = 0, int childIdx = 0, int childCount = 0) =>
AddLeafNode(type, obj, ExpressionType.Extension, kind, flags, childIdx, childCount);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddObjectReferenceNode(Type type, object obj) =>
AddRawLeafAuxNode(type, obj, ExprNodeKind.ObjectReference);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddChildListNode(in ChildList children) =>
AddRawAuxNode(null, null, ExprNodeKind.ChildList, in children);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int AddUInt16PairNode(int first, int second) =>
AddRawLeafAuxNode(null, null, ExprNodeKind.UInt16Pair, childIdx: checked((ushort)first), childCount: checked((ushort)second));
private ChildList CreateDebugInfoChildren(int startLine, int startColumn, int endLine, int endColumn)
{
ChildList children = default;
children.Add(AddUInt16PairNode(startLine, startColumn));
children.Add(AddUInt16PairNode(endLine, endColumn));
return children;
}
private static ChildList PrependToChildList(int first, int[] rest)
{
ChildList children = default;
children.Add(first);
if (rest != null)
for (var i = 0; i < rest.Length; ++i)
children.Add(rest[i]);
return children;
}
/// <summary>Builds the flat representation while preserving parameter and label identity with stack-friendly maps.</summary>
private struct Builder
{
private SmallMap16<object, int, RefEq<object>> _parameterIds;
private SmallMap16<object, int, RefEq<object>> _labelIds;
private ExprTree _tree;
public ExprTree Build(SysExpr expression)
{
_tree.RootIndex = AddExpression(expression);
return _tree;
}
private int AddExpression(SysExpr expression)
{
switch (expression.NodeType)
{
case ExpressionType.Constant:
return AddConstant((System.Linq.Expressions.ConstantExpression)expression);
case ExpressionType.Default:
return _tree.AddRawExpressionNode(expression.Type, null, expression.NodeType);
case ExpressionType.Parameter:
{
var parameter = (SysParameterExpression)expression;
return _tree.AddRawLeafExpressionNode(expression.Type, parameter.Name, expression.NodeType,
parameter.IsByRef ? ParameterByRefFlag : (byte)0, childIdx: GetId(ref _parameterIds, parameter));
}
case ExpressionType.Lambda:
{
// Layout: children[0] = body, children[1..n] = parameter decl nodes.
// Body is stored before parameters so that the Reader encounters parameter
// refs in the body before their decl nodes (out-of-order decl); identity
// is preserved via the shared _parametersById id-map.
var lambda = (System.Linq.Expressions.LambdaExpression)expression;
ChildList children = default;
children.Add(AddExpression(lambda.Body));
for (var i = 0; i < lambda.Parameters.Count; ++i)
children.Add(AddExpression(lambda.Parameters[i]));
var lambdaIndex = _tree.AddRawExpressionNode(expression.Type, null, expression.NodeType, children);
_tree.LambdaNodes.Add(lambdaIndex);
CollectLambdaClosureParameterUsages(lambda, lambdaIndex);
return lambdaIndex;
}
case ExpressionType.Block:
{
// Layout (with variables): children[0] = ChildList(var₀, var₁, …)
// children[1] = ChildList(expr₀, expr₁, …)
// Layout (without variables): children[0] = ChildList(expr₀, expr₁, …)
// children.Count == 2 is the canonical test for the presence of variables.
var block = (System.Linq.Expressions.BlockExpression)expression;
ChildList children = default;
var hasVariables = block.Variables.Count != 0;
if (hasVariables)
{
ChildList variables = default;
for (var i = 0; i < block.Variables.Count; ++i)
variables.Add(AddExpression(block.Variables[i]));
children.Add(_tree.AddChildListNode(in variables));
}
ChildList expressions = default;
for (var i = 0; i < block.Expressions.Count; ++i)
expressions.Add(AddExpression(block.Expressions[i]));
children.Add(_tree.AddChildListNode(in expressions));
var blockIndex = _tree.AddRawExpressionNode(expression.Type, null, expression.NodeType, in children);
if (hasVariables)
_tree.BlocksWithVariables.Add(blockIndex);
return blockIndex;
}
case ExpressionType.MemberAccess:
{
var member = (System.Linq.Expressions.MemberExpression)expression;
ChildList children = default;
if (member.Expression != null)
children.Add(AddExpression(member.Expression));
return _tree.AddRawExpressionNode(expression.Type, member.Member, expression.NodeType,
children);
}
case ExpressionType.Call:
{
var call = (System.Linq.Expressions.MethodCallExpression)expression;
ChildList children = default;
if (call.Object != null)
children.Add(AddExpression(call.Object));
for (var i = 0; i < call.Arguments.Count; ++i)
children.Add(AddExpression(call.Arguments[i]));
return _tree.AddRawExpressionNode(expression.Type, call.Method, expression.NodeType, children);
}
case ExpressionType.New:
{
var @new = (System.Linq.Expressions.NewExpression)expression;
ChildList children = default;
for (var i = 0; i < @new.Arguments.Count; ++i)
children.Add(AddExpression(@new.Arguments[i]));
return _tree.AddRawExpressionNode(expression.Type, @new.Constructor, expression.NodeType, children);
}
case ExpressionType.NewArrayInit:
case ExpressionType.NewArrayBounds:
{
var array = (System.Linq.Expressions.NewArrayExpression)expression;
ChildList children = default;
for (var i = 0; i < array.Expressions.Count; ++i)
children.Add(AddExpression(array.Expressions[i]));
return _tree.AddRawExpressionNode(expression.Type, null, expression.NodeType, children);
}
case ExpressionType.Invoke:
{
var invoke = (System.Linq.Expressions.InvocationExpression)expression;
ChildList children = default;
children.Add(AddExpression(invoke.Expression));
for (var i = 0; i < invoke.Arguments.Count; ++i)
children.Add(AddExpression(invoke.Arguments[i]));
return _tree.AddRawExpressionNode(expression.Type, null, expression.NodeType, children);
}
case ExpressionType.Index:
{