use super::Register;
use crate::{
bytecompiler::{ByteCompiler, Label},
vm::Handler,
};
use bitflags::bitflags;
use boa_interner::Sym;
use thin_vec::thin_vec;
#[derive(Debug, Clone, Copy)]
pub(crate) enum JumpRecordAction {
Transfer {
index: u32,
},
PopEnvironments { count: u32 },
CloseIterator { r#async: bool },
HandleFinally {
index: u32,
finally_throw_flag: u32,
finally_throw_index: u32,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum JumpRecordKind {
Break,
Continue,
Return { return_value_on_stack: bool },
}
#[derive(Debug, Clone)]
pub(crate) struct JumpRecord {
kind: JumpRecordKind,
label: Label,
actions: Vec<JumpRecordAction>,
}
impl JumpRecord {
pub(crate) const fn new(kind: JumpRecordKind, actions: Vec<JumpRecordAction>) -> Self {
Self {
kind,
label: ByteCompiler::DUMMY_LABEL,
actions,
}
}
pub(crate) fn perform_actions(mut self, start_address: u32, compiler: &mut ByteCompiler<'_>) {
while let Some(action) = self.actions.pop() {
match action {
JumpRecordAction::Transfer { index } => {
self.label = compiler.jump();
compiler.jump_info[index as usize].jumps.push(self);
return;
}
JumpRecordAction::PopEnvironments { count } => {
for _ in 0..count {
compiler.bytecode.emit_pop_environment();
}
}
JumpRecordAction::HandleFinally {
index: value,
finally_throw_flag,
finally_throw_index,
} => {
let index = value as i32 + 1;
compiler.bytecode.emit_push_false(finally_throw_flag.into());
compiler.emit_push_integer_with_index(index, finally_throw_index.into());
}
JumpRecordAction::CloseIterator { r#async } => {
compiler.iterator_close(r#async);
}
}
}
match self.kind {
JumpRecordKind::Break => compiler.patch_jump(self.label),
JumpRecordKind::Continue => compiler.patch_jump_with_target(self.label, start_address),
JumpRecordKind::Return {
return_value_on_stack,
} => {
if return_value_on_stack {
let value = compiler.register_allocator.alloc();
compiler.pop_into_register(&value);
compiler.bytecode.emit_set_accumulator(value.variable());
compiler.register_allocator.dealloc(value);
}
match (compiler.is_async(), compiler.is_generator()) {
(true, true) => compiler.bytecode.emit_async_generator_close(),
(true, false) => compiler.bytecode.emit_complete_promise_capability(),
(false, false) => {
compiler.bytecode.emit_check_return();
}
(false, true) => {}
}
compiler.bytecode.emit_return();
}
}
}
}
#[derive(Debug)]
pub(crate) struct JumpControlInfo {
label: Option<Sym>,
start_address: u32,
pub(crate) flags: JumpControlInfoFlags,
pub(crate) jumps: Vec<JumpRecord>,
current_open_environments_count: u32,
pub(crate) finally_throw: Option<(u32, u32)>,
}
bitflags! {
#[derive(Debug, Clone, Copy)]
pub(crate) struct JumpControlInfoFlags: u8 {
const LOOP = 0b0000_0001;
const SWITCH = 0b0000_0010;
const TRY_WITH_FINALLY = 0b0000_0100;
const IN_FINALLY = 0b0000_1000;
const LABELLED = 0b0001_0000;
const ITERATOR_LOOP = 0b0010_0000;
const FOR_AWAIT_OF_LOOP = 0b0100_0000;
const USE_EXPR = 0b1000_0000;
}
}
impl Default for JumpControlInfoFlags {
fn default() -> Self {
Self::empty()
}
}
impl JumpControlInfo {
fn new(current_open_environments_count: u32) -> Self {
Self {
label: None,
start_address: ByteCompiler::DUMMY_ADDRESS,
flags: JumpControlInfoFlags::default(),
jumps: Vec::new(),
current_open_environments_count,
finally_throw: None,
}
}
pub(crate) const fn with_label(mut self, label: Option<Sym>) -> Self {
self.label = label;
self
}
pub(crate) const fn with_start_address(mut self, address: u32) -> Self {
self.start_address = address;
self
}
pub(crate) fn with_loop_flag(mut self, value: bool) -> Self {
self.flags.set(JumpControlInfoFlags::LOOP, value);
self
}
pub(crate) fn with_switch_flag(mut self, value: bool) -> Self {
self.flags.set(JumpControlInfoFlags::SWITCH, value);
self
}
pub(crate) fn with_try_with_finally_flag(mut self, flag: &Register, index: &Register) -> Self {
self.finally_throw = Some((flag.index(), index.index()));
self
}
pub(crate) fn with_labelled_block_flag(mut self, value: bool) -> Self {
self.flags.set(JumpControlInfoFlags::LABELLED, value);
self
}
pub(crate) fn with_iterator_loop(mut self, value: bool) -> Self {
self.flags.set(JumpControlInfoFlags::ITERATOR_LOOP, value);
self
}
pub(crate) fn with_for_await_of_loop(mut self, value: bool) -> Self {
self.flags
.set(JumpControlInfoFlags::FOR_AWAIT_OF_LOOP, value);
self
}
}
impl JumpControlInfo {
pub(crate) const fn label(&self) -> Option<Sym> {
self.label
}
pub(crate) const fn start_address(&self) -> u32 {
self.start_address
}
pub(crate) const fn is_loop(&self) -> bool {
self.flags.contains(JumpControlInfoFlags::LOOP)
}
pub(crate) const fn is_switch(&self) -> bool {
self.flags.contains(JumpControlInfoFlags::SWITCH)
}
pub(crate) const fn is_try_with_finally_block(&self) -> bool {
self.finally_throw.is_some()
}
pub(crate) const fn is_labelled(&self) -> bool {
self.flags.contains(JumpControlInfoFlags::LABELLED)
}
pub(crate) const fn in_finally(&self) -> bool {
self.flags.contains(JumpControlInfoFlags::IN_FINALLY)
}
pub(crate) const fn use_expr(&self) -> bool {
self.flags.contains(JumpControlInfoFlags::USE_EXPR)
}
pub(crate) const fn iterator_loop(&self) -> bool {
self.flags.contains(JumpControlInfoFlags::ITERATOR_LOOP)
}
pub(crate) const fn for_await_of_loop(&self) -> bool {
self.flags.contains(JumpControlInfoFlags::FOR_AWAIT_OF_LOOP)
}
}
impl JumpControlInfo {
pub(crate) fn set_label(&mut self, label: Option<Sym>) {
assert!(self.label.is_none());
self.label = label;
}
pub(crate) fn set_start_address(&mut self, start_address: u32) {
self.start_address = start_address;
}
}
impl ByteCompiler<'_> {
pub(crate) fn push_empty_loop_jump_control(&mut self, use_expr: bool) {
let new_info =
JumpControlInfo::new(self.current_open_environments_count).with_loop_flag(true);
self.push_contol_info(new_info, use_expr);
}
pub(crate) fn current_jump_control_mut(&mut self) -> Option<&mut JumpControlInfo> {
self.jump_info.last_mut()
}
pub(crate) fn push_contol_info(&mut self, mut info: JumpControlInfo, use_expr: bool) {
info.flags.set(JumpControlInfoFlags::USE_EXPR, use_expr);
if let Some(last) = self.jump_info.last() {
info.flags |= last.flags & JumpControlInfoFlags::USE_EXPR;
}
self.jump_info.push(info);
}
#[must_use]
pub(crate) fn push_handler(&mut self) -> u32 {
let handler_index = self.handlers.len() as u32;
let start_address = self.next_opcode_location();
let environment_count = self.current_open_environments_count;
self.handlers.push(Handler {
start: start_address,
end: Self::DUMMY_ADDRESS,
environment_count,
});
handler_index
}
pub(crate) fn patch_handler(&mut self, handler_index: u32) {
let handler_index = handler_index as usize;
let handler_address = self.next_opcode_location();
assert_eq!(
self.handlers[handler_index].end,
Self::DUMMY_ADDRESS,
"handler already set"
);
assert!(
handler_address >= self.handlers[handler_index].start,
"handler end is before that start"
);
self.handlers[handler_index].end = handler_address;
}
pub(crate) fn jump_control_info_has_use_expr(&self) -> bool {
if let Some(last) = self.jump_info.last() {
return last.use_expr();
}
false
}
pub(crate) fn push_labelled_control_info(
&mut self,
label: Sym,
start_address: u32,
use_expr: bool,
) {
let new_info = JumpControlInfo::new(self.current_open_environments_count)
.with_labelled_block_flag(true)
.with_label(Some(label))
.with_start_address(start_address);
self.push_contol_info(new_info, use_expr);
}
pub(crate) fn pop_labelled_control_info(&mut self) {
assert!(!self.jump_info.is_empty());
let info = self.jump_info.pop().expect("no jump information found");
assert!(info.is_labelled());
assert!(info.label().is_some());
let start_address = info.start_address();
for jump_record in info.jumps {
jump_record.perform_actions(start_address, self);
}
}
pub(crate) fn push_loop_control_info(
&mut self,
label: Option<Sym>,
start_address: u32,
use_expr: bool,
) {
let new_info = JumpControlInfo::new(self.current_open_environments_count)
.with_loop_flag(true)
.with_label(label)
.with_start_address(start_address);
self.push_contol_info(new_info, use_expr);
}
pub(crate) fn push_loop_control_info_for_of_in_loop(
&mut self,
label: Option<Sym>,
start_address: u32,
use_expr: bool,
) {
let new_info = JumpControlInfo::new(self.current_open_environments_count)
.with_loop_flag(true)
.with_label(label)
.with_start_address(start_address)
.with_iterator_loop(true);
self.push_contol_info(new_info, use_expr);
}
pub(crate) fn push_loop_control_info_for_await_of_loop(
&mut self,
label: Option<Sym>,
start_address: u32,
use_expr: bool,
) {
let new_info = JumpControlInfo::new(self.current_open_environments_count)
.with_loop_flag(true)
.with_label(label)
.with_start_address(start_address)
.with_iterator_loop(true)
.with_for_await_of_loop(true);
self.push_contol_info(new_info, use_expr);
}
pub(crate) fn pop_loop_control_info(&mut self) {
assert!(!self.jump_info.is_empty());
let info = self.jump_info.pop().expect("no jump information found");
assert!(info.is_loop());
let start_address = info.start_address();
for jump_record in info.jumps {
jump_record.perform_actions(start_address, self);
}
}
pub(crate) fn push_switch_control_info(
&mut self,
label: Option<Sym>,
start_address: u32,
use_expr: bool,
) {
let new_info = JumpControlInfo::new(self.current_open_environments_count)
.with_switch_flag(true)
.with_label(label)
.with_start_address(start_address);
self.push_contol_info(new_info, use_expr);
}
pub(crate) fn pop_switch_control_info(&mut self) {
assert!(!self.jump_info.is_empty());
let info = self.jump_info.pop().expect("no jump information found");
assert!(info.is_switch());
let start_address = info.start_address();
for jump_record in info.jumps {
jump_record.perform_actions(start_address, self);
}
}
pub(crate) fn push_try_with_finally_control_info(
&mut self,
flag: &Register,
index: &Register,
use_expr: bool,
) {
let new_info = JumpControlInfo::new(self.current_open_environments_count)
.with_try_with_finally_flag(flag, index);
self.push_contol_info(new_info, use_expr);
}
pub(crate) fn pop_try_with_finally_control_info(&mut self, finally_start: u32) {
assert!(!self.jump_info.is_empty());
let info = self.jump_info.pop().expect("no jump information found");
assert!(info.is_try_with_finally_block());
if info.jumps.is_empty() {
return;
}
let (_, finally_throw_index) = info.finally_throw.expect("try with finally");
for JumpRecord { label, .. } in &info.jumps {
self.patch_jump_with_target(*label, finally_start);
}
let jump_table_index = self.next_opcode_location() + size_of::<u32>() as u32;
self.bytecode.emit_jump_table(
finally_throw_index,
Self::DUMMY_ADDRESS,
thin_vec![Self::DUMMY_ADDRESS; info.jumps.len()],
);
let mut patch_jumps = Vec::with_capacity(info.jumps.len());
for i in 0..info.jumps.len() {
patch_jumps.push(self.next_opcode_location());
let jump_record = info.jumps[i].clone();
jump_record.perform_actions(Self::DUMMY_ADDRESS, self);
}
let default = self.bytecode.next_opcode_location();
self.bytecode
.patch_jump_table(jump_table_index, (default, &patch_jumps));
}
pub(crate) fn jump_info_open_environment_count(&self, index: usize) -> u32 {
let current = &self.jump_info[index];
if let Some(next) = self.jump_info.get(index + 1) {
return next.current_open_environments_count - current.current_open_environments_count;
}
self.current_open_environments_count - current.current_open_environments_count
}
}