Add 'tool/abi-generator/' from commit '635bf51bb1cde626d477df477604d58d107bc037'

git-subtree-dir: tool/abi-generator
git-subtree-mainline: 7a0d528cdabd0ff344911b64e447a21e40bcbeaa
git-subtree-split: 635bf51bb1cde626d477df477604d58d107bc037
This commit is contained in:
Mark Poliakov 2024-03-12 16:13:37 +02:00
commit d9b70439b8
28 changed files with 3037 additions and 0 deletions

1
tool/abi-generator/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

110
tool/abi-generator/Cargo.lock generated Normal file
View File

@ -0,0 +1,110 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "abi-generator"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
"thiserror",
]
[[package]]
name = "abi-lib"
version = "0.1.0"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
]
[[package]]
name = "compiler_builtins"
version = "0.1.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d68bc55329711cd719c2687bb147bc06211b0521f97ef398280108ccb23227e9"
dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "example-abi"
version = "0.1.0"
dependencies = [
"abi-generator",
"abi-lib",
"prettyplease",
]
[[package]]
name = "prettyplease"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustc-std-workspace-core"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1956f5517128a2b6f23ab2dadf1a976f4f5b27962e7724c2bf3d45e539ec098c"
[[package]]
name = "syn"
version = "2.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"

View File

@ -0,0 +1,13 @@
[package]
name = "abi-generator"
version = "0.1.0"
edition = "2021"
[dependencies]
proc-macro2 = "^1.0.63"
quote = "^1.0"
syn = { version = "2.0.32", features = ["full"] }
thiserror = "1.0.47"
[workspace]
members = ["abi-lib", "example-abi"]

View File

@ -0,0 +1,15 @@
[package]
name = "abi-lib"
version = "0.1.0"
edition = "2021"
[dependencies]
core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" }
compiler_builtins = { version = "0.1", optional = true }
[features]
default = []
rustc-dep-of-std = [
"core",
"compiler_builtins/rustc-dep-of-std",
]

View File

@ -0,0 +1,19 @@
#![feature(never_type)]
#![no_std]
mod primitive_impls;
mod wrapper_impls;
pub trait SyscallRegister {
fn into_syscall_register(self) -> usize;
fn from_syscall_register(value: usize) -> Self;
}
pub trait SyscallFatRegister {
fn as_syscall_meta(&self) -> usize;
fn as_syscall_data(&self) -> usize;
fn from_syscall_registers(meta: usize, data: usize) -> Self;
}
pub unsafe trait ThinWrapped: SyscallRegister {}

View File

@ -0,0 +1,56 @@
use core::num::NonZeroUsize;
use crate::{SyscallRegister, ThinWrapped};
macro_rules! numeric_impl {
($($ty:ident),+ $(,)?) => {
$(
unsafe impl ThinWrapped for $ty {}
impl SyscallRegister for $ty {
fn into_syscall_register(self) -> usize {
self as _
}
fn from_syscall_register(value: usize) -> Self {
value as _
}
}
)+
};
}
numeric_impl!(u8, u16, u32, u64);
numeric_impl!(i8, i16, i32, i64);
unsafe impl ThinWrapped for () {}
impl SyscallRegister for NonZeroUsize {
fn from_syscall_register(value: usize) -> Self {
unsafe { Self::new_unchecked(value) }
}
fn into_syscall_register(self) -> usize {
self.into()
}
}
impl SyscallRegister for () {
fn into_syscall_register(self) -> usize {
0
}
fn from_syscall_register(_value: usize) -> Self {
()
}
}
impl SyscallRegister for ! {
fn from_syscall_register(_value: usize) -> Self {
unreachable!()
}
fn into_syscall_register(self) -> usize {
self
}
}

View File

@ -0,0 +1,95 @@
use core::num::NonZeroUsize;
use crate::{SyscallRegister, ThinWrapped};
pub fn make_error<E: SyscallRegister>(e: E) -> usize {
(-(e.into_syscall_register() as isize)) as usize
}
pub fn get_error<E: SyscallRegister>(value: usize) -> E {
E::from_syscall_register((-(value as isize)) as usize)
}
// Result<usize> is special: usize is not thinly-wrapped, but is represented in the ABI similar
// to Unix ssize_t-returning system calls
impl<E: SyscallRegister> SyscallRegister for Result<usize, E> {
fn into_syscall_register(self) -> usize {
match self {
Ok(value) => value,
Err(err) => make_error(err),
}
}
fn from_syscall_register(value: usize) -> Self {
if (value as isize) < 0 {
Err(get_error(value))
} else {
Ok(value as _)
}
}
}
impl<T: ThinWrapped> SyscallRegister for Option<T> {
fn into_syscall_register(self) -> usize {
match self {
None => usize::MAX,
Some(value) => value.into_syscall_register(),
}
}
fn from_syscall_register(value: usize) -> Self {
match value {
usize::MAX => None,
_ => Some(T::from_syscall_register(value)),
}
}
}
impl<T: ThinWrapped, E: SyscallRegister> SyscallRegister for Result<T, E> {
fn into_syscall_register(self) -> usize {
match self {
Ok(value) => value.into_syscall_register(),
Err(err) => make_error(err),
}
}
fn from_syscall_register(value: usize) -> Self {
if (value as isize) < 0 {
Err(get_error(value))
} else {
Ok(T::from_syscall_register(value))
}
}
}
impl SyscallRegister for Option<NonZeroUsize> {
fn into_syscall_register(self) -> usize {
match self {
Some(value) => value.into(),
None => 0,
}
}
fn from_syscall_register(value: usize) -> Self {
NonZeroUsize::new(value)
}
}
impl<E: SyscallRegister> SyscallRegister for Result<NonZeroUsize, E> {
fn into_syscall_register(self) -> usize {
match self {
Ok(value) => value.into(),
Err(err) => make_error(err),
}
}
fn from_syscall_register(value: usize) -> Self {
if (value as isize) < 0 {
Err(get_error(value))
} else if value != 0 {
Ok(unsafe { NonZeroUsize::new_unchecked(value) })
} else {
unreachable!()
}
}
}

View File

@ -0,0 +1,11 @@
[package]
name = "example-abi"
version = "0.1.0"
edition = "2021"
[dependencies]
abi-lib = { path = "../abi-lib" }
[build-dependencies]
abi-generator = { path = ".." }
prettyplease = "0.2.15"

View File

@ -0,0 +1,34 @@
use std::{env, path::Path};
use abi_generator::{
abi::{ty::TypeWidth, AbiBuilder},
syntax::UnwrapFancy,
TargetEnv,
};
fn main() {
println!("cargo:rerun-if-changed={}", "syscall.abi");
let output_dir = env::var("OUT_DIR").unwrap();
let output = Path::new(&output_dir);
let abi = AbiBuilder::from_file(
"syscall.abi",
TargetEnv {
thin_pointer_width: TypeWidth::U64,
fat_pointer_width: TypeWidth::U128,
},
)
.unwrap_fancy("syscall.abi");
let types_file = prettyplease::unparse(
&abi.emit_file(true, false)
.unwrap_fancy("Could not emit generated types"),
);
let syscalls_file = prettyplease::unparse(
&abi.emit_file(false, true)
.unwrap_fancy("Could not emit generated syscalls"),
);
std::fs::write(output.join("generated_types.rs"), types_file).unwrap();
std::fs::write(output.join("generated_syscalls.rs"), syscalls_file).unwrap();
}

View File

