1use crate::{
8 Context, JsError, JsExpect, JsNativeError, JsObject, JsResult, JsString, JsValue, Module,
9 builtins::promise::{PromiseCapability, ResolvingFunctions},
10 environments::EnvironmentStack,
11 error::RuntimeLimitError,
12 object::JsFunction,
13 realm::Realm,
14 script::Script,
15 vm::opcode::{OPCODE_HANDLERS, OPCODE_HANDLERS_BUDGET},
16 vm::shadow_stack::ErrorStack,
17};
18use boa_gc::{Finalize, Gc, Trace, custom_trace};
19use shadow_stack::ShadowStack;
20use std::{future::Future, ops::ControlFlow, path::Path, pin::Pin, task};
21
22#[cfg(feature = "trace")]
23use crate::sys::time::Instant;
24
25#[allow(unused_imports)]
26pub(crate) use opcode::{Instruction, InstructionIterator, Opcode};
27
28pub(crate) use {
29 call_frame::CallFrameFlags,
30 code_block::{
31 CodeBlockFlags, Constant, Handler, create_function_object, create_function_object_fast,
32 },
33 completion_record::CompletionRecord,
34 inline_cache::InlineCache,
35};
36
37pub use runtime_limits::RuntimeLimits;
38pub use {
39 call_frame::{CallFrame, GeneratorResumeKind},
40 code_block::CodeBlock,
41 source_info::{NativeSourceInfo, SourcePath},
42};
43
44pub(crate) use code_block::GlobalFunctionBinding;
45
46mod call_frame;
47mod code_block;
48mod completion_record;
49mod inline_cache;
50mod runtime_limits;
51
52pub(crate) mod opcode;
53pub(crate) mod shadow_stack;
54pub(crate) mod source_info;
55
56#[cfg(feature = "flowgraph")]
57pub mod flowgraph;
58
59#[cfg(test)]
60mod tests;
61
62#[derive(Debug)]
64pub struct Vm {
65 pub(crate) frames: Vec<CallFrame>,
70
71 pub(crate) stack: Stack,
72
73 pub(crate) return_value: JsValue,
74
75 pub(crate) pending_exception: Option<JsError>,
84 pub(crate) runtime_limits: RuntimeLimits,
85
86 pub(crate) native_active_function: Option<JsObject>,
89
90 pub(crate) host_call_depth: usize,
96
97 pub(crate) shadow_stack: ShadowStack,
98
99 #[cfg(feature = "trace")]
100 pub(crate) trace: bool,
101 #[cfg(feature = "trace")]
102 pub(crate) current_frame: Option<*const CallFrame>,
103}
104
105#[derive(Clone, Debug, Trace, Finalize)]
124pub(crate) struct Stack {
125 stack: Vec<JsValue>,
126}
127
128impl Stack {
129 fn new(capacity: usize) -> Self {
131 Self {
132 stack: Vec::with_capacity(capacity),
133 }
134 }
135
136 pub(crate) fn get_register(&self, frame: &CallFrame, index: usize) -> Option<&JsValue> {
138 self.stack.get(frame.rp as usize + index)
139 }
140
141 pub(crate) fn set_register(&mut self, frame: &CallFrame, index: usize, value: JsValue) {
143 self.stack[frame.rp as usize + index] = value;
144 }
145
146 pub(crate) fn truncate_to_frame(&mut self, frame: &CallFrame) {
148 self.stack.truncate(frame.frame_pointer());
149 }
150
151 pub(crate) fn split_off_frame(&mut self, frame: &CallFrame) -> Self {
153 let frame_pointer = frame.frame_pointer();
154 Self {
155 stack: self.stack.split_off(frame_pointer),
156 }
157 }
158
159 pub(crate) fn get_this(&self, frame: &CallFrame) -> JsValue {
161 self.stack[frame.this_index()].clone()
162 }
163
164 pub(crate) fn set_this(&mut self, frame: &CallFrame, this: JsValue) {
166 self.stack[frame.this_index()] = this;
167 }
168
169 pub(crate) fn get_function(&self, frame: &CallFrame) -> Option<JsObject> {
171 if let Some(object) = self.stack[frame.function_index()].as_object() {
172 return Some(object.clone());
173 }
174 None
175 }
176
177 pub(crate) fn get_arguments(&self, frame: &CallFrame) -> &[JsValue] {
179 &self.stack[frame.arguments_range()]
180 }
181
182 pub(crate) fn get_argument(&self, frame: &CallFrame, index: usize) -> Option<&JsValue> {
184 self.get_arguments(frame).get(index)
185 }
186
187 pub(crate) fn pop_rest_arguments(&mut self, frame: &CallFrame) -> Option<Vec<JsValue>> {
189 let argument_count = frame.argument_count as usize;
190 let param_count = frame.code_block().parameter_length as usize;
191 if argument_count < param_count {
192 return None;
193 }
194 let args_start = frame.fp as usize + CallFrame::FUNCTION_PROLOGUE as usize;
195 let args_end = args_start + argument_count;
196 let rest_count = argument_count - param_count + 1;
197
198 Some(
199 self.stack
200 .drain((args_end - rest_count)..args_end)
201 .collect(),
202 )
203 }
204
205 pub(crate) fn push<T>(&mut self, value: T)
207 where
208 T: Into<JsValue>,
209 {
210 self.stack.push(value.into());
211 }
212
213 #[track_caller]
219 pub(crate) fn pop(&mut self) -> JsValue {
220 self.stack.pop().expect("stack was empty")
221 }
222
223 pub(crate) fn calling_convention_pop_arguments(
226 &mut self,
227 argument_count: usize,
228 ) -> Vec<JsValue> {
229 let index = self.stack.len() - argument_count;
230 self.stack.split_off(index)
231 }
232
233 pub(crate) fn calling_convention_push_arguments(&mut self, values: &[JsValue]) {
236 self.stack.extend_from_slice(values);
237 }
238
239 #[track_caller]
241 pub(crate) fn calling_convention_get_function(&self, argument_count: usize) -> &JsValue {
242 let index = self.stack.len() - 1 - argument_count;
243 self.stack
244 .get(index)
245 .expect("invalid calling convention function index")
246 }
247
248 #[track_caller]
250 pub(crate) fn calling_convention_set_function(
251 &mut self,
252 argument_count: usize,
253 function: JsValue,
254 ) {
255 let index = self.stack.len() - 1 - argument_count;
256 self.stack[index] = function;
257 }
258
259 #[track_caller]
261 pub(crate) fn calling_convention_set_this(&mut self, argument_count: usize, function: JsValue) {
262 let index = self.stack.len() - 2 - argument_count;
263 self.stack[index] = function;
264 }
265
266 pub(crate) fn calling_convention_insert_arguments(
269 &mut self,
270 existing_argument_count: usize,
271 arguments: &[JsValue],
272 ) {
273 let index = self.stack.len() - existing_argument_count;
274 self.stack.splice(index..index, arguments.iter().cloned());
275 }
276
277 #[cfg(feature = "trace")]
278 const MAX_VALUE_LEN: usize = 18;
279 #[cfg(feature = "trace")]
280 const MAX_STACK_WIDTH: usize = 68;
281
282 #[cfg(feature = "trace")]
283 fn raw_value(value: &JsValue) -> String {
284 match value {
285 v if v.is_callable() => "func".to_string(),
286 v if v.is_object() => "obj".to_string(),
287 v if v.is_undefined() => "und".to_string(),
288 v if v.is_null() => "null".to_string(),
289 v => v.display().to_string(),
290 }
291 }
292
293 #[cfg(feature = "trace")]
294 fn truncate_display(val: &str) -> String {
295 if val.len() <= Self::MAX_VALUE_LEN {
296 return val.to_string();
297 }
298 let mut end = Self::MAX_VALUE_LEN - 2;
299 while !val.is_char_boundary(end) && end > 0 {
300 end -= 1;
301 }
302 format!("{}..", &val[..end])
303 }
304
305 #[cfg(feature = "trace")]
306 fn display_trace(&self, frame: &CallFrame, frame_count: usize) -> String {
307 let total = self.stack.len();
308 if total == 0 {
309 return "[ <empty> ]".to_string();
310 }
311
312 let mut groups: Vec<(String, usize, Option<usize>)> = Vec::new();
313 let mut force_truncate = false;
314
315 for (idx, v) in self.stack.iter().enumerate().rev() {
317 let is_frame = frame.frame_pointer() == idx;
318 let raw = Self::raw_value(v);
319
320 if !is_frame
321 && let Some(last) = groups.last_mut()
322 && last.0 == raw
323 && last.2.is_none()
324 {
325 last.1 += 1;
326 } else {
327 let marker = if is_frame { Some(frame_count) } else { None };
328 groups.push((raw, 1, marker));
329
330 if groups.len() > Self::MAX_STACK_WIDTH / 2 {
333 force_truncate = true;
334 break;
335 }
336 }
337 }
338
339 let mut string = String::from("[ ");
340 let mut truncated = force_truncate;
341 let suffix = format!(".. ({total} total) ]");
342
343 for (i, (val, count, marker)) in groups.iter().enumerate() {
344 let display_val = Self::truncate_display(val);
345 let part = if *count > 1 {
346 format!("{display_val} (x{count})")
347 } else {
348 display_val
349 };
350
351 let separator = if let Some(fc) = marker {
352 format!(" |{fc}|")
353 } else if i + 1 < groups.len() {
354 ",".to_string()
355 } else {
356 String::new()
357 };
358
359 let addition = format!("{part}{separator} ");
360 if string.len() + addition.len() + suffix.len() > Self::MAX_STACK_WIDTH {
361 truncated = true;
362 break;
363 }
364 string.push_str(&addition);
365 }
366
367 if truncated {
368 string.push_str(&suffix);
369 } else {
370 string.push(']');
371 }
372 string
373 }
374}
375
376#[derive(Debug, Clone, Finalize)]
378pub enum ActiveRunnable {
379 Script(Script),
381 Module(Module),
383}
384
385unsafe impl Trace for ActiveRunnable {
386 custom_trace!(this, mark, {
387 match this {
388 Self::Script(script) => mark(script),
389 Self::Module(module) => mark(module),
390 }
391 });
392}
393
394impl ActiveRunnable {
395 #[must_use]
397 pub fn path(&self) -> Option<&Path> {
398 match self {
399 Self::Script(script) => script.path(),
400 Self::Module(module) => module.path(),
401 }
402 }
403}
404
405impl Vm {
406 pub(crate) fn new(realm: Realm) -> Self {
408 let mut frames = Vec::with_capacity(16);
409 frames.push(CallFrame::new(
410 Gc::new(CodeBlock::new(JsString::default(), 0, true)),
411 None,
412 EnvironmentStack::new(),
413 realm,
414 ));
415 Self {
416 frames,
417 stack: Stack::new(1024),
418 return_value: JsValue::undefined(),
419 pending_exception: None,
420 runtime_limits: RuntimeLimits::default(),
421 native_active_function: None,
422 host_call_depth: 0,
423 shadow_stack: ShadowStack::default(),
424 #[cfg(feature = "trace")]
425 trace: false,
426 #[cfg(feature = "trace")]
427 current_frame: None,
428 }
429 }
430
431 #[track_caller]
432 #[inline]
433 pub(crate) fn set_register(&mut self, index: usize, value: JsValue) {
434 let rp = self.frame().rp as usize;
435 debug_assert!(
436 rp + index < self.stack.stack.len(),
437 "register index out of bounds: rp {rp}, index {index}, stack len {}",
438 self.stack.stack.len()
439 );
440 unsafe {
444 *self.stack.stack.get_unchecked_mut(rp + index) = value;
445 }
446 }
447
448 #[track_caller]
449 #[inline]
450 pub(crate) fn get_register(&self, index: usize) -> &JsValue {
451 let rp = self.frame().rp as usize;
452 debug_assert!(
453 rp + index < self.stack.stack.len(),
454 "register index out of bounds: rp {rp}, index {index}, stack len {}",
455 self.stack.stack.len()
456 );
457 unsafe { self.stack.stack.get_unchecked(rp + index) }
461 }
462
463 #[track_caller]
468 #[inline]
469 pub(crate) fn take_register(&mut self, index: usize) -> JsValue {
470 let rp = self.frame().rp as usize;
471 debug_assert!(
472 rp + index < self.stack.stack.len(),
473 "register index out of bounds: rp {rp}, index {index}, stack len {}",
474 self.stack.stack.len()
475 );
476 unsafe { std::mem::take(self.stack.stack.get_unchecked_mut(rp + index)) }
480 }
481
482 #[track_caller]
484 pub(crate) fn set_promise_capability(
485 &mut self,
486 promise_capability: PromiseCapability,
487 ) -> JsResult<()> {
488 #[cfg(debug_assertions)]
489 {
490 if !self.frame().code_block().is_async() {
491 return Err(crate::error::PanicError::new(
492 "only async functions and modules with a top-level-await \
493 can have a promise capability",
494 )
495 .into());
496 }
497 }
498
499 let rp = self.frame().rp as usize;
500 self.stack.stack[rp + CallFrame::PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX] =
501 promise_capability.promise.into();
502 self.stack.stack[rp + CallFrame::PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX] =
503 promise_capability.functions.resolve.into();
504 self.stack.stack[rp + CallFrame::PROMISE_CAPABILITY_REJECT_REGISTER_INDEX] =
505 promise_capability.functions.reject.into();
506
507 Ok(())
508 }
509
510 #[track_caller]
512 pub(crate) fn get_promise_capability(&self) -> JsResult<PromiseCapability> {
513 #[cfg(debug_assertions)]
514 if !self.frame().code_block().is_async() {
515 return Err(crate::error::PanicError::new(
516 "cannot get promise capability from non-async code",
517 )
518 .into());
519 }
520
521 let rp = self.frame().rp as usize;
522 let promise = self
523 .stack
524 .stack
525 .get(rp + CallFrame::PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX)
526 .and_then(JsValue::as_object)
527 .js_expect("registers must have a promise capability")?;
528 let resolve = self
529 .stack
530 .stack
531 .get(rp + CallFrame::PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX)
532 .and_then(JsValue::as_object)
533 .and_then(JsFunction::from_object)
534 .js_expect("registers must have a resolve function")?;
535 let reject = self
536 .stack
537 .stack
538 .get(rp + CallFrame::PROMISE_CAPABILITY_REJECT_REGISTER_INDEX)
539 .and_then(JsValue::as_object)
540 .and_then(JsFunction::from_object)
541 .js_expect("registers must have a reject function")?;
542
543 Ok(PromiseCapability {
544 promise,
545 functions: ResolvingFunctions { resolve, reject },
546 })
547 }
548
549 #[track_caller]
551 pub(crate) fn async_generator_object(&self) -> Option<JsObject> {
552 if !self.frame().code_block().is_async_generator() {
553 return None;
554 }
555
556 let rp = self.frame().rp as usize;
557 self.stack
558 .stack
559 .get(rp + CallFrame::ASYNC_GENERATOR_OBJECT_REGISTER_INDEX)
560 .expect("registers must have an async generator object")
561 .as_object()
562 }
563
564 #[track_caller]
570 #[inline]
571 pub(crate) fn frame(&self) -> &CallFrame {
572 unsafe { self.frames.last().unwrap_unchecked() }
574 }
575
576 #[track_caller]
582 #[inline]
583 pub(crate) fn frame_mut(&mut self) -> &mut CallFrame {
584 unsafe { self.frames.last_mut().unwrap_unchecked() }
586 }
587
588 pub(crate) fn push_frame(&mut self, mut frame: CallFrame) {
589 self.return_value = JsValue::undefined();
591
592 if !frame.registers_already_pushed() {
596 let current_stack_length = self.stack.stack.len() as u32;
597 frame.fp = current_stack_length - frame.argument_count - CallFrame::FUNCTION_PROLOGUE;
598
599 let register_count = frame.code_block.register_count as usize;
600 frame.rp = self.stack.stack.len() as u32;
601 self.stack.stack.resize(
602 self.stack.stack.len() + register_count,
603 JsValue::undefined(),
604 );
605 }
606
607 if frame.active_runnable.is_none() {
610 let current = self.frame();
611 frame.active_runnable.clone_from(¤t.active_runnable);
612 }
613
614 let current_pc = self.frame().pc;
615 self.shadow_stack
616 .push_bytecode(current_pc, frame.code_block().source_info.clone());
617
618 self.frames.push(frame);
619 }
620
621 pub(crate) fn push_frame_with_stack(
622 &mut self,
623 frame: CallFrame,
624 this: JsValue,
625 function: JsValue,
626 ) {
627 self.stack.push(this);
628 self.stack.push(function);
629
630 self.push_frame(frame);
631 }
632
633 pub(crate) fn pop_frame(&mut self) -> Option<CallFrame> {
634 if self.frames.len() <= 1 {
636 return None;
637 }
638 self.shadow_stack.pop();
639 self.frames.pop()
640 }
641
642 #[inline]
646 pub(crate) fn handle_exception_at(&mut self, pc: u32) -> bool {
647 let frame = self.frame_mut();
648 let Some((_, handler)) = frame.code_block().find_handler(pc) else {
649 return false;
650 };
651
652 let catch_address = handler.handler();
653 let environment_sp = frame.env_fp + handler.environment_count;
654
655 frame.pc = u32::from(catch_address);
657
658 self.frame_mut()
659 .environments
660 .truncate(environment_sp as usize);
661
662 true
663 }
664
665 pub(crate) fn get_return_value(&self) -> JsValue {
666 self.return_value.clone()
667 }
668
669 pub(crate) fn set_return_value(&mut self, value: JsValue) {
670 self.return_value = value;
671 }
672
673 pub(crate) fn take_return_value(&mut self) -> JsValue {
674 std::mem::take(&mut self.return_value)
675 }
676}
677
678#[allow(clippy::print_stdout)]
679#[cfg(feature = "trace")]
680impl Context {
681 const COLUMN_WIDTH: usize = 26;
682 const TIME_COLUMN_WIDTH: usize = Self::COLUMN_WIDTH / 2;
683 const OPCODE_COLUMN_WIDTH: usize = Self::COLUMN_WIDTH;
684 const OPERAND_COLUMN_WIDTH: usize = Self::COLUMN_WIDTH;
685 const NUMBER_OF_COLUMNS: usize = 4;
686
687 pub(crate) fn trace_call_frame(&self) {
688 let frame = self.vm.frame();
689 let msg = if self.vm.frames.is_empty() {
690 " VM Start ".to_string()
691 } else {
692 format!(
693 " Call Frame '{}'{} ",
694 frame.code_block().name().to_std_string_escaped(),
695 if frame.code_block().name().is_empty() {
696 format!(" [anon#{}]", frame.code_block().debug_id)
697 } else {
698 String::new()
699 }
700 )
701 };
702
703 if !frame.code_block.traced.get() {
705 println!("{}", frame.code_block);
706 frame.code_block.traced.set(true);
707 }
708 println!(
709 "{msg:-^width$}",
710 width = Self::COLUMN_WIDTH * Self::NUMBER_OF_COLUMNS - 10
711 );
712 println!(
713 "{:<TIME_COLUMN_WIDTH$} {:<OPCODE_COLUMN_WIDTH$} {:<OPERAND_COLUMN_WIDTH$} Stack\n",
714 "Time",
715 "Opcode",
716 "Operands",
717 TIME_COLUMN_WIDTH = Self::TIME_COLUMN_WIDTH,
718 OPCODE_COLUMN_WIDTH = Self::OPCODE_COLUMN_WIDTH,
719 OPERAND_COLUMN_WIDTH = Self::OPERAND_COLUMN_WIDTH,
720 );
721 }
722
723 fn trace_execute_instruction<F>(
724 &mut self,
725 f: F,
726 opcode: Opcode,
727 ) -> ControlFlow<CompletionRecord>
728 where
729 F: FnOnce(&mut Context, Opcode) -> ControlFlow<CompletionRecord>,
730 {
731 if self.vm.current_frame != Some(self.vm.frame()) {
732 println!();
733 self.trace_call_frame();
734 self.vm.current_frame = Some(self.vm.frame());
735 }
736 let frame = self.vm.frame();
737 let (instruction, _) = frame
738 .code_block
739 .bytecode
740 .next_instruction(frame.pc as usize);
741 let operands = self
742 .vm
743 .frame()
744 .code_block()
745 .instruction_operands(&instruction);
746
747 let instant = Instant::now();
748 let result = self.execute_instruction(f, opcode);
749 let duration = instant.elapsed();
750
751 let stack = self
752 .vm
753 .stack
754 .display_trace(self.vm.frame(), self.vm.frames.len() - 1);
755
756 println!(
757 "{:<TIME_COLUMN_WIDTH$} {:<OPCODE_COLUMN_WIDTH$} {operands:<OPERAND_COLUMN_WIDTH$} {stack}",
758 format!("{}μs", duration.as_micros()),
759 format!("{}", opcode.as_str()),
760 TIME_COLUMN_WIDTH = Self::TIME_COLUMN_WIDTH,
761 OPCODE_COLUMN_WIDTH = Self::OPCODE_COLUMN_WIDTH,
762 OPERAND_COLUMN_WIDTH = Self::OPERAND_COLUMN_WIDTH,
763 );
764
765 result
766 }
767}
768
769impl Context {
770 fn execute_instruction<F>(&mut self, f: F, opcode: Opcode) -> ControlFlow<CompletionRecord>
771 where
772 F: FnOnce(&mut Context, Opcode) -> ControlFlow<CompletionRecord>,
773 {
774 f(self, opcode)
775 }
776
777 fn execute_one<F>(&mut self, f: F, opcode: Opcode) -> ControlFlow<CompletionRecord>
778 where
779 F: FnOnce(&mut Context, Opcode) -> ControlFlow<CompletionRecord>,
780 {
781 #[cfg(feature = "fuzz")]
782 {
783 use crate::error::EngineError;
784 if self.instructions_remaining == 0 {
785 return ControlFlow::Break(CompletionRecord::Throw(
786 EngineError::NoInstructionsRemain.into(),
787 ));
788 }
789 self.instructions_remaining -= 1;
790 }
791
792 #[cfg(feature = "trace")]
793 if self.vm.trace || self.vm.frame().code_block.traceable() {
794 self.trace_execute_instruction(f, opcode)
795 } else {
796 self.execute_instruction(f, opcode)
797 }
798
799 #[cfg(not(feature = "trace"))]
800 self.execute_instruction(f, opcode)
801 }
802
803 fn handle_error(&mut self, mut err: JsError) -> ControlFlow<CompletionRecord> {
804 if err.backtrace.is_none() {
808 err.backtrace = Some(
809 self.vm
810 .shadow_stack
811 .take(self.vm.runtime_limits.backtrace_limit(), self.vm.frame().pc),
812 );
813 }
814
815 if !err.is_catchable() {
818 let mut frame = None;
819 let mut env_fp = self.vm.frame().environments.len();
820 loop {
821 if self.vm.frame().exit_early() {
822 break;
823 }
824
825 env_fp = self.vm.frame().env_fp as usize;
826
827 let Some(f) = self.vm.pop_frame() else {
828 break;
829 };
830 frame = Some(f);
831 }
832 self.vm.frame_mut().environments.truncate(env_fp);
833 if let Some(frame) = frame {
834 self.vm.stack.truncate_to_frame(&frame);
835 }
836 return ControlFlow::Break(CompletionRecord::Throw(err));
837 }
838
839 if let Some(native) = err.as_native_mut()
840 && let ErrorStack::Position(position) = &mut native.stack.0
841 {
842 let backtrace = self.vm.shadow_stack.take_and_push(
843 self.vm.runtime_limits.backtrace_limit(),
844 self.vm.frame().pc,
845 position.clone(),
846 );
847 native.stack.0 = ErrorStack::Backtrace(backtrace);
848 }
849
850 let pc = self.vm.frame().pc.saturating_sub(1);
852 if self.vm.handle_exception_at(pc) {
853 self.vm.pending_exception = Some(err);
854 return ControlFlow::Continue(());
855 }
856
857 let err = err.inject_realm(self.realm().clone());
859
860 self.vm.pending_exception = Some(err);
861 self.handle_throw()
862 }
863
864 fn handle_return(&mut self) -> ControlFlow<CompletionRecord> {
865 let exit_early = self.vm.frame().exit_early();
866 let frame = self.vm.frames.last().expect("frame must exist");
867 self.vm.stack.truncate_to_frame(frame);
868
869 let result = self.vm.take_return_value();
870 if exit_early {
871 return ControlFlow::Break(CompletionRecord::Return(result));
872 }
873
874 self.vm.stack.push(result);
875 self.vm.pop_frame().expect("frame must exist");
876 ControlFlow::Continue(())
877 }
878
879 fn handle_yield(&mut self) -> ControlFlow<CompletionRecord> {
880 let result = self.vm.take_return_value();
881 if self.vm.frame().exit_early() {
882 return ControlFlow::Break(CompletionRecord::Normal(result));
883 }
884
885 self.vm.stack.push(result);
886 self.vm.pop_frame().expect("frame must exist");
887 ControlFlow::Continue(())
888 }
889
890 fn handle_throw(&mut self) -> ControlFlow<CompletionRecord> {
891 if self
892 .vm
893 .pending_exception
894 .as_ref()
895 .is_some_and(|err| err.backtrace.is_none())
896 {
897 let pc = self.vm.frames.last().expect("frame must exist").pc;
898 let limit = self.vm.runtime_limits.backtrace_limit();
899 let backtrace = self.vm.shadow_stack.take(limit, pc);
900 self.vm
901 .pending_exception
902 .as_mut()
903 .expect("pending exception must exist")
904 .backtrace = Some(backtrace);
905 }
906
907 let mut env_fp = self.vm.frame().env_fp;
908 if self.vm.frame().exit_early() {
909 self.vm.frame_mut().environments.truncate(env_fp as usize);
910 let frame = self.vm.frames.last().expect("frame must exist");
911 self.vm.stack.truncate_to_frame(frame);
912 return ControlFlow::Break(CompletionRecord::Throw(
913 self.vm
914 .pending_exception
915 .take()
916 .expect("Err must exist for a CompletionType::Throw"),
917 ));
918 }
919
920 let mut frame = self.vm.pop_frame().expect("frame must exist");
921
922 loop {
923 env_fp = self.vm.frame().env_fp;
924 let pc = self.vm.frame().pc;
925 let exit_early = self.vm.frame().exit_early();
926
927 if self.vm.handle_exception_at(pc) {
928 return ControlFlow::Continue(());
929 }
930
931 if exit_early {
932 return ControlFlow::Break(CompletionRecord::Throw(
933 self.vm
934 .pending_exception
935 .take()
936 .expect("Err must exist for a CompletionType::Throw"),
937 ));
938 }
939
940 let Some(f) = self.vm.pop_frame() else {
941 break;
942 };
943 frame = f;
944 }
945 self.vm.frame_mut().environments.truncate(env_fp as usize);
946 self.vm.stack.truncate_to_frame(&frame);
947 ControlFlow::Continue(())
948 }
949
950 #[allow(clippy::future_not_send)]
953 pub(crate) async fn run_async_with_budget(&mut self, budget: u32) -> CompletionRecord {
954 let mut runtime_budget: u32 = budget;
955
956 while let Some(byte) = self
957 .vm
958 .frame()
959 .code_block
960 .bytecode
961 .bytes
962 .get(self.vm.frame().pc as usize)
963 {
964 let opcode = Opcode::decode(*byte);
965
966 match self.execute_one(
967 |context, opcode| {
968 let frame = context.vm.frame();
969 let pc = frame.pc as usize;
970
971 OPCODE_HANDLERS_BUDGET[opcode as usize](context, pc, &mut runtime_budget)
972 },
973 opcode,
974 ) {
975 ControlFlow::Continue(()) => {}
976 ControlFlow::Break(value) => return value,
977 }
978
979 if runtime_budget == 0 {
980 runtime_budget = budget;
981 yield_now().await;
982 }
983 }
984
985 CompletionRecord::Throw(JsError::from_native(JsNativeError::error()))
986 }
987
988 pub(crate) fn run(&mut self) -> CompletionRecord {
989 while let Some(byte) = self
990 .vm
991 .frame()
992 .code_block
993 .bytecode
994 .bytes
995 .get(self.vm.frame().pc as usize)
996 {
997 let opcode = Opcode::decode(*byte);
998
999 match self.execute_one(
1000 |context, opcode| {
1001 let frame = context.vm.frame();
1002 let pc = frame.pc as usize;
1003
1004 OPCODE_HANDLERS[opcode as usize](context, pc)
1005 },
1006 opcode,
1007 ) {
1008 ControlFlow::Continue(()) => {}
1009 ControlFlow::Break(value) => return value,
1010 }
1011 }
1012
1013 CompletionRecord::Throw(JsError::from_native(JsNativeError::error()))
1014 }
1015
1016 pub(crate) fn check_runtime_limits(&self) -> JsResult<()> {
1018 let recursion_depth = (self.vm.frames.len() - 1).saturating_add(self.vm.host_call_depth);
1024 if self.vm.runtime_limits.recursion_limit() <= recursion_depth {
1025 return Err(RuntimeLimitError::Recursion.into());
1026 }
1027 if self.vm.runtime_limits.stack_size_limit() <= self.vm.stack.stack.len() {
1029 return Err(RuntimeLimitError::StackSize.into());
1030 }
1031
1032 Ok(())
1033 }
1034}
1035
1036fn yield_now() -> impl Future<Output = ()> {
1038 struct YieldNow(bool);
1039
1040 impl Future for YieldNow {
1041 type Output = ();
1042
1043 fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
1044 if self.0 {
1045 task::Poll::Ready(())
1046 } else {
1047 self.0 = true;
1048 cx.waker().wake_by_ref();
1049 task::Poll::Pending
1050 }
1051 }
1052 }
1053
1054 YieldNow(false)
1055}