2222namespace IronPython . Compiler . Ast {
2323
2424 public class CallExpression : Expression , IInstructionProvider {
25- public CallExpression ( Expression target , Arg [ ] args ) {
26- // TODO: use two arrays (args/keywords) instead
27- if ( args == null ) throw new ArgumentNullException ( nameof ( args ) ) ;
25+ public CallExpression ( Expression target , IReadOnlyList < Arg > ? args , IReadOnlyList < Arg > ? kwargs ) {
2826 Target = target ;
29- Args = args ;
27+ Args = args ? . ToArray ( ) ?? Array . Empty < Arg > ( ) ;
28+ Kwargs = kwargs ? . ToArray ( ) ?? Array . Empty < Arg > ( ) ;
3029 }
3130
3231 public Expression Target { get ; }
3332
34- public Arg [ ] Args { get ; }
33+ public IReadOnlyList < Arg > Args { get ; }
3534
36- internal IList < Arg > ImplicitArgs { get ; } = new List < Arg > ( ) ;
35+ public IReadOnlyList < Arg > Kwargs { get ; }
36+
37+ internal IList < Arg > ImplicitArgs => _implicitArgs ??= new List < Arg > ( ) ;
38+ private List < Arg > ? _implicitArgs ;
3739
3840 public bool NeedsLocalsDictionary ( ) {
3941 if ( ! ( Target is NameExpression nameExpr ) ) return false ;
4042
4143 if ( nameExpr . Name == "eval" || nameExpr . Name == "exec" ) return true ;
4244 if ( nameExpr . Name == "dir" || nameExpr . Name == "vars" || nameExpr . Name == "locals" ) {
4345 // could be splatting empty list or dict resulting in 0-param call which needs context
44- return Args . All ( arg => arg . Name == "*" || arg . Name == "**" ) ;
46+ return Args . All ( arg => arg . Name == "*" ) && Kwargs . All ( arg => arg . Name == "**" ) ;
4547 }
4648
4749 return false ;
4850 }
4951
5052 public override MSAst . Expression Reduce ( ) {
51- Arg [ ] args = Args ;
52- if ( Args . Length == 0 && ImplicitArgs . Count > 0 ) {
53- args = ImplicitArgs . ToArray ( ) ;
53+ IReadOnlyList < Arg > args = Args ;
54+ if ( Args . Count == 0 && _implicitArgs != null && _implicitArgs . Count > 0 ) {
55+ args = _implicitArgs ;
5456 }
5557
56- SplitArgs ( args , out var simpleArgs , out var listArgs , out var namedArgs , out var dictArgs , out var numDict ) ;
58+ // count splatted list args and find the lowest index of a list argument, if any
59+ ScanArgs ( args , ArgumentType . List , out var numList , out int firstListPos ) ;
60+ Debug . Assert ( numList == 0 || firstListPos < args . Count ) ;
61+
62+ // count splatted dictionary args and find the lowest index of a dict argument, if any
63+ ScanArgs ( Kwargs , ArgumentType . Dictionary , out var numDict , out int firstDictPos ) ;
64+ Debug . Assert ( numDict == 0 || firstDictPos < Kwargs . Count ) ;
5765
58- Argument [ ] kinds = new Argument [ simpleArgs . Length + Math . Min ( listArgs . Length , 1 ) + namedArgs . Length + ( dictArgs . Length - numDict ) + Math . Min ( numDict , 1 ) ] ;
66+ // all list arguments and all simple arguments after the first list will be collated into a single list for the actual call
67+ // all dictionary arguments will be merged into a single dictionary for the actual call
68+ Argument [ ] kinds = new Argument [ firstListPos + Math . Min ( numList , 1 ) + ( Kwargs . Count - numDict ) + Math . Min ( numDict , 1 ) ] ;
5969 MSAst . Expression [ ] values = new MSAst . Expression [ 2 + kinds . Length ] ;
6070
6171 values [ 0 ] = Parent . LocalContext ;
@@ -64,31 +74,28 @@ public override MSAst.Expression Reduce() {
6474 int i = 0 ;
6575
6676 // add simple arguments
67- foreach ( var arg in simpleArgs ) {
68- kinds [ i ] = arg . GetArgumentInfo ( ) ;
69- values [ i + 2 ] = arg . Expression ;
70- i ++ ;
77+ if ( firstListPos > 0 ) {
78+ foreach ( var arg in args ) {
79+ if ( i == firstListPos ) break ;
80+ Debug . Assert ( arg . GetArgumentInfo ( ) . Kind == ArgumentType . Simple ) ;
81+ kinds [ i ] = arg . GetArgumentInfo ( ) ;
82+ values [ i + 2 ] = arg . Expression ;
83+ i ++ ;
84+ }
7185 }
7286
7387 // unpack list arguments
74- if ( listArgs . Length > 0 ) {
75- var arg = listArgs [ 0 ] ;
88+ if ( numList > 0 ) {
89+ var arg = args [ firstListPos ] ;
7690 Debug . Assert ( arg . GetArgumentInfo ( ) . Kind == ArgumentType . List ) ;
7791 kinds [ i ] = arg . GetArgumentInfo ( ) ;
78- values [ i + 2 ] = UnpackListHelper ( listArgs ) ;
92+ values [ i + 2 ] = UnpackListHelper ( args , firstListPos ) ;
7993 i ++ ;
8094 }
8195
8296 // add named arguments
83- foreach ( var arg in namedArgs ) {
84- kinds [ i ] = arg . GetArgumentInfo ( ) ;
85- values [ i + 2 ] = arg . Expression ;
86- i ++ ;
87- }
88-
89- // add named arguments specified after a dict unpack
90- if ( dictArgs . Length != numDict ) {
91- foreach ( var arg in dictArgs ) {
97+ if ( Kwargs . Count != numDict ) {
98+ foreach ( var arg in Kwargs ) {
9299 var info = arg . GetArgumentInfo ( ) ;
93100 if ( info . Kind == ArgumentType . Named ) {
94101 kinds [ i ] = info ;
@@ -99,74 +106,45 @@ public override MSAst.Expression Reduce() {
99106 }
100107
101108 // unpack dict arguments
102- if ( dictArgs . Length > 0 ) {
103- var arg = dictArgs [ 0 ] ;
109+ if ( numDict > 0 ) {
110+ var arg = Kwargs [ firstDictPos ] ;
104111 Debug . Assert ( arg . GetArgumentInfo ( ) . Kind == ArgumentType . Dictionary ) ;
105112 kinds [ i ] = arg . GetArgumentInfo ( ) ;
106- values [ i + 2 ] = UnpackDictHelper ( Parent . LocalContext , dictArgs ) ;
113+ values [ i + 2 ] = UnpackDictHelper ( Parent . LocalContext , Kwargs , numDict , firstDictPos ) ;
107114 }
108115
109116 return Parent . Invoke (
110117 new CallSignature ( kinds ) ,
111118 values
112119 ) ;
113120
114- static void SplitArgs ( Arg [ ] args , out ReadOnlySpan < Arg > simpleArgs , out ReadOnlySpan < Arg > listArgs , out ReadOnlySpan < Arg > namedArgs , out ReadOnlySpan < Arg > dictArgs , out int numDict ) {
115- if ( args . Length == 0 ) {
116- simpleArgs = default ;
117- listArgs = default ;
118- namedArgs = default ;
119- dictArgs = default ;
120- numDict = 0 ;
121- return ;
122- }
121+ static void ScanArgs ( IReadOnlyList < Arg > args , ArgumentType scanForType , out int numArgs , out int firstArgPos ) {
122+ numArgs = 0 ;
123+ firstArgPos = args . Count ;
123124
124- int idxSimple = args . Length ;
125- int idxList = args . Length ;
126- int idxNamed = args . Length ;
127- int idxDict = args . Length ;
128- numDict = 0 ;
125+ if ( args . Count == 0 ) return ;
129126
130- // we want idxSimple <= idxList <= idxNamed <= idxDict
131- for ( var i = args . Length - 1 ; i >= 0 ; i -- ) {
127+ for ( var i = args . Count - 1 ; i >= 0 ; i -- ) {
132128 var arg = args [ i ] ;
133129 var info = arg . GetArgumentInfo ( ) ;
134- switch ( info . Kind ) {
135- case ArgumentType . Simple :
136- idxSimple = i ;
137- break ;
138- case ArgumentType . List :
139- idxList = i ;
140- break ;
141- case ArgumentType . Named :
142- idxNamed = i ;
143- break ;
144- case ArgumentType . Dictionary :
145- idxDict = i ;
146- numDict ++ ;
147- break ;
148- default :
149- throw new InvalidOperationException ( ) ;
130+ if ( info . Kind == scanForType ) {
131+ firstArgPos = i ;
132+ numArgs ++ ;
150133 }
151134 }
152- dictArgs = args . AsSpan ( idxDict ) ;
153- if ( idxNamed > idxDict ) idxNamed = idxDict ;
154- namedArgs = args . AsSpan ( idxNamed , idxDict - idxNamed ) ;
155- if ( idxList > idxNamed ) idxList = idxNamed ;
156- listArgs = args . AsSpan ( idxList , idxNamed - idxList ) ;
157- if ( idxSimple > idxList ) idxSimple = idxList ;
158- simpleArgs = args . AsSpan ( idxSimple , idxList - idxSimple ) ;
159135 }
160136
161- static MSAst . Expression UnpackListHelper ( ReadOnlySpan < Arg > args ) {
162- Debug . Assert ( args . Length > 0 ) ;
163- Debug . Assert ( args [ 0 ] . GetArgumentInfo ( ) . Kind == ArgumentType . List ) ;
164- if ( args . Length == 1 ) return args [ 0 ] . Expression ;
137+ static MSAst . Expression UnpackListHelper ( IReadOnlyList < Arg > args , int firstListPos ) {
138+ Debug . Assert ( args . Count > 0 ) ;
139+ Debug . Assert ( args [ firstListPos ] . GetArgumentInfo ( ) . Kind == ArgumentType . List ) ;
140+
141+ if ( args . Count - firstListPos == 1 ) return args [ firstListPos ] . Expression ;
165142
166- var expressions = new ReadOnlyCollectionBuilder < MSAst . Expression > ( args . Length + 2 ) ;
143+ var expressions = new ReadOnlyCollectionBuilder < MSAst . Expression > ( args . Count - firstListPos + 2 ) ;
167144 var varExpr = Expression . Variable ( typeof ( PythonList ) , "$coll" ) ;
168145 expressions . Add ( Expression . Assign ( varExpr , Expression . Call ( AstMethods . MakeEmptyList ) ) ) ;
169- foreach ( var arg in args ) {
146+ for ( int i = firstListPos ; i < args . Count ; i ++ ) {
147+ var arg = args [ i ] ;
170148 if ( arg . GetArgumentInfo ( ) . Kind == ArgumentType . List ) {
171149 expressions . Add ( Expression . Call ( AstMethods . ListExtend , varExpr , AstUtils . Convert ( arg . Expression , typeof ( object ) ) ) ) ;
172150 } else {
@@ -177,15 +155,18 @@ static MSAst.Expression UnpackListHelper(ReadOnlySpan<Arg> args) {
177155 return Expression . Block ( typeof ( PythonList ) , new MSAst . ParameterExpression [ ] { varExpr } , expressions ) ;
178156 }
179157
180- static MSAst . Expression UnpackDictHelper ( MSAst . Expression context , ReadOnlySpan < Arg > args ) {
181- Debug . Assert ( args . Length > 0 ) ;
182- Debug . Assert ( args [ 0 ] . GetArgumentInfo ( ) . Kind == ArgumentType . Dictionary ) ;
183- if ( args . Length == 1 ) return args [ 0 ] . Expression ;
158+ static MSAst . Expression UnpackDictHelper ( MSAst . Expression context , IReadOnlyList < Arg > kwargs , int numDict , int firstDictPos ) {
159+ Debug . Assert ( kwargs . Count > 0 ) ;
160+ Debug . Assert ( 0 < numDict && numDict <= kwargs . Count ) ;
161+ Debug . Assert ( kwargs [ firstDictPos ] . GetArgumentInfo ( ) . Kind == ArgumentType . Dictionary ) ;
184162
185- var expressions = new List < MSAst . Expression > ( args . Length + 2 ) ;
163+ if ( numDict == 1 ) return kwargs [ firstDictPos ] . Expression ;
164+
165+ var expressions = new ReadOnlyCollectionBuilder < MSAst . Expression > ( numDict + 2 ) ;
186166 var varExpr = Expression . Variable ( typeof ( PythonDictionary ) , "$dict" ) ;
187167 expressions . Add ( Expression . Assign ( varExpr , Expression . Call ( AstMethods . MakeEmptyDict ) ) ) ;
188- foreach ( var arg in args ) {
168+ for ( int i = firstDictPos ; i < kwargs . Count ; i ++ ) {
169+ var arg = kwargs [ i ] ;
189170 if ( arg . GetArgumentInfo ( ) . Kind == ArgumentType . Dictionary ) {
190171 expressions . Add ( Expression . Call ( AstMethods . DictMerge , context , varExpr , arg . Expression ) ) ;
191172 }
@@ -198,19 +179,24 @@ static MSAst.Expression UnpackDictHelper(MSAst.Expression context, ReadOnlySpan<
198179 #region IInstructionProvider Members
199180
200181 void IInstructionProvider . AddInstructions ( LightCompiler compiler ) {
201- Arg [ ] args = Args ;
202- if ( args . Length == 0 && ImplicitArgs . Count > 0 ) {
203- args = ImplicitArgs . ToArray ( ) ;
182+ IReadOnlyList < Arg > args = Args ;
183+ if ( args . Count == 0 && _implicitArgs != null && _implicitArgs . Count > 0 ) {
184+ args = _implicitArgs ;
204185 }
205186
206- for ( int i = 0 ; i < args . Length ; i ++ ) {
187+ if ( Kwargs . Count > 0 ) {
188+ compiler . Compile ( Reduce ( ) ) ;
189+ return ;
190+ }
191+
192+ for ( int i = 0 ; i < args . Count ; i ++ ) {
207193 if ( ! args [ i ] . GetArgumentInfo ( ) . IsSimple ) {
208194 compiler . Compile ( Reduce ( ) ) ;
209195 return ;
210196 }
211197 }
212198
213- switch ( args . Length ) {
199+ switch ( args . Count ) {
214200 #region Generated Python Call Expression Instruction Switch
215201
216202 // *** BEGIN GENERATED CODE ***
@@ -441,11 +427,14 @@ public override void Walk(PythonWalker walker) {
441427 arg . Walk ( walker ) ;
442428 }
443429 }
444- if ( ImplicitArgs . Count > 0 ) {
430+ if ( _implicitArgs != null && _implicitArgs . Count > 0 ) {
445431 foreach ( Arg arg in ImplicitArgs ) {
446432 arg . Walk ( walker ) ;
447433 }
448434 }
435+ foreach ( Arg arg in Kwargs ) {
436+ arg . Walk ( walker ) ;
437+ }
449438 }
450439 walker . PostWalk ( this ) ;
451440 }
0 commit comments