(&mut self, is_async: bool)
| 546 | /* with / async with: each CM is a SetupFinally cleanup frame whose handler runs `WithExit`. The pending exit (none/return/break/exception) selects the `__exit__` args and suppression. */ |
| 547 | |
| 548 | pub(super) fn with_stmt_inner(&mut self, is_async: bool) { |
| 549 | self.advance(); |
| 550 | let operand = is_async as u16; |
| 551 | let mut setups: Vec<usize> = Vec::new(); |
| 552 | loop { |
| 553 | self.expr(); |
| 554 | self.chunk.emit(OpCode::SetupWith, operand); |
| 555 | if self.eat_if(TokenType::As) { |
| 556 | let name = self.advance_text(); |
| 557 | self.store_name(name); |
| 558 | } else { |
| 559 | // Discard the unbound `__enter__` result. |
| 560 | self.chunk.emit(OpCode::PopTop, 0); |
| 561 | } |
| 562 | setups.push(self.emit_jump(OpCode::SetupFinally)); |
| 563 | self.cleanup_count += 1; |
| 564 | if !self.eat_if(TokenType::Comma) { break; } |
| 565 | } |
| 566 | self.eat(TokenType::Colon); |
| 567 | self.compile_block(); |
| 568 | |
| 569 | // Cleanups innermost-first: normal path pops the frame and marks the entry, then runs `__exit__`. |
| 570 | for s in setups.into_iter().rev() { |
| 571 | self.cleanup_count -= 1; |
| 572 | self.chunk.emit(OpCode::PopExcept, 0); |
| 573 | self.chunk.emit(OpCode::BeginFinally, 0); |
| 574 | let h = self.chunk.instructions.len() as u16; |
| 575 | self.patch_to(s, h); |
| 576 | self.chunk.emit(OpCode::WithExit, operand); |
| 577 | self.chunk.emit(OpCode::EndFinally, 0); |
| 578 | } |
| 579 | } |
| 580 | |
| 581 | /* Delegates to imports.rs; compile-time only, no import opcodes reach the VM. */ |
| 582 |
no test coverage detected