use crate::expr::Expr;
use crate::parse::{
parse_bytes_at, parse_uleb128_at, ParseAt, ParseErrorContext as _, ParseResult,
};
use crate::xo65::Xo65Bytes;
#[derive(Debug)]
pub struct ExportTable<'data> {
bytes: Xo65Bytes<'data>,
exports: Box<[Export]>,
}
impl<'data> ExportTable<'data> {
pub fn as_bytes(&self) -> &'data [u8] {
self.bytes.as_inner()
}
pub fn count(&self) -> usize {
self.exports.len()
}
pub fn get(&self, idx: usize) -> Option<&Export> {
self.exports.get(idx)
}
pub fn iter(
&self,
) -> impl DoubleEndedIterator<Item = &Export> + ExactSizeIterator + std::iter::FusedIterator + Clone
{
self.exports.iter()
}
pub(crate) fn parse(bytes: Xo65Bytes<'data>) -> ParseResult<Self> {
let mut off = 0;
let count = parse_uleb128_at(&bytes, &mut off)? as usize;
let mut exports = Vec::<Export>::with_capacity(count);
for i in 0..count {
let export = Export::parse_at(&bytes, &mut off)
.with_context(|| format!("export {i} parse error"))?;
exports.push(export);
}
Ok(Self {
bytes,
exports: exports.into(),
})
}
}
#[derive(Debug)]
pub struct Export {
name: u32,
info: ExportInfo,
addr_size: u8,
size: Option<u32>,
expr: Expr,
attrs: Box<[ExportAttr]>,
def_lines: Box<[u32]>,
ref_lines: Box<[u32]>,
}
impl Export {
pub fn name(&self) -> u32 {
self.name
}
pub fn info(&self) -> ExportInfo {
self.info
}
pub fn addr_size(&self) -> u8 {
self.addr_size
}
pub fn size(&self) -> Option<u32> {
self.size
}
pub fn expr(&self) -> &Expr {
&self.expr
}
pub fn attrs(&self) -> &[ExportAttr] {
&self.attrs
}
pub fn def_lines(&self) -> &[u32] {
&self.def_lines
}
pub fn ref_lines(&self) -> &[u32] {
&self.ref_lines
}
}
impl ParseAt<'_> for Export {
fn parse_at(bytes: &Xo65Bytes<'_>, off: &mut usize) -> ParseResult<Self> {
let info = parse_uleb128_at(bytes, off).map(ExportInfo)?;
let addr_size = u8::parse_at(bytes, off)?;
let attrs = parse_attrs_at(bytes, off, info.attr_count())?;
let name = parse_uleb128_at(bytes, off)?;
let expr = if info.is_const() {
let value = i32::parse_at(bytes, off)?;
Expr::literal(i64::from(value))
} else {
Expr::parse_at(bytes, off)?
};
let size = info
.has_size()
.then(|| parse_uleb128_at(bytes, off))
.transpose()?;
let def_lines = parse_lines_at(bytes, off)?;
let ref_lines = parse_lines_at(bytes, off)?;
Ok(Self {
name,
info,
addr_size,
size,
expr,
attrs,
def_lines,
ref_lines,
})
}
}
fn parse_attrs_at(
bytes: &Xo65Bytes<'_>,
off: &mut usize,
count: u8,
) -> ParseResult<Box<[ExportAttr]>> {
let count = usize::from(count);
let ary = parse_bytes_at(bytes, off, count)?;
let ary: Box<_> = ary.iter().copied().map(ExportAttr).collect();
Ok(ary)
}
fn parse_lines_at(bytes: &Xo65Bytes<'_>, off: &mut usize) -> ParseResult<Box<[u32]>> {
let count = parse_uleb128_at(bytes, off)? as usize;
let mut lines = Vec::<u32>::with_capacity(count);
for _ in 0..count {
let line = parse_uleb128_at(bytes, off)?;
lines.push(line);
}
Ok(lines.into())
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ExportInfo(u32);
impl ExportInfo {
pub fn get(self) -> u32 {
self.0
}
pub fn attr_count(self) -> u8 {
(self.0 & 0x7) as u8
}
pub fn has_size(self) -> bool {
(self.0 & (1 << 3)) != 0
}
pub fn is_const(self) -> bool {
(self.0 & (1 << 4)) == 0
}
pub fn is_expr(self) -> bool {
!self.is_const()
}
pub fn is_label(self) -> bool {
(self.0 & (1 << 5)) != 0
}
pub fn is_cheap_local(self) -> bool {
(self.0 & (1 << 6)) != 0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ExportAttr(u8);
impl ExportAttr {
pub fn get(self) -> u8 {
self.0
}
pub fn kind(self) -> ExportAttrKind {
ExportAttrKind::new(self.0 >> 5)
}
pub fn priority(self) -> u8 {
self.0 & 0x1F
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ExportAttrKind {
Constructor = 0,
Destructor = 1,
Interruptor = 2,
Kind3 = 3,
Kind4 = 4,
Kind5 = 5,
Kind6 = 6,
Kind7 = 7,
}
impl ExportAttrKind {
fn new(inner: u8) -> Self {
match inner {
0 => Self::Constructor,
1 => Self::Destructor,
2 => Self::Interruptor,
3 => Self::Kind3,
4 => Self::Kind4,
5 => Self::Kind5,
6 => Self::Kind6,
7 => Self::Kind7,
_ => unreachable!(),
}
}
}