use syn::parse;
use syn::spanned::Spanned;
use proc_macro2::{Span, TokenStream, TokenTree, Literal};
use quote::{quote, quote_spanned, ToTokens};
use byteorder::{ByteOrder, LittleEndian};
use crate::common::{Size, Stmt, delimited, strip_parenthesis, Relocation};
use std::convert::TryInto;
pub fn serialize(name: &TokenTree, stmts: Vec<Stmt>) -> TokenStream {
let mut folded_stmts = Vec::new();
let mut const_buffer = Vec::new();
for stmt in stmts {
match stmt {
Stmt::Const(value, size) => {
match size {
Size::BYTE => const_buffer.push(value as u8),
Size::B_2 => {
let mut buffer = [0u8; 2];
LittleEndian::write_u16(&mut buffer, value as u16);
const_buffer.extend(&buffer);
},
Size::B_4 => {
let mut buffer = [0u8; 4];
LittleEndian::write_u32(&mut buffer, value as u32);
const_buffer.extend(&buffer);
},
Size::B_8 => {
let mut buffer = [0u8; 8];
LittleEndian::write_u64(&mut buffer, value);
const_buffer.extend(&buffer);
},
_ => unimplemented!()
}
},
Stmt::Extend(data) => {
const_buffer.extend(data);
},
s => {
if !const_buffer.is_empty() {
folded_stmts.push(Stmt::Extend(const_buffer));
const_buffer = Vec::new();
}
folded_stmts.push(s);
}
}
while const_buffer.len() > 32 {
let new_buffer = const_buffer.split_off(32);
folded_stmts.push(Stmt::Extend(const_buffer));
const_buffer = new_buffer;
}
}
if !const_buffer.is_empty() {
folded_stmts.push(Stmt::Extend(const_buffer));
}
let mut output = TokenStream::new();
for stmt in folded_stmts {
let (method, args) = match stmt {
Stmt::Const(_, _) => unreachable!(),
Stmt::ExprUnsigned(expr, Size::BYTE) => ("push", vec![expr]),
Stmt::ExprUnsigned(expr, Size::B_2) => ("push_u16", vec![expr]),
Stmt::ExprUnsigned(expr, Size::B_4) => ("push_u32", vec![expr]),
Stmt::ExprUnsigned(expr, Size::B_8) => ("push_u64", vec![expr]),
Stmt::ExprUnsigned(_, _) => unimplemented!(),
Stmt::ExprSigned( expr, Size::BYTE) => ("push_i8", vec![expr]),
Stmt::ExprSigned( expr, Size::B_2) => ("push_i16", vec![expr]),
Stmt::ExprSigned( expr, Size::B_4) => ("push_i32", vec![expr]),
Stmt::ExprSigned( expr, Size::B_8) => ("push_i64", vec![expr]),
Stmt::ExprSigned(_, _) => unimplemented!(),
Stmt::Extend(data) => ("extend", vec![Literal::byte_string(&data).into()]),
Stmt::ExprExtend(expr) => ("extend", vec![expr]),
Stmt::Align(expr, with) => ("align", vec![expr, with]),
Stmt::GlobalLabel(n) => ("global_label", vec![expr_string_from_ident(&n)]),
Stmt::LocalLabel(n) => ("local_label", vec![expr_string_from_ident(&n)]),
Stmt::DynamicLabel(expr) => ("dynamic_label", vec![expr]),
Stmt::GlobalJumpTarget(n, Relocation { target_offset, field_offset, ref_offset, kind, encoding }) =>
("global_reloc" , vec![
expr_string_from_ident(&n),
target_offset,
Literal::u8_suffixed(field_offset).into(),
Literal::u8_suffixed(ref_offset).into(),
Literal::u8_suffixed(encoding.encode(kind)).into()
]),
Stmt::ForwardJumpTarget(n, Relocation { target_offset, field_offset, ref_offset, kind, encoding }) =>
("forward_reloc" , vec![
expr_string_from_ident(&n),
target_offset,
Literal::u8_suffixed(field_offset).into(),
Literal::u8_suffixed(ref_offset).into(),
Literal::u8_suffixed(encoding.encode(kind)).into()
]),
Stmt::BackwardJumpTarget(n, Relocation { target_offset, field_offset, ref_offset, kind, encoding }) =>
("backward_reloc", vec![
expr_string_from_ident(&n),
target_offset,
Literal::u8_suffixed(field_offset).into(),
Literal::u8_suffixed(ref_offset).into(),
Literal::u8_suffixed(encoding.encode(kind)).into()
]),
Stmt::DynamicJumpTarget(expr, Relocation { target_offset, field_offset, ref_offset, kind, encoding }) =>
("dynamic_reloc" , vec![
expr,
target_offset,
Literal::u8_suffixed(field_offset).into(),
Literal::u8_suffixed(ref_offset).into(),
Literal::u8_suffixed(encoding.encode(kind)).into()
]),
Stmt::ValueJumpTarget(expr, Relocation { field_offset, ref_offset, kind, encoding, .. }) =>
("value_reloc" , vec![
expr,
Literal::u8_suffixed(field_offset).into(),
Literal::u8_suffixed(ref_offset).into(),
Literal::u8_suffixed(encoding.encode(kind)).into()
]),
Stmt::Stmt(s) => {
output.extend(quote! {
#s ;
});
continue;
}
};
let method = syn::Ident::new(method, Span::mixed_site());
let mut args = args;
args.iter_mut().for_each(strip_parenthesis);
output.extend(quote! {
#name . #method ( #( #args ),* ) ;
})
}
if output.is_empty() {
output
} else {
quote!{
{
#output
}
}
}
}
pub fn invert(stmts: Vec<Stmt>) -> Vec<Stmt> {
let mut reversed = Vec::new();
let mut relocation_buf = Vec::new();
let mut counter = 0usize;
let mut iter = stmts.into_iter().rev().peekable();
while let Some(stmt) = iter.next() {
match stmt {
Stmt::GlobalJumpTarget(_, Relocation { field_offset, ref_offset, .. } )
| Stmt::ForwardJumpTarget(_, Relocation { field_offset, ref_offset, .. } )
| Stmt::BackwardJumpTarget(_, Relocation { field_offset, ref_offset, .. } )
| Stmt::DynamicJumpTarget(_, Relocation { field_offset, ref_offset, .. } )
| Stmt::ValueJumpTarget(_, Relocation { field_offset, ref_offset, .. } ) => {
let trigger = counter + std::cmp::max(field_offset, ref_offset) as usize;
relocation_buf.push((trigger, counter, stmt));
continue;
},
_ => ()
};
let size = match &stmt {
Stmt::Const(_, size)
| Stmt::ExprUnsigned(_, size)
| Stmt::ExprSigned(_, size) => size.in_bytes() as usize,
Stmt::Extend(buf) => buf.len(),
Stmt::ExprExtend(_)
| Stmt::Align(_, _) => {
assert!(relocation_buf.is_empty(), "Tried to hoist relocation over unknown size");
0
},
Stmt::GlobalLabel(_)
| Stmt::LocalLabel(_)
| Stmt::DynamicLabel(_)
| Stmt::GlobalJumpTarget(_, _)
| Stmt::ForwardJumpTarget(_, _)
| Stmt::BackwardJumpTarget(_, _)
| Stmt::DynamicJumpTarget(_, _)
| Stmt::ValueJumpTarget(_, _)
| Stmt::Stmt(_) => 0,
};
counter += size;
reversed.push(stmt);
let mut new_relocation_buf = Vec::new();
for (trigger, orig_counter, mut stmt) in relocation_buf {
if counter < trigger {
new_relocation_buf.push((trigger, orig_counter, stmt));
continue;
}
let change: u8 = (counter - orig_counter).try_into().expect("Tried to hoist a relocation by over 255 bytes");
match &mut stmt {
Stmt::GlobalJumpTarget(_, Relocation { field_offset, ref_offset, .. } )
| Stmt::ForwardJumpTarget(_, Relocation { field_offset, ref_offset, .. } )
| Stmt::BackwardJumpTarget(_, Relocation { field_offset, ref_offset, .. } )
| Stmt::DynamicJumpTarget(_, Relocation { field_offset, ref_offset, .. } )
| Stmt::ValueJumpTarget(_, Relocation { field_offset, ref_offset, .. } ) => {
*field_offset = change - *field_offset;
*ref_offset = change - *ref_offset;
},
_ => unreachable!()
}
reversed.push(stmt);
}
relocation_buf = new_relocation_buf;
}
reversed
}
pub fn expr_string_from_ident(i: &syn::Ident) -> TokenTree {
let name = i.to_string();
proc_macro2::Literal::string(&name).into()
}
pub fn expr_add_many<T: Iterator<Item=TokenTree>>(span: Span, mut exprs: T) -> Option<TokenTree> {
let first_expr = exprs.next()?;
let tokens = quote_spanned!{ span=>
#first_expr #( + #exprs )*
};
Some(delimited(tokens))
}
pub fn expr_size_of_scale(ty: &syn::Path, value: &TokenTree, size: Size) -> TokenTree {
let span = value.span();
let size = size.as_literal();
delimited(quote_spanned! { span=>
(::std::mem::size_of::<#ty>() as #size) * #value
})
}
pub fn expr_offset_of(path: &syn::Path, attr: &syn::Ident, size: Size) -> TokenTree {
let span = path.span();
let size = size.as_literal();
delimited(quote_spanned! { span=>
::std::mem::offset_of!(#path, #attr) as #size
})
}
pub fn expr_size_of(path: &syn::Path) -> TokenTree {
let span = path.span();
delimited(quote_spanned! { span=>
::std::mem::size_of::<#path>()
})
}
pub fn reparse(tt: &TokenTree) -> parse::Result<syn::Expr> {
syn::parse2(tt.into_token_stream())
}