#![cfg_attr(feature = "filelocal", feature(proc_macro_span))]
extern crate proc_macro;
use syn::parse;
use syn::{Token, parse_macro_input};
use proc_macro2::{TokenTree, TokenStream};
use quote::quote;
use proc_macro_error2::proc_macro_error;
use std::collections::HashMap;
#[cfg(feature = "filelocal")]
use std::sync::{MutexGuard, Mutex};
#[cfg(feature = "filelocal")]
use std::path::PathBuf;
#[cfg(any(feature = "filelocal", feature = "dynasm_opmap", feature = "dynasm_extract"))]
use proc_macro2::Span;
mod common;
mod arch;
mod directive;
mod serialize;
mod parse_helpers;
#[proc_macro]
#[proc_macro_error]
pub fn dynasm(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
let dynasm = parse_macro_input!(tokens as Dynasm);
serialize::serialize(&dynasm.target, dynasm.stmts).into()
}
#[proc_macro]
#[proc_macro_error]
pub fn dynasm_backwards(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
let dynasm = parse_macro_input!(tokens as Dynasm);
let stmts = serialize::invert(dynasm.stmts);
serialize::serialize(&dynasm.target, stmts).into()
}
struct Dynasm {
target: TokenTree,
stmts: Vec<common::Stmt>
}
impl parse::Parse for Dynasm {
fn parse(input: parse::ParseStream) -> parse::Result<Self> {
let target: syn::Expr = input.parse()?;
let target = common::delimited(target);
let mut provider = ContextProvider::new();
let invocation_context = provider.get_context_mut();
let mut stmts = Vec::new();
while !input.is_empty() {
let _: Token![;] = input.parse()?;
if input.peek(Token![;]) {
let _: Token![;] = input.parse()?;
let mut buffer = TokenStream::new();
while !(input.is_empty() || input.peek(Token![;])) {
buffer.extend(std::iter::once(input.parse::<TokenTree>()?));
}
buffer.extend(quote! { ; } );
if !buffer.is_empty() {
let stmt: syn::Stmt = syn::parse2(buffer)?;
stmts.push(common::Stmt::Stmt(quote!{ #stmt }));
}
continue;
}
if input.peek(Token![->]) {
let _: Token![->] = input.parse()?;
let name: syn::Ident = input.parse()?;
let _: Token![:] = input.parse()?;
stmts.push(common::Stmt::GlobalLabel(name));
continue;
}
if input.peek(Token![=>]) {
let _: Token![=>] = input.parse()?;
let expr: syn::Expr = input.parse()?;
stmts.push(common::Stmt::DynamicLabel(common::delimited(expr)));
continue;
}
if input.peek(syn::Ident) && input.peek2(Token![:]) {
let name: syn::Ident = input.parse()?;
let _: Token![:] = input.parse()?;
stmts.push(common::Stmt::LocalLabel(name));
continue;
}
if input.peek(Token![.]) {
let _: Token![.] = input.parse()?;
directive::evaluate_directive(invocation_context, &mut stmts, input)?;
} else {
let mut state = State {
stmts: &mut stmts,
invocation_context: &*invocation_context,
};
invocation_context.current_arch.compile_instruction(&mut state, input)?;
}
}
Ok(Dynasm {
target,
stmts
})
}
}
#[cfg(feature = "dynasm_opmap")]
#[proc_macro]
pub fn dynasm_opmap(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
let opmap = parse_macro_input!(tokens as DynasmOpmap);
let mut s = String::new();
s.push_str("% Instruction Reference\n\n");
s.push_str(&match opmap.arch.as_str() {
"x64" | "x86" => arch::x64::create_opmap(),
"aarch64" => arch::aarch64::create_opmap(),
"riscv" => arch::riscv::create_opmap(),
x => panic!("Unknown architecture {}", x)
});
let token = quote::quote_spanned! { Span::mixed_site()=>
#s
};
token.into()
}
#[cfg(feature = "dynasm_extract")]
#[proc_macro]
pub fn dynasm_extract(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
let opmap = parse_macro_input!(tokens as DynasmOpmap);
let s = match opmap.arch.as_str() {
"x64" | "x86" => "UNIMPLEMENTED".into(),
"aarch64" => arch::aarch64::extract_opmap(),
"riscv" => arch::riscv::extract_opmap(),
x => panic!("Unknown architecture {}", x)
};
let token = quote::quote_spanned! { Span::mixed_site()=>
#s
};
token.into()
}
#[cfg(any(feature="dynasm_extract", feature="dynasm_opmap"))]
struct DynasmOpmap {
pub arch: String
}
#[cfg(any(feature="dynasm_extract", feature="dynasm_opmap"))]
impl parse::Parse for DynasmOpmap {
fn parse(input: parse::ParseStream) -> parse::Result<Self> {
let arch: syn::Ident = input.parse()?;
Ok(DynasmOpmap {
arch: arch.to_string()
})
}
}
struct State<'a> {
pub stmts: &'a mut Vec<common::Stmt>,
pub invocation_context: &'a DynasmContext,
}
struct DynasmContext {
pub current_arch: Box<dyn arch::Arch>,
pub aliases: HashMap<String, String>,
}
impl DynasmContext {
fn new() -> DynasmContext {
DynasmContext {
current_arch: arch::from_str(arch::CURRENT_ARCH).expect("Invalid default architecture"),
aliases: HashMap::new()
}
}
}
#[cfg(not(feature = "filelocal"))]
struct ContextProvider {
context: DynasmContext
}
#[cfg(not(feature = "filelocal"))]
impl ContextProvider {
pub fn new() -> ContextProvider {
ContextProvider {
context: DynasmContext::new()
}
}
pub fn get_context_mut(&mut self) -> &mut DynasmContext {
&mut self.context
}
}
#[cfg(feature = "filelocal")]
struct ContextProvider {
guard: MutexGuard<'static, HashMap<PathBuf, DynasmContext>>
}
#[cfg(feature = "filelocal")]
impl ContextProvider {
pub fn new() -> ContextProvider {
ContextProvider {
guard: CONTEXT_STORAGE.lock().unwrap()
}
}
pub fn get_context_mut(&mut self) -> &mut DynasmContext {
let span = Span::call_site().unstable();
let id = span.source_file().path();
self.guard.entry(id).or_insert_with(DynasmContext::new)
}
}
#[cfg(feature = "filelocal")]
lazy_static::lazy_static! {
static ref CONTEXT_STORAGE: Mutex<HashMap<PathBuf, DynasmContext>> = Mutex::new(HashMap::new());
}