boa_engine/vm/call_frame/
mod.rs1use super::ActiveRunnable;
6use crate::{
7 JsValue,
8 builtins::iterable::IteratorRecord,
9 bytecompiler::Register,
10 environments::EnvironmentStack,
11 realm::Realm,
12 vm::{CodeBlock, SourcePath},
13};
14use boa_ast::Position;
15use boa_ast::scope::BindingLocator;
16use boa_gc::{Finalize, Gc, Trace};
17use boa_string::JsString;
18use thin_vec::ThinVec;
19
20bitflags::bitflags! {
21 #[derive(Debug, Default, Clone, Copy)]
23 pub(crate) struct CallFrameFlags: u8 {
24 const EXIT_EARLY = 0b0000_0001;
27
28 const CONSTRUCT = 0b0000_0010;
30
31 const REGISTERS_ALREADY_PUSHED = 0b0000_0100;
33
34 const THIS_VALUE_CACHED = 0b0000_1000;
36 }
37}
38
39#[derive(Debug, Clone, Eq, PartialEq)]
40pub struct CallFrameLocation {
41 pub function_name: JsString,
42 pub path: SourcePath,
43 pub position: Option<Position>,
44}
45
46#[derive(Clone, Debug, Finalize, Trace)]
48pub struct CallFrame {
49 pub(crate) code_block: Gc<CodeBlock>,
50 pub(crate) pc: u32,
51 pub(crate) fp: u32,
54 pub(crate) argument_count: u32,
55 pub(crate) env_fp: u32,
56
57 pub(crate) rp: u32,
59
60 pub(crate) iterators: ThinVec<IteratorRecord>,
62
63 #[unsafe_ignore_trace]
66 pub(crate) binding_stack: ThinVec<BindingLocator>,
67
68 pub(crate) loop_iteration_count: u64,
70
71 pub(crate) active_runnable: Option<ActiveRunnable>,
73
74 pub(crate) environments: EnvironmentStack,
76
77 pub(crate) realm: Realm,
79
80 #[unsafe_ignore_trace]
82 pub(crate) flags: CallFrameFlags,
83}
84
85impl CallFrame {
87 #[inline]
89 #[must_use]
90 pub const fn code_block(&self) -> &Gc<CodeBlock> {
91 &self.code_block
92 }
93
94 #[inline]
97 #[must_use]
98 pub fn position(&self) -> CallFrameLocation {
99 let source_info = &self.code_block.source_info;
100 CallFrameLocation {
101 function_name: source_info.function_name().clone(),
102 path: source_info.map().path().clone(),
103 position: source_info.map().find(self.pc),
104 }
105 }
106}
107
108impl CallFrame {
110 pub(crate) const FUNCTION_PROLOGUE: u32 = 2;
111 pub(crate) const UNDEFINED_REGISTER_INDEX: usize = 0;
112 pub(crate) const PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX: usize = 1;
113 pub(crate) const PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX: usize = 2;
114 pub(crate) const PROMISE_CAPABILITY_REJECT_REGISTER_INDEX: usize = 3;
115 pub(crate) const ASYNC_GENERATOR_OBJECT_REGISTER_INDEX: usize = 4;
116
117 pub(crate) fn undefined_register() -> Register {
118 Register::persistent(Self::UNDEFINED_REGISTER_INDEX as u32)
119 }
120
121 pub(crate) fn promise_capability_promise_register() -> Register {
122 Register::persistent(Self::PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX as u32)
123 }
124
125 pub(crate) fn promise_capability_resolve_register() -> Register {
126 Register::persistent(Self::PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX as u32)
127 }
128
129 pub(crate) fn promise_capability_reject_register() -> Register {
130 Register::persistent(Self::PROMISE_CAPABILITY_REJECT_REGISTER_INDEX as u32)
131 }
132
133 pub(crate) fn async_generator_object_register() -> Register {
134 Register::persistent(Self::ASYNC_GENERATOR_OBJECT_REGISTER_INDEX as u32)
135 }
136
137 pub(crate) fn new(
139 code_block: Gc<CodeBlock>,
140 active_runnable: Option<ActiveRunnable>,
141 environments: EnvironmentStack,
142 realm: Realm,
143 ) -> Self {
144 Self {
145 pc: 0,
146 fp: 0,
147 env_fp: 0,
148 argument_count: 0,
149 rp: 0,
150 iterators: ThinVec::new(),
151 binding_stack: ThinVec::new(),
152 code_block,
153 loop_iteration_count: 0,
154 active_runnable,
155 environments,
156 realm,
157 flags: CallFrameFlags::empty(),
158 }
159 }
160
161 pub(crate) fn with_argument_count(mut self, count: u32) -> Self {
163 self.argument_count = count;
164 self
165 }
166
167 pub(crate) fn with_env_fp(mut self, env_fp: u32) -> Self {
169 self.env_fp = env_fp;
170 self
171 }
172
173 pub(crate) fn with_flags(mut self, flags: CallFrameFlags) -> Self {
175 self.flags = flags;
176 self
177 }
178
179 pub(crate) fn this_index(&self) -> usize {
181 self.fp as usize
182 }
183
184 pub(crate) fn function_index(&self) -> usize {
186 self.fp as usize + 1
187 }
188
189 pub(crate) fn arguments_range(&self) -> std::ops::Range<usize> {
191 let start = self.fp as usize + Self::FUNCTION_PROLOGUE as usize;
192 start..start + self.argument_count as usize
193 }
194
195 pub(crate) fn frame_pointer(&self) -> usize {
197 self.fp as usize
198 }
199
200 pub(crate) fn exit_early(&self) -> bool {
202 self.flags.contains(CallFrameFlags::EXIT_EARLY)
203 }
204
205 pub(crate) fn set_exit_early(&mut self, early_exit: bool) {
207 self.flags.set(CallFrameFlags::EXIT_EARLY, early_exit);
208 }
209
210 pub(crate) fn construct(&self) -> bool {
212 self.flags.contains(CallFrameFlags::CONSTRUCT)
213 }
214
215 pub(crate) fn registers_already_pushed(&self) -> bool {
217 self.flags
218 .contains(CallFrameFlags::REGISTERS_ALREADY_PUSHED)
219 }
220
221 pub(crate) fn has_this_value_cached(&self) -> bool {
225 self.flags.contains(CallFrameFlags::THIS_VALUE_CACHED)
226 }
227}
228
229#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
231#[repr(u8)]
232#[allow(missing_docs)]
233pub enum GeneratorResumeKind {
234 #[default]
235 Normal = 0,
236 Throw,
237 Return,
238}
239
240impl From<GeneratorResumeKind> for JsValue {
241 fn from(value: GeneratorResumeKind) -> Self {
242 Self::new(value as u8)
243 }
244}
245
246impl JsValue {
247 #[track_caller]
253 pub(crate) fn to_generator_resume_kind(&self) -> GeneratorResumeKind {
254 if let Some(value) = self.as_i32() {
255 match value {
256 0 => return GeneratorResumeKind::Normal,
257 1 => return GeneratorResumeKind::Throw,
258 2 => return GeneratorResumeKind::Return,
259 _ => unreachable!("generator kind must be an integer between 1..=2, got {value}"),
260 }
261 }
262
263 unreachable!("generator kind must be an integer type")
264 }
265}