use crate::bytecompiler::{ByteCompiler, Label};
use bitflags::bitflags;
use boa_interner::Sym;
#[derive(Debug, Clone)]
pub(crate) struct JumpControlInfo {
label: Option<Sym>,
start_address: u32,
flags: JumpControlInfoFlags,
set_jumps: Vec<Label>,
breaks: Vec<Label>,
try_continues: Vec<Label>,
}
bitflags! {
#[derive(Debug, Clone, Copy)]
pub(crate) struct JumpControlInfoFlags: u16 {
const LOOP = 0b0000_0001;
const SWITCH = 0b0000_0010;
const TRY_BLOCK = 0b0000_0100;
const LABELLED = 0b0000_1000;
const IN_FINALLY = 0b0001_0000;
const HAS_FINALLY = 0b0010_0000;
const ITERATOR_LOOP = 0b0100_0000;
const FOR_AWAIT_OF_LOOP = 0b1000_0000;
const USE_EXPR = 0b0001_0000_0000;
}
}
impl Default for JumpControlInfoFlags {
fn default() -> Self {
Self::empty()
}
}
impl Default for JumpControlInfo {
fn default() -> Self {
Self {
label: None,
start_address: u32::MAX,
flags: JumpControlInfoFlags::default(),
set_jumps: Vec::new(),
breaks: Vec::new(),
try_continues: Vec::new(),
}
}
}
impl JumpControlInfo {
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_block_flag(mut self, value: bool) -> Self {
self.flags.set(JumpControlInfoFlags::TRY_BLOCK, value);
self
}
pub(crate) fn with_labelled_block_flag(mut self, value: bool) -> Self {
self.flags.set(JumpControlInfoFlags::LABELLED, value);
self
}
pub(crate) fn with_has_finally(mut self, value: bool) -> Self {
self.flags.set(JumpControlInfoFlags::HAS_FINALLY, 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_block(&self) -> bool {
self.flags.contains(JumpControlInfoFlags::TRY_BLOCK)
}
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 has_finally(&self) -> bool {
self.flags.contains(JumpControlInfoFlags::HAS_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;
}
pub(crate) fn set_in_finally(&mut self, value: bool) {
self.flags.set(JumpControlInfoFlags::IN_FINALLY, value);
}
pub(crate) fn push_break_label(&mut self, break_label: Label) {
self.breaks.push(break_label);
}
pub(crate) fn push_try_continue_label(&mut self, try_continue_label: Label) {
self.try_continues.push(try_continue_label);
}
pub(crate) fn push_set_jumps(&mut self, set_jump_label: Label) {
self.set_jumps.push(set_jump_label);
}
}
impl ByteCompiler<'_, '_> {
pub(crate) fn push_empty_loop_jump_control(&mut self, use_expr: bool) {
let new_info = JumpControlInfo::default().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 set_jump_control_start_address(&mut self, start_address: u32) {
let info = self.jump_info.last_mut().expect("jump_info must exist");
info.set_start_address(start_address);
}
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);
}
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::default()
.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());
for label in info.breaks {
self.patch_jump(label);
}
for label in info.try_continues {
self.patch_jump_with_target(label, info.start_address);
}
}
pub(crate) fn push_loop_control_info(
&mut self,
label: Option<Sym>,
start_address: u32,
use_expr: bool,
) {
let new_info = JumpControlInfo::default()
.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::default()
.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::default()
.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 label in info.try_continues {
self.patch_jump_with_target(label, start_address);
}
for label in info.breaks {
self.patch_jump(label);
}
}
pub(crate) fn push_switch_control_info(
&mut self,
label: Option<Sym>,
start_address: u32,
use_expr: bool,
) {
let new_info = JumpControlInfo::default()
.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());
for label in info.breaks {
self.patch_jump(label);
}
}
pub(crate) fn push_try_control_info(
&mut self,
has_finally: bool,
start_address: u32,
use_expr: bool,
) {
let new_info = JumpControlInfo::default()
.with_try_block_flag(true)
.with_start_address(start_address)
.with_has_finally(has_finally);
self.push_contol_info(new_info, use_expr);
}
pub(crate) fn pop_try_control_info(&mut self, try_end: u32) {
assert!(!self.jump_info.is_empty());
let mut info = self.jump_info.pop().expect("no jump information found");
assert!(info.is_try_block());
if info.has_finally() {
for label in info.breaks {
self.patch_jump_with_target(label, try_end);
}
} else {
for jump_info in self.jump_info.iter_mut().rev() {
if !jump_info.is_labelled() {
jump_info.breaks.append(&mut info.breaks);
break;
}
}
}
for label in info.set_jumps {
for jump_info in self.jump_info.iter_mut().rev() {
if jump_info.is_loop() || jump_info.is_switch() {
jump_info.breaks.push(label);
break;
}
}
}
if let Some(jump_info) = self.jump_info.last_mut() {
jump_info.try_continues.append(&mut info.try_continues);
}
}
pub(crate) fn push_init_finally_control_info(&mut self, use_expr: bool) {
let mut new_info = JumpControlInfo::default().with_try_block_flag(true);
new_info.set_in_finally(true);
self.push_contol_info(new_info, use_expr);
}
pub(crate) fn pop_finally_control_info(&mut self) {
assert!(!self.jump_info.is_empty());
let mut info = self.jump_info.pop().expect("no jump information found");
assert!(info.in_finally());
for label in info.set_jumps {
for jump_info in self.jump_info.iter_mut().rev() {
if jump_info.is_loop() || jump_info.is_switch() {
jump_info.breaks.push(label);
break;
}
}
}
for label in info.breaks {
self.patch_jump(label);
}
if let Some(jump_info) = self.jump_info.last_mut() {
jump_info.try_continues.append(&mut info.try_continues);
}
}
}