use std::collections::HashMap;
use std::convert::TryInto;
use std::iter;
use llvm_bitcursor::BitCursor;
use llvm_support::bitcodes::{BlockInfoCode, ReservedAbbrevId, ReservedBlockId};
use llvm_support::{FIRST_APPLICATION_ABBREV_ID, INITIAL_ABBREV_ID_WIDTH};
use crate::abbrev::{self, AbbrevId};
use crate::error::Error;
use crate::record::{Block, Fields, Record};
#[derive(Debug)]
pub enum StreamEntry {
EndBlock,
SubBlock(Block),
Record(Record),
}
impl StreamEntry {
pub fn as_block(self) -> Option<Block> {
match self {
StreamEntry::SubBlock(block) => Some(block),
_ => None,
}
}
}
#[derive(Debug)]
enum Scope {
Initial,
Block {
abbrev_id_width: u64,
block_id: u64,
blockinfo_block_id: Option<u64>,
abbrevs: Vec<abbrev::Abbrev>,
},
}
impl Default for Scope {
fn default() -> Self {
Self::Initial
}
}
impl Scope {
pub(self) fn new(abbrev_id_width: u64, block_id: u64) -> Self {
Self::Block {
abbrev_id_width: abbrev_id_width,
block_id: block_id,
blockinfo_block_id: None,
abbrevs: vec![],
}
}
pub(self) fn abbrev_id_width(&self) -> u64 {
match self {
Scope::Initial => INITIAL_ABBREV_ID_WIDTH,
Scope::Block {
abbrev_id_width, ..
} => *abbrev_id_width,
}
}
pub(self) fn extend_abbrevs(
&mut self,
new_abbrevs: impl iter::IntoIterator<Item = abbrev::Abbrev>,
) -> Result<(), Error> {
match self {
Scope::Initial => Err(Error::BadScope(
"non-block scope cannot reference abbreviations".into(),
)),
Scope::Block { abbrevs, .. } => {
abbrevs.extend(new_abbrevs);
Ok(())
}
}
}
pub(self) fn get_abbrev(&self, abbrev_id: u64) -> Result<&abbrev::Abbrev, Error> {
match self {
Scope::Initial => Err(Error::BadScope(
"non-block scope cannot contain records".into(),
)),
Scope::Block { abbrevs, .. } => {
let idx = (abbrev_id as usize) - FIRST_APPLICATION_ABBREV_ID;
abbrevs.get(idx).ok_or(Error::BadAbbrev(abbrev_id))
}
}
}
pub(self) fn is_blockinfo(&self) -> bool {
match self {
Scope::Initial => false,
Scope::Block { block_id, .. } => *block_id == ReservedBlockId::BlockInfo as u64,
}
}
pub(self) fn blockinfo_block_id(&self) -> Option<u64> {
match self {
Scope::Initial => None,
Scope::Block {
blockinfo_block_id, ..
} => *blockinfo_block_id,
}
}
pub(self) fn set_blockinfo_block_id(&mut self, new_bid: u64) -> Result<(), Error> {
if let Scope::Block {
blockinfo_block_id, ..
} = self
{
*blockinfo_block_id = Some(new_bid);
return Ok(());
}
Err(Error::BadScope(
"can't set BLOCKINFO block ID for non-BLOCKINFO scope".into(),
))
}
}
#[derive(Debug)]
pub struct StreamParser<T: AsRef<[u8]>> {
cursor: BitCursor<T>,
scopes: Vec<Scope>,
blockinfo: HashMap<u64, Vec<abbrev::Abbrev>>,
}
impl<T: AsRef<[u8]>> StreamParser<T> {
pub(crate) fn new(cur: BitCursor<T>) -> Self {
Self {
cursor: cur,
scopes: vec![Scope::default()],
blockinfo: Default::default(),
}
}
fn scope(&self) -> &Scope {
#[allow(clippy::unwrap_used)]
self.scopes.last().unwrap()
}
fn scope_mut(&mut self) -> &mut Scope {
#[allow(clippy::unwrap_used)]
self.scopes.last_mut().unwrap()
}
fn enter_block(&mut self) -> Result<Option<StreamEntry>, Error> {
let block_id = self.cursor.read_vbr(8)?;
let new_width = self.cursor.read_vbr(4)?;
self.cursor.align32();
if new_width < 1 {
return Err(Error::BadScope(format!(
"can't enter block: invalid code side: {}",
new_width
)));
}
let block_len = self.cursor.read(32)? * 4;
log::debug!(
"entered block: ID={}, new abbrev width={}, block_len={} @ bit position {}",
block_id,
new_width,
block_len,
self.cursor.tell_bit()
);
self.scopes.push(Scope::new(new_width, block_id));
if let Some(abbrevs) = self.blockinfo.get(&block_id).map(|a| a.to_vec()) {
self.scope_mut().extend_abbrevs(abbrevs)?;
}
if self.scope().is_blockinfo() {
return Ok(None);
}
Ok(Some(StreamEntry::SubBlock(Block {
block_id: block_id,
len: block_len,
})))
}
fn exit_block(&mut self) -> Result<Option<StreamEntry>, Error> {
self.cursor.align32();
if self.scopes.len() <= 1 {
return Err(Error::BadScope(
"malformed stream: cannot perform END_BLOCK because scope stack is empty".into(),
));
}
#[allow(clippy::unwrap_used)]
let scope = self.scopes.pop().unwrap();
log::debug!("exit_block: new active scope is {:?}", self.scope());
if scope.is_blockinfo() {
return Ok(None);
}
Ok(Some(StreamEntry::EndBlock))
}
fn define_abbrev(&mut self) -> Result<(), Error> {
let abbrev = abbrev::Abbrev::new(&mut self.cursor)?;
log::debug!("new abbrev: {:?}", abbrev);
if self.scope().is_blockinfo() {
let block_id = self.scope().blockinfo_block_id().ok_or_else(|| {
Error::StreamParse("DEFINE_ABBREV in BLOCKINFO but no preceding SETBID".into())
})?;
self.blockinfo
.entry(block_id)
.or_insert_with(Vec::new)
.push(abbrev);
} else {
self.scope_mut().extend_abbrevs(iter::once(abbrev))?;
}
Ok(())
}
fn parse_unabbrev(&mut self) -> Result<Option<StreamEntry>, Error> {
if matches!(self.scope(), Scope::Initial) {
return Err(Error::StreamParse(
"UNABBREV_RECORD outside of any block scope".into(),
));
}
let code: u64 = self.cursor.read_vbr(6)?;
let num_opnds = self.cursor.read_vbr(6)?;
log::debug!("unabbrev record code={}, num_opnds={}", code, num_opnds);
let mut fields: Fields = Vec::with_capacity(num_opnds as usize);
for _ in 0..num_opnds {
fields.push(self.cursor.read_vbr(6)?);
}
let record = Record::from_unabbrev(code, fields);
if self.scope().is_blockinfo() {
let code: BlockInfoCode = record.code.try_into()?;
match code {
BlockInfoCode::SetBid => {
let block_id: u64 = record.fields[0];
log::debug!("SETBID: BLOCKINFO block ID is now {}", block_id);
self.scope_mut().set_blockinfo_block_id(block_id)?;
}
BlockInfoCode::BlockName => log::debug!("skipping BLOCKNAME code in BLOCKINFO"),
BlockInfoCode::SetRecordName => {
log::debug!("skipping SETRECORDNAME code in BLOCKINFO")
}
o => log::debug!("skipping unsupported record {:?} in BLOCKINFO", o),
};
return Ok(None);
}
Ok(Some(StreamEntry::Record(record)))
}
fn parse_with_abbrev(&mut self, abbrev_id: u64) -> Result<Option<StreamEntry>, Error> {
let abbrev = self.scope().get_abbrev(abbrev_id)?.clone();
let mut fields = abbrev.parse(&mut self.cursor)?;
log::debug!("parsed fields: {:?}", fields);
let code: u64 = fields.remove(0);
if self.scope().is_blockinfo() {
return Ok(None);
}
Ok(Some(StreamEntry::Record(Record {
abbrev_id: Some(abbrev_id),
code: code,
fields: fields,
})))
}
pub fn advance(&mut self) -> Result<StreamEntry, Error> {
if self.cursor.exhausted() {
return Err(Error::Exhausted);
}
log::debug!(
"advancing, current scope: {:?} @ bit position {}",
self.scope(),
self.cursor.tell_bit()
);
let id: abbrev::AbbrevId = self
.cursor
.read(self.scope().abbrev_id_width() as usize)?
.into();
log::debug!("next entry ID: {:?}", id);
match id {
AbbrevId::Reserved(ReservedAbbrevId::EndBlock) => {
self.exit_block()?.map(Ok).unwrap_or_else(|| self.advance())
}
AbbrevId::Reserved(ReservedAbbrevId::EnterSubBlock) => self
.enter_block()?
.map(Ok)
.unwrap_or_else(|| self.advance()),
AbbrevId::Reserved(ReservedAbbrevId::DefineAbbrev) => {
self.define_abbrev()?;
self.advance()
}
AbbrevId::Reserved(ReservedAbbrevId::UnabbrevRecord) => self
.parse_unabbrev()?
.map(Ok)
.unwrap_or_else(|| self.advance()),
AbbrevId::Defined(abbrev_id) => self
.parse_with_abbrev(abbrev_id)?
.map(Ok)
.unwrap_or_else(|| self.advance()),
}
}
}