Resumes (or starts) execution from the last compiled code.
(&mut self)
| 197 | |
| 198 | /// Resumes (or starts) execution from the last compiled code. |
| 199 | pub async fn exec(&mut self) -> Result<Option<i32>> { |
| 200 | let mut running_stored_program = false; |
| 201 | let result = loop { |
| 202 | match self.vm.exec(&self.image) { |
| 203 | StopReason::Eof => { |
| 204 | break Ok(None); |
| 205 | } |
| 206 | |
| 207 | StopReason::End(code) => { |
| 208 | if !running_stored_program { |
| 209 | break Ok(Some(code.to_i32())); |
| 210 | } |
| 211 | |
| 212 | if !code.is_success() { |
| 213 | self.console |
| 214 | .borrow_mut() |
| 215 | .print(&format!("Program exited with code {}", code.to_i32()))?; |
| 216 | } |
| 217 | |
| 218 | break Ok(None); |
| 219 | } |
| 220 | |
| 221 | StopReason::Exception(pos, msg) => { |
| 222 | break Err(Error::RuntimeError(pos, msg)); |
| 223 | } |
| 224 | |
| 225 | StopReason::UpcallAsync(handler) => { |
| 226 | let upcall_result = handler.invoke().await; |
| 227 | |
| 228 | // Before checking if the upcall failed, we need to honor stop signals. |
| 229 | // This is because we want to favor forceful termination over any errors that might |
| 230 | // arise from the upcall so that, e.g. Ctrl+C cannot be caught as a keyboard event |
| 231 | // and instead we abort execution. |
| 232 | if self.should_stop() { |
| 233 | self.clear_actions(); |
| 234 | self.vm.interrupt(&self.image); |
| 235 | break Err(Error::Break); |
| 236 | } |
| 237 | |
| 238 | if let Err(e) = upcall_result { |
| 239 | self.clear_actions(); |
| 240 | let (pos, message) = e.parts(); |
| 241 | break Err(Error::RuntimeError(pos, message)); |
| 242 | } |
| 243 | |
| 244 | if self.drain_actions()? { |
| 245 | running_stored_program = true; |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | StopReason::Yield => { |
| 250 | if self.should_stop_after_yield().await { |
| 251 | self.vm.interrupt(&self.image); |
| 252 | break Err(Error::Break); |
| 253 | } |
| 254 | } |
| 255 | } |
| 256 | }; |