use super::ActiveRunnable;
use crate::{
JsValue, builtins::iterable::IteratorRecord, environments::EnvironmentStack, realm::Realm,
vm::CodeBlock, vm::SourcePath,
};
use boa_ast::Position;
use boa_ast::scope::BindingLocator;
use boa_gc::{Finalize, Gc, Trace};
use boa_string::JsString;
use thin_vec::ThinVec;
bitflags::bitflags! {
#[derive(Debug, Default, Clone, Copy)]
pub(crate) struct CallFrameFlags: u8 {
const EXIT_EARLY = 0b0000_0001;
const CONSTRUCT = 0b0000_0010;
const REGISTERS_ALREADY_PUSHED = 0b0000_0100;
const THIS_VALUE_CACHED = 0b0000_1000;
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct CallFrameLocation {
pub function_name: JsString,
pub path: SourcePath,
pub position: Option<Position>,
}
#[derive(Clone, Debug, Finalize, Trace)]
pub struct CallFrame {
pub(crate) code_block: Gc<CodeBlock>,
pub(crate) pc: u32,
pub(crate) rp: u32,
pub(crate) argument_count: u32,
pub(crate) env_fp: u32,
pub(crate) iterators: ThinVec<IteratorRecord>,
#[unsafe_ignore_trace]
pub(crate) binding_stack: Vec<BindingLocator>,
pub(crate) loop_iteration_count: u64,
pub(crate) active_runnable: Option<ActiveRunnable>,
pub(crate) environments: EnvironmentStack,
pub(crate) realm: Realm,
#[unsafe_ignore_trace]
pub(crate) flags: CallFrameFlags,
}
impl CallFrame {
#[inline]
#[must_use]
pub const fn code_block(&self) -> &Gc<CodeBlock> {
&self.code_block
}
#[inline]
#[must_use]
pub fn position(&self) -> CallFrameLocation {
let source_info = &self.code_block.source_info;
CallFrameLocation {
function_name: source_info.function_name().clone(),
path: source_info.map().path().clone(),
position: source_info.map().find(self.pc),
}
}
}
impl CallFrame {
pub(crate) const FUNCTION_PROLOGUE: u32 = 2;
const THIS_POSITION: usize = 2;
const FUNCTION_POSITION: usize = 1;
pub(crate) const PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX: usize = 0;
pub(crate) const PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX: usize = 1;
pub(crate) const PROMISE_CAPABILITY_REJECT_REGISTER_INDEX: usize = 2;
pub(crate) const ASYNC_GENERATOR_OBJECT_REGISTER_INDEX: usize = 3;
pub(crate) fn new(
code_block: Gc<CodeBlock>,
active_runnable: Option<ActiveRunnable>,
environments: EnvironmentStack,
realm: Realm,
) -> Self {
Self {
pc: 0,
rp: 0,
env_fp: 0,
argument_count: 0,
iterators: ThinVec::new(),
binding_stack: Vec::new(),
code_block,
loop_iteration_count: 0,
active_runnable,
environments,
realm,
flags: CallFrameFlags::empty(),
}
}
pub(crate) fn with_argument_count(mut self, count: u32) -> Self {
self.argument_count = count;
self
}
pub(crate) fn with_env_fp(mut self, env_fp: u32) -> Self {
self.env_fp = env_fp;
self
}
pub(crate) fn with_flags(mut self, flags: CallFrameFlags) -> Self {
self.flags = flags;
self
}
pub(crate) fn this_index(&self) -> usize {
self.rp as usize - self.argument_count as usize - Self::THIS_POSITION
}
pub(crate) fn function_index(&self) -> usize {
self.rp as usize - self.argument_count as usize - Self::FUNCTION_POSITION
}
pub(crate) fn promise_capability_promise_register_index(&self) -> usize {
self.rp as usize + Self::PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX
}
pub(crate) fn promise_capability_resolve_register_index(&self) -> usize {
self.rp as usize + Self::PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX
}
pub(crate) fn promise_capability_reject_register_index(&self) -> usize {
self.rp as usize + Self::PROMISE_CAPABILITY_REJECT_REGISTER_INDEX
}
pub(crate) fn async_generator_object_register_index(&self) -> usize {
self.rp as usize + Self::ASYNC_GENERATOR_OBJECT_REGISTER_INDEX
}
pub(crate) fn arguments_range(&self) -> std::ops::Range<usize> {
(self.rp as usize - self.argument_count as usize)..self.rp as usize
}
pub(crate) fn frame_pointer(&self) -> usize {
(self.rp - self.argument_count - Self::FUNCTION_PROLOGUE) as usize
}
pub(crate) fn exit_early(&self) -> bool {
self.flags.contains(CallFrameFlags::EXIT_EARLY)
}
pub(crate) fn set_exit_early(&mut self, early_exit: bool) {
self.flags.set(CallFrameFlags::EXIT_EARLY, early_exit);
}
pub(crate) fn construct(&self) -> bool {
self.flags.contains(CallFrameFlags::CONSTRUCT)
}
pub(crate) fn registers_already_pushed(&self) -> bool {
self.flags
.contains(CallFrameFlags::REGISTERS_ALREADY_PUSHED)
}
pub(crate) fn has_this_value_cached(&self) -> bool {
self.flags.contains(CallFrameFlags::THIS_VALUE_CACHED)
}
}
impl CallFrame {
pub(crate) fn set_register_pointer(&mut self, pointer: u32) {
self.rp = pointer;
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
#[repr(u8)]
#[allow(missing_docs)]
pub enum GeneratorResumeKind {
#[default]
Normal = 0,
Throw,
Return,
}
impl From<GeneratorResumeKind> for JsValue {
fn from(value: GeneratorResumeKind) -> Self {
Self::new(value as u8)
}
}
impl JsValue {
#[track_caller]
pub(crate) fn to_generator_resume_kind(&self) -> GeneratorResumeKind {
if let Some(value) = self.as_i32() {
match value {
0 => return GeneratorResumeKind::Normal,
1 => return GeneratorResumeKind::Throw,
2 => return GeneratorResumeKind::Return,
_ => unreachable!("generator kind must be a integer between 1..=2, got {value}"),
}
}
unreachable!("generator kind must be a integer type")
}
}