use indexmap::IndexMap;
use std::cell::RefCell;
use crate::disassembly::{Assertation, GlobalSet, Variable, VariableId};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote, ToTokens};
use crate::codegen::builder::formater::from_sleigh;
use crate::codegen::builder::{Disassembler, DisassemblyGenerator, ToLiteral, WorkType};
use super::ConstructorStruct;
pub const DISASSEMBLY_WORK_TYPE: WorkType =
WorkType::new_int_bits(crate::DisassemblyType::BITS, true);
pub struct DisassemblyDisplay<'a> {
pub disassembler: &'a Disassembler,
pub constructor: &'a ConstructorStruct,
pub display_param: &'a Ident,
pub context_param: &'a Ident,
pub inst_start: &'a Ident,
pub inst_next: &'a Ident,
pub global_set_param: &'a Ident,
pub vars: RefCell<IndexMap<VariableId, Ident>>,
}
impl DisassemblyDisplay<'_> {
fn inst_start(&self) -> TokenStream {
let inst_start = &self.inst_start;
quote! {#DISASSEMBLY_WORK_TYPE::from(#inst_start)}
}
fn inst_next(&self) -> TokenStream {
let inst_next = &self.inst_next;
quote! {#DISASSEMBLY_WORK_TYPE::from(#inst_next)}
}
fn ass_field(&self, ass: crate::TokenFieldId) -> TokenStream {
let field = self.constructor.ass_fields.get(&ass).unwrap();
let token_field = self.disassembler.sleigh.token_field(ass);
let field = self.disassembler.meanings.disassembly_function_call(
token_field.bits.len().get().try_into().unwrap(),
quote! {self.#field},
token_field.meaning(),
);
quote! {#DISASSEMBLY_WORK_TYPE::from(#field)}
}
fn context_field(&self, context: &crate::ContextId) -> TokenStream {
let read_call = self
.disassembler
.context
.read_call(*context, self.context_param);
quote! { #DISASSEMBLY_WORK_TYPE::from(#read_call)}
}
fn table_field(&self, table: &crate::TableId) -> Option<TokenStream> {
let field = self.constructor.table_fields.get(table).unwrap();
use crate::execution::ExportLen;
let table = self.disassembler.sleigh.table(*table);
match table.export {
Some(ExportLen::Const(_len)) => {
}
None
| Some(ExportLen::Value(_))
| Some(ExportLen::Reference(_))
| Some(ExportLen::Multiple(_)) => return None,
}
Some(quote! {self.#field})
}
}
impl ToTokens for DisassemblyDisplay<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let constructor = self
.disassembler
.sleigh
.table(self.constructor.table_id)
.constructor(self.constructor.constructor_id);
let mut asses = constructor
.pattern
.blocks()
.iter()
.flat_map(|block| match block {
crate::pattern::Block::And { pre, pos, .. } => pre.iter().chain(pos.iter()),
crate::pattern::Block::Or { pos, .. } => {
pos.iter().chain([].iter())
}
})
.chain(constructor.pattern.disassembly_pos_match());
tokens.extend(self.disassembly(&mut asses));
}
}
impl<'a> DisassemblyGenerator for DisassemblyDisplay<'a> {
fn global_set(&self, global_set: &GlobalSet) -> TokenStream {
let addr_type = &self.disassembler.addr_type;
use crate::disassembly::AddrScope::*;
let address = match &global_set.address {
Integer(value) => {
let value = value.unsuffixed();
quote! { Some(#value) }
}
Local(var) => {
let name = self.var_name(var);
quote! { #addr_type::try_from(#name).ok() }
}
Table(table) => {
self.table_field(table)
.map(|table| quote! {Some(#table)})
.unwrap_or(quote! {None})
}
InstNext(_) => {
let name = self.inst_next;
quote! { Some(#name) }
}
InstStart(_) => {
let name = self.inst_start;
quote! { Some(#name) }
}
};
let global_set_param = self.global_set_param;
let context = &self.disassembler.context;
let set_fun = &context.globalset.set_fun;
let context_param = format_ident!("context");
let value = self.context_field(&global_set.context);
let write_call = context.write_call(
self.disassembler,
&context_param,
global_set.context,
&value,
);
quote! {
#global_set_param.#set_fun(#address, |#context_param| #write_call);
}
}
fn value(&self, value: &crate::disassembly::ReadScope) -> TokenStream {
use crate::disassembly::ReadScope;
match value {
ReadScope::Integer(value) => value.signed_super().suffixed().into_token_stream(),
ReadScope::Context(context) => self.context_field(context).to_token_stream(),
ReadScope::TokenField(ass) => self.ass_field(*ass),
ReadScope::Local(var) => self.var_name(var),
ReadScope::InstStart(_) => self.inst_start().to_token_stream(),
ReadScope::InstNext(_) => self.inst_next().to_token_stream(),
}
}
fn set_context(&self, _context: &crate::ContextId, _value: TokenStream) -> TokenStream {
quote! {}
}
fn new_variable(&mut self, var_id: &VariableId, var: &Variable) -> TokenStream {
let mut vars = self.vars.borrow_mut();
use indexmap::map::Entry::*;
let Vacant(entry) = vars.entry(*var_id) else {
unreachable!("Variable duplicated")
};
let var_name = format_ident!("calc_{}", from_sleigh(var.name()));
let name = entry.insert(var_name);
quote! {let mut #name: #DISASSEMBLY_WORK_TYPE = 0;}
}
fn var_name(&self, var: &VariableId) -> TokenStream {
let vars = self.vars.borrow();
let Some(name) = vars.get(var) else {
unreachable!("Variable not created")
};
name.into_token_stream()
}
}
pub struct DisassemblyPattern<'a> {
pub disassembler: &'a Disassembler,
pub context_instance: &'a Ident,
pub tokens: &'a Ident,
pub inst_start: &'a Ident,
pub root_tables: &'a IndexMap<crate::TableId, Ident>,
pub root_token_fields: &'a IndexMap<crate::TokenFieldId, Ident>,
pub vars: &'a mut IndexMap<VariableId, Ident>,
}
impl DisassemblyPattern<'_> {
fn inst_start(&self) -> TokenStream {
let inst_start = &self.inst_start;
quote! {#DISASSEMBLY_WORK_TYPE::from(#inst_start)}
}
fn ass_field(&self, ass: &crate::TokenFieldId) -> TokenStream {
let tokens = self.tokens;
let token_field_new = &self.disassembler.token_field_function(*ass).read;
let token_field = self.disassembler.sleigh.token_field(*ass);
if token_field.raw_value_is_signed() {
let bits = token_field.bits.len().get();
match bits {
8 => quote! { #DISASSEMBLY_WORK_TYPE::from(#token_field_new(#tokens) as i8) },
16 => quote! { #DISASSEMBLY_WORK_TYPE::from(#token_field_new(#tokens) as i16) },
32 => quote! { #DISASSEMBLY_WORK_TYPE::from(#token_field_new(#tokens) as i32) },
_ => {
let sign_bit = 1u64 << (bits - 1);
let sign_ext_mask = !((1i128 << bits) - 1);
quote! {{
let val = #DISASSEMBLY_WORK_TYPE::from(#token_field_new(#tokens));
if val & #sign_bit as i128 != 0 { val | #sign_ext_mask } else { val }
}}
}
}
} else {
quote! { #DISASSEMBLY_WORK_TYPE::from(#token_field_new(#tokens)) }
}
}
fn context_field(&self, context: &crate::ContextId) -> TokenStream {
let read_call = self
.disassembler
.context
.read_call(*context, self.context_instance);
quote! { #DISASSEMBLY_WORK_TYPE::from(#read_call)}
}
fn can_execute(&self, expr: &crate::disassembly::Expr) -> bool {
use crate::disassembly::ExprElement::*;
use crate::disassembly::ReadScope::*;
match expr {
crate::disassembly::Expr::Value(element) => match element {
Value { value, location: _ } => match value {
Integer(_) | Context(_) | InstStart(_) | Local(_) | TokenField(_) => true,
InstNext(_) => false,
},
Op(_, _, inner) => self.can_execute(inner),
},
crate::disassembly::Expr::Op(_, _, left, right) => {
self.can_execute(left) && self.can_execute(right)
}
}
}
}
impl DisassemblyGenerator for DisassemblyPattern<'_> {
fn disassembly(&self, assertations: &mut dyn Iterator<Item = &Assertation>) -> TokenStream {
let mut tokens = TokenStream::new();
for ass in assertations {
use crate::disassembly::Assertation::*;
match ass {
GlobalSet(_) => (),
Assignment(ass) => {
if !self.can_execute(&ass.right) {
break;
}
tokens.extend(self.assignment(ass));
}
}
}
tokens
}
fn global_set(&self, _global_set: &GlobalSet) -> TokenStream {
unreachable!()
}
fn value(&self, value: &crate::disassembly::ReadScope) -> TokenStream {
use crate::disassembly::ReadScope;
match value {
ReadScope::Integer(value) => value.signed_super().suffixed().into_token_stream(),
ReadScope::Context(context) => self.context_field(context).to_token_stream(),
ReadScope::TokenField(ass) => self.ass_field(ass),
ReadScope::Local(var) => self.var_name(var),
ReadScope::InstStart(_) => self.inst_start().to_token_stream(),
ReadScope::InstNext(_) => unreachable!(),
}
}
fn set_context(&self, context: &crate::ContextId, value: TokenStream) -> TokenStream {
let write = self.disassembler.context.write_call(
self.disassembler,
self.context_instance,
*context,
&value,
);
quote! { #write; }
}
fn new_variable(&mut self, var_id: &VariableId, var: &Variable) -> TokenStream {
use indexmap::map::Entry::*;
let Vacant(entry) = self.vars.entry(*var_id) else {
unreachable!("Variable duplicated")
};
let var_name = format_ident!("calc_{}", from_sleigh(var.name()));
let name = entry.insert(var_name);
quote! {let mut #name: #DISASSEMBLY_WORK_TYPE = 0;}
}
fn var_name(&self, var: &VariableId) -> TokenStream {
let var = self.vars.get(var).unwrap();
var.to_token_stream()
}
}