use std::collections::BTreeSet;
use super::encode::{leb_u32, local_get, VOID_BLOCKTYPE};
use crate::vm::instruction::Op;
pub(crate) struct Blocks {
leaders: Vec<usize>,
n: usize,
}
impl Blocks {
pub(crate) fn new(ops: &[Op]) -> Option<Blocks> {
let n = ops.len();
let mut leaders: BTreeSet<usize> = BTreeSet::new();
leaders.insert(0);
for (i, op) in ops.iter().enumerate() {
match *op {
Op::Jump { target }
| Op::JumpIfFalse { target, .. }
| Op::JumpIfTrue { target, .. } => {
if target >= n {
return None;
}
leaders.insert(target);
if i + 1 < n {
leaders.insert(i + 1);
}
}
Op::IterNext { exit, .. } => {
if exit >= n {
return None;
}
leaders.insert(exit);
if i + 1 < n {
leaders.insert(i + 1);
}
}
Op::Return { .. } | Op::ReturnNothing | Op::Halt | Op::FailWith { .. } => {
if i + 1 < n {
leaders.insert(i + 1);
}
}
_ => {}
}
}
Some(Blocks { leaders: leaders.into_iter().collect(), n })
}
pub(crate) fn num_blocks(&self) -> usize {
self.leaders.len()
}
pub(crate) fn is_leader(&self, pc: usize) -> bool {
self.leaders.binary_search(&pc).is_ok()
}
pub(crate) fn block_of(&self, pc: usize) -> usize {
self.leaders.binary_search(&pc).expect("pc is a leader by construction")
}
pub(crate) fn start(&self, k: usize) -> usize {
self.leaders[k]
}
pub(crate) fn end(&self, k: usize) -> usize {
if k + 1 < self.leaders.len() {
self.leaders[k + 1]
} else {
self.n
}
}
pub(crate) fn br_loop(&self, k: usize) -> u32 {
(self.num_blocks() - 1 - k) as u32
}
}
pub(crate) fn assemble_dispatch_loop(pc_local: u32, blocks_code: &[Vec<u8>]) -> Vec<u8> {
let num_blocks = blocks_code.len();
let mut body = Vec::new();
body.push(0x02); body.push(VOID_BLOCKTYPE);
body.push(0x03); body.push(VOID_BLOCKTYPE);
for _ in 0..num_blocks {
body.push(0x02); body.push(VOID_BLOCKTYPE);
}
local_get(&mut body, pc_local);
body.push(0x0E); leb_u32(&mut body, num_blocks as u32); for k in 0..num_blocks {
leb_u32(&mut body, k as u32);
}
leb_u32(&mut body, (num_blocks + 1) as u32); for code in blocks_code {
body.push(0x0B); body.extend_from_slice(code);
}
body.push(0x0B); body.push(0x0B); body
}