use proc_macro2::{Span, TokenTree, TokenStream, Literal, Group, Delimiter};
use quote::ToTokens;
use syn::spanned::Spanned;
use syn::parse;
use syn::Token;
use crate::parse_helpers::{ParseOpt, eat_pseudo_keyword};
#[allow(non_camel_case_types)]
#[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash, Clone, Copy)]
pub enum Size {
BYTE = 1,
B_2 = 2,
B_4 = 4,
B_6 = 6,
B_8 = 8,
B_10 = 10,
B_16 = 16,
B_32 = 32,
B_64 = 64,
}
impl Size {
pub fn in_bytes(self) -> u8 {
self as u8
}
pub fn as_literal(self) -> syn::Ident {
syn::Ident::new(match self {
Size::BYTE => "i8",
Size::B_2 => "i16",
Size::B_4 => "i32",
Size::B_6 => "i48",
Size::B_8 => "i64",
Size::B_10 => "i80",
Size::B_16 => "i128",
Size::B_32 => "i256",
Size::B_64 => "i512",
}, Span::mixed_site())
}
}
#[derive(Debug, Clone)]
pub struct JumpTarget {
pub kind: JumpTargetKind,
pub offset: Option<syn::Expr>
}
#[derive(Debug, Clone)]
pub enum JumpTargetKind {
Global(syn::Ident),
Backward(syn::Ident),
Forward(syn::Ident),
Dynamic(TokenTree),
Absolute(TokenTree),
Relative(TokenTree),
}
impl ParseOpt for JumpTarget {
fn parse(input: parse::ParseStream) -> parse::Result<Option<JumpTarget>> {
if eat_pseudo_keyword(input, "extern") {
let expr: syn::Expr = input.parse()?;
return Ok(Some(JumpTarget { kind: JumpTargetKind::Absolute(delimited(expr)), offset: None }));
}
let kind = if input.peek(Token![->]) {
let _: Token![->] = input.parse()?;
let name: syn::Ident = input.parse()?;
JumpTargetKind::Global(name)
} else if input.peek(Token![>]) {
let _: Token![>] = input.parse()?;
let name: syn::Ident = input.parse()?;
JumpTargetKind::Forward(name)
} else if input.peek(Token![<]) {
let _: Token![<] = input.parse()?;
let name: syn::Ident = input.parse()?;
JumpTargetKind::Backward(name)
} else if input.peek(Token![=>]) {
let _: Token![=>] = input.parse()?;
let expr: syn::Expr = if input.peek(syn::token::Paren) {
let inner;
let _ = syn::parenthesized!(inner in input);
let inner = &inner;
inner.parse()?
} else {
input.parse()?
};
JumpTargetKind::Dynamic(delimited(expr))
} else {
return Ok(None);
};
let offset = if input.peek(Token![-]) || input.peek(Token![+]) {
if input.peek(Token![+]) {
let _: Token![+] = input.parse()?;
}
let expr: syn::Expr = input.parse()?;
Some(expr)
} else {
None
};
Ok(Some(JumpTarget::new(kind, offset)))
}
}
impl JumpTarget {
pub fn new(kind: JumpTargetKind, offset: Option<syn::Expr>) -> JumpTarget {
JumpTarget {
kind,
offset
}
}
pub fn target_is_absolute(&self) -> bool {
match self.kind {
JumpTargetKind::Absolute(_) => true,
_ => false
}
}
pub fn encode(self, field_offset: u8, ref_offset: u8, relative_encoding: bool, encoding: RelocationEncoding) -> Stmt {
let kind = match (relative_encoding, self.target_is_absolute()) {
(true, false) => RelocationKind::Relative,
(true, true) => RelocationKind::RelToAbs,
(false, false) => RelocationKind::AbsToRel,
(false, true) => RelocationKind::Absolute,
};
let target_offset = if let Some(offset) = self.offset {
delimited(offset)
} else {
TokenTree::Literal(Literal::isize_suffixed(0))
};
let relocation = Relocation {
target_offset,
field_offset,
ref_offset,
kind,
encoding
};
match self.kind {
JumpTargetKind::Global(ident) => Stmt::GlobalJumpTarget(ident, relocation),
JumpTargetKind::Backward(ident) => Stmt::BackwardJumpTarget(ident, relocation),
JumpTargetKind::Forward(ident) => Stmt::ForwardJumpTarget(ident, relocation),
JumpTargetKind::Dynamic(expr) => Stmt::DynamicJumpTarget(expr, relocation),
JumpTargetKind::Absolute(expr)
| JumpTargetKind::Relative(expr) => Stmt::ValueJumpTarget(expr, relocation),
}
}
pub fn span(&self) -> Span {
match &self.kind {
JumpTargetKind::Global(ident)
| JumpTargetKind::Backward(ident)
| JumpTargetKind::Forward(ident) => ident.span(),
JumpTargetKind::Dynamic(expr)
| JumpTargetKind::Absolute(expr)
| JumpTargetKind::Relative(expr) => expr.span(),
}
}
}
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum RelocationKind {
Relative = 0,
AbsToRel = 1,
RelToAbs = 2,
Absolute = 3,
}
#[derive(Debug, Clone, Copy)]
pub enum RelocationEncoding {
Simple(Size),
Custom(u8)
}
impl RelocationEncoding {
pub fn encode(&self, kind: RelocationKind) -> u8 {
(match self {
RelocationEncoding::Simple(size) => match size {
Size::BYTE => 0,
Size::B_2 => 1,
Size::B_4 => 2,
Size::B_8 => 3,
_ => panic!("Unencodable size given for simple relocation")
},
RelocationEncoding::Custom(code) => {
(code + 4) & 0x3F
}
}) | ((kind as u8) << 6)
}
}
#[derive(Debug, Clone)]
pub struct Relocation {
pub target_offset: TokenTree,
pub field_offset: u8,
pub ref_offset: u8,
pub kind: RelocationKind,
pub encoding: RelocationEncoding
}
#[derive(Debug, Clone)]
pub enum Stmt {
Const(u64, Size),
ExprUnsigned(TokenTree, Size),
ExprSigned(TokenTree, Size),
Extend(Vec<u8>),
ExprExtend(TokenTree),
Align(TokenTree, TokenTree),
GlobalLabel(syn::Ident),
LocalLabel(syn::Ident),
DynamicLabel(TokenTree),
GlobalJumpTarget(syn::Ident, Relocation),
ForwardJumpTarget(syn::Ident, Relocation),
BackwardJumpTarget(syn::Ident, Relocation),
DynamicJumpTarget(TokenTree, Relocation),
ValueJumpTarget(TokenTree, Relocation),
Stmt(TokenStream)
}
impl Stmt {
#![allow(dead_code)]
pub fn u8(value: u8) -> Stmt {
Stmt::Const(u64::from(value), Size::BYTE)
}
pub fn u16(value: u16) -> Stmt {
Stmt::Const(u64::from(value), Size::B_2)
}
pub fn u32(value: u32) -> Stmt {
Stmt::Const(u64::from(value), Size::B_4)
}
pub fn u64(value: u64) -> Stmt {
Stmt::Const(value, Size::B_8)
}
}
pub fn delimited<T: ToTokens>(expr: T) -> TokenTree {
let stream = expr.into_token_stream();
let mut iter = stream.clone().into_iter();
let first = iter.next().unwrap();
if iter.next().is_none() {
return first;
}
let span = stream.span();
let mut group = Group::new(
proc_macro2::Delimiter::Parenthesis, stream
);
group.set_span(span);
TokenTree::Group(group)
}
pub fn is_parenthesized(group: &Group) -> bool {
if group.delimiter() != Delimiter::Parenthesis {
return false
}
for item in group.stream() {
if let TokenTree::Punct(punct) = item {
if punct.as_char() == ',' {
return false
}
}
}
true
}
pub fn strip_parenthesis(expr: &mut TokenTree) {
if let TokenTree::Group(group) = &*expr {
if is_parenthesized(group) {
let mut stripped = TokenTree::Group(Group::new(Delimiter::None, group.stream()));
stripped.set_span(group.span());
*expr = stripped;
}
}
}
pub fn bitmask(scale: u8) -> u32 {
1u32.checked_shl(u32::from(scale)).unwrap_or(0).wrapping_sub(1)
}
pub fn bitmask64(scale: u8) -> u64 {
1u64.checked_shl(u32::from(scale)).unwrap_or(0).wrapping_sub(1)
}