@ -0,0 +1,152 @@
#![feature(exposed_provenance)]
use std::sync::Mutex;
mod types {
use std::marker::PhantomData;
use abi_lib::{SyscallFatRegister, SyscallRegister};
pub enum MappingSource {}
pub struct MountOptions;
pub struct UnmountOptions;
pub struct DirectoryEntry;
pub struct FileAttr;
pub struct SpawnOptions<'a> {
_a: PhantomData<&'a ()>,
}
pub struct ThreadSpawnOptions;
pub enum MutexOperation {}
pub struct SignalEntryData;
pub struct DeviceRequest;
pub enum SentMessage {}
pub enum ReceivedMessageMetadata {}
pub struct TerminalOptions;
pub struct TerminalSize;
pub struct ExecveOptions;
pub struct FileMetadataUpdate;
pub enum ProcessInfoElement {}
pub enum SocketOption {}
pub enum SystemInfo {}
pub enum ExitCode {
Exited(u32),
Killed(u32),
}
pub enum SeekFrom {
Start(u64),
Current(i64),
End(i64),
}
impl SyscallRegister for ExitCode {
fn into_syscall_register(self) -> usize {
match self {
Self::Killed(value) => (value as usize) | (1 << u32::BITS),
Self::Exited(value) => value as usize,
}
}
fn from_syscall_register(value: usize) -> Self {
if value & (1 << u32::BITS) != 0 {
Self::Killed(value as _)
} else {
Self::Exited(value as _)
}
}
}
impl SyscallFatRegister for SeekFrom {
fn as_syscall_meta(&self) -> usize {
match self {
Self::Start(_) => 0,
Self::Current(_) => 1,
Self::End(_) => 2,
}
}
fn as_syscall_data(&self) -> usize {
match self {
&Self::Start(value) => value as _,
&Self::Current(value) | &Self::End(value) => value as _,
}
}
fn from_syscall_registers(_meta: usize, _data: usize) -> Self {
todo!()
}
}
include!(concat!(env!("OUT_DIR"), "/generated_types.rs"));
}
mod calls {
use super::types::*;
macro_rules! syscall {
($f:expr $(,)?) => {
$crate::syscall0($f)
};
($f:expr, $a0:expr $(,)?) => {
$crate::syscall1($f, $a0)
};
($f:expr, $a0:expr, $a1:expr $(,)?) => {
$crate::syscall2($f, $a0, $a1)
};
($f:expr, $a0:expr, $a1:expr, $a2:expr $(,)?) => {
$crate::syscall3($f, $a0, $a1, $a2)
};
($f:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr $(,)?) => {
$crate::syscall4($f, $a0, $a1, $a2, $a3)
};
($f:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr $(,)?) => {
$crate::syscall5($f, $a0, $a1, $a2, $a3, $a4)
};
}
include!(concat!(env!("OUT_DIR"), "/generated_syscalls.rs"));
}
pub use calls::*;
pub use types::*;
static SYSCALL_TRACE: Mutex<Vec<(SyscallFunction, Vec<usize>)>> = Mutex::new(Vec::new());
fn syscall0(f: SyscallFunction) -> usize {
SYSCALL_TRACE.lock().unwrap().push((f, vec![]));
0
}
fn syscall1(f: SyscallFunction, a0: usize) -> usize {
SYSCALL_TRACE.lock().unwrap().push((f, vec![a0]));
0
}
fn syscall2(f: SyscallFunction, a0: usize, a1: usize) -> usize {
SYSCALL_TRACE.lock().unwrap().push((f, vec![a0, a1]));
0
}
fn syscall3(f: SyscallFunction, a0: usize, a1: usize, a2: usize) -> usize {
SYSCALL_TRACE.lock().unwrap().push((f, vec![a0, a1, a2]));
0
}
fn syscall4(f: SyscallFunction, a0: usize, a1: usize, a2: usize, a3: usize) -> usize {
SYSCALL_TRACE
.lock()
.unwrap()
.push((f, vec![a0, a1, a2, a3]));
0
}
fn syscall5(f: SyscallFunction, a0: usize, a1: usize, a2: usize, a3: usize, a4: usize) -> usize {
SYSCALL_TRACE
.lock()
.unwrap()
.push((f, vec![a0, a1, a2, a3, a4]));
0
}
fn main() {}

View File

@ -0,0 +1,201 @@
// vi:syntax=yggdrasil_abi:
extern {
type Duration = core::time::Duration;
type Mutex = core::sync::atomic::AtomicU32;
type SocketAddr = core::net::SocketAddr;
type SystemInfo;
type MappingSource;
type SpawnOptions;
type ThreadSpawnOptions;
type MutexOperation;
type SignalEntryData;
type ProcessInfoElement;
type ExecveOptions;
type MountOptions;
type UnmountOptions;
type DirectoryEntry;
type FileAttr;
type DeviceRequest;
type SentMessage;
type ReceivedMessageMetadata;
type TerminalOptions;
type TerminalSize;
type FileMetadataUpdate;
type SocketOption;
#[thin]
type ExitCode;
#[fat]
type SeekFrom;
}
newtype Fd(u32);
newtype ProcessId(u32);
newtype ThreadId(u32);
newtype ProcessGroupId(u32);
// TODO define these as enums
newtype MessageDestination(u32);
bitfield OpenOptions(u32) {
/// Open the file with read capability
READ: 0,
/// Open the file with write capability
WRITE: 1,
/// Position at the end of the opened file
APPEND: 2,
/// Truncate the file to zero bytes when opening
TRUNCATE: 3,
/// Create a new file if it does not exist
CREATE: 4,
}
bitfield FileMode(u32) {
/// Other user execute access
OTHER_EXEC: 0,
/// Other user write access
OTHER_WRITE: 1,
/// Other user read access
OTHER_READ: 2,
/// Group execute access
GROUP_EXEC: 0,
/// Group write access
GROUP_WRITE: 1,
/// Group read access
GROUP_READ: 2,
/// Owner execute access
USER_EXEC: 0,
/// Owner write access
USER_WRITE: 1,
/// Owner read access
USER_READ: 2,
}
enum Signal(u32) {
MemoryAccessViolation = 1,
Aborted = 2,
Killed = 3,
Interrupted = 4
}
enum PollControl(u32) {
AddFd = 1
}
enum SocketType(u32) {
RawPacket = 1,
TcpStream = 2,
UdpPacket = 3
}
enum Error(u32) {
Dummy = 1,
UndefinedSyscall = 2,
InvalidArgument = 3,
}
syscall test_syscall(a: &(u32, u64));
//
// // Misc
//
// syscall debug_trace(msg: &str);
// syscall get_random(buffer: &mut [u8]);
// syscall get_system_info(info: &mut SystemInfo) -> Result<Fd>;
//
// // Memory management
//
// syscall map_memory(hint: Option<NonZeroUsize>, len: usize, source: &MappingSource) -> Result<usize>;
// syscall unmap_memory(virt: usize, len: usize) -> Result<()>;
//
// // Process/thread management
//
// syscall spawn_process(options: &SpawnOptions<'_>) -> Result<ProcessId>;
// syscall spawn_thread(options: &ThreadSpawnOptions) -> Result<ThreadId>;
//
// syscall exit_thread(code: ExitCode) -> !;
// syscall exit_process(code: ExitCode) -> !;
// syscall wait_process(pid: ProcessId, status: &mut ExitCode) -> Result<()>;
// syscall send_signal(pid: ProcessId, signal: Signal) -> Result<()>;
// syscall nanosleep(duration: &Duration);
//
// syscall mutex(mutex: &Mutex, op: &MutexOperation) -> Result<()>;
//
// syscall set_signal_entry(entry: usize, stack: usize);
// syscall exit_signal(frame: &SignalEntryData) -> !;
//
// syscall get_pid() -> ProcessId;
//
// syscall start_session() -> Result<()>;
// syscall set_process_group_id(pid: ProcessId, pgid: Option<ProcessGroupId>) -> Result<ProcessGroupId>;
//
// syscall get_process_info(what: &mut ProcessInfoElement) -> Result<()>;
// syscall set_process_info(what: &ProcessInfoElement) -> Result<()>;
//
// // C compat
//
// syscall fork() -> Result<ProcessId>;
// syscall execve(opts: &ExecveOptions) -> Result<()>;
//
// // I/O
//
// syscall write(fd: Fd, data: &[u8]) -> Result<usize>;
// syscall read(fd: Fd, data: &mut [u8]) -> Result<usize>;
// syscall open(at: Option<Fd>, path: &str, opts: OpenOptions, mode: FileMode) -> Result<Fd>;
// syscall close(fd: Fd) -> Result<()>;
// syscall mount(options: &MountOptions) -> Result<()>;
// syscall unmount(options: &UnmountOptions) -> Result<()>;
// syscall open_directory(at: Option<Fd>, path: &str) -> Result<Fd>;
// syscall read_directory_entries(fd: Fd, buffer: &mut [MaybeUninit<DirectoryEntry>]) -> Result<usize>;
// syscall create_directory(at: Option<Fd>, path: &str, mode: FileMode) -> Result<()>;
// syscall remove(at: Option<Fd>, path: &str) -> Result<()>;
// syscall remove_directory(at: Option<Fd>, path: &str) -> Result<()>;
// syscall get_metadata(at: Option<Fd>, path: &str, metadata: &mut FileAttr, follow: bool) -> Result<()>;
// // TODO this one is broken
// syscall seek(fd: Fd, pos: SeekFrom) -> Result<usize>;
//
// syscall device_request(fd: Fd, req: &mut DeviceRequest) -> Result<()>;
//
// syscall create_pipe(ends: &mut [MaybeUninit<Fd>; 2]) -> Result<()>;
//
// syscall create_poll_channel() -> Result<Fd>;
// syscall poll_channel_control(poll_fd: Fd, ctl: PollControl, fd: Fd) -> Result<()>;
// syscall poll_channel_wait(poll_fd: Fd, timeout: &Option<Duration>, out: &mut Option<Fd>) -> Result<()>;
//
// syscall open_channel(name: &str, subscribe: bool) -> Result<Fd>;
// syscall send_message(fd: Fd, message: &SentMessage, destination: MessageDestination) -> Result<()>;
// syscall receive_message(
// fd: Fd,
// metadata: &mut MaybeUninit<ReceivedMessageMetadata>,
// buffer: &mut [u8],
// from: &mut MaybeUninit<u32>
// ) -> Result<usize>;
//
// syscall create_shared_memory(size: usize) -> Result<Fd>;
// syscall create_pty(options: &TerminalOptions, size: &TerminalSize, fds: &mut [MaybeUninit<Fd>; 2]) -> Result<()>;
//
// syscall clone_fd(source: Fd, target: Option<Fd>) -> Result<Fd>;
//
// syscall update_metadata(at: Option<Fd>, path: &str, update: &FileMetadataUpdate) -> Result<()>;
//
// syscall create_timer(repeat: bool) -> Result<Fd>;
//
// // Network
//
// syscall bind_socket(listen_address: &SocketAddr, ty: SocketType) -> Result<Fd>;
// syscall connect_socket(
// socket_fd: Option<Fd>,
// remote_address: &SocketAddr,
// ty: SocketType,
// local_address: &mut MaybeUninit<SocketAddr>
// ) -> Result<Fd>;
// syscall send_to(socket_fd: Fd, data: &[u8], recepient: &Option<SocketAddr>) -> Result<usize>;
// syscall receive_from(socket_fd: Fd, buffer: &mut [u8], sender: &mut MaybeUninit<SocketAddr>) -> Result<usize>;
// syscall get_socket_option(socket_fd: Fd, option: &mut SocketOption) -> Result<()>;
// syscall set_socket_option(socket_fd: Fd, option: &SocketOption) -> Result<()>;
// syscall accept(listener: Fd, address: &mut MaybeUninit<SocketAddr>) -> Result<Fd>;

