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;
50#[cfg(feature = "jit")]
51pub(crate) mod jit;
52mod runtime_limits;
53
54#[cfg(debug_assertions)]
55pub(crate) mod verify;
56
57pub(crate) mod opcode;
58pub(crate) mod shadow_stack;
59pub(crate) mod source_info;
60
61#[cfg(feature = "flowgraph")]
62pub mod flowgraph;
63
64#[cfg(test)]
65mod tests;
66
67#[derive(Debug)]
69pub struct Vm {
70 pub(crate) frames: Vec<CallFrame>,
75
76 pub(crate) stack: Stack,
77
78 pub(crate) return_value: JsValue,
79
80 pub(crate) pending_exception: Option<JsError>,
89 pub(crate) runtime_limits: RuntimeLimits,
90 pub(crate) interrupt_flag: Option<std::sync::Arc<std::sync::atomic::AtomicBool>>,
92
93 pub(crate) native_active_function: Option<JsObject>,
96
97 pub(crate) native_constructor_receiver: Option<JsValue>,
100
101 pub(crate) host_call_depth: usize,
107
108 pub(crate) shadow_stack: ShadowStack,
109
110 #[cfg(feature = "trace")]
111 pub(crate) trace: bool,
112 #[cfg(feature = "trace")]
113 pub(crate) current_frame: Option<*const CallFrame>,
114}
115
116#[derive(Clone, Debug, Trace, Finalize)]
135pub(crate) struct Stack {
136 stack: Vec<JsValue>,
137}
138
139impl Stack {
140 fn new(capacity: usize) -> Self {
142 Self {
143 stack: Vec::with_capacity(capacity),
144 }
145 }
146
147 pub(crate) fn get_register(&self, frame: &CallFrame, index: usize) -> Option<&JsValue> {
149 self.stack.get(frame.rp as usize + index)
150 }
151
152 pub(crate) fn set_register(&mut self, frame: &CallFrame, index: usize, value: JsValue) {
154 let old = std::mem::replace(&mut self.stack[frame.rp as usize + index], value);
155 unsafe { old.release_outside_collection() };
157 }
158
159 pub(crate) fn truncate_to_frame(&mut self, frame: &CallFrame) {
161 for value in self.stack.drain(frame.frame_pointer()..) {
162 unsafe { value.release_outside_collection() };
164 }
165 }
166
167 pub(crate) fn split_off_frame(&mut self, frame: &CallFrame) -> Self {
169 let frame_pointer = frame.frame_pointer();
170 Self {
171 stack: self.stack.split_off(frame_pointer),
172 }
173 }
174
175 pub(crate) fn get_this(&self, frame: &CallFrame) -> JsValue {
177 self.stack[frame.this_index()].clone()
178 }
179
180 pub(crate) fn set_this(&mut self, frame: &CallFrame, this: JsValue) {
182 self.stack[frame.this_index()] = this;
183 }
184
185 pub(crate) fn get_function(&self, frame: &CallFrame) -> Option<JsObject> {
187 if let Some(object) = self.stack[frame.function_index()].as_object() {
188 return Some(object.clone());
189 }
190 None
191 }
192
193 pub(crate) fn get_arguments(&self, frame: &CallFrame) -> &[JsValue] {
195 &self.stack[frame.arguments_range()]
196 }
197
198 pub(crate) fn get_argument(&self, frame: &CallFrame, index: usize) -> Option<&JsValue> {
200 self.get_arguments(frame).get(index)
201 }
202
203 pub(crate) fn pop_rest_arguments(&mut self, frame: &CallFrame) -> Option<Vec<JsValue>> {
205 let argument_count = frame.argument_count as usize;
206 let param_count = frame.code_block().parameter_length as usize;
207 if argument_count < param_count {
208 return None;
209 }
210 let args_start = frame.fp as usize + CallFrame::FUNCTION_PROLOGUE as usize;
211 let args_end = args_start + argument_count;
212 let rest_count = argument_count - param_count + 1;
213
214 Some(
215 self.stack
216 .drain((args_end - rest_count)..args_end)
217 .collect(),
218 )
219 }
220
221 pub(crate) fn push<T>(&mut self, value: T)
223 where
224 T: Into<JsValue>,
225 {
226 self.stack.push(value.into());
227 }
228
229 #[track_caller]
235 pub(crate) fn pop(&mut self) -> JsValue {
236 self.stack.pop().expect("stack was empty")
237 }
238
239 pub(crate) fn calling_convention_pop_arguments(
242 &mut self,
243 argument_count: usize,
244 ) -> Vec<JsValue> {
245 let index = self.stack.len() - argument_count;
246 self.stack.split_off(index)
247 }
248
249 pub(crate) fn calling_convention_pop_arguments_into(
252 &mut self,
253 argument_count: usize,
254 out: &mut impl Extend<JsValue>,
255 ) {
256 let index = self.stack.len() - argument_count;
257 out.extend(self.stack.drain(index..));
258 }
259
260 #[cfg(feature = "jit")]
263 pub(crate) fn calling_convention_numbers(
264 &self,
265 argument_count: usize,
266 out: &mut [f64],
267 ) -> bool {
268 let start = self.stack.len() - argument_count;
269 for (slot, value) in out.iter_mut().zip(&self.stack[start..]) {
270 let Some(number) = value.as_number() else {
271 return false;
272 };
273 *slot = number;
274 }
275 true
276 }
277
278 #[cfg(feature = "jit")]
281 pub(crate) fn calling_convention_replace_call(
282 &mut self,
283 argument_count: usize,
284 result: JsValue,
285 ) {
286 let len = self.stack.len() - argument_count - 2;
287 self.stack.truncate(len);
288 self.stack.push(result);
289 }
290
291 pub(crate) fn calling_convention_push_arguments(&mut self, values: &[JsValue]) {
294 self.stack.extend_from_slice(values);
295 }
296
297 #[track_caller]
299 pub(crate) fn calling_convention_get_function(&self, argument_count: usize) -> &JsValue {
300 let index = self.stack.len() - 1 - argument_count;
301 self.stack
302 .get(index)
303 .expect("invalid calling convention function index")
304 }
305
306 #[track_caller]
308 pub(crate) fn calling_convention_set_function(
309 &mut self,
310 argument_count: usize,
311 function: JsValue,
312 ) {
313 let index = self.stack.len() - 1 - argument_count;
314 self.stack[index] = function;
315 }
316
317 #[track_caller]
319 pub(crate) fn calling_convention_set_this(&mut self, argument_count: usize, function: JsValue) {
320 let index = self.stack.len() - 2 - argument_count;
321 self.stack[index] = function;
322 }
323
324 pub(crate) fn calling_convention_insert_arguments(
327 &mut self,
328 existing_argument_count: usize,
329 arguments: &[JsValue],
330 ) {
331 let index = self.stack.len() - existing_argument_count;
332 self.stack.splice(index..index, arguments.iter().cloned());
333 }
334
335 #[cfg(feature = "trace")]
336 const MAX_VALUE_LEN: usize = 18;
337 #[cfg(feature = "trace")]
338 const MAX_STACK_WIDTH: usize = 68;
339
340 #[cfg(feature = "trace")]
341 fn raw_value(value: &JsValue) -> String {
342 match value {
343 v if v.is_callable() => "func".to_string(),
344 v if v.is_object() => "obj".to_string(),
345 v if v.is_undefined() => "und".to_string(),
346 v if v.is_null() => "null".to_string(),
347 v => v.display().to_string(),
348 }
349 }
350
351 #[cfg(feature = "trace")]
352 fn truncate_display(val: &str) -> String {
353 if val.len() <= Self::MAX_VALUE_LEN {
354 return val.to_string();
355 }
356 let mut end = Self::MAX_VALUE_LEN - 2;
357 while !val.is_char_boundary(end) && end > 0 {
358 end -= 1;
359 }
360 format!("{}..", &val[..end])
361 }
362
363 #[cfg(feature = "trace")]
364 fn display_trace(&self, frame: &CallFrame, frame_count: usize) -> String {
365 let total = self.stack.len();
366 if total == 0 {
367 return "[ <empty> ]".to_string();
368 }
369
370 let mut groups: Vec<(String, usize, Option<usize>)> = Vec::new();
371 let mut force_truncate = false;
372
373 for (idx, v) in self.stack.iter().enumerate().rev() {
375 let is_frame = frame.frame_pointer() == idx;
376 let raw = Self::raw_value(v);
377
378 if !is_frame
379 && let Some(last) = groups.last_mut()
380 && last.0 == raw
381 && last.2.is_none()
382 {
383 last.1 += 1;
384 } else {
385 let marker = if is_frame { Some(frame_count) } else { None };
386 groups.push((raw, 1, marker));
387
388 if groups.len() > Self::MAX_STACK_WIDTH / 2 {
391 force_truncate = true;
392 break;
393 }
394 }
395 }
396
397 let mut string = String::from("[ ");
398 let mut truncated = force_truncate;
399 let suffix = format!(".. ({total} total) ]");
400
401 for (i, (val, count, marker)) in groups.iter().enumerate() {
402 let display_val = Self::truncate_display(val);
403 let part = if *count > 1 {
404 format!("{display_val} (x{count})")
405 } else {
406 display_val
407 };
408
409 let separator = if let Some(fc) = marker {
410 format!(" |{fc}|")
411 } else if i + 1 < groups.len() {
412 ",".to_string()
413 } else {
414 String::new()
415 };
416
417 let addition = format!("{part}{separator} ");
418 if string.len() + addition.len() + suffix.len() > Self::MAX_STACK_WIDTH {
419 truncated = true;
420 break;
421 }
422 string.push_str(&addition);
423 }
424
425 if truncated {
426 string.push_str(&suffix);
427 } else {
428 string.push(']');
429 }
430 string
431 }
432}
433
434#[derive(Debug, Clone, Finalize)]
436pub enum ActiveRunnable {
437 Script(Script),
439 Module(Module),
441}
442
443unsafe impl Trace for ActiveRunnable {
444 custom_trace!(this, mark, {
445 match this {
446 Self::Script(script) => mark(script),
447 Self::Module(module) => mark(module),
448 }
449 });
450}
451
452impl ActiveRunnable {
453 #[must_use]
455 pub fn path(&self) -> Option<&Path> {
456 match self {
457 Self::Script(script) => script.path(),
458 Self::Module(module) => module.path(),
459 }
460 }
461}
462
463impl Vm {
464 pub(crate) fn new(realm: Realm) -> Self {
466 let mut frames = Vec::with_capacity(16);
467 frames.push(CallFrame::new(
468 Gc::new(CodeBlock::new(JsString::default(), 0, true)),
469 None,
470 EnvironmentStack::new(),
471 realm,
472 ));
473 Self {
474 frames,
475 stack: Stack::new(1024),
476 return_value: JsValue::undefined(),
477 pending_exception: None,
478 runtime_limits: RuntimeLimits::default(),
479 interrupt_flag: None,
480 native_active_function: None,
481 native_constructor_receiver: None,
482 host_call_depth: 0,
483 shadow_stack: ShadowStack::default(),
484 #[cfg(feature = "trace")]
485 trace: false,
486 #[cfg(feature = "trace")]
487 current_frame: None,
488 }
489 }
490
491 #[track_caller]
492 #[inline]
493 pub(crate) fn set_register(&mut self, index: usize, value: JsValue) {
494 let rp = self.frame().rp as usize;
495 debug_assert!(
496 rp + index < self.stack.stack.len(),
497 "register index out of bounds: rp {rp}, index {index}, stack len {}",
498 self.stack.stack.len()
499 );
500 let old =
504 unsafe { std::mem::replace(self.stack.stack.get_unchecked_mut(rp + index), value) };
505 unsafe { old.release_outside_collection() };
507 }
508
509 #[track_caller]
510 #[inline]
511 pub(crate) fn get_register(&self, index: usize) -> &JsValue {
512 let rp = self.frame().rp as usize;
513 debug_assert!(
514 rp + index < self.stack.stack.len(),
515 "register index out of bounds: rp {rp}, index {index}, stack len {}",
516 self.stack.stack.len()
517 );
518 unsafe { self.stack.stack.get_unchecked(rp + index) }
522 }
523
524 #[track_caller]
529 #[inline]
530 pub(crate) fn take_register(&mut self, index: usize) -> JsValue {
531 let rp = self.frame().rp as usize;
532 debug_assert!(
533 rp + index < self.stack.stack.len(),
534 "register index out of bounds: rp {rp}, index {index}, stack len {}",
535 self.stack.stack.len()
536 );
537 unsafe { std::mem::take(self.stack.stack.get_unchecked_mut(rp + index)) }
541 }
542
543 #[track_caller]
545 pub(crate) fn set_promise_capability(
546 &mut self,
547 promise_capability: PromiseCapability,
548 ) -> JsResult<()> {
549 #[cfg(debug_assertions)]
550 {
551 if !self.frame().code_block().is_async() {
552 return Err(crate::error::PanicError::new(
553 "only async functions and modules with a top-level-await \
554 can have a promise capability",
555 )
556 .into());
557 }
558 }
559
560 let rp = self.frame().rp as usize;
561 self.stack.stack[rp + CallFrame::PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX] =
562 promise_capability.promise.into();
563 self.stack.stack[rp + CallFrame::PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX] =
564 promise_capability.functions.resolve.into();
565 self.stack.stack[rp + CallFrame::PROMISE_CAPABILITY_REJECT_REGISTER_INDEX] =
566 promise_capability.functions.reject.into();
567
568 Ok(())
569 }
570
571 #[track_caller]
573 pub(crate) fn get_promise_capability(&self) -> JsResult<PromiseCapability> {
574 #[cfg(debug_assertions)]
575 if !self.frame().code_block().is_async() {
576 return Err(crate::error::PanicError::new(
577 "cannot get promise capability from non-async code",
578 )
579 .into());
580 }
581
582 let rp = self.frame().rp as usize;
583 let promise = self
584 .stack
585 .stack
586 .get(rp + CallFrame::PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX)
587 .and_then(JsValue::as_object)
588 .js_expect("registers must have a promise capability")?;
589 let resolve = self
590 .stack
591 .stack
592 .get(rp + CallFrame::PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX)
593 .and_then(JsValue::as_object)
594 .and_then(JsFunction::from_object)
595 .js_expect("registers must have a resolve function")?;
596 let reject = self
597 .stack
598 .stack
599 .get(rp + CallFrame::PROMISE_CAPABILITY_REJECT_REGISTER_INDEX)
600 .and_then(JsValue::as_object)
601 .and_then(JsFunction::from_object)
602 .js_expect("registers must have a reject function")?;
603
604 Ok(PromiseCapability {
605 promise,
606 functions: ResolvingFunctions { resolve, reject },
607 })
608 }
609
610 #[track_caller]
612 pub(crate) fn async_generator_object(&self) -> Option<JsObject> {
613 if !self.frame().code_block().is_async_generator() {
614 return None;
615 }
616
617 let rp = self.frame().rp as usize;
618 self.stack
619 .stack
620 .get(rp + CallFrame::ASYNC_GENERATOR_OBJECT_REGISTER_INDEX)
621 .expect("registers must have an async generator object")
622 .as_object()
623 }
624
625 #[track_caller]
631 #[inline]
632 pub(crate) fn frame(&self) -> &CallFrame {
633 unsafe { self.frames.last().unwrap_unchecked() }
635 }
636
637 #[track_caller]
643 #[inline]
644 pub(crate) fn frame_mut(&mut self) -> &mut CallFrame {
645 unsafe { self.frames.last_mut().unwrap_unchecked() }
647 }
648
649 pub(crate) fn push_frame(&mut self, mut frame: CallFrame) {
650 self.return_value = JsValue::undefined();
652
653 if !frame.registers_already_pushed() {
657 let current_stack_length = self.stack.stack.len() as u32;
658 frame.fp = current_stack_length - frame.argument_count - CallFrame::FUNCTION_PROLOGUE;
659
660 let register_count = frame.code_block.register_count as usize;
661 frame.rp = self.stack.stack.len() as u32;
662 self.stack.stack.resize(
663 self.stack.stack.len() + register_count,
664 JsValue::undefined(),
665 );
666 }
667
668 if frame.active_runnable.is_none() {
671 let current = self.frame();
672 frame.active_runnable.clone_from(¤t.active_runnable);
673 }
674
675 let current_pc = self.frame().pc;
676 self.shadow_stack
677 .push_bytecode(current_pc, frame.code_block().source_info.clone());
678
679 self.frames.push(frame);
680 }
681
682 pub(crate) fn push_frame_with_stack(
683 &mut self,
684 frame: CallFrame,
685 this: JsValue,
686 function: JsValue,
687 ) {
688 self.stack.push(this);
689 self.stack.push(function);
690
691 self.push_frame(frame);
692 }
693
694 pub(crate) fn pop_frame(&mut self) -> Option<CallFrame> {
695 if self.frames.len() <= 1 {
697 return None;
698 }
699 self.shadow_stack.pop();
700 self.frames.pop()
701 }
702
703 #[inline]
707 pub(crate) fn handle_exception_at(&mut self, pc: u32) -> bool {
708 let frame = self.frame_mut();
709 let Some((_, handler)) = frame.code_block().find_handler(pc) else {
710 return false;
711 };
712
713 let catch_address = handler.handler();
714 let environment_sp = frame.env_fp + handler.environment_count;
715
716 frame.pc = u32::from(catch_address);
718
719 self.frame_mut()
720 .environments
721 .truncate(environment_sp as usize);
722
723 true
724 }
725
726 pub(crate) fn get_return_value(&self) -> JsValue {
727 self.return_value.clone()
728 }
729
730 pub(crate) fn set_return_value(&mut self, value: JsValue) {
731 self.return_value = value;
732 }
733
734 pub(crate) fn take_return_value(&mut self) -> JsValue {
735 std::mem::take(&mut self.return_value)
736 }
737}
738
739#[allow(clippy::print_stdout)]
740#[cfg(feature = "trace")]
741impl Context {
742 const COLUMN_WIDTH: usize = 26;
743 const TIME_COLUMN_WIDTH: usize = Self::COLUMN_WIDTH / 2;
744 const OPCODE_COLUMN_WIDTH: usize = Self::COLUMN_WIDTH;
745 const OPERAND_COLUMN_WIDTH: usize = Self::COLUMN_WIDTH;
746 const NUMBER_OF_COLUMNS: usize = 4;
747
748 pub(crate) fn trace_call_frame(&self) {
749 let frame = self.vm.frame();
750 let msg = if self.vm.frames.is_empty() {
751 " VM Start ".to_string()
752 } else {
753 format!(
754 " Call Frame '{}'{} ",
755 frame.code_block().name().to_std_string_escaped(),
756 if frame.code_block().name().is_empty() {
757 format!(" [anon#{}]", frame.code_block().debug_id)
758 } else {
759 String::new()
760 }
761 )
762 };
763
764 if !frame.code_block.traced.get() {
766 println!("{}", frame.code_block);
767 frame.code_block.traced.set(true);
768 }
769 println!(
770 "{msg:-^width$}",
771 width = Self::COLUMN_WIDTH * Self::NUMBER_OF_COLUMNS - 10
772 );
773 println!(
774 "{:<TIME_COLUMN_WIDTH$} {:<OPCODE_COLUMN_WIDTH$} {:<OPERAND_COLUMN_WIDTH$} Stack\n",
775 "Time",
776 "Opcode",
777 "Operands",
778 TIME_COLUMN_WIDTH = Self::TIME_COLUMN_WIDTH,
779 OPCODE_COLUMN_WIDTH = Self::OPCODE_COLUMN_WIDTH,
780 OPERAND_COLUMN_WIDTH = Self::OPERAND_COLUMN_WIDTH,
781 );
782 }
783
784 fn trace_execute_instruction<F>(
785 &mut self,
786 f: F,
787 opcode: Opcode,
788 ) -> ControlFlow<CompletionRecord>
789 where
790 F: FnOnce(&mut Context, Opcode) -> ControlFlow<CompletionRecord>,
791 {
792 if self.vm.current_frame != Some(self.vm.frame()) {
793 println!();
794 self.trace_call_frame();
795 self.vm.current_frame = Some(self.vm.frame());
796 }
797 let frame = self.vm.frame();
798 let (instruction, _) = frame
799 .code_block
800 .bytecode
801 .next_instruction(frame.pc as usize);
802 let operands = self
803 .vm
804 .frame()
805 .code_block()
806 .instruction_operands(&instruction);
807
808 let instant = Instant::now();
809 let result = self.execute_instruction(f, opcode);
810 let duration = instant.elapsed();
811
812 let stack = self
813 .vm
814 .stack
815 .display_trace(self.vm.frame(), self.vm.frames.len() - 1);
816
817 println!(
818 "{:<TIME_COLUMN_WIDTH$} {:<OPCODE_COLUMN_WIDTH$} {operands:<OPERAND_COLUMN_WIDTH$} {stack}",
819 format!("{}μs", duration.as_micros()),
820 opcode.as_str(),
821 TIME_COLUMN_WIDTH = Self::TIME_COLUMN_WIDTH,
822 OPCODE_COLUMN_WIDTH = Self::OPCODE_COLUMN_WIDTH,
823 OPERAND_COLUMN_WIDTH = Self::OPERAND_COLUMN_WIDTH,
824 );
825
826 result
827 }
828}
829
830impl Context {
831 pub fn set_interrupt_flag(&mut self, flag: std::sync::Arc<std::sync::atomic::AtomicBool>) {
836 self.vm.interrupt_flag = Some(flag);
837 }
838
839 fn execute_instruction<F>(&mut self, f: F, opcode: Opcode) -> ControlFlow<CompletionRecord>
840 where
841 F: FnOnce(&mut Context, Opcode) -> ControlFlow<CompletionRecord>,
842 {
843 f(self, opcode)
844 }
845
846 fn execute_one<F>(&mut self, f: F, opcode: Opcode) -> ControlFlow<CompletionRecord>
847 where
848 F: FnOnce(&mut Context, Opcode) -> ControlFlow<CompletionRecord>,
849 {
850 if self
851 .vm
852 .interrupt_flag
853 .as_ref()
854 .is_some_and(|flag| flag.load(std::sync::atomic::Ordering::Acquire))
855 {
856 return self.handle_error(RuntimeLimitError::Interrupted.into());
857 }
858
859 #[cfg(feature = "fuzz")]
860 {
861 use crate::error::EngineError;
862 if self.instructions_remaining == 0 {
863 return ControlFlow::Break(CompletionRecord::Throw(
864 EngineError::NoInstructionsRemain.into(),
865 ));
866 }
867 self.instructions_remaining -= 1;
868 }
869
870 #[cfg(feature = "trace")]
871 if self.vm.trace || self.vm.frame().code_block.traceable() {
872 self.trace_execute_instruction(f, opcode)
873 } else {
874 self.execute_instruction(f, opcode)
875 }
876
877 #[cfg(not(feature = "trace"))]
878 self.execute_instruction(f, opcode)
879 }
880
881 fn handle_error(&mut self, mut err: JsError) -> ControlFlow<CompletionRecord> {
882 if err.backtrace.is_none() {
886 err.backtrace = Some(
887 self.vm
888 .shadow_stack
889 .take(self.vm.runtime_limits.backtrace_limit(), self.vm.frame().pc),
890 );
891 }
892
893 if !err.is_catchable() {
896 let mut frame = None;
897 let mut env_fp = self.vm.frame().environments.len();
898 loop {
899 if self.vm.frame().exit_early() {
900 break;
901 }
902
903 env_fp = self.vm.frame().env_fp as usize;
904
905 let Some(f) = self.vm.pop_frame() else {
906 break;
907 };
908 frame = Some(f);
909 }
910 self.vm.frame_mut().environments.truncate(env_fp);
911 if let Some(frame) = frame {
912 self.vm.stack.truncate_to_frame(&frame);
913 }
914 return ControlFlow::Break(CompletionRecord::Throw(err));
915 }
916
917 if let Some(native) = err.as_native_mut()
918 && let ErrorStack::Position(position) = &mut native.stack.0
919 {
920 let backtrace = self.vm.shadow_stack.take_and_push(
921 self.vm.runtime_limits.backtrace_limit(),
922 self.vm.frame().pc,
923 position.clone(),
924 );
925 native.stack.0 = ErrorStack::Backtrace(backtrace);
926 }
927
928 let pc = self.vm.frame().pc.saturating_sub(1);
930 if self.vm.handle_exception_at(pc) {
931 self.vm.pending_exception = Some(err);
932 return ControlFlow::Continue(());
933 }
934
935 let err = err.inject_realm(self.realm().clone());
937
938 self.vm.pending_exception = Some(err);
939 self.handle_throw()
940 }
941
942 fn handle_return(&mut self) -> ControlFlow<CompletionRecord> {
943 let exit_early = self.vm.frame().exit_early();
944 let frame = self.vm.frames.last().expect("frame must exist");
945 self.vm.stack.truncate_to_frame(frame);
946
947 let result = self.vm.take_return_value();
948 if exit_early {
949 return ControlFlow::Break(CompletionRecord::Return(result));
950 }
951
952 self.vm.stack.push(result);
953 self.vm.pop_frame().expect("frame must exist");
954 ControlFlow::Continue(())
955 }
956
957 fn handle_yield(&mut self) -> ControlFlow<CompletionRecord> {
958 let result = self.vm.take_return_value();
959 if self.vm.frame().exit_early() {
960 return ControlFlow::Break(CompletionRecord::Normal(result));
961 }
962
963 self.vm.stack.push(result);
964 self.vm.pop_frame().expect("frame must exist");
965 ControlFlow::Continue(())
966 }
967
968 fn handle_throw(&mut self) -> ControlFlow<CompletionRecord> {
969 if self
970 .vm
971 .pending_exception
972 .as_ref()
973 .is_some_and(|err| err.backtrace.is_none())
974 {
975 let pc = self.vm.frames.last().expect("frame must exist").pc;
976 let limit = self.vm.runtime_limits.backtrace_limit();
977 let backtrace = self.vm.shadow_stack.take(limit, pc);
978 self.vm
979 .pending_exception
980 .as_mut()
981 .expect("pending exception must exist")
982 .backtrace = Some(backtrace);
983 }
984
985 let mut env_fp = self.vm.frame().env_fp;
986 if self.vm.frame().exit_early() {
987 self.vm.frame_mut().environments.truncate(env_fp as usize);
988 let frame = self.vm.frames.last().expect("frame must exist");
989 self.vm.stack.truncate_to_frame(frame);
990 return ControlFlow::Break(CompletionRecord::Throw(
991 self.vm
992 .pending_exception
993 .take()
994 .expect("Err must exist for a CompletionType::Throw"),
995 ));
996 }
997
998 let mut frame = self.vm.pop_frame().expect("frame must exist");
999
1000 loop {
1001 env_fp = self.vm.frame().env_fp;
1002 let pc = self.vm.frame().pc;
1003 let exit_early = self.vm.frame().exit_early();
1004
1005 if self.vm.handle_exception_at(pc) {
1006 return ControlFlow::Continue(());
1007 }
1008
1009 if exit_early {
1010 return ControlFlow::Break(CompletionRecord::Throw(
1011 self.vm
1012 .pending_exception
1013 .take()
1014 .expect("Err must exist for a CompletionType::Throw"),
1015 ));
1016 }
1017
1018 let Some(f) = self.vm.pop_frame() else {
1019 break;
1020 };
1021 frame = f;
1022 }
1023 self.vm.frame_mut().environments.truncate(env_fp as usize);
1024 self.vm.stack.truncate_to_frame(&frame);
1025 ControlFlow::Continue(())
1026 }
1027
1028 #[allow(clippy::future_not_send)]
1031 pub(crate) async fn run_async_with_budget(&mut self, budget: u32) -> CompletionRecord {
1032 let mut runtime_budget: u32 = budget;
1033
1034 while let Some(byte) = self
1035 .vm
1036 .frame()
1037 .code_block
1038 .bytecode
1039 .bytes
1040 .get(self.vm.frame().pc as usize)
1041 {
1042 let opcode = Opcode::decode(*byte);
1043
1044 match self.execute_one(
1045 |context, opcode| {
1046 let frame = context.vm.frame();
1047 let pc = frame.pc as usize;
1048
1049 OPCODE_HANDLERS_BUDGET[opcode as usize](context, pc, &mut runtime_budget)
1050 },
1051 opcode,
1052 ) {
1053 ControlFlow::Continue(()) => {}
1054 ControlFlow::Break(value) => return value,
1055 }
1056
1057 if runtime_budget == 0 {
1058 runtime_budget = budget;
1059 yield_now().await;
1060 }
1061 }
1062
1063 CompletionRecord::Throw(JsError::from_native(JsNativeError::error()))
1064 }
1065
1066 pub(crate) fn run(&mut self) -> CompletionRecord {
1067 while let Some(byte) = self
1068 .vm
1069 .frame()
1070 .code_block
1071 .bytecode
1072 .bytes
1073 .get(self.vm.frame().pc as usize)
1074 {
1075 let opcode = Opcode::decode(*byte);
1076
1077 match self.execute_one(
1078 |context, opcode| {
1079 let frame = context.vm.frame();
1080 let pc = frame.pc as usize;
1081
1082 OPCODE_HANDLERS[opcode as usize](context, pc)
1083 },
1084 opcode,
1085 ) {
1086 ControlFlow::Continue(()) => {}
1087 ControlFlow::Break(value) => return value,
1088 }
1089 }
1090
1091 CompletionRecord::Throw(JsError::from_native(JsNativeError::error()))
1092 }
1093
1094 pub(crate) fn check_runtime_limits(&self) -> JsResult<()> {
1096 let js_depth = self.vm.frames.len().saturating_sub(1);
1102 if self.vm.runtime_limits.recursion_limit() <= js_depth
1103 || self.vm.runtime_limits.host_recursion_limit() <= self.vm.host_call_depth
1104 {
1105 return Err(JsNativeError::range()
1106 .with_message("Maximum call stack size exceeded")
1107 .into());
1108 }
1109 if self.vm.runtime_limits.stack_size_limit() <= self.vm.stack.stack.len() {
1113 return Err(JsNativeError::range()
1114 .with_message("Maximum call stack size exceeded")
1115 .into());
1116 }
1117
1118 Ok(())
1119 }
1120}
1121
1122fn yield_now() -> impl Future<Output = ()> {
1124 struct YieldNow(bool);
1125
1126 impl Future for YieldNow {
1127 type Output = ();
1128
1129 fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
1130 if self.0 {
1131 task::Poll::Ready(())
1132 } else {
1133 self.0 = true;
1134 cx.waker().wake_by_ref();
1135 task::Poll::Pending
1136 }
1137 }
1138 }
1139
1140 YieldNow(false)
1141}