22// The .NET Foundation licenses this file to you under the Apache 2.0 License.
33// See the LICENSE file in the project root for more information.
44
5- using MSAst = System . Linq . Expressions ;
5+ #nullable enable
66
77using System ;
88using System . Collections . Generic ;
99using System . Diagnostics ;
1010using System . Linq ;
1111using System . Runtime . CompilerServices ;
1212
13+ using IronPython . Runtime ;
14+
1315using Microsoft . Scripting . Actions ;
14- using Microsoft . Scripting . Utils ;
1516using Microsoft . Scripting . Interpreter ;
16- using Microsoft . Scripting . Runtime ;
17+ using Microsoft . Scripting . Utils ;
1718
18- using IronPython . Runtime ;
19- using IronPython . Runtime . Operations ;
20- using IronPython . Runtime . Types ;
19+ using AstUtils = Microsoft . Scripting . Ast . Utils ;
20+ using MSAst = System . Linq . Expressions ;
2121
2222namespace IronPython . Compiler . Ast {
2323
2424 public class CallExpression : Expression , IInstructionProvider {
2525 public CallExpression ( Expression target , Arg [ ] args ) {
26+ // TODO: use two arrays (args/keywords) instead
27+ if ( args == null ) throw new ArgumentNullException ( nameof ( args ) ) ;
2628 Target = target ;
2729 Args = args ;
2830 }
@@ -36,55 +38,168 @@ public CallExpression(Expression target, Arg[] args) {
3638 public bool NeedsLocalsDictionary ( ) {
3739 if ( ! ( Target is NameExpression nameExpr ) ) return false ;
3840
39- if ( Args . Length == 0 ) {
40- if ( nameExpr . Name == "locals" ) return true ;
41- if ( nameExpr . Name == "vars" ) return true ;
42- if ( nameExpr . Name == "dir" ) return true ;
43- return false ;
44- } else if ( Args . Length == 1 && ( nameExpr . Name == "dir" || nameExpr . Name == "vars" ) ) {
45- if ( Args [ 0 ] . Name == "*" || Args [ 0 ] . Name == "**" ) {
46- // could be splatting empty list or dict resulting in 0-param call which needs context
47- return true ;
48- }
49- } else if ( Args . Length == 2 && ( nameExpr . Name == "dir" || nameExpr . Name == "vars" ) ) {
50- if ( Args [ 0 ] . Name == "*" && Args [ 1 ] . Name == "**" ) {
51- // could be splatting empty list and dict resulting in 0-param call which needs context
52- return true ;
53- }
54- } else {
55- if ( nameExpr . Name == "eval" || nameExpr . Name == "exec" ) return true ;
41+ if ( nameExpr . Name == "eval" || nameExpr . Name == "exec" ) return true ;
42+ if ( nameExpr . Name == "dir" || nameExpr . Name == "vars" || nameExpr . Name == "locals" ) {
43+ // could be splatting empty list or dict resulting in 0-param call which needs context
44+ return Args . All ( arg => arg . Name == "*" || arg . Name == "**" ) ;
5645 }
46+
5747 return false ;
5848 }
5949
6050 public override MSAst . Expression Reduce ( ) {
6151 Arg [ ] args = Args ;
62- if ( Args . Length == 0 && ImplicitArgs . Count > 0 ) {
52+ if ( Args . Length == 0 && ImplicitArgs . Count > 0 ) {
6353 args = ImplicitArgs . ToArray ( ) ;
6454 }
6555
66- MSAst . Expression [ ] values = new MSAst . Expression [ args . Length + 2 ] ;
67- Argument [ ] kinds = new Argument [ args . Length ] ;
56+ SplitArgs ( args , out var simpleArgs , out var listArgs , out var namedArgs , out var dictArgs , out var numDict ) ;
57+
58+ Argument [ ] kinds = new Argument [ simpleArgs . Length + Math . Min ( listArgs . Length , 1 ) + namedArgs . Length + ( dictArgs . Length - numDict ) + Math . Min ( numDict , 1 ) ] ;
59+ MSAst . Expression [ ] values = new MSAst . Expression [ 2 + kinds . Length ] ;
6860
6961 values [ 0 ] = Parent . LocalContext ;
7062 values [ 1 ] = Target ;
7163
72- for ( int i = 0 ; i < args . Length ; i ++ ) {
73- kinds [ i ] = args [ i ] . GetArgumentInfo ( ) ;
74- values [ i + 2 ] = args [ i ] . Expression ;
64+ int i = 0 ;
65+
66+ // add simple arguments
67+ foreach ( var arg in simpleArgs ) {
68+ kinds [ i ] = arg . GetArgumentInfo ( ) ;
69+ values [ i + 2 ] = arg . Expression ;
70+ i ++ ;
71+ }
72+
73+ // unpack list arguments
74+ if ( listArgs . Length > 0 ) {
75+ var arg = listArgs [ 0 ] ;
76+ Debug . Assert ( arg . GetArgumentInfo ( ) . Kind == ArgumentType . List ) ;
77+ kinds [ i ] = arg . GetArgumentInfo ( ) ;
78+ values [ i + 2 ] = UnpackListHelper ( listArgs ) ;
79+ i ++ ;
80+ }
81+
82+ // 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 ) {
92+ var info = arg . GetArgumentInfo ( ) ;
93+ if ( info . Kind == ArgumentType . Named ) {
94+ kinds [ i ] = info ;
95+ values [ i + 2 ] = arg . Expression ;
96+ i ++ ;
97+ }
98+ }
99+ }
100+
101+ // unpack dict arguments
102+ if ( dictArgs . Length > 0 ) {
103+ var arg = dictArgs [ 0 ] ;
104+ Debug . Assert ( arg . GetArgumentInfo ( ) . Kind == ArgumentType . Dictionary ) ;
105+ kinds [ i ] = arg . GetArgumentInfo ( ) ;
106+ values [ i + 2 ] = UnpackDictHelper ( Parent . LocalContext , dictArgs ) ;
75107 }
76108
77109 return Parent . Invoke (
78110 new CallSignature ( kinds ) ,
79111 values
80112 ) ;
113+
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+ }
123+
124+ int idxSimple = args . Length ;
125+ int idxList = args . Length ;
126+ int idxNamed = args . Length ;
127+ int idxDict = args . Length ;
128+ numDict = 0 ;
129+
130+ // we want idxSimple <= idxList <= idxNamed <= idxDict
131+ for ( var i = args . Length - 1 ; i >= 0 ; i -- ) {
132+ var arg = args [ i ] ;
133+ 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 ( ) ;
150+ }
151+ }
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 ) ;
159+ }
160+
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 ;
165+
166+ var expressions = new ReadOnlyCollectionBuilder < MSAst . Expression > ( args . Length + 2 ) ;
167+ var varExpr = Expression . Variable ( typeof ( PythonList ) , "$coll" ) ;
168+ expressions . Add ( Expression . Assign ( varExpr , Expression . Call ( AstMethods . MakeEmptyList ) ) ) ;
169+ foreach ( var arg in args ) {
170+ if ( arg . GetArgumentInfo ( ) . Kind == ArgumentType . List ) {
171+ expressions . Add ( Expression . Call ( AstMethods . ListExtend , varExpr , AstUtils . Convert ( arg . Expression , typeof ( object ) ) ) ) ;
172+ } else {
173+ expressions . Add ( Expression . Call ( AstMethods . ListAppend , varExpr , AstUtils . Convert ( arg . Expression , typeof ( object ) ) ) ) ;
174+ }
175+ }
176+ expressions . Add ( varExpr ) ;
177+ return Expression . Block ( typeof ( PythonList ) , new MSAst . ParameterExpression [ ] { varExpr } , expressions ) ;
178+ }
179+
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 ;
184+
185+ var expressions = new List < MSAst . Expression > ( args . Length + 2 ) ;
186+ var varExpr = Expression . Variable ( typeof ( PythonDictionary ) , "$dict" ) ;
187+ expressions . Add ( Expression . Assign ( varExpr , Expression . Call ( AstMethods . MakeEmptyDict ) ) ) ;
188+ foreach ( var arg in args ) {
189+ if ( arg . GetArgumentInfo ( ) . Kind == ArgumentType . Dictionary ) {
190+ expressions . Add ( Expression . Call ( AstMethods . DictMerge , context , varExpr , arg . Expression ) ) ;
191+ }
192+ }
193+ expressions . Add ( varExpr ) ;
194+ return Expression . Block ( typeof ( PythonDictionary ) , new MSAst . ParameterExpression [ ] { varExpr } , expressions ) ;
195+ }
81196 }
82197
83198 #region IInstructionProvider Members
84199
85200 void IInstructionProvider . AddInstructions ( LightCompiler compiler ) {
86201 Arg [ ] args = Args ;
87- if ( args . Length == 0 && ImplicitArgs . Count > 0 ) {
202+ if ( args . Length == 0 && ImplicitArgs . Count > 0 ) {
88203 args = ImplicitArgs . ToArray ( ) ;
89204 }
90205
@@ -158,27 +273,19 @@ void IInstructionProvider.AddInstructions(LightCompiler compiler) {
158273 compiler . Instructions . Emit ( new Invoke6Instruction ( Parent . PyContext ) ) ;
159274 return ;
160275
161- // *** END GENERATED CODE ***
276+ // *** END GENERATED CODE ***
162277
163- #endregion
278+ #endregion
164279 }
165280 compiler . Compile ( Reduce ( ) ) ;
166281 }
167282
168283 #endregion
169284
170285 private abstract class InvokeInstruction : Instruction {
171- public override int ProducedStack {
172- get {
173- return 1 ;
174- }
175- }
286+ public override int ProducedStack => 1 ;
176287
177- public override string InstructionName {
178- get {
179- return "Python Invoke" + ( ConsumedStack - 1 ) ;
180- }
181- }
288+ public override string InstructionName => "Python Invoke" + ( ConsumedStack - 1 ) ;
182289 }
183290
184291 #region Generated Python Call Expression Instructions
0 commit comments