View File

@ -0,0 +1,513 @@
use std::{collections::HashMap, path::Path, rc::Rc};
use proc_macro2::{Span, TokenStream};
use quote::{quote, TokenStreamExt};
use syn::{punctuated::Punctuated, spanned::Spanned, token, Token};
pub mod ty;
use crate::{
abi::ty::ComplexType,
syntax::{
parse_abi, Attributes, BitfieldRange, BitfieldType, BitfieldTypeField,
DocumentTypeDefinition, EnumType, EnumTypeVariant, NewType, ParseError, TypeRepr,
},
TargetEnv,
};
use self::ty::{complex::ExternKind, Type};
mod syscall;
pub use syscall::Syscall;
pub trait GenerateTypeDefinition {
fn generate_type_definition(&self, abi_type: &ComplexType, env: &TargetEnv) -> TokenStream;
}
pub struct TypeEnv {
pub locals: HashMap<String, (Rc<ComplexType>, DocumentTypeDefinition)>,
pub externs: HashMap<String, (syn::Type, ExternKind)>,
}
impl TypeEnv {
pub fn new() -> Self {
Self {
locals: HashMap::new(),
externs: HashMap::new(),
}
}
pub fn lookup_local(&self, name: &str) -> Option<&(Rc<ComplexType>, DocumentTypeDefinition)> {
self.locals.get(name)
}
pub fn lookup_extern(&self, name: &str) -> Option<&(syn::Type, ExternKind)> {
self.externs.get(name)
}
pub fn insert_local(
&mut self,
name: String,
abi_type: Rc<ComplexType>,
def: DocumentTypeDefinition,
) {
self.locals.insert(name, (abi_type, def));
}
pub fn insert_extern(&mut self, name: String, def: syn::Type, kind: ExternKind) {
self.externs.insert(name, (def, kind));
}
}
pub struct Abi {
pub types: TypeEnv,
// Contains syscalls and their corresponding SyscallFunction enum variants
pub syscalls: Vec<(syn::Ident, Syscall)>,
}
pub struct AbiBuilder {
abi: Abi,
target_env: TargetEnv,
}
pub fn syscall_enum_variant_identifier(name: &syn::Ident) -> syn::Ident {
let name = name.to_string();
let string = name.split('_').fold(String::new(), |mut acc, word| {
word.chars().enumerate().for_each(|(i, c)| {
if i == 0 {
acc.extend(c.to_uppercase());
} else {
acc.push(c);
}
});
acc
});
syn::Ident::new(string.as_str(), name.span())
}
impl AbiBuilder {
pub fn from_file<P: AsRef<Path>>(path: P, target_env: TargetEnv) -> Result<Self, ParseError> {
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();
if emit_types {
stream.extend(self.emit_syscall_enum());
stream.extend(self.emit_type_definitions());
}
if emit_calls {
// Preamble
stream.extend(quote! {
#[allow(unused_imports)]
use abi_lib::{SyscallRegister, SyscallFatRegister};
});
stream.extend(self.emit_syscalls());
}
let file = syn::parse_file(&stream.to_string())?;
Ok(file)
}
fn emit_syscall_enum(&self) -> TokenStream {
let syscall_discriminants = self
.abi
.syscalls
.iter()
.enumerate()
.map(|(index, (variant, _))| {
let index = index + 1;
quote! { #variant = #index }
})
.collect::<Punctuated<_, token::Comma>>();
let syscall_try_from_branches = self
.abi
.syscalls
.iter()
.enumerate()
.map(|(index, (variant, _))| {
let index = index + 1;
quote! { #index => Ok(Self::#variant) }
})
.collect::<Punctuated<_, token::Comma>>();
quote! {
#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)]
#[repr(usize)]
pub enum SyscallFunction {
#syscall_discriminants
}
impl From<SyscallFunction> for usize {
fn from(value: SyscallFunction) -> usize {
value as usize
}
}
impl TryFrom<usize> for SyscallFunction {
type Error = Error;
fn try_from(value: usize) -> Result<Self, Error> {
match value {
#syscall_try_from_branches,
_ => Err(Error::UndefinedSyscall)
}
}
}
}
}
fn emit_type_definitions(&self) -> TokenStream {
let mut stream = TokenStream::new();
for (_, (ty, def)) in &self.abi.types.locals {
stream.extend(def.generate_type_definition(ty, &self.target_env));
}
stream
}
fn emit_syscalls(&self) -> TokenStream {
let mut stream = TokenStream::new();
for (variant, syscall) in &self.abi.syscalls {
stream.append_all(syscall.emit_stub(variant, &self.target_env));
}
stream
}
}
impl Abi {
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 {
fn generate_type_definition(&self, abi_type: &ComplexType, env: &TargetEnv) -> TokenStream {
let Self {
attributes,
name,
repr,
variants,
} = self;
let TypeRepr(repr) = &repr;
// TODO auto-assign numbers to enum variants
let try_from_branches = variants
.iter()
.map(|variant| {
let EnumTypeVariant {
discriminant,
value,
..
} = variant;
quote! { #value => Ok(Self::#discriminant) }
})
.collect::<Punctuated<_, token::Comma>>();
let direct = quote! {
#attributes
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
#[repr(#repr)]
pub enum #name {
#variants
}
impl #name {
pub const fn into_raw(self) -> #repr {
self as #repr
}
pub const unsafe fn from_raw(value: #repr) -> Self {
core::mem::transmute(value)
}
}
impl From<#name> for #repr {
#[inline]
fn from(value: #name) -> #repr {
value as #repr
}
}
impl TryFrom<#repr> for #name {
type Error = Error;
fn try_from(value: #repr) -> Result<#name, Error> {
match value {
#try_from_branches,
_ => Err(Error::InvalidArgument)
}
}
}
impl abi_lib::SyscallRegister for #name {
fn into_syscall_register(self) -> usize {
self as #repr as usize
}
fn from_syscall_register(value: usize) -> Self {
unsafe { core::mem::transmute(value as #repr) }
}
}
};
let wrapped = if abi_type.width(env) < env.fat_pointer_width {
quote! {
#direct
unsafe impl abi_lib::ThinWrapped for #name {}
}
} else {
direct
};
wrapped
}
}
fn generate_transparent_struct(
attributes: &Attributes,
name: &syn::Ident,
repr: &TypeRepr,
is_thin: bool,
) -> TokenStream {
let TypeRepr(repr) = &repr;
let direct = quote! {
#attributes
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct #name(pub(crate) #repr);
impl #name {
pub const fn into_raw(self) -> #repr {
self.0
}
pub const fn bits(self) -> #repr {
self.0
}
pub const unsafe fn from_raw(value: #repr) -> Self {
Self(value)
}
}
impl From<#name> for #repr {
#[inline]
fn from(value: #name) -> #repr {
value.0
}
}
impl abi_lib::SyscallRegister for #name {
fn into_syscall_register(self) -> usize {
self.0 as _
}
fn from_syscall_register(value: usize) -> Self {
Self(value as _)
}
}
};
let wrapped = if is_thin {
quote! {
#direct
unsafe impl abi_lib::ThinWrapped for #name {}
}
} else {
direct
};
wrapped
}
impl GenerateTypeDefinition for NewType {
fn generate_type_definition(&self, abi_type: &ComplexType, env: &TargetEnv) -> TokenStream {
let Self {
attributes,
name,
repr,
} = self;
generate_transparent_struct(
attributes,
name,
repr,
abi_type.width(env) < env.fat_pointer_width,
)
}
}
impl GenerateTypeDefinition for BitfieldType {
fn generate_type_definition(&self, abi_type: &ComplexType, env: &TargetEnv) -> TokenStream {
let Self {
attributes,
name,
repr,
fields,
} = self;
let base = generate_transparent_struct(
attributes,
name,
repr,
abi_type.width(env) < env.fat_pointer_width,
);
let mut field_impls = TokenStream::new();
for BitfieldTypeField {
attributes,
name,
range,
} in fields
{
match range {
BitfieldRange::Single(offset) => {
field_impls.extend(quote! {
#attributes
pub const #name: Self = Self(1 << #offset);
});
}
BitfieldRange::Range(_start, _end) => todo!(),
}
}
let base = quote! {
#base
impl #name {
#field_impls
pub const fn empty() -> Self {
Self(0)
}
pub const fn contains(&self, value: Self) -> bool {
self.0 & value.0 == value.0
}
}
impl core::ops::BitOrAssign for #name {
fn bitor_assign(&mut self, other: Self) {
self.0 |= other.0;
}
}
impl core::ops::BitAndAssign for #name {
fn bitand_assign(&mut self, other: Self) {
self.0 &= other.0;
}
}
impl core::ops::BitAnd for #name {
type Output = #name;
fn bitand(self, other: Self) -> Self {
Self(self.0 & other.0)
}
}
impl core::ops::BitOr for #name {
type Output = #name;
fn bitor(self, other: Self) -> Self {
Self(self.0 | other.0)
}
}
impl core::ops::Not for #name {
type Output = #name;
fn not(self) -> Self {
Self(!self.0)
}
}
};
base
}
}
impl GenerateTypeDefinition for DocumentTypeDefinition {
fn generate_type_definition(&self, abi_type: &ComplexType, env: &TargetEnv) -> TokenStream {
match self {
Self::Enum(inner) => inner.generate_type_definition(abi_type, env),
Self::Bitfield(inner) => inner.generate_type_definition(abi_type, env),
Self::NewType(inner) => inner.generate_type_definition(abi_type, env),
// Extern types require no definition
Self::ExternType(_) => TokenStream::new(),
}
}
}

