@@ -7,7 +7,7 @@ use crate::compiler::instructions::{
7
7
use crate :: compiler:: tokens:: Span ;
8
8
use crate :: output:: CaptureMode ;
9
9
use crate :: value:: ops:: neg;
10
- use crate :: value:: Value ;
10
+ use crate :: value:: { Kwargs , Value , ValueMap } ;
11
11
12
12
#[ cfg( test) ]
13
13
use similar_asserts:: assert_eq;
@@ -503,7 +503,7 @@ impl<'source> CodeGenerator<'source> {
503
503
self . add_with_span ( Instruction :: FastSuper , call. span ( ) ) ;
504
504
return ;
505
505
} else if name == "loop" && call. args . len ( ) == 1 {
506
- self . compile_expr ( & call. args [ 0 ] ) ;
506
+ self . compile_call_args ( std :: slice :: from_ref ( & call. args [ 0 ] ) , 0 , None ) ;
507
507
self . add ( Instruction :: FastRecurse ) ;
508
508
return ;
509
509
}
@@ -660,21 +660,17 @@ impl<'source> CodeGenerator<'source> {
660
660
if let Some ( ref expr) = f. expr {
661
661
self . compile_expr ( expr) ;
662
662
}
663
- for arg in & f. args {
664
- self . compile_expr ( arg) ;
665
- }
663
+ let arg_count = self . compile_call_args ( & f. args , 1 , None ) ;
666
664
let local_id = get_local_id ( & mut self . filter_local_ids , f. name ) ;
667
- self . add ( Instruction :: ApplyFilter ( f. name , f . args . len ( ) + 1 , local_id) ) ;
665
+ self . add ( Instruction :: ApplyFilter ( f. name , arg_count , local_id) ) ;
668
666
self . pop_span ( ) ;
669
667
}
670
668
ast:: Expr :: Test ( f) => {
671
669
self . push_span ( f. span ( ) ) ;
672
670
self . compile_expr ( & f. expr ) ;
673
- for arg in & f. args {
674
- self . compile_expr ( arg) ;
675
- }
671
+ let arg_count = self . compile_call_args ( & f. args , 1 , None ) ;
676
672
let local_id = get_local_id ( & mut self . test_local_ids , f. name ) ;
677
- self . add ( Instruction :: PerformTest ( f. name , f . args . len ( ) + 1 , local_id) ) ;
673
+ self . add ( Instruction :: PerformTest ( f. name , arg_count , local_id) ) ;
678
674
self . pop_span ( ) ;
679
675
}
680
676
ast:: Expr :: GetAttr ( g) => {
@@ -717,18 +713,6 @@ impl<'source> CodeGenerator<'source> {
717
713
self . add ( Instruction :: BuildMap ( m. keys . len ( ) ) ) ;
718
714
}
719
715
}
720
- ast:: Expr :: Kwargs ( m) => {
721
- if let Some ( val) = m. as_const ( ) {
722
- self . add ( Instruction :: LoadConst ( val) ) ;
723
- } else {
724
- self . set_line_from_span ( m. span ( ) ) ;
725
- for ( key, value) in & m. pairs {
726
- self . add ( Instruction :: LoadConst ( Value :: from ( * key) ) ) ;
727
- self . compile_expr ( value) ;
728
- }
729
- self . add ( Instruction :: BuildKwargs ( m. pairs . len ( ) ) ) ;
730
- }
731
- }
732
716
}
733
717
}
734
718
@@ -740,7 +724,7 @@ impl<'source> CodeGenerator<'source> {
740
724
self . push_span ( c. span ( ) ) ;
741
725
match c. identify_call ( ) {
742
726
ast:: CallType :: Function ( name) => {
743
- let arg_count = self . compile_call_args ( & c. args , caller) ;
727
+ let arg_count = self . compile_call_args ( & c. args , 0 , caller) ;
744
728
self . add ( Instruction :: CallFunction ( name, arg_count) ) ;
745
729
}
746
730
#[ cfg( feature = "multi_template" ) ]
@@ -751,71 +735,126 @@ impl<'source> CodeGenerator<'source> {
751
735
}
752
736
ast:: CallType :: Method ( expr, name) => {
753
737
self . compile_expr ( expr) ;
754
- let arg_count = self . compile_call_args ( & c. args , caller) ;
755
- self . add ( Instruction :: CallMethod ( name, arg_count + 1 ) ) ;
738
+ let arg_count = self . compile_call_args ( & c. args , 1 , caller) ;
739
+ self . add ( Instruction :: CallMethod ( name, arg_count) ) ;
756
740
}
757
741
ast:: CallType :: Object ( expr) => {
758
742
self . compile_expr ( expr) ;
759
- let arg_count = self . compile_call_args ( & c. args , caller) ;
760
- self . add ( Instruction :: CallObject ( arg_count + 1 ) ) ;
743
+ let arg_count = self . compile_call_args ( & c. args , 1 , caller) ;
744
+ self . add ( Instruction :: CallObject ( arg_count) ) ;
761
745
}
762
746
} ;
763
747
self . pop_span ( ) ;
764
748
}
765
749
766
750
fn compile_call_args (
767
751
& mut self ,
768
- args : & [ ast:: Expr < ' source > ] ,
752
+ args : & [ ast:: CallArg < ' source > ] ,
753
+ extra_args : usize ,
769
754
caller : Option < & Caller < ' source > > ,
770
- ) -> usize {
771
- match caller {
772
- // we can conditionally compile the caller part here since this will
773
- // nicely call through for non macro builds
774
- #[ cfg( feature = "macros" ) ]
775
- Some ( caller) => self . compile_call_args_with_caller ( args, caller) ,
776
- _ => {
777
- for arg in args {
778
- self . compile_expr ( arg) ;
755
+ ) -> Option < u16 > {
756
+ let mut pending_args = extra_args;
757
+ let mut num_args_batches = 0 ;
758
+ let mut has_kwargs = caller. is_some ( ) ;
759
+ let mut static_kwargs = caller. is_none ( ) ;
760
+
761
+ for arg in args {
762
+ match arg {
763
+ ast:: CallArg :: Pos ( expr) => {
764
+ self . compile_expr ( expr) ;
765
+ pending_args += 1 ;
766
+ }
767
+ ast:: CallArg :: PosSplat ( expr) => {
768
+ if pending_args > 0 {
769
+ self . add ( Instruction :: BuildList ( Some ( pending_args) ) ) ;
770
+ pending_args = 0 ;
771
+ num_args_batches += 1 ;
772
+ }
773
+ self . compile_expr ( expr) ;
774
+ num_args_batches += 1 ;
775
+ }
776
+ ast:: CallArg :: Kwarg ( _, expr) => {
777
+ if !matches ! ( expr, ast:: Expr :: Const ( _) ) {
778
+ static_kwargs = false ;
779
+ }
780
+ has_kwargs = true ;
781
+ }
782
+ ast:: CallArg :: KwargSplat ( _) => {
783
+ static_kwargs = false ;
784
+ has_kwargs = true ;
779
785
}
780
- args. len ( )
781
786
}
782
787
}
783
- }
784
788
785
- #[ cfg( feature = "macros" ) ]
786
- fn compile_call_args_with_caller (
787
- & mut self ,
788
- args : & [ ast:: Expr < ' source > ] ,
789
- caller : & Caller < ' source > ,
790
- ) -> usize {
791
- let mut injected_caller = false ;
789
+ if has_kwargs {
790
+ let mut pending_kwargs = 0 ;
791
+ let mut num_kwargs_batches = 0 ;
792
+ let mut collected_kwargs = ValueMap :: new ( ) ;
793
+ for arg in args {
794
+ match arg {
795
+ ast:: CallArg :: Kwarg ( key, value) => {
796
+ if static_kwargs {
797
+ if let ast:: Expr :: Const ( c) = value {
798
+ collected_kwargs. insert ( Value :: from ( * key) , c. value . clone ( ) ) ;
799
+ } else {
800
+ unreachable ! ( ) ;
801
+ }
802
+ } else {
803
+ self . add ( Instruction :: LoadConst ( Value :: from ( * key) ) ) ;
804
+ self . compile_expr ( value) ;
805
+ pending_kwargs += 1 ;
806
+ }
807
+ }
808
+ ast:: CallArg :: KwargSplat ( expr) => {
809
+ if pending_kwargs > 0 {
810
+ self . add ( Instruction :: BuildKwargs ( pending_kwargs) ) ;
811
+ num_kwargs_batches += 1 ;
812
+ pending_kwargs = 0 ;
813
+ }
814
+ self . compile_expr ( expr) ;
815
+ num_kwargs_batches += 1 ;
816
+ }
817
+ ast:: CallArg :: Pos ( _) | ast:: CallArg :: PosSplat ( _) => { }
818
+ }
819
+ }
792
820
793
- // try to add the caller to already existing keyword arguments.
794
- for arg in args {
795
- if let ast:: Expr :: Kwargs ( ref m) = arg {
796
- self . set_line_from_span ( m. span ( ) ) ;
797
- for ( key, value) in & m. pairs {
798
- self . add ( Instruction :: LoadConst ( Value :: from ( * key) ) ) ;
799
- self . compile_expr ( value) ;
800
- }
801
- self . add ( Instruction :: LoadConst ( Value :: from ( "caller" ) ) ) ;
802
- self . compile_macro_expression ( caller) ;
803
- self . add ( Instruction :: BuildKwargs ( m. pairs . len ( ) + 1 ) ) ;
804
- injected_caller = true ;
821
+ if !collected_kwargs. is_empty ( ) {
822
+ self . add ( Instruction :: LoadConst ( Kwargs :: wrap ( collected_kwargs) ) ) ;
805
823
} else {
806
- self . compile_expr ( arg) ;
824
+ // The conditions above guarantee that if we collect static kwargs
825
+ // we cannot enter this block (single kwargs batch, no caller).
826
+
827
+ #[ cfg( feature = "macros" ) ]
828
+ {
829
+ if let Some ( caller) = caller {
830
+ self . add ( Instruction :: LoadConst ( Value :: from ( "caller" ) ) ) ;
831
+ self . compile_macro_expression ( caller) ;
832
+ pending_kwargs += 1
833
+ }
834
+ }
835
+ if num_kwargs_batches > 0 {
836
+ if pending_kwargs > 0 {
837
+ self . add ( Instruction :: BuildKwargs ( pending_kwargs) ) ;
838
+ num_kwargs_batches += 1 ;
839
+ }
840
+ self . add ( Instruction :: MergeKwargs ( num_kwargs_batches) ) ;
841
+ } else {
842
+ self . add ( Instruction :: BuildKwargs ( pending_kwargs) ) ;
843
+ }
807
844
}
845
+ pending_args += 1 ;
808
846
}
809
847
810
- // if there are no keyword args so far, create a new kwargs object
811
- // and add caller to that.
812
- if !injected_caller {
813
- self . add ( Instruction :: LoadConst ( Value :: from ( "caller" ) ) ) ;
814
- self . compile_macro_expression ( caller ) ;
815
- self . add ( Instruction :: BuildKwargs ( 1 ) ) ;
816
- args . len ( ) + 1
848
+ if num_args_batches > 0 {
849
+ if pending_args > 0 {
850
+ self . add ( Instruction :: BuildList ( Some ( pending_args ) ) ) ;
851
+ num_args_batches += 1 ;
852
+ }
853
+ self . add ( Instruction :: UnpackLists ( num_args_batches ) ) ;
854
+ None
817
855
} else {
818
- args. len ( )
856
+ assert ! ( pending_args as u16 as usize == pending_args) ;
857
+ Some ( pending_args as u16 )
819
858
}
820
859
}
821
860
0 commit comments