(&mut self, op: u16, ip: &mut usize, n: usize, chunk: &SSAChunk, slots: &mut [Val])
| 797 | |
| 798 | #[inline(never)] |
| 799 | fn exec_for_iter(&mut self, op: u16, ip: &mut usize, n: usize, chunk: &SSAChunk, slots: &mut [Val]) -> Result<(), VmErr> { |
| 800 | if !self.sandbox_off { |
| 801 | if self.budget == 0 { return Err(cold_budget()); } |
| 802 | self.budget -= 1; |
| 803 | } |
| 804 | if self.heap.needs_gc() { self.collect(slots); } |
| 805 | // Coroutine iteration: resume via call instead of next_item(). |
| 806 | if let Some(IterFrame::Coroutine(coro_val)) = self.iter_stack.last() { |
| 807 | let cv = *coro_val; |
| 808 | // Resume directly so `yielded` distinguishes a yielded value (even None) from exhaustion. |
| 809 | let result = self.resume_coroutine(cv)?; |
| 810 | if self.yielded { |
| 811 | self.yielded = false; |
| 812 | self.push(result); |
| 813 | } else { |
| 814 | // `yield from gen` evaluates to the subgenerator's return value. |
| 815 | self.yield_from_value = result; |
| 816 | self.iter_stack.pop(); |
| 817 | *ip = op as usize; |
| 818 | } |
| 819 | return Ok(()); |
| 820 | } |
| 821 | // user-defined iterator calls `__next__`; `StopIteration` ends the loop without propagating, other exceptions surface. |
| 822 | if let Some(IterFrame::UserDefined(iter_val)) = self.iter_stack.last() { |
| 823 | let iter = *iter_val; |
| 824 | match self.try_call_dunder(iter, "__next__", &[], chunk, slots) { |
| 825 | Ok(Some(item)) => { self.push(item); } |
| 826 | Ok(None) => { |
| 827 | self.yield_from_value = Val::none(); |
| 828 | self.iter_stack.pop(); |
| 829 | if op as usize > n { return Err(cold_runtime("jump target out of bounds")); } |
| 830 | *ip = op as usize; |
| 831 | } |
| 832 | Err(VmErr::Raised(m)) if m == "StopIteration" || m.starts_with("StopIteration:") => { |
| 833 | // `raise StopIteration(v)` carries `v` as the yield-from value via the lifted instance. |
| 834 | self.yield_from_value = if m.starts_with("StopIteration:") { |
| 835 | match self.pending.exc_val { |
| 836 | Some(e) if e.is_heap() => match self.heap.get(e) { |
| 837 | HeapObj::ExcInstance(_, args) => args.first().copied().unwrap_or(Val::none()), |
| 838 | _ => Val::none(), |
| 839 | }, |
| 840 | _ => Val::none(), |
| 841 | } |
| 842 | } else { Val::none() }; |
| 843 | self.iter_stack.pop(); |
| 844 | if op as usize > n { return Err(cold_runtime("jump target out of bounds")); } |
| 845 | *ip = op as usize; |
| 846 | } |
| 847 | Err(e) => return Err(e), |
| 848 | } |
| 849 | return Ok(()); |
| 850 | } |
| 851 | // Split-borrow heap so the Range step can promote to LongInt. |
| 852 | let next = match self.iter_stack.last_mut() { |
| 853 | Some(f) => f.next_item(&mut self.heap)?, |
| 854 | None => None, |
| 855 | }; |
| 856 | match next { |
no test coverage detected