use crate::{intern::StringId, parse::CodeRange, value::Value};
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Code {
bytecode: Vec<u8>,
constants: ConstPool,
location_table: Vec<LocationEntry>,
exception_table: Vec<ExceptionEntry>,
num_locals: u16,
stack_size: u16,
local_names: Vec<StringId>,
}
impl Code {
#[must_use]
pub fn new(
bytecode: Vec<u8>,
constants: ConstPool,
location_table: Vec<LocationEntry>,
exception_table: Vec<ExceptionEntry>,
num_locals: u16,
stack_size: u16,
local_names: Vec<StringId>,
) -> Self {
Self {
bytecode,
constants,
location_table,
exception_table,
num_locals,
stack_size,
local_names,
}
}
#[must_use]
pub fn bytecode(&self) -> &[u8] {
&self.bytecode
}
#[must_use]
pub fn constants(&self) -> &ConstPool {
&self.constants
}
#[must_use]
pub fn local_name(&self, slot: u16) -> Option<StringId> {
self.local_names.get(slot as usize).copied()
}
#[must_use]
pub fn location_for_offset(&self, offset: usize) -> Option<&LocationEntry> {
let offset_u32 = u32::try_from(offset).ok()?;
self.location_table
.iter()
.rev()
.find(|entry| entry.bytecode_offset <= offset_u32)
}
#[must_use]
pub fn find_exception_handler(&self, offset: u32) -> Option<&ExceptionEntry> {
self.exception_table.iter().find(|entry| entry.contains(offset))
}
}
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub(crate) struct ConstPool {
values: Vec<Value>,
}
impl Clone for ConstPool {
fn clone(&self) -> Self {
let values = self.values.iter().map(Value::clone_immediate).collect();
Self { values }
}
}
impl ConstPool {
#[must_use]
pub fn from_vec(values: Vec<Value>) -> Self {
Self { values }
}
#[must_use]
pub fn get(&self, index: u16) -> &Value {
&self.values[index as usize]
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct LocationEntry {
bytecode_offset: u32,
range: CodeRange,
focus: Option<CodeRange>,
}
impl LocationEntry {
#[must_use]
pub fn new(bytecode_offset: u32, range: CodeRange, focus: Option<CodeRange>) -> Self {
Self {
bytecode_offset,
range,
focus,
}
}
#[must_use]
pub fn range(&self) -> CodeRange {
self.range
}
}
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub struct ExceptionEntry {
start: u32,
end: u32,
handler: u32,
stack_depth: u16,
exception_stack_count: u16,
}
impl ExceptionEntry {
#[must_use]
pub fn new(start: u32, end: u32, handler: u32, stack_depth: u16, exception_stack_count: u16) -> Self {
Self {
start,
end,
handler,
stack_depth,
exception_stack_count,
}
}
#[must_use]
pub fn handler(&self) -> u32 {
self.handler
}
#[must_use]
pub fn stack_depth(&self) -> u16 {
self.stack_depth
}
#[must_use]
pub fn exception_stack_count(&self) -> u16 {
self.exception_stack_count
}
#[must_use]
pub fn contains(&self, offset: u32) -> bool {
offset >= self.start && offset < self.end
}
}