View File

@ -0,0 +1,124 @@
use core::fmt;
use std::rc::Rc;
use proc_macro2::TokenStream;
use quote::quote;
use crate::{
abi::ty::{SimpleType, Type},
TargetEnv,
};
use super::ty::ComplexType;
pub struct Syscall {
pub name: syn::Ident,
pub args: Vec<(syn::Ident, Rc<ComplexType>)>,
pub return_type: Option<Rc<ComplexType>>,
}
impl fmt::Debug for Syscall {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Syscall")
.field("name", &self.name)
.field("args", &self.args)
.field("return_type", &self.return_type)
.finish_non_exhaustive()
}
}
impl Syscall {
// Syntax:
//
// syscall_handler!("abi-document.abi", pub syscall_handler {
// read(fd, buffer) => ...,
// write(fd, buffer) => ...,
// })
pub fn emit_handler_branch(
&self,
named_args: &[syn::Ident],
raw_args: &syn::Ident,
body: TokenStream,
enum_variant: &syn::Ident,
env: &TargetEnv,
) -> TokenStream {
if self.args.len() != named_args.len() {
todo!(
"Argument length mismatch: got {} expectd {}",
named_args.len(),
self.args.len()
)
}
let mut extracted_arguments = TokenStream::new();
let mut index = 0;
for ((_, arg), name) in std::iter::zip(self.args.iter(), named_args) {
let (arg_expr, arg_width) = arg.emit_from_syscall_arguments(env, raw_args, index);
let arg_assignment = quote! {
let #name = #arg_expr;
};
index += arg_width;
extracted_arguments.extend(arg_assignment);
}
quote! {
SyscallFunction::#enum_variant => {
#extracted_arguments
#body
}
}
}
pub fn emit_stub(&self, enum_variant: &syn::Ident, env: &TargetEnv) -> TokenStream {
let Self {
name,
args,
return_type,
} = self;
let syscall_args = args
.iter()
.map(|(name, ty)| {
let arg = ty.emit_to_syscall_arguments(env, name);
quote!(#arg,)
})
.collect::<TokenStream>();
let args = args
.iter()
.map(|(name, ty)| {
let ty = ty.as_rust_type();
quote!(#name: #ty,)
})
.collect::<TokenStream>();
let return_type_arrow = return_type.as_ref().map(|ty| {
let ty = ty.as_rust_type();
quote!(-> #ty)
});
let sys_call = quote!(syscall!(SyscallFunction::#enum_variant, #syscall_args));
let wrapped = match return_type.as_deref() {
Some(ComplexType::Simple(SimpleType::Never)) => {
quote! {
#sys_call;
unreachable!()
}
}
Some(ty) => {
let ty = ty.as_rust_type();
quote!(<#ty>::from_syscall_register(#sys_call))
}
None => quote!(#sys_call;),
};
let v = quote! {
pub unsafe fn #name(#args) #return_type_arrow {
#wrapped
}
};
v
}
}

View File

@ -0,0 +1,181 @@
use std::{fmt, rc::Rc};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{punctuated::Punctuated, Ident, Token};
use crate::TargetEnv;
use super::{AbiPrimitive, SimpleType, Type, TypeKind, TypeWidth};
#[derive(Clone, Copy)]
pub enum ExternKind {
None,
Thin,
Fat,
}
#[derive(Clone)]
pub enum ComplexType {
Simple(SimpleType),
MaybeUninit(Rc<ComplexType>),
Result(Rc<ComplexType>),
Option(Rc<ComplexType>),
Tuple(Vec<Rc<ComplexType>>),
Extern {
kind: ExternKind,
alias: syn::Type,
arguments: syn::PathArguments,
},
}
impl ComplexType {
pub fn primitive(val: AbiPrimitive) -> Self {
Self::Simple(SimpleType::Primitive(val))
}
pub fn transparent(name: Ident, val: AbiPrimitive) -> Self {
Self::Simple(SimpleType::Transparent { name, inner: val })
}
pub fn as_primitive(&self) -> Option<AbiPrimitive> {
if let Self::Simple(SimpleType::Primitive(ty)) = self {
Some(*ty)
} else {
None
}
}
}
impl Type for ComplexType {
fn width(&self, env: &TargetEnv) -> TypeWidth {
match self {
Self::Simple(ty) => ty.width(env),
Self::Option(ty) | Self::Result(ty) => match ty.kind() {
TypeKind::Other => ty.width(env).double(),
// Some(&[1, 2, 3]) -> (ptr, len), where ptr and len are non-zero
// None -> (null, 0)
TypeKind::FatPointer => env.fat_pointer_width,
// Some(&x) -> ptr, where ptr is non-zero
// None -> null
TypeKind::ThinPointer => env.thin_pointer_width,
},
Self::Tuple(_) => TypeWidth::Unknown,
Self::Extern { .. } => TypeWidth::Unknown,
// MaybeUninit has the same layout as its underlying data
Self::MaybeUninit(ty) => ty.width(env),
}
}
fn kind(&self) -> TypeKind {
match self {
Self::Simple(ty) => ty.kind(),
Self::MaybeUninit(ty) => ty.kind(),
_ => TypeKind::Other,
}
}
fn as_rust_type(&self) -> TokenStream {
match self {
Self::Simple(ty) => ty.as_rust_type(),
Self::Option(ty) => {
let ty = ty.as_rust_type();
quote!(Option<#ty>)
}
Self::Tuple(elems) => {
let elems = elems
.iter()
.map(|e| e.as_rust_type())
.collect::<Punctuated<_, Token![,]>>();
quote!((#elems))
}
Self::Result(ty) => {
let ty = ty.as_rust_type();
// TODO take from TargetEnv
quote!(Result<#ty, Error>)
}
Self::Extern {
alias, arguments, ..
} => {
quote!(#alias #arguments)
}
Self::MaybeUninit(ty) => {
let ty = ty.as_rust_type();
quote!(core::mem::MaybeUninit<#ty>)
}
}
}
fn emit_to_syscall_arguments(&self, env: &TargetEnv, value: &Ident) -> TokenStream {
// TODO: Option/Result<ExternType>
match self {
Self::Simple(ty) => ty.emit_to_syscall_arguments(env, value),
Self::Option(ty)
if ty.width(env) < env.thin_pointer_width || ty.kind() == TypeKind::ThinPointer =>
{
quote!(#value.into_syscall_register())
}
Self::Extern {
kind: ExternKind::Thin,
..
} => {
quote!(#value.into_syscall_register())
}
Self::Extern {
kind: ExternKind::Fat,
..
} => {
quote!(#value.as_syscall_meta(), #value.as_syscall_data())
}
_ => todo!("???"),
}
}
fn emit_from_syscall_arguments(
&self,
env: &TargetEnv,
args: &Ident,
index: usize,
) -> (TokenStream, usize) {
match self {
Self::Simple(ty) => ty.emit_from_syscall_arguments(env, args, index),
Self::Option(ty)
if ty.width(env) < env.thin_pointer_width || ty.kind() == TypeKind::ThinPointer =>
{
let ty = ty.as_rust_type();
(
quote!(<Option<#ty>>::from_syscall_register(#args[#index])),
1,
)
}
Self::Result(_) => todo!("Result"),
Self::Extern {
kind: ExternKind::Thin,
alias,
..
} => (quote!(<#alias>::from_syscall_register(#args[#index])), 1),
Self::MaybeUninit(_) => todo!("MaybeUninit"),
Self::Tuple(_) => unimplemented!(),
_ => todo!("{:?}", self),
}
}
}
impl fmt::Debug for ComplexType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Simple(ty) => f.debug_tuple("Simple").field(&ty).finish(),
Self::Result(ty) => f.debug_tuple("Result").field(&ty).finish(),
Self::Option(ty) => f.debug_tuple("Option").field(&ty).finish(),
Self::Tuple(elems) => {
let mut r = f.debug_tuple("Tuple");
for elem in elems {
r.field(elem);
}
r.finish()
}
Self::Extern { alias, .. } => f.debug_tuple("Extern").field(&quote!(#alias)).finish(),
Self::MaybeUninit(ty) => f.debug_tuple("MaybeUninit").field(&ty).finish(),
}
}
}

View File

@ -0,0 +1,167 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::Ident;
use crate::TargetEnv;
pub mod complex;
pub mod primitive;
pub mod simple;
pub use complex::ComplexType;
pub use primitive::AbiPrimitive;
pub use simple::SimpleType;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum TypeKind {
ThinPointer,
FatPointer,
Other,
}
pub trait Type {
fn width(&self, env: &TargetEnv) -> TypeWidth;
fn kind(&self) -> TypeKind;
fn as_rust_type(&self) -> TokenStream;
fn emit_to_syscall_arguments(&self, env: &TargetEnv, value: &Ident) -> TokenStream;
fn emit_from_syscall_arguments(
&self,
env: &TargetEnv,
args: &Ident,
index: usize,
) -> (TokenStream, usize);
fn as_rust_ref_type(&self, mutable: bool) -> TokenStream {
let inner = self.as_rust_type();
match mutable {
true => quote!(&mut #inner),
false => quote!(& #inner),
}
}
fn as_rust_slice_type(&self, mutable: bool) -> TokenStream {
let inner = self.as_rust_type();
match mutable {
true => quote!(&mut [#inner]),
false => quote!(&[#inner]),
}
}
fn as_rust_array_type(&self, mutable: bool, length: usize) -> TokenStream {
let inner = self.as_rust_type();
match mutable {
true => quote!(&mut [#inner; #length]),
false => quote!(&[#inner; #length]),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
pub enum TypeWidth {
Zero,
U8,
U16,
U32,
U64,
U128,
Unknown,
}
impl TypeWidth {
pub fn double(&self) -> Self {
match self {
Self::Zero => todo!(),
Self::U8 => Self::U16,
Self::U16 => Self::U32,
Self::U32 => Self::U64,
Self::U64 => Self::U128,
Self::U128 => todo!(),
Self::Unknown => Self::Unknown,
}
}
}
#[cfg(test)]
mod tests {
use std::rc::Rc;
use proc_macro2::Span;
use quote::quote;
use syn::Ident;
use crate::abi::ty::Type;
use super::{AbiPrimitive, ComplexType, SimpleType, TargetEnv, TypeWidth};
const TARGET_64: TargetEnv = TargetEnv {
thin_pointer_width: TypeWidth::U64,
fat_pointer_width: TypeWidth::U128,
};
#[test]
fn type_width() {
let ty = SimpleType::Reference {
mutable: false,
pointee: Rc::new(ComplexType::Simple(SimpleType::Primitive(
AbiPrimitive::U32,
))),
};
assert_eq!(ty.width(&TARGET_64), TypeWidth::U64);
let ty = SimpleType::Primitive(AbiPrimitive::U64);
assert_eq!(ty.width(&TARGET_64), TypeWidth::U64);
}
#[test]
fn option_width() {
let ty = ComplexType::Option(Rc::new(ComplexType::primitive(AbiPrimitive::U64)));
assert_eq!(ty.width(&TARGET_64), TypeWidth::U128);
let ty = ComplexType::Option(Rc::new(ComplexType::Simple(SimpleType::Reference {
mutable: true,
pointee: Rc::new(ComplexType::Simple(SimpleType::Primitive(
AbiPrimitive::U64,
))),
})));
assert_eq!(ty.width(&TARGET_64), TypeWidth::U64);
}
#[test]
fn simple_rust_types() {
let ty = ComplexType::Option(Rc::new(ComplexType::primitive(AbiPrimitive::U32)));
assert_eq!(
quote!(Option<u32>).to_string(),
ty.as_rust_type().to_string()
);
let ty = SimpleType::Reference {
mutable: true,
pointee: Rc::new(ComplexType::Simple(SimpleType::Primitive(
AbiPrimitive::U64,
))),
};
assert_eq!(quote!(&mut u64).to_string(), ty.as_rust_type().to_string());
let ty = SimpleType::Slice {
mutable: false,
element: Rc::new(ComplexType::Simple(SimpleType::Primitive(AbiPrimitive::U8))),
};
assert_eq!(quote!(&[u8]).to_string(), ty.as_rust_type().to_string());
}
#[test]
fn emit_to_syscall_arguments() {
let ty = SimpleType::Reference {
mutable: false,
pointee: Rc::new(ComplexType::Simple(SimpleType::Primitive(
AbiPrimitive::U64,
))),
};
let val = Ident::new("val0", Span::call_site());
assert_eq!(
ty.emit_to_syscall_arguments(&TARGET_64, &val).to_string(),
quote!((val0 as *const _).expose_addr()).to_string()
);
}
}

View File

@ -0,0 +1,98 @@
use std::str::FromStr;
use proc_macro2::TokenStream;
use quote::quote;
use syn::Ident;
use crate::TargetEnv;
use super::{Type, TypeKind, TypeWidth};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum AbiPrimitive {
Unit,
Bool,
U8,
I8,
U16,
I16,
U32,
I32,
U64,
I64,
USize,
}
impl Type for AbiPrimitive {
fn width(&self, env: &TargetEnv) -> TypeWidth {
match self {
Self::Unit => TypeWidth::Zero,
Self::U8 | Self::I8 => TypeWidth::U8,
Self::U16 | Self::I16 => TypeWidth::U16,
Self::U32 | Self::I32 => TypeWidth::U32,
Self::U64 | Self::I64 => TypeWidth::U64,
Self::USize => env.thin_pointer_width,
// ???
Self::Bool => TypeWidth::U32,
}
}
fn kind(&self) -> TypeKind {
TypeKind::Other
}
fn as_rust_type(&self) -> TokenStream {
match self {
Self::Unit => quote!(()),
Self::U8 => quote!(u8),
Self::I8 => quote!(i8),
Self::U16 => quote!(u16),
Self::I16 => quote!(i16),
Self::U32 => quote!(u32),
Self::I32 => quote!(i32),
Self::U64 => quote!(u64),
Self::I64 => quote!(i64),
Self::USize => quote!(usize),
Self::Bool => quote!(bool),
}
}
fn emit_to_syscall_arguments(&self, _env: &TargetEnv, value: &Ident) -> TokenStream {
quote!(#value as usize)
}
fn emit_from_syscall_arguments(
&self,
_env: &TargetEnv,
args: &Ident,
index: usize,
) -> (TokenStream, usize) {
match self {
Self::Bool => (quote!(#args[#index] != 0), 1),
_ => {
let ty = self.as_rust_type();
(quote!(#args[#index] as #ty), 1)
}
}
}
}
impl FromStr for AbiPrimitive {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"i8" => Ok(Self::I8),
"u8" => Ok(Self::U8),
"i16" => Ok(Self::I16),
"u16" => Ok(Self::U16),
"i32" => Ok(Self::I32),
"u32" => Ok(Self::U32),
"i64" => Ok(Self::I64),
"u64" => Ok(Self::U64),
"bool" => Ok(Self::Bool),
"usize" => Ok(Self::USize),
_ => Err(()),
}
}
}

View File

@ -0,0 +1,158 @@
use std::rc::Rc;
use proc_macro2::TokenStream;
use quote::quote;
use syn::Ident;
use crate::TargetEnv;
use super::{AbiPrimitive, ComplexType, Type, TypeKind, TypeWidth};
#[derive(Debug, Clone)]
pub enum SimpleType {
Primitive(AbiPrimitive),
Str,
NonZeroUsize,
Never,
Transparent {
name: Ident,
inner: AbiPrimitive,
},
Reference {
mutable: bool,
pointee: Rc<ComplexType>,
},
Slice {
mutable: bool,
element: Rc<ComplexType>,
},
Array {
mutable: bool,
element: Rc<ComplexType>,
length: usize,
},
}
impl Type for SimpleType {
fn width(&self, env: &TargetEnv) -> TypeWidth {
match self {
Self::Str => env.fat_pointer_width,
Self::NonZeroUsize => env.thin_pointer_width,
Self::Reference { .. } => env.thin_pointer_width,
Self::Array { .. } => env.thin_pointer_width,
Self::Slice { .. } => env.fat_pointer_width,
Self::Primitive(ty) => ty.width(env),
Self::Transparent { inner, .. } => inner.width(env),
Self::Never => TypeWidth::Zero,
}
}
fn kind(&self) -> TypeKind {
match self {
Self::Str => TypeKind::FatPointer,
Self::NonZeroUsize => TypeKind::ThinPointer,
Self::Primitive(ty) => ty.kind(),
Self::Transparent { inner, .. } => inner.kind(),
Self::Reference { .. } => TypeKind::ThinPointer,
Self::Array { .. } => TypeKind::ThinPointer,
Self::Slice { .. } => TypeKind::FatPointer,
Self::Never => TypeKind::Other,
}
}
fn as_rust_type(&self) -> TokenStream {
match self {
Self::Str => quote!(&str),
Self::NonZeroUsize => quote!(core::num::NonZeroUsize),
Self::Reference { mutable, pointee } => pointee.as_rust_ref_type(*mutable),
Self::Array {
mutable,
element,
length,
} => element.as_rust_array_type(*mutable, *length),
Self::Slice { mutable, element } => element.as_rust_slice_type(*mutable),
Self::Primitive(ty) => ty.as_rust_type(),
Self::Transparent { name, .. } => quote!(#name),
Self::Never => quote!(!),
}
}
fn emit_to_syscall_arguments(&self, env: &TargetEnv, value: &Ident) -> TokenStream {
match self {
Self::NonZeroUsize => quote!(#value.into()),
Self::Primitive(ty) => ty.emit_to_syscall_arguments(env, value),
Self::Transparent { .. } => quote!(#value.into_syscall_register()),
Self::Str => quote!(#value.as_ptr().expose_addr(), #value.len()),
Self::Never => todo!("Prevent Never type from being a syscall argument"),
Self::Reference { mutable, pointee } => {
let pointee = pointee.as_rust_type();
match mutable {
true => quote!((#value as *mut #pointee).expose_addr()),
false => quote!((#value as *const #pointee).expose_addr()),
}
}
Self::Array { mutable, .. } => match mutable {
true => quote!(#value.as_mut_ptr().expose_addr()),
false => quote!(#value.as_ptr().expose_addr()),
},
Self::Slice { mutable, .. } => match mutable {
true => quote!(#value.as_mut_ptr().expose_addr(), #value.len()),
false => quote!(#value.as_ptr().expose_addr(), #value.len()),
},
}
}
fn emit_from_syscall_arguments(
&self,
env: &TargetEnv,
args: &Ident,
index: usize,
) -> (TokenStream, usize) {
match self {
Self::Str => (quote!(arg::str_ref(#args[#index], #args[#index + 1])?), 2),
Self::Never => todo!("!"),
Self::Slice { mutable, element } => {
let element = element.as_rust_type();
match mutable {
true => (
quote!(arg::slice_mut::<#element>(#args[#index], #args[#index + 1])?),
2,
),
false => (
quote!(arg::slice_ref::<#element>(#args[#index], #args[#index + 1])?),
2,
),
}
}
Self::Reference { mutable, pointee } => {
let pointee = pointee.as_rust_type();
match mutable {
true => (quote!(arg::ref_mut::<#pointee>(#args[#index])?), 1),
false => (quote!(arg::ref_const::<#pointee>(#args[#index])?), 1),
}
}
Self::Array {
mutable,
element,
length,
} => {
let element = element.as_rust_type();
match mutable {
true => (
quote!(arg::ref_mut::<[#element; #length]>(#args[#index])?),
1,
),
false => (
quote!(arg::ref_const::<[#element; #length]>(#args[#index])?),
1,
),
}
}
Self::NonZeroUsize => todo!("NonZeroUsize"),
Self::Primitive(ty) => ty.emit_from_syscall_arguments(env, args, index),
Self::Transparent { name, .. } => {
(quote!(<#name>::from_syscall_register(#args[#index])), 1)
}
}
}
}

View File

@ -0,0 +1,22 @@
use std::io;
use proc_macro2::Span;
use syn::Ident;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("{0}")]
IoError(#[from] io::Error),
#[error("{1:?}: Incorrectly constructed type: {0}")]
TypeError(String, Span),
#[error("{0:?}: Unhandled type name")]
UnhandledTypeNameError(Ident),
#[error("{0:?}: Only primitive types are allowed inside newtype X(Y) constructs")]
UnhandledNonPrimitive(Span),
#[error("{0}")]
LexError(#[from] proc_macro2::LexError),
#[error("{0}")]
ParseError(#[from] syn::parse::Error),
}

View File

@ -0,0 +1,13 @@
#![feature(if_let_guard, extend_one, proc_macro_span)]
use crate::abi::ty::TypeWidth;
pub mod abi;
pub mod error;
pub mod syntax;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct TargetEnv {
pub thin_pointer_width: TypeWidth,
pub fat_pointer_width: TypeWidth,
}

View File

@ -0,0 +1,120 @@
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{braced, punctuated::Punctuated, Token};
use super::{
common::{Attributes, TypeRepr},
document::DocumentItemAttributes,
};
#[derive(Clone)]
pub enum BitfieldRange {
Single(syn::LitInt),
Range(syn::LitInt, syn::LitInt),
}
#[derive(Clone)]
pub struct BitfieldTypeField {
pub attributes: Attributes,
pub name: syn::Ident,
pub range: BitfieldRange,
}
#[derive(Clone)]
pub struct BitfieldType {
pub attributes: Attributes,
pub name: syn::Ident,
pub repr: TypeRepr,
pub fields: Punctuated<BitfieldTypeField, Token![,]>,
}
impl DocumentItemAttributes for BitfieldType {
fn extend_attributes<I: IntoIterator<Item = syn::Attribute>>(&mut self, attrs: I) {
self.attributes.extend(attrs);
}
}
impl syn::parse::Parse for BitfieldRange {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let start = input.parse()?;
if input.peek(Token![..]) {
input.parse::<Token![..]>()?;
let end = input.parse()?;
Ok(Self::Range(start, end))
} else {
Ok(Self::Single(start))
}
}
}
impl syn::parse::Parse for BitfieldTypeField {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let attributes = input.parse()?;
let name = input.parse()?;
input.parse::<Token![:]>()?;
let range = input.parse()?;
Ok(Self {
attributes,
name,
range,
})
}
}
impl syn::parse::Parse for BitfieldType {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let fields;
let attributes = input.parse()?;
let name = input.parse()?;
let repr = input.parse()?;
braced!(fields in input);
let fields = fields.parse_terminated(BitfieldTypeField::parse, Token![,])?;
Ok(Self {
attributes,
name,
repr,
fields,
})
}
}
impl ToTokens for BitfieldRange {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Self::Range(start, end) => tokens.extend(quote!(#start..#end)),
Self::Single(offset) => tokens.extend(quote!(#offset)),
}
}
}
impl ToTokens for BitfieldType {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
attributes,
name,
repr,
fields,
} = self;
tokens.extend(quote! {
#attributes
bitfield #name #repr {
#fields
}
})
}
}
impl ToTokens for BitfieldTypeField {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
attributes,
name,
range,
} = self;
tokens.extend(quote! {
#attributes
#name: #range
})
}
}

View File

@ -0,0 +1,66 @@
use std::ops::{Deref, DerefMut};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{parenthesized, Token};
#[derive(Clone)]
pub struct TypeRepr(pub syn::Ident);
#[derive(Clone)]
pub struct Attributes(pub Vec<syn::Attribute>);
impl Attributes {
pub fn new() -> Self {
Self(vec![])
}
}
impl Deref for Attributes {
type Target = Vec<syn::Attribute>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Attributes {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl syn::parse::Parse for Attributes {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
syn::Attribute::parse_outer(input).map(Self)
}
}
impl syn::parse::Parse for TypeRepr {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let content;
parenthesized!(content in input);
let content = content.parse_terminated(syn::Ident::parse, Token![,])?;
let mut content = content.into_iter();
let ty = content.next().expect("TODO insert error here");
if content.len() != 0 {
todo!("TODO insert error here");
}
Ok(Self(ty))
}
}
impl ToTokens for TypeRepr {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self(repr) = self;
tokens.extend(quote!((#repr)))
}
}
impl ToTokens for Attributes {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self(attrs) = self;
for attr in attrs {
attr.to_tokens(tokens);
}
}
}

View File

@ -0,0 +1,135 @@
use quote::ToTokens;
use syn::{parse::ParseStream, Token};
use super::{
bitfield_type::BitfieldType,
enum_type::EnumType,
extern_block::{ExternType, ExternTypeBlock},
newtype::NewType,
syscall::SyscallDefinition,
};
#[derive(Clone)]
pub enum DocumentTypeDefinition {
NewType(NewType),
Bitfield(BitfieldType),
Enum(EnumType),
ExternType(ExternType),
}
pub struct Document {
pub types: Vec<DocumentTypeDefinition>,
pub syscalls: Vec<SyscallDefinition>,
}
pub trait DocumentItemAttributes {
fn extend_attributes<I: IntoIterator<Item = syn::Attribute>>(&mut self, attrs: I);
}
enum DocumentItem {
ExternTypeBlock(ExternTypeBlock),
// Type definitions
NewType(NewType),
Bitfield(BitfieldType),
Enum(EnumType),
// Syscall definitions
SyscallDefinition(SyscallDefinition),
}
impl syn::parse::Parse for DocumentItem {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(Token![extern]) {
input.parse::<Token![extern]>()?;
return input.parse().map(Self::ExternTypeBlock);
}
let item_attributes = syn::Attribute::parse_outer(input)?;
let lookahead = input.lookahead1();
let mut item = if lookahead.peek(syn::Ident) {
// TODO parse attributes
let keyword: syn::Ident = input.parse()?;
match keyword.to_string().as_str() {
"newtype" => NewType::parse(input).map(Self::NewType),
"bitfield" => BitfieldType::parse(input).map(Self::Bitfield),
"syscall" => SyscallDefinition::parse(input).map(Self::SyscallDefinition),
_ => todo!(),
}
} else if lookahead.peek(Token![enum]) {
input.parse::<Token![enum]>()?;
EnumType::parse(input).map(Self::Enum)
} else {
todo!()
}?;
item.extend_attributes(item_attributes);
Ok(item)
}
}
impl DocumentItemAttributes for DocumentItem {
fn extend_attributes<I: IntoIterator<Item = syn::Attribute>>(&mut self, attrs: I) {
match self {
Self::NewType(inner) => inner.extend_attributes(attrs),
Self::Enum(inner) => inner.extend_attributes(attrs),
Self::Bitfield(inner) => inner.extend_attributes(attrs),
Self::SyscallDefinition(inner) => inner.extend_attributes(attrs),
Self::ExternTypeBlock(_) => unreachable!(),
}
}
}
impl syn::parse::Parse for Document {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let mut types = vec![];
let mut syscalls = vec![];
loop {
if input.is_empty() {
break;
}
let toplevel = DocumentItem::parse(input)?;
match toplevel {
DocumentItem::Enum(inner) => types.push(DocumentTypeDefinition::Enum(inner)),
DocumentItem::NewType(inner) => types.push(DocumentTypeDefinition::NewType(inner)),
DocumentItem::Bitfield(inner) => {
types.push(DocumentTypeDefinition::Bitfield(inner))
}
DocumentItem::ExternTypeBlock(inner) => {
types.extend(
inner
.types
.into_iter()
.map(DocumentTypeDefinition::ExternType),
);
}
DocumentItem::SyscallDefinition(inner) => {
syscalls.push(inner);
}
}
}
Ok(Self { types, syscalls })
}
}
impl ToTokens for DocumentItem {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
match self {
Self::Enum(inner) => inner.to_tokens(tokens),
Self::Bitfield(inner) => inner.to_tokens(tokens),
Self::NewType(inner) => inner.to_tokens(tokens),
Self::SyscallDefinition(inner) => inner.to_tokens(tokens),
Self::ExternTypeBlock(inner) => inner.to_tokens(tokens),
}
}
}

View File

@ -0,0 +1,99 @@
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{braced, punctuated::Punctuated, Token};
use super::{
common::{Attributes, TypeRepr},
document::DocumentItemAttributes,
};
#[derive(Clone)]
pub struct EnumTypeVariant {
pub attributes: Attributes,
pub discriminant: syn::Ident,
pub value: syn::LitInt,
}
#[derive(Clone)]
pub struct EnumType {
pub attributes: Attributes,
pub name: syn::Ident,
pub repr: TypeRepr,
pub variants: Punctuated<EnumTypeVariant, Token![,]>,
}
impl syn::parse::Parse for EnumTypeVariant {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let attributes = input.parse()?;
let discriminant = input.parse()?;
input.parse::<Token![=]>()?;
let value = input.parse()?;
// let value = if input.peek(Token![=]) {
// input.parse::<Token![=]>()?;
// let value = input.parse()?;
// Some(value)
// } else {
// None
// };
Ok(Self {
attributes,
discriminant,
value,
})
}
}
impl syn::parse::Parse for EnumType {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let variants;
let attributes = input.parse()?;
let name = input.parse()?;
let repr = input.parse()?;
braced!(variants in input);
let variants = variants.parse_terminated(EnumTypeVariant::parse, Token![,])?;
Ok(Self {
attributes,
name,
repr,
variants,
})
}
}
impl DocumentItemAttributes for EnumType {
fn extend_attributes<I: IntoIterator<Item = syn::Attribute>>(&mut self, attrs: I) {
self.attributes.extend(attrs);
}
}
impl ToTokens for EnumTypeVariant {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
attributes,
discriminant,
value,
} = self;
// let value = value.as_ref().map(|val| quote!(= #val));
tokens.extend(quote! {
#attributes
#discriminant = #value
})
}
}
impl ToTokens for EnumType {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
attributes,
name,
repr,
variants,
} = self;
tokens.extend(quote! {
#attributes
enum #name #repr {
#variants
}
})
}
}

View File

@ -0,0 +1,103 @@
use std::path::Path;
use proc_macro2::Span;
use syn::spanned::Spanned;
pub enum SyntaxError {
UnhandledType(syn::Type),
UndefinedIdentifier(syn::Ident),
}
pub enum ParseError {
HardError(syn::parse::Error),
IoError(std::io::Error),
SyntaxError(Vec<SyntaxError>),
}
pub trait UnwrapFancy<T> {
fn unwrap_fancy<P: AsRef<Path>>(self, context: P) -> T;
}
impl From<std::io::Error> for ParseError {
fn from(value: std::io::Error) -> Self {
Self::IoError(value)
}
}
impl From<syn::parse::Error> for ParseError {
fn from(value: syn::parse::Error) -> Self {
Self::HardError(value)
}
}
impl From<SyntaxError> for ParseError {
fn from(value: SyntaxError) -> Self {
Self::SyntaxError(vec![value])
}
}
impl SyntaxError {
pub fn span(&self) -> Span {
match self {
Self::UndefinedIdentifier(id) => id.span(),
Self::UnhandledType(ty) => ty.span(),
}
}
pub fn describe(&self) -> String {
match self {
Self::UnhandledType(_ty) => "Unhandled type".into(),
Self::UndefinedIdentifier(id) => format!("Undefined identifier '{}'", id),
}
}
}
impl<T> UnwrapFancy<T> for Result<T, ParseError> {
fn unwrap_fancy<P: AsRef<Path>>(self, path: P) -> T {
match self {
Self::Ok(value) => value,
Self::Err(err) => {
match err {
ParseError::IoError(err) => {
eprintln!("{}: {}", path.as_ref().display(), err);
}
ParseError::HardError(err) => {
eprintln!("{}: {}", path.as_ref().display(), err);
}
ParseError::SyntaxError(errs) => {
eprintln!("{}:", path.as_ref().display());
for err in errs {
eprintln!("* ...: {}", err.describe());
}
}
}
panic!("Compilation aborted");
}
}
}
}
impl ParseError {
pub fn syntax1(e: SyntaxError) -> Self {
Self::SyntaxError(vec![e])
}
pub fn fold<T, I: IntoIterator<Item = Result<T, Self>>, Q: Extend<T>>(
mut acc: Q,
it: I,
) -> Result<Q, Self> {
let mut errors = vec![];
for item in it {
match item {
Ok(val) => acc.extend_one(val),
Err(ParseError::SyntaxError(err)) => errors.extend(err),
Err(err) => return Err(err),
}
}
if errors.is_empty() {
Ok(acc)
} else {
Err(ParseError::SyntaxError(errors))
}
}
}

View File

@ -0,0 +1,70 @@
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{braced, punctuated::Punctuated, Token};
use super::Attributes;
#[derive(Clone)]
pub struct ExternType {
pub attributes: Attributes,
pub name: syn::Ident,
pub alias: Option<syn::Type>,
}
pub struct ExternTypeBlock {
pub types: Punctuated<ExternType, Token![;]>,
}
impl syn::parse::Parse for ExternType {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let attributes = input.parse()?;
input.parse::<Token![type]>()?;
let name = input.parse()?;
let alias = if input.peek(Token![=]) {
input.parse::<Token![=]>()?;
Some(input.parse()?)
} else {
None
};
Ok(Self {
attributes,
name,
alias,
})
}
}
impl syn::parse::Parse for ExternTypeBlock {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let types;
braced!(types in input);
let types = types.parse_terminated(ExternType::parse, Token![;])?;
Ok(Self { types })
}
}
impl ToTokens for ExternType {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
attributes,
name,
alias,
} = self;
let alias = alias.as_ref().map(|ty| quote!(= #ty));
tokens.extend(quote! {
#attributes
type #name #alias
});
}
}
impl ToTokens for ExternTypeBlock {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self { types } = self;
tokens.extend(quote! {
extern {
#types
}
})
}
}

View File

@ -0,0 +1,329 @@
use std::{rc::Rc, str::FromStr};
use syn::{punctuated::Punctuated, Expr, ExprLit, Lit, PathSegment};
use crate::abi::{
syscall_enum_variant_identifier,
ty::{complex::ExternKind, AbiPrimitive, ComplexType, SimpleType},
Abi, Syscall, TypeEnv,
};
mod common;
// Types
mod bitfield_type;
mod enum_type;
mod extern_block;
mod newtype;
mod document;
mod error;
mod syscall;
pub use bitfield_type::{BitfieldRange, BitfieldType, BitfieldTypeField};
pub use common::{Attributes, TypeRepr};
pub use document::{Document, DocumentTypeDefinition};
pub use enum_type::{EnumType, EnumTypeVariant};
pub use error::{ParseError, SyntaxError, UnwrapFancy};
pub use extern_block::{ExternType, ExternTypeBlock};
pub use newtype::NewType;
pub fn parse_abi_document(input: &str) -> Result<Document, ParseError> {
let document: Document = syn::parse_str(input)?;
Ok(document)
}
fn as_single_segment_path(path: &syn::TypePath) -> Option<&PathSegment> {
if path.qself.is_some() || path.path.leading_colon.is_some() || path.path.segments.len() != 1 {
return None;
}
Some(&path.path.segments[0])
}
fn as_single_type_argument(args: &syn::PathArguments) -> Option<&syn::Type> {
let syn::PathArguments::AngleBracketed(args) = args else {
return None;
};
if args.args.len() != 1 {
return None;
}
let syn::GenericArgument::Type(ty) = &args.args[0] else {
return None;
};
Some(ty)
}
fn as_const_array_len(len: &Expr) -> Option<usize> {
let Expr::Lit(ExprLit {
attrs,
lit: Lit::Int(lit),
}) = len
else {
return None;
};
if !attrs.is_empty() {
todo!()
}
lit.base10_parse().ok()
}
fn process_type(t: &syn::Type, known_types: &TypeEnv) -> Result<Rc<ComplexType>, ParseError> {
match t {
syn::Type::Path(path) => {
let segment = as_single_segment_path(path).expect("TODO: more complex paths");
match segment.ident.to_string().as_str() {
seg if let Ok(ty) = AbiPrimitive::from_str(seg) => {
Ok(Rc::new(ComplexType::primitive(ty)))
}
"NonZeroUsize" => Ok(Rc::new(ComplexType::Simple(SimpleType::NonZeroUsize))),
"Result" if let Some(ty) = as_single_type_argument(&segment.arguments) => {
let ty = process_type(ty, known_types)?;
Ok(Rc::new(ComplexType::Result(ty)))
}
"Option" if let Some(ty) = as_single_type_argument(&segment.arguments) => {
let ty = process_type(ty, known_types)?;
Ok(Rc::new(ComplexType::Option(ty)))
}
"MaybeUninit" if let Some(ty) = as_single_type_argument(&segment.arguments) => {
let ty = process_type(ty, known_types)?;
Ok(Rc::new(ComplexType::MaybeUninit(ty)))
}
name if let Some((ty, _)) = known_types.lookup_local(name) => Ok(ty.clone()),
name if let Some((ty, kind)) = known_types.lookup_extern(name) => {
Ok(Rc::new(ComplexType::Extern {
kind: *kind,
alias: ty.clone(),
arguments: segment.arguments.clone(),
}))
}
_ => Err(ParseError::syntax1(SyntaxError::UndefinedIdentifier(
segment.ident.clone(),
))),
}
}
syn::Type::Never(_) => Ok(Rc::new(ComplexType::Simple(SimpleType::Never))),
syn::Type::Reference(r) => {
// TODO lifetimes
if r.lifetime.is_some() {
todo!()
}
let mutable = r.mutability.is_some();
match r.elem.as_ref() {
syn::Type::Slice(ty) => {
let element = process_type(&ty.elem, known_types)?;
Ok(Rc::new(ComplexType::Simple(SimpleType::Slice {
mutable,
element,
})))
}
syn::Type::Path(path)
if !mutable
&& as_single_segment_path(path)
.map(|seg| seg.ident.to_string() == "str" && seg.arguments.is_empty())
.unwrap_or(false) =>
{
Ok(Rc::new(ComplexType::Simple(SimpleType::Str)))
}
syn::Type::Array(array) if let Some(length) = as_const_array_len(&array.len) => {
let element = process_type(&array.elem, known_types)?;
Ok(Rc::new(ComplexType::Simple(SimpleType::Array {
mutable,
element,
length,
})))
}
_ => {
let pointee = process_type(&r.elem, known_types)?;
Ok(Rc::new(ComplexType::Simple(SimpleType::Reference {
mutable,
pointee,
})))
}
}
}
syn::Type::Tuple(t) => {
if t.elems.is_empty() {
Ok(Rc::new(ComplexType::primitive(AbiPrimitive::Unit)))
} else {
let elems = t
.elems
.iter()
.map(|e| process_type(e, known_types))
.collect::<Result<_, _>>()?;
Ok(Rc::new(ComplexType::Tuple(elems)))
}
}
_ => todo!(),
}
}
fn require_primitive_repr(repr: &TypeRepr) -> Result<AbiPrimitive, ParseError> {
let TypeRepr(repr) = repr;
Ok(AbiPrimitive::from_str(repr.to_string().as_str()).expect("TODO proper error message"))
}
fn make_single_ident_type(ident: syn::Ident) -> syn::Type {
syn::Type::Path(syn::TypePath {
qself: None,
path: syn::Path {
leading_colon: None,
segments: Punctuated::from_iter([syn::PathSegment {
arguments: syn::PathArguments::None,
ident,
}]),
},
})
}
pub enum ProcessedType {
Extern(syn::Type, ExternKind),
Local(Rc<ComplexType>, DocumentTypeDefinition),
}
fn process_extern_type(
def: &ExternType,
_known_types: &TypeEnv,
) -> Result<(syn::Ident, syn::Type, ExternKind), ParseError> {
// TODO allow mixing arguments from
// extern { type X = ::A<B, C> };
// and use sites like
// syscall my_call(arg: X<D, E>);
// Process extern attributes
let mut kind = ExternKind::None;
for attr in def.attributes.iter() {
if attr.path().is_ident("thin") {
kind = ExternKind::Thin;
} else if attr.path().is_ident("fat") {
kind = ExternKind::Fat;
}
}
let alias = def
.alias
.clone()
.unwrap_or_else(|| make_single_ident_type(def.name.clone()));
Ok((def.name.clone(), alias, kind))
}
fn process_bitfield_type(
def: &BitfieldType,
_known_types: &TypeEnv,
) -> Result<(syn::Ident, Rc<ComplexType>), ParseError> {
let repr = require_primitive_repr(&def.repr)?;
Ok((
def.name.clone(),
Rc::new(ComplexType::Simple(SimpleType::Transparent {
name: def.name.clone(),
inner: repr,
})),
))
}
fn process_enum_type(
def: &EnumType,
_known_types: &TypeEnv,
) -> Result<(syn::Ident, Rc<ComplexType>), ParseError> {
let repr = require_primitive_repr(&def.repr)?;
Ok((
def.name.clone(),
Rc::new(ComplexType::Simple(SimpleType::Transparent {
name: def.name.clone(),
inner: repr,
})),
))
}
fn process_newtype(
def: &NewType,
_known_types: &TypeEnv,
) -> Result<(syn::Ident, Rc<ComplexType>), ParseError> {
let repr = require_primitive_repr(&def.repr)?;
Ok((
def.name.clone(),
Rc::new(ComplexType::Simple(SimpleType::Transparent {
name: def.name.clone(),
inner: repr,
})),
))
}
fn process_type_definition(
def: &DocumentTypeDefinition,
known_types: &TypeEnv,
) -> Result<(syn::Ident, ProcessedType), ParseError> {
match def {
DocumentTypeDefinition::ExternType(inner) => process_extern_type(inner, known_types)
.map(|(ident, def_ty, kind)| (ident, ProcessedType::Extern(def_ty, kind))),
DocumentTypeDefinition::Enum(inner) => process_enum_type(inner, known_types)
.map(|(ident, def_ty)| (ident, ProcessedType::Local(def_ty, def.clone()))),
DocumentTypeDefinition::Bitfield(inner) => process_bitfield_type(inner, known_types)
.map(|(ident, def_ty)| (ident, ProcessedType::Local(def_ty, def.clone()))),
DocumentTypeDefinition::NewType(inner) => process_newtype(inner, known_types)
.map(|(ident, def_ty)| (ident, ProcessedType::Local(def_ty, def.clone()))),
}
}
pub fn parse_abi(input: &str) -> Result<Abi, ParseError> {
let doc = parse_abi_document(input)?;
let mut abi = Abi {
types: TypeEnv::new(),
syscalls: Vec::new(),
};
ParseError::fold(
(),
doc.types.iter().map(|nt| {
let (name, value) = process_type_definition(nt, &abi.types)?;
match value {
ProcessedType::Local(abi_ty, def_ty) => {
abi.types.insert_local(name.to_string(), abi_ty, def_ty)
}
ProcessedType::Extern(def_ty, kind) => {
abi.types.insert_extern(name.to_string(), def_ty, kind)
}
}
Ok(())
}),
)?;
ParseError::fold(
(),
doc.syscalls.iter().map(|syscall| {
let args = syscall
.arguments
.iter()
.map(|arg| {
let ty = process_type(&arg.ty, &abi.types)?;
Ok((arg.name.clone(), ty))
})
.collect::<Result<Vec<_>, ParseError>>()?;
let return_type = syscall
.return_type
.as_ref()
.map(|ty| process_type(ty, &abi.types))
.transpose()?;
let variant = syscall_enum_variant_identifier(&syscall.name);
let def = Syscall {
name: syscall.name.clone(),
args,
return_type,
};
abi.syscalls.push((variant, def));
Ok(())
}),
)?;
Ok(abi)
}

View File

@ -0,0 +1,50 @@
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::Token;
use super::{
common::{Attributes, TypeRepr},
document::DocumentItemAttributes,
};
#[derive(Clone)]
pub struct NewType {
pub attributes: Attributes,
pub name: syn::Ident,
// No need for a full type, since only certain primitives are allowed
pub repr: TypeRepr,
}
impl DocumentItemAttributes for NewType {
fn extend_attributes<I: IntoIterator<Item = syn::Attribute>>(&mut self, attrs: I) {
self.attributes.extend(attrs);
}
}
impl syn::parse::Parse for NewType {
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
let attributes = input.parse()?;
let name = input.parse()?;
let repr = input.parse()?;
input.parse::<Token![;]>()?;
Ok(Self {
attributes,
name,
repr,
})
}
}
impl ToTokens for NewType {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
attributes,
name,
repr,
} = self;
tokens.extend(quote! {
#attributes
newtype #name #repr;
})
}
}

View File

@ -0,0 +1,82 @@
use quote::{quote, ToTokens};
use syn::{parenthesized, parse::ParseStream, punctuated::Punctuated, Ident, Token};
use super::{common::Attributes, document::DocumentItemAttributes};
pub struct SyscallDefinition {
pub attributes: Attributes,
pub name: Ident,
pub arguments: Punctuated<SyscallArgument, Token![,]>,
pub return_type: Option<syn::Type>,
}
pub struct SyscallArgument {
pub name: syn::Ident,
pub ty: syn::Type,
}
impl DocumentItemAttributes for SyscallDefinition {
fn extend_attributes<I: IntoIterator<Item = syn::Attribute>>(&mut self, attrs: I) {
self.attributes.extend(attrs);
}
}
impl syn::parse::Parse for SyscallDefinition {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let arguments;
let name = input.parse()?;
parenthesized!(arguments in input);
let arguments = arguments.parse_terminated(SyscallArgument::parse, Token![,])?;
let return_type = if input.peek(Token![->]) {
input.parse::<Token![->]>()?;
let ty = input.parse()?;
Some(ty)
} else {
None
};
input.parse::<Token![;]>()?;
Ok(Self {
attributes: Attributes::new(),
name,
arguments,
return_type,
})
}
}
impl syn::parse::Parse for SyscallArgument {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let name = input.parse()?;
input.parse::<Token![:]>()?;
let ty = input.parse()?;
Ok(Self { name, ty })
}
}
impl ToTokens for SyscallArgument {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let Self { name, ty } = self;
tokens.extend(quote!(#name: #ty))
}
}
impl ToTokens for SyscallDefinition {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let Self {
attributes,
name,
arguments,
return_type,
} = self;
let return_type = return_type.as_ref().map(|ty| quote!(-> #ty));
tokens.extend(quote! {
#attributes
syscall #name (#arguments) #return_type;
});
}
}