@@ -523,6 +523,7 @@ impl Pure<'_> {
523523 } ,
524524 _ => return ,
525525 } ;
526+
526527 if args. len ( ) > 1
527528 || args. first ( ) . is_some_and ( |arg| {
528529 if arg. spread . is_some ( ) {
@@ -535,8 +536,7 @@ impl Pure<'_> {
535536 return ;
536537 }
537538
538- let args = args. iter ( ) . map ( |i| Some ( i. clone ( ) ) ) . collect :: < Vec < _ > > ( ) ;
539- let first_arg = get_first_arg ( & args, & self . expr_ctx ) ;
539+ let first_arg = get_first_arg ( args. iter ( ) . map ( Some ) , & self . expr_ctx ) ;
540540
541541 if & * method. sym == "toFixed" {
542542 // https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number.prototype.tofixed
@@ -1175,28 +1175,51 @@ fn f64_to_exponential_with_precision(n: f64, prec: usize) -> String {
11751175 res
11761176}
11771177
1178- fn get_first_arg ( args : & Vec < Option < ExprOrSpread > > , expr_ctx : & ExprCtx ) -> Option < Option < f64 > > {
1179- args. iter ( )
1180- . find_map ( |arg| {
1181- if let Some ( arg) = arg {
1182- if arg. spread . is_some ( ) {
1183- return match arg. expr . as_array ( ) {
1184- Some ( args) => {
1185- if args. elems . is_empty ( ) {
1186- // next argument is used if the first spread argument is empty
1187- // so we can't evaluate the call.
1188- None
1189- } else {
1190- Some ( get_first_arg ( & args. elems , expr_ctx) )
1191- }
1192- }
1193- None => Some ( None ) ,
1194- } ;
1195- } else if !arg. expr . is_undefined ( * expr_ctx) {
1196- return Some ( Some ( eval_as_number ( * expr_ctx, & arg. expr ) ) ) ;
1178+ /// Helper function to get the first argument of a call expression,
1179+ /// taking into account spread elements.
1180+ ///
1181+ /// `Some(None)` means that the first argument is a spread element, but we can't
1182+ /// evaluate it.
1183+ ///
1184+ /// `None` means that there is no argument.
1185+ fn get_first_arg < ' a > (
1186+ args : impl IntoIterator < Item = Option < & ' a ExprOrSpread > > ,
1187+ expr_ctx : & ExprCtx ,
1188+ ) -> Option < Option < f64 > > {
1189+ // A stack of iterators to process
1190+ let mut stack: Vec < Box < dyn Iterator < Item = Option < & ' a ExprOrSpread > > > > =
1191+ vec ! [ Box :: new( args. into_iter( ) ) ] ;
1192+
1193+ while let Some ( current_iter) = stack. last_mut ( ) {
1194+ // Get the next item from the top-most iterator
1195+ let Some ( arg_opt) = current_iter. next ( ) else {
1196+ // This iterator is exhausted, pop it and continue with the previous one
1197+ stack. pop ( ) ;
1198+ continue ;
1199+ } ;
1200+
1201+ if let Some ( arg) = arg_opt {
1202+ if arg. spread . is_some ( ) {
1203+ let Some ( args_array) = arg. expr . as_array ( ) else {
1204+ // It's a spread we can't evaluate (like ...x)
1205+ return Some ( None ) ;
1206+ } ;
1207+ if args_array. elems . is_empty ( ) {
1208+ // next argument is used if the first spread argument is empty
1209+ // so we can't evaluate the call.
1210+ continue ;
1211+ } else {
1212+ // Instead of recursing, push a new iterator onto the stack
1213+ stack. push ( Box :: new ( args_array. elems . iter ( ) . map ( Option :: as_ref) ) ) ;
1214+ continue ;
11971215 }
1216+ } else if !arg. expr . is_undefined ( * expr_ctx) {
1217+ // We found our first concrete argument!
1218+ return Some ( eval_as_number ( * expr_ctx, & arg. expr ) ) ;
11981219 }
1199- Some ( None )
1200- } )
1201- . unwrap_or ( None )
1220+ }
1221+ }
1222+
1223+ // If we exhausted all iterators and found nothing
1224+ None
12021225}
0 commit comments