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 execution_budget::ExecutionScope;
38pub use runtime_limits::RuntimeLimits;
39pub use {
40 call_frame::{CallFrame, GeneratorResumeKind},
41 code_block::CodeBlock,
42 source_info::{NativeSourceInfo, SourcePath},
43};
44
45pub(crate) use code_block::GlobalFunctionBinding;
46
47mod call_frame;
48mod code_block;
49mod completion_record;
50mod execution_budget;
51mod inline_cache;
52#[cfg(feature = "jit")]
53pub(crate) mod jit;
54mod runtime_limits;
55
56#[cfg(debug_assertions)]
57pub(crate) mod verify;
58
59pub(crate) mod opcode;
60pub(crate) mod shadow_stack;
61pub(crate) mod source_info;
62
63#[cfg(feature = "flowgraph")]
64pub mod flowgraph;
65
66#[cfg(test)]
67mod tests;
68
69#[derive(Debug)]
71pub struct Vm {
72 pub(crate) frames: Vec<CallFrame>,
77
78 pub(crate) stack: Stack,
79
80 pub(crate) return_value: JsValue,
81
82 pub(crate) pending_exception: Option<JsError>,
91 pub(crate) runtime_limits: RuntimeLimits,
92 pub(crate) interrupt_flag: Option<std::sync::Arc<std::sync::atomic::AtomicBool>>,
94 pub(crate) execution_budget: std::sync::Arc<execution_budget::ExecutionBudget>,
95
96 pub(crate) native_active_function: Option<JsObject>,
99
100 pub(crate) native_constructor_receiver: Option<JsValue>,
103
104 pub(crate) host_call_depth: usize,
110
111 pub(crate) shadow_stack: ShadowStack,
112
113 #[cfg(feature = "jit")]
115 pub(crate) jit_depth: u32,
116
117 #[cfg(feature = "jit")]
119 pub(crate) jit_break: Option<CompletionRecord>,
120
121 #[cfg(feature = "jit")]
123 pub(crate) jit_frame: boa_jit::baseline::JitFrame,
124
125 #[cfg(feature = "trace")]
126 pub(crate) trace: bool,
127 #[cfg(feature = "trace")]
128 pub(crate) current_frame: Option<*const CallFrame>,
129}
130
131#[derive(Clone, Debug, Trace, Finalize)]
150pub(crate) struct Stack {
151 stack: Vec<JsValue>,
152}
153
154impl Stack {
155 fn new(capacity: usize) -> Self {
157 Self {
158 stack: Vec::with_capacity(capacity),
159 }
160 }
161
162 pub(crate) fn get_register(&self, frame: &CallFrame, index: usize) -> Option<&JsValue> {
164 self.stack.get(frame.rp as usize + index)
165 }
166
167 pub(crate) fn set_register(&mut self, frame: &CallFrame, index: usize, value: JsValue) {
169 let old = std::mem::replace(&mut self.stack[frame.rp as usize + index], value);
170 unsafe { old.release_outside_collection() };
172 }
173
174 #[inline]
176 pub(crate) fn truncate_to_frame(&mut self, frame: &CallFrame) {
177 for value in self.stack.drain(frame.frame_pointer()..) {
178 unsafe { value.release_outside_collection() };
180 }
181 }
182
183 pub(crate) fn split_off_frame(&mut self, frame: &CallFrame) -> Self {
185 let frame_pointer = frame.frame_pointer();
186 Self {
187 stack: self.stack.split_off(frame_pointer),
188 }
189 }
190
191 pub(crate) fn get_this(&self, frame: &CallFrame) -> JsValue {
193 self.stack[frame.this_index()].clone()
194 }
195
196 pub(crate) fn set_this(&mut self, frame: &CallFrame, this: JsValue) {
198 let old = std::mem::replace(&mut self.stack[frame.this_index()], this);
199 unsafe { old.release_outside_collection() };
201 }
202
203 pub(crate) fn get_function(&self, frame: &CallFrame) -> Option<JsObject> {
205 self.stack[frame.function_index()].as_object()
206 }
207
208 pub(crate) fn get_arguments(&self, frame: &CallFrame) -> &[JsValue] {
210 &self.stack[frame.arguments_range()]
211 }
212
213 #[inline]
215 pub(crate) fn get_argument(&self, frame: &CallFrame, index: usize) -> Option<&JsValue> {
216 if index >= frame.argument_count as usize {
217 return None;
218 }
219 let start = frame.fp as usize + CallFrame::FUNCTION_PROLOGUE as usize;
220 self.stack.get(start + index)
221 }
222
223 pub(crate) fn pop_rest_arguments(&mut self, frame: &CallFrame) -> Option<Vec<JsValue>> {
225 let argument_count = frame.argument_count as usize;
226 let param_count = frame.code_block().parameter_length as usize;
227 if argument_count < param_count {
228 return None;
229 }
230 let args_start = frame.fp as usize + CallFrame::FUNCTION_PROLOGUE as usize;
231 let args_end = args_start + argument_count;
232 let rest_count = argument_count - param_count + 1;
233
234 Some(
235 self.stack
236 .drain((args_end - rest_count)..args_end)
237 .collect(),
238 )
239 }
240
241 pub(crate) fn push<T>(&mut self, value: T)
243 where
244 T: Into<JsValue>,
245 {
246 self.stack.push(value.into());
247 }
248
249 #[track_caller]
255 pub(crate) fn pop(&mut self) -> JsValue {
256 self.stack.pop().expect("stack was empty")
257 }
258
259 pub(crate) fn calling_convention_pop_arguments(
262 &mut self,
263 argument_count: usize,
264 ) -> Vec<JsValue> {
265 let index = self.stack.len() - argument_count;
266 self.stack.split_off(index)
267 }
268
269 pub(crate) fn calling_convention_pop_arguments_into(
272 &mut self,
273 argument_count: usize,
274 out: &mut impl Extend<JsValue>,
275 ) {
276 let index = self.stack.len() - argument_count;
277 out.extend(self.stack.drain(index..));
278 }
279
280 #[cfg(feature = "jit")]
283 pub(crate) fn calling_convention_numbers(
284 &self,
285 argument_count: usize,
286 out: &mut [f64],
287 ) -> bool {
288 let start = self.stack.len() - argument_count;
289 for (slot, value) in out.iter_mut().zip(&self.stack[start..]) {
290 let Some(number) = value.as_number() else {
291 return false;
292 };
293 *slot = number;
294 }
295 true
296 }
297
298 #[cfg(feature = "jit")]
301 pub(crate) fn calling_convention_replace_call(
302 &mut self,
303 argument_count: usize,
304 result: JsValue,
305 ) {
306 let len = self.stack.len() - argument_count - 2;
307 self.stack.truncate(len);
308 self.stack.push(result);
309 }
310
311 #[inline]
313 pub(crate) fn calling_convention_push_call(
314 &mut self,
315 this: JsValue,
316 function: JsValue,
317 values: &[JsValue],
318 ) {
319 self.stack
320 .reserve(CallFrame::FUNCTION_PROLOGUE as usize + values.len());
321 self.stack.push(this);
322 self.stack.push(function);
323 self.stack.extend_from_slice(values);
324 }
325
326 #[inline]
329 pub(crate) fn calling_convention_push_arguments(&mut self, values: &[JsValue]) {
330 self.stack.extend_from_slice(values);
331 }
332
333 #[track_caller]
335 pub(crate) fn calling_convention_get_function(&self, argument_count: usize) -> &JsValue {
336 let index = self.stack.len() - 1 - argument_count;
337 self.stack
338 .get(index)
339 .expect("invalid calling convention function index")
340 }
341
342 #[track_caller]
344 pub(crate) fn calling_convention_set_function(
345 &mut self,
346 argument_count: usize,
347 function: JsValue,
348 ) {
349 let index = self.stack.len() - 1 - argument_count;
350 self.stack[index] = function;
351 }
352
353 #[track_caller]
355 pub(crate) fn calling_convention_set_this(&mut self, argument_count: usize, function: JsValue) {
356 let index = self.stack.len() - 2 - argument_count;
357 self.stack[index] = function;
358 }
359
360 pub(crate) fn calling_convention_insert_arguments(
363 &mut self,
364 existing_argument_count: usize,
365 arguments: &[JsValue],
366 ) {
367 let index = self.stack.len() - existing_argument_count;
368 self.stack.splice(index..index, arguments.iter().cloned());
369 }
370
371 #[cfg(feature = "trace")]
372 const MAX_VALUE_LEN: usize = 18;
373 #[cfg(feature = "trace")]
374 const MAX_STACK_WIDTH: usize = 68;
375
376 #[cfg(feature = "trace")]
377 fn raw_value(value: &JsValue) -> String {
378 match value {
379 v if v.is_callable() => "func".to_string(),
380 v if v.is_object() => "obj".to_string(),
381 v if v.is_undefined() => "und".to_string(),
382 v if v.is_null() => "null".to_string(),
383 v => v.display().to_string(),
384 }
385 }
386
387 #[cfg(feature = "trace")]
388 fn truncate_display(val: &str) -> String {
389 if val.len() <= Self::MAX_VALUE_LEN {
390 return val.to_string();
391 }
392 let mut end = Self::MAX_VALUE_LEN - 2;
393 while !val.is_char_boundary(end) && end > 0 {
394 end -= 1;
395 }
396 format!("{}..", &val[..end])
397 }
398
399 #[cfg(feature = "trace")]
400 fn display_trace(&self, frame: &CallFrame, frame_count: usize) -> String {
401 let total = self.stack.len();
402 if total == 0 {
403 return "[ <empty> ]".to_string();
404 }
405
406 let mut groups: Vec<(String, usize, Option<usize>)> = Vec::new();
407 let mut force_truncate = false;
408
409 for (idx, v) in self.stack.iter().enumerate().rev() {
411 let is_frame = frame.frame_pointer() == idx;
412 let raw = Self::raw_value(v);
413
414 if !is_frame
415 && let Some(last) = groups.last_mut()
416 && last.0 == raw
417 && last.2.is_none()
418 {
419 last.1 += 1;
420 } else {
421 let marker = if is_frame { Some(frame_count) } else { None };
422 groups.push((raw, 1, marker));
423
424 if groups.len() > Self::MAX_STACK_WIDTH / 2 {
427 force_truncate = true;
428 break;
429 }
430 }
431 }
432
433 let mut string = String::from("[ ");
434 let mut truncated = force_truncate;
435 let suffix = format!(".. ({total} total) ]");
436
437 for (i, (val, count, marker)) in groups.iter().enumerate() {
438 let display_val = Self::truncate_display(val);
439 let part = if *count > 1 {
440 format!("{display_val} (x{count})")
441 } else {
442 display_val
443 };
444
445 let separator = if let Some(fc) = marker {
446 format!(" |{fc}|")
447 } else if i + 1 < groups.len() {
448 ",".to_string()
449 } else {
450 String::new()
451 };
452
453 let addition = format!("{part}{separator} ");
454 if string.len() + addition.len() + suffix.len() > Self::MAX_STACK_WIDTH {
455 truncated = true;
456 break;
457 }
458 string.push_str(&addition);
459 }
460
461 if truncated {
462 string.push_str(&suffix);
463 } else {
464 string.push(']');
465 }
466 string
467 }
468}
469
470impl Extend<JsValue> for Stack {
471 #[inline]
472 fn extend<T: IntoIterator<Item = JsValue>>(&mut self, values: T) {
473 self.stack.extend(values);
474 }
475}
476
477#[derive(Debug, Clone, Finalize)]
479pub enum ActiveRunnable {
480 Script(Script),
482 Module(Module),
484}
485
486unsafe impl Trace for ActiveRunnable {
487 custom_trace!(this, mark, {
488 match this {
489 Self::Script(script) => mark(script),
490 Self::Module(module) => mark(module),
491 }
492 });
493}
494
495impl ActiveRunnable {
496 #[must_use]
498 pub fn path(&self) -> Option<&Path> {
499 match self {
500 Self::Script(script) => script.path(),
501 Self::Module(module) => module.path(),
502 }
503 }
504}
505
506impl Vm {
507 pub(crate) fn new(realm: Realm) -> Self {
509 let mut frames = Vec::with_capacity(16);
510 frames.push(CallFrame::new(
511 Gc::new(CodeBlock::new(JsString::default(), 0, true)),
512 None,
513 EnvironmentStack::new(),
514 realm,
515 ));
516 Self {
517 frames,
518 stack: Stack::new(1024),
519 return_value: JsValue::undefined(),
520 pending_exception: None,
521 runtime_limits: RuntimeLimits::default(),
522 interrupt_flag: None,
523 execution_budget: std::sync::Arc::new(execution_budget::ExecutionBudget::default()),
524 native_active_function: None,
525 native_constructor_receiver: None,
526 host_call_depth: 0,
527 #[cfg(feature = "jit")]
528 jit_depth: 0,
529 #[cfg(feature = "jit")]
530 jit_break: None,
531 #[cfg(feature = "jit")]
532 jit_frame: boa_jit::baseline::JitFrame {
533 regs: std::ptr::null_mut(),
534 argc: 0,
535 stack: std::ptr::null_mut(),
536 depth: 0,
537 },
538 shadow_stack: ShadowStack::default(),
539 #[cfg(feature = "trace")]
540 trace: false,
541 #[cfg(feature = "trace")]
542 current_frame: None,
543 }
544 }
545
546 #[track_caller]
547 #[inline]
548 pub(crate) fn set_register(&mut self, index: usize, value: JsValue) {
549 let rp = self.frame().rp as usize;
550 debug_assert!(
551 rp + index < self.stack.stack.len(),
552 "register index out of bounds: rp {rp}, index {index}, stack len {}",
553 self.stack.stack.len()
554 );
555 let old =
559 unsafe { std::mem::replace(self.stack.stack.get_unchecked_mut(rp + index), value) };
560 unsafe { old.release_outside_collection() };
562 }
563
564 #[track_caller]
565 #[inline]
566 pub(crate) fn get_register(&self, index: usize) -> &JsValue {
567 let rp = self.frame().rp as usize;
568 debug_assert!(
569 rp + index < self.stack.stack.len(),
570 "register index out of bounds: rp {rp}, index {index}, stack len {}",
571 self.stack.stack.len()
572 );
573 unsafe { self.stack.stack.get_unchecked(rp + index) }
577 }
578
579 #[track_caller]
584 #[inline]
585 pub(crate) fn take_register(&mut self, index: usize) -> JsValue {
586 let rp = self.frame().rp as usize;
587 debug_assert!(
588 rp + index < self.stack.stack.len(),
589 "register index out of bounds: rp {rp}, index {index}, stack len {}",
590 self.stack.stack.len()
591 );
592 unsafe { std::mem::take(self.stack.stack.get_unchecked_mut(rp + index)) }
596 }
597
598 #[track_caller]
600 pub(crate) fn set_promise_capability(
601 &mut self,
602 promise_capability: PromiseCapability,
603 ) -> JsResult<()> {
604 #[cfg(debug_assertions)]
605 {
606 if !self.frame().code_block().is_async() {
607 return Err(crate::error::PanicError::new(
608 "only async functions and modules with a top-level-await \
609 can have a promise capability",
610 )
611 .into());
612 }
613 }
614
615 let rp = self.frame().rp as usize;
616 self.stack.stack[rp + CallFrame::PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX] =
617 promise_capability.promise.into();
618 self.stack.stack[rp + CallFrame::PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX] =
619 promise_capability.functions.resolve.into();
620 self.stack.stack[rp + CallFrame::PROMISE_CAPABILITY_REJECT_REGISTER_INDEX] =
621 promise_capability.functions.reject.into();
622
623 Ok(())
624 }
625
626 #[track_caller]
628 pub(crate) fn get_promise_capability(&self) -> JsResult<PromiseCapability> {
629 #[cfg(debug_assertions)]
630 if !self.frame().code_block().is_async() {
631 return Err(crate::error::PanicError::new(
632 "cannot get promise capability from non-async code",
633 )
634 .into());
635 }
636
637 let rp = self.frame().rp as usize;
638 let promise = self
639 .stack
640 .stack
641 .get(rp + CallFrame::PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX)
642 .and_then(JsValue::as_object)
643 .js_expect("registers must have a promise capability")?;
644 let resolve = self
645 .stack
646 .stack
647 .get(rp + CallFrame::PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX)
648 .and_then(JsValue::as_object)
649 .and_then(JsFunction::from_object)
650 .js_expect("registers must have a resolve function")?;
651 let reject = self
652 .stack
653 .stack
654 .get(rp + CallFrame::PROMISE_CAPABILITY_REJECT_REGISTER_INDEX)
655 .and_then(JsValue::as_object)
656 .and_then(JsFunction::from_object)
657 .js_expect("registers must have a reject function")?;
658
659 Ok(PromiseCapability {
660 promise,
661 functions: ResolvingFunctions { resolve, reject },
662 })
663 }
664
665 #[track_caller]
667 pub(crate) fn async_generator_object(&self) -> Option<JsObject> {
668 if !self.frame().code_block().is_async_generator() {
669 return None;
670 }
671
672 let rp = self.frame().rp as usize;
673 self.stack
674 .stack
675 .get(rp + CallFrame::ASYNC_GENERATOR_OBJECT_REGISTER_INDEX)
676 .expect("registers must have an async generator object")
677 .as_object()
678 }
679
680 #[track_caller]
686 #[inline]
687 pub(crate) fn frame(&self) -> &CallFrame {
688 unsafe { self.frames.last().unwrap_unchecked() }
690 }
691
692 #[track_caller]
698 #[inline]
699 pub(crate) fn frame_mut(&mut self) -> &mut CallFrame {
700 unsafe { self.frames.last_mut().unwrap_unchecked() }
702 }
703
704 pub(crate) fn push_frame(&mut self, mut frame: CallFrame) {
705 self.set_return_value(JsValue::undefined());
707
708 if !frame.registers_already_pushed() {
712 let current_stack_length = self.stack.stack.len() as u32;
713 frame.fp = current_stack_length - frame.argument_count - CallFrame::FUNCTION_PROLOGUE;
714
715 let register_count = frame.code_block.register_count as usize;
716 frame.rp = self.stack.stack.len() as u32;
717 self.stack.stack.resize(
718 self.stack.stack.len() + register_count,
719 JsValue::undefined(),
720 );
721 }
722
723 if frame.active_runnable.is_none() {
726 let current = self.frame();
727 frame.active_runnable.clone_from(¤t.active_runnable);
728 }
729
730 let current_pc = self.frame().pc;
731 self.shadow_stack
732 .push_bytecode(current_pc, frame.code_block().source_info.clone());
733
734 self.frames.push(frame);
735 }
736
737 pub(crate) fn push_frame_with_stack(
738 &mut self,
739 frame: CallFrame,
740 this: JsValue,
741 function: JsValue,
742 ) {
743 self.stack.push(this);
744 self.stack.push(function);
745
746 self.push_frame(frame);
747 }
748
749 pub(crate) fn pop_frame(&mut self) -> Option<CallFrame> {
750 if self.frames.len() <= 1 {
752 return None;
753 }
754 self.shadow_stack.pop();
755 self.frames.pop()
756 }
757
758 #[inline]
762 pub(crate) fn handle_exception_at(&mut self, pc: u32) -> bool {
763 let frame = self.frame_mut();
764 let Some((_, handler)) = frame.code_block().find_handler(pc) else {
765 return false;
766 };
767
768 let catch_address = handler.handler();
769 let environment_sp = frame.env_fp + handler.environment_count;
770
771 frame.pc = u32::from(catch_address);
773
774 self.frame_mut()
775 .environments
776 .truncate(environment_sp as usize);
777
778 true
779 }
780
781 pub(crate) fn get_return_value(&self) -> JsValue {
782 self.return_value.clone()
783 }
784
785 #[inline]
786 pub(crate) fn set_return_value(&mut self, value: JsValue) {
787 let old = std::mem::replace(&mut self.return_value, value);
788 unsafe { old.release_outside_collection() };
790 }
791
792 pub(crate) fn take_return_value(&mut self) -> JsValue {
793 std::mem::take(&mut self.return_value)
794 }
795}
796
797#[allow(clippy::print_stdout)]
798#[cfg(feature = "trace")]
799impl Context {
800 const COLUMN_WIDTH: usize = 26;
801 const TIME_COLUMN_WIDTH: usize = Self::COLUMN_WIDTH / 2;
802 const OPCODE_COLUMN_WIDTH: usize = Self::COLUMN_WIDTH;
803 const OPERAND_COLUMN_WIDTH: usize = Self::COLUMN_WIDTH;
804 const NUMBER_OF_COLUMNS: usize = 4;
805
806 pub(crate) fn trace_call_frame(&self) {
807 let frame = self.vm.frame();
808 let msg = if self.vm.frames.is_empty() {
809 " VM Start ".to_string()
810 } else {
811 format!(
812 " Call Frame '{}'{} ",
813 frame.code_block().name().to_std_string_escaped(),
814 if frame.code_block().name().is_empty() {
815 format!(" [anon#{}]", frame.code_block().debug_id)
816 } else {
817 String::new()
818 }
819 )
820 };
821
822 if !frame.code_block.traced.get() {
824 println!("{}", frame.code_block);
825 frame.code_block.traced.set(true);
826 }
827 println!(
828 "{msg:-^width$}",
829 width = Self::COLUMN_WIDTH * Self::NUMBER_OF_COLUMNS - 10
830 );
831 println!(
832 "{:<TIME_COLUMN_WIDTH$} {:<OPCODE_COLUMN_WIDTH$} {:<OPERAND_COLUMN_WIDTH$} Stack\n",
833 "Time",
834 "Opcode",
835 "Operands",
836 TIME_COLUMN_WIDTH = Self::TIME_COLUMN_WIDTH,
837 OPCODE_COLUMN_WIDTH = Self::OPCODE_COLUMN_WIDTH,
838 OPERAND_COLUMN_WIDTH = Self::OPERAND_COLUMN_WIDTH,
839 );
840 }
841
842 fn trace_execute_instruction<F>(
843 &mut self,
844 f: F,
845 opcode: Opcode,
846 ) -> ControlFlow<CompletionRecord>
847 where
848 F: FnOnce(&mut Context, Opcode) -> ControlFlow<CompletionRecord>,
849 {
850 if self.vm.current_frame != Some(self.vm.frame()) {
851 println!();
852 self.trace_call_frame();
853 self.vm.current_frame = Some(self.vm.frame());
854 }
855 let frame = self.vm.frame();
856 let (instruction, _) = frame
857 .code_block
858 .bytecode
859 .next_instruction(frame.pc as usize);
860 let operands = self
861 .vm
862 .frame()
863 .code_block()
864 .instruction_operands(&instruction);
865
866 let instant = Instant::now();
867 let result = self.execute_instruction(f, opcode);
868 let duration = instant.elapsed();
869
870 let stack = self
871 .vm
872 .stack
873 .display_trace(self.vm.frame(), self.vm.frames.len() - 1);
874
875 println!(
876 "{:<TIME_COLUMN_WIDTH$} {:<OPCODE_COLUMN_WIDTH$} {operands:<OPERAND_COLUMN_WIDTH$} {stack}",
877 format!("{}μs", duration.as_micros()),
878 opcode.as_str(),
879 TIME_COLUMN_WIDTH = Self::TIME_COLUMN_WIDTH,
880 OPCODE_COLUMN_WIDTH = Self::OPCODE_COLUMN_WIDTH,
881 OPERAND_COLUMN_WIDTH = Self::OPERAND_COLUMN_WIDTH,
882 );
883
884 result
885 }
886}
887
888impl Context {
889 pub fn set_interrupt_flag(&mut self, flag: std::sync::Arc<std::sync::atomic::AtomicBool>) {
894 self.vm.interrupt_flag = Some(flag);
895 }
896
897 pub fn set_execution_time_limit(&mut self, duration: Option<std::time::Duration>) {
903 self.vm.execution_budget.set_duration(duration);
904 }
905
906 pub fn enter_execution_time_limit(&self) -> ExecutionScope {
911 self.vm.execution_budget.begin()
912 }
913
914 pub(crate) fn execution_time_limit_exceeded(&self) -> bool {
915 self.vm.execution_budget.expired()
916 }
917
918 fn execute_instruction<F>(&mut self, f: F, opcode: Opcode) -> ControlFlow<CompletionRecord>
919 where
920 F: FnOnce(&mut Context, Opcode) -> ControlFlow<CompletionRecord>,
921 {
922 f(self, opcode)
923 }
924
925 fn execute_one<F>(&mut self, f: F, opcode: Opcode) -> ControlFlow<CompletionRecord>
926 where
927 F: FnOnce(&mut Context, Opcode) -> ControlFlow<CompletionRecord>,
928 {
929 if self
930 .vm
931 .interrupt_flag
932 .as_ref()
933 .is_some_and(|flag| flag.load(std::sync::atomic::Ordering::Acquire))
934 || self.vm.execution_budget.should_interrupt()
935 {
936 return self.handle_error(RuntimeLimitError::Interrupted.into());
937 }
938
939 #[cfg(feature = "fuzz")]
940 {
941 use crate::error::EngineError;
942 if self.instructions_remaining == 0 {
943 return ControlFlow::Break(CompletionRecord::Throw(
944 EngineError::NoInstructionsRemain.into(),
945 ));
946 }
947 self.instructions_remaining -= 1;
948 }
949
950 #[cfg(feature = "trace")]
951 if self.vm.trace || self.vm.frame().code_block.traceable() {
952 self.trace_execute_instruction(f, opcode)
953 } else {
954 self.execute_instruction(f, opcode)
955 }
956
957 #[cfg(not(feature = "trace"))]
958 self.execute_instruction(f, opcode)
959 }
960
961 fn handle_error(&mut self, mut err: JsError) -> ControlFlow<CompletionRecord> {
962 if err.backtrace.is_none() {
966 err.backtrace = Some(
967 self.vm
968 .shadow_stack
969 .take(self.vm.runtime_limits.backtrace_limit(), self.vm.frame().pc),
970 );
971 }
972
973 if !err.is_catchable() {
976 let mut frame = None;
977 let mut env_fp = self.vm.frame().environments.len();
978 loop {
979 if self.vm.frame().exit_early() {
980 break;
981 }
982
983 env_fp = self.vm.frame().env_fp as usize;
984
985 let Some(f) = self.vm.pop_frame() else {
986 break;
987 };
988 frame = Some(f);
989 }
990 self.vm.frame_mut().environments.truncate(env_fp);
991 if let Some(frame) = frame {
992 self.vm.stack.truncate_to_frame(&frame);
993 }
994 return ControlFlow::Break(CompletionRecord::Throw(err));
995 }
996
997 if let Some(native) = err.as_native_mut()
998 && let ErrorStack::Position(position) = &mut native.stack.0
999 {
1000 let backtrace = self.vm.shadow_stack.take_and_push(
1001 self.vm.runtime_limits.backtrace_limit(),
1002 self.vm.frame().pc,
1003 position.clone(),
1004 );
1005 native.stack.0 = ErrorStack::Backtrace(backtrace);
1006 }
1007
1008 let pc = self.vm.frame().pc.saturating_sub(1);
1010 if self.vm.handle_exception_at(pc) {
1011 self.vm.pending_exception = Some(err);
1012 return ControlFlow::Continue(());
1013 }
1014
1015 let err = err.inject_realm(self.realm().clone());
1017
1018 self.vm.pending_exception = Some(err);
1019 self.handle_throw()
1020 }
1021
1022 #[inline]
1023 fn handle_return(&mut self) -> ControlFlow<CompletionRecord> {
1024 let exit_early = self.vm.frame().exit_early();
1025 let frame = self.vm.frames.last().expect("frame must exist");
1026 self.vm.stack.truncate_to_frame(frame);
1027
1028 let result = self.vm.take_return_value();
1029 if exit_early {
1030 return ControlFlow::Break(CompletionRecord::Return(result));
1031 }
1032
1033 self.vm.stack.push(result);
1034 self.vm.pop_frame().expect("frame must exist");
1035 ControlFlow::Continue(())
1036 }
1037
1038 fn handle_yield(&mut self) -> ControlFlow<CompletionRecord> {
1039 let result = self.vm.take_return_value();
1040 if self.vm.frame().exit_early() {
1041 return ControlFlow::Break(CompletionRecord::Normal(result));
1042 }
1043
1044 self.vm.stack.push(result);
1045 self.vm.pop_frame().expect("frame must exist");
1046 ControlFlow::Continue(())
1047 }
1048
1049 fn handle_throw(&mut self) -> ControlFlow<CompletionRecord> {
1050 if self
1051 .vm
1052 .pending_exception
1053 .as_ref()
1054 .is_some_and(|err| err.backtrace.is_none())
1055 {
1056 let pc = self.vm.frames.last().expect("frame must exist").pc;
1057 let limit = self.vm.runtime_limits.backtrace_limit();
1058 let backtrace = self.vm.shadow_stack.take(limit, pc);
1059 self.vm
1060 .pending_exception
1061 .as_mut()
1062 .expect("pending exception must exist")
1063 .backtrace = Some(backtrace);
1064 }
1065
1066 let mut env_fp = self.vm.frame().env_fp;
1067 if self.vm.frame().exit_early() {
1068 self.vm.frame_mut().environments.truncate(env_fp as usize);
1069 let frame = self.vm.frames.last().expect("frame must exist");
1070 self.vm.stack.truncate_to_frame(frame);
1071 return ControlFlow::Break(CompletionRecord::Throw(
1072 self.vm
1073 .pending_exception
1074 .take()
1075 .expect("Err must exist for a CompletionType::Throw"),
1076 ));
1077 }
1078
1079 let mut frame = self.vm.pop_frame().expect("frame must exist");
1080
1081 loop {
1082 env_fp = self.vm.frame().env_fp;
1083 let pc = self.vm.frame().pc;
1084 let exit_early = self.vm.frame().exit_early();
1085
1086 if self.vm.handle_exception_at(pc) {
1087 return ControlFlow::Continue(());
1088 }
1089
1090 if exit_early {
1091 return ControlFlow::Break(CompletionRecord::Throw(
1092 self.vm
1093 .pending_exception
1094 .take()
1095 .expect("Err must exist for a CompletionType::Throw"),
1096 ));
1097 }
1098
1099 let Some(f) = self.vm.pop_frame() else {
1100 break;
1101 };
1102 frame = f;
1103 }
1104 self.vm.frame_mut().environments.truncate(env_fp as usize);
1105 self.vm.stack.truncate_to_frame(&frame);
1106 ControlFlow::Continue(())
1107 }
1108
1109 #[allow(clippy::future_not_send)]
1112 #[cfg_attr(not(feature = "jit"), allow(clippy::while_let_loop))]
1113 pub(crate) async fn run_async_with_budget(&mut self, budget: u32) -> CompletionRecord {
1114 let _execution = self.enter_execution_time_limit();
1115 let mut runtime_budget: u32 = budget;
1116
1117 loop {
1118 let Some(byte) = self
1119 .vm
1120 .frame()
1121 .code_block
1122 .bytecode
1123 .bytes
1124 .get(self.vm.frame().pc as usize)
1125 else {
1126 #[cfg(feature = "jit")]
1127 if self.vm.frame().pc == jit::ENTRY {
1128 match jit::run_frame(self) {
1129 ControlFlow::Continue(()) => continue,
1130 ControlFlow::Break(value) => return value,
1131 }
1132 }
1133 break;
1134 };
1135 let opcode = Opcode::decode(*byte);
1136
1137 match self.execute_one(
1138 |context, opcode| {
1139 let frame = context.vm.frame();
1140 let pc = frame.pc as usize;
1141
1142 OPCODE_HANDLERS_BUDGET[opcode as usize](context, pc, &mut runtime_budget)
1143 },
1144 opcode,
1145 ) {
1146 ControlFlow::Continue(()) => {}
1147 ControlFlow::Break(value) => return value,
1148 }
1149
1150 if runtime_budget == 0 {
1151 runtime_budget = budget;
1152 yield_now().await;
1153 }
1154 }
1155
1156 CompletionRecord::Throw(JsError::from_native(JsNativeError::error()))
1157 }
1158
1159 #[cfg_attr(not(feature = "jit"), allow(clippy::while_let_loop))]
1160 pub(crate) fn run(&mut self) -> CompletionRecord {
1161 let _execution = self.enter_execution_time_limit();
1162 loop {
1163 let Some(byte) = self
1164 .vm
1165 .frame()
1166 .code_block
1167 .bytecode
1168 .bytes
1169 .get(self.vm.frame().pc as usize)
1170 else {
1171 #[cfg(feature = "jit")]
1172 if self.vm.frame().pc == jit::ENTRY {
1173 match jit::run_frame(self) {
1174 ControlFlow::Continue(()) => continue,
1175 ControlFlow::Break(value) => return value,
1176 }
1177 }
1178 break;
1179 };
1180 let opcode = Opcode::decode(*byte);
1181
1182 match self.execute_one(
1183 |context, opcode| {
1184 let frame = context.vm.frame();
1185 let pc = frame.pc as usize;
1186
1187 OPCODE_HANDLERS[opcode as usize](context, pc)
1188 },
1189 opcode,
1190 ) {
1191 ControlFlow::Continue(()) => {}
1192 ControlFlow::Break(value) => return value,
1193 }
1194 }
1195
1196 CompletionRecord::Throw(JsError::from_native(JsNativeError::error()))
1197 }
1198
1199 #[cfg(feature = "jit")]
1202 pub(crate) fn run_nested(&mut self, depth: usize) -> ControlFlow<CompletionRecord> {
1203 while self.vm.frames.len() > depth {
1204 let frame = self.vm.frame();
1205 let pc = frame.pc;
1206 let Some(&byte) = frame.code_block.bytecode.bytes.get(pc as usize) else {
1207 if pc == jit::ENTRY {
1208 jit::run_frame(self)?;
1209 continue;
1210 }
1211 return ControlFlow::Break(CompletionRecord::Throw(JsError::from_native(
1212 JsNativeError::error(),
1213 )));
1214 };
1215 let opcode = Opcode::decode(byte);
1216 self.execute_one(
1217 |context, opcode| OPCODE_HANDLERS[opcode as usize](context, pc as usize),
1218 opcode,
1219 )?;
1220 }
1221 ControlFlow::Continue(())
1222 }
1223
1224 pub(crate) fn check_runtime_limits(&self) -> JsResult<()> {
1226 let js_depth = self.vm.frames.len().saturating_sub(1);
1232 if self.vm.runtime_limits.recursion_limit() <= js_depth
1233 || self.vm.runtime_limits.host_recursion_limit() <= self.vm.host_call_depth
1234 {
1235 return Err(JsNativeError::range()
1236 .with_message("Maximum call stack size exceeded")
1237 .into());
1238 }
1239 if self.vm.runtime_limits.stack_size_limit() <= self.vm.stack.stack.len() {
1243 return Err(JsNativeError::range()
1244 .with_message("Maximum call stack size exceeded")
1245 .into());
1246 }
1247
1248 Ok(())
1249 }
1250}
1251
1252fn yield_now() -> impl Future<Output = ()> {
1254 struct YieldNow(bool);
1255
1256 impl Future for YieldNow {
1257 type Output = ();
1258
1259 fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
1260 if self.0 {
1261 task::Poll::Ready(())
1262 } else {
1263 self.0 = true;
1264 cx.waker().wake_by_ref();
1265 task::Poll::Pending
1266 }
1267 }
1268 }
1269
1270 YieldNow(false)
1271}