Ability to read ABI from string

This commit is contained in:
Mark Poliakov 2024-03-12 13:17:17 +02:00
parent aab7ac67c3
commit 635bf51bb1
5 changed files with 66 additions and 139 deletions

10
Cargo.lock generated
View File

@ -12,16 +12,6 @@ dependencies = [
"thiserror",
]
[[package]]
name = "abi-kernel-macros"
version = "0.1.0"
dependencies = [
"abi-generator",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "abi-lib"
version = "0.1.0"

View File

@ -10,4 +10,4 @@ syn = { version = "2.0.32", features = ["full"] }
thiserror = "1.0.47"
[workspace]
members = ["abi-lib", "example-abi", "abi-kernel-macros"]
members = ["abi-lib", "example-abi"]

View File

@ -1,14 +0,0 @@
[package]
name = "abi-kernel-macros"
version = "0.1.0"
edition = "2021"
[dependencies]
proc-macro2 = "1.0.78"
quote = "1.0.35"
syn = { version = "2.0.52", features = ["full"] }
abi-generator = { path = ".." }
[lib]
proc-macro = true

View File

@ -1,106 +0,0 @@
use abi_generator::{
abi::{ty::TypeWidth, Abi},
syntax::UnwrapFancy,
TargetEnv,
};
use proc_macro::TokenStream as TokenStream1;
use proc_macro2::Span;
use quote::quote;
use syn::{parse_macro_input, punctuated::Punctuated, Token};
enum DispatcherAbiPath {
Env(syn::LitStr),
}
struct DispatcherInput {
abi_document_path: DispatcherAbiPath,
dispatcher_name: syn::Ident,
impl_module: syn::Path,
}
impl syn::parse::Parse for DispatcherAbiPath {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let key = input.parse::<syn::Ident>()?;
input.parse::<Token![=]>()?;
let value = input.parse::<syn::LitStr>()?;
match key.to_string().as_str() {
"env" => Ok(Self::Env(value)),
_ => todo!(),
}
}
}
impl syn::parse::Parse for DispatcherInput {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let abi_document_path = input.parse()?;
input.parse::<Token![,]>()?;
let dispatcher_name = input.parse()?;
input.parse::<Token![,]>()?;
let impl_module = input.parse()?;
Ok(Self {
abi_document_path,
dispatcher_name,
impl_module,
})
}
}
impl DispatcherAbiPath {
pub fn value(&self) -> String {
match self {
Self::Env(var) => std::env::var(var.value()).unwrap(),
}
}
}
#[proc_macro]
pub fn syscall_dispatcher(input: TokenStream1) -> TokenStream1 {
let input = parse_macro_input!(input as DispatcherInput);
let abi = Abi::read(input.abi_document_path.value()).unwrap_fancy("TODO: proper error here");
let env = TargetEnv {
thin_pointer_width: TypeWidth::U64,
fat_pointer_width: TypeWidth::U128,
};
let handler_module = input.impl_module;
let raw_fn = syn::Ident::new("__fn", Span::call_site());
let raw_args = syn::Ident::new("__args", Span::call_site());
let mut branches = Punctuated::<_, Token![,]>::new();
for (enum_variant, syscall) in abi.syscalls.iter() {
let named_args = (0..syscall.args.len())
.map(|i| syn::Ident::new(&format!("__a{}", i), Span::call_site()))
.collect::<Vec<_>>();
let call_args = named_args
.iter()
.cloned()
.collect::<Punctuated<_, Token![,]>>();
let syscall_name = &syscall.name;
let handler_fn = quote! { #handler_module::#syscall_name };
let body = quote! {
Ok((#handler_fn(#call_args)).into_syscall_register())
};
let match_arm =
syscall.emit_handler_branch(&named_args, &raw_args, body, enum_variant, &env);
branches.push(match_arm);
}
let dispatcher_name = input.dispatcher_name;
let dispatcher_fn = quote! {
pub(crate) fn #dispatcher_name(#raw_fn: SyscallFunction, #raw_args: &[usize]) -> Result<usize, Error> {
match #raw_fn {
#branches
}
}
};
dispatcher_fn.into()
}

View File

@ -1,12 +1,8 @@
use std::{collections::HashMap, path::Path, rc::Rc};
use proc_macro2::TokenStream;
use proc_macro2::{Span, TokenStream};
use quote::{quote, TokenStreamExt};
use syn::{
punctuated::Punctuated,
spanned::Spanned,
token::{self},
};
use syn::{punctuated::Punctuated, spanned::Spanned, token, Token};
pub mod ty;
@ -92,10 +88,67 @@ pub fn syscall_enum_variant_identifier(name: &syn::Ident) -> syn::Ident {
impl AbiBuilder {
pub fn from_file<P: AsRef<Path>>(path: P, target_env: TargetEnv) -> Result<Self, ParseError> {
let abi = Abi::read(path)?;
let abi = Abi::read_file(path)?;
Ok(Self { abi, target_env })
}
pub fn from_string<S: AsRef<str>>(data: S, target_env: TargetEnv) -> Result<Self, ParseError> {
let abi = Abi::read_string(data)?;
Ok(Self { abi, target_env })
}
pub fn emit_syscall_dispatcher(
&self,
dispatcher_fn: &str,
impl_mod: &str,
) -> Result<syn::File, ParseError> {
let dispatcher_fn = syn::Ident::new(dispatcher_fn, Span::call_site());
let impl_mod = syn::Ident::new(impl_mod, Span::call_site());
let raw_fn = syn::Ident::new("__fn", Span::call_site());
let raw_args = syn::Ident::new("__args", Span::call_site());
let mut branches = Punctuated::<_, Token![,]>::new();
for (enum_variant, syscall) in self.abi.syscalls.iter() {
let named_args = (0..syscall.args.len())
.map(|i| syn::Ident::new(&format!("__a{}", i), Span::call_site()))
.collect::<Vec<_>>();
let call_args = named_args
.iter()
.cloned()
.collect::<Punctuated<_, Token![,]>>();
let syscall_name = &syscall.name;
let handler_fn = quote! { #impl_mod::#syscall_name };
let body = quote! {
Ok((#handler_fn(#call_args)).into_syscall_register())
};
let match_arm = syscall.emit_handler_branch(
&named_args,
&raw_args,
body,
enum_variant,
&self.target_env,
);
branches.push(match_arm);
}
let dispatcher_fn = quote! {
pub(crate) fn #dispatcher_fn(#raw_fn: SyscallFunction, #raw_args: &[usize]) -> Result<usize, Error> {
match #raw_fn {
#branches
}
}
};
let file = syn::parse_file(&dispatcher_fn.to_string())?;
Ok(file)
}
pub fn emit_file(&self, emit_types: bool, emit_calls: bool) -> Result<syn::File, ParseError> {
let mut stream = TokenStream::new();
@ -188,10 +241,14 @@ impl AbiBuilder {
}
impl Abi {
pub fn read<P: AsRef<Path>>(path: P) -> Result<Self, ParseError> {
pub fn read_file<P: AsRef<Path>>(path: P) -> Result<Self, ParseError> {
let data = std::fs::read_to_string(path)?;
parse_abi(&data)
}
pub fn read_string<S: AsRef<str>>(data: S) -> Result<Self, ParseError> {
parse_abi(data.as_ref())
}
}
impl GenerateTypeDefinition for EnumType {