Initial commit
This commit is contained in:
commit
2fd9cf1128
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
86
Cargo.lock
generated
Normal file
86
Cargo.lock
generated
Normal file
@ -0,0 +1,86 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "example-abi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"syscall-generator",
|
||||
]
|
||||
|
||||
[[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 = "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 = "syscall-generator"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[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"
|
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "syscall-generator"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
prettyplease = "0.2.16"
|
||||
proc-macro2 = "1.0.78"
|
||||
quote = "1.0.35"
|
||||
syn = { version = "2.0.52", features = ["full"] }
|
||||
thiserror = "1.0.57"
|
||||
|
||||
[workspace]
|
||||
members = ["example-abi"]
|
7
example-abi/Cargo.toml
Normal file
7
example-abi/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "example-abi"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[build-dependencies]
|
||||
syscall-generator = { path = ".." }
|
25
example-abi/build.rs
Normal file
25
example-abi/build.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use std::{env, path::Path};
|
||||
|
||||
use syscall_generator::{
|
||||
abi::{ty::TypeWidth, AbiBuilder},
|
||||
parse::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");
|
||||
|
||||
abi.write(output.join("generated_abi.rs"))
|
||||
.expect("Could not write the generated ABI file");
|
||||
}
|
118
example-abi/src/main.rs
Normal file
118
example-abi/src/main.rs
Normal file
@ -0,0 +1,118 @@
|
||||
#![feature(exposed_provenance)]
|
||||
|
||||
use std::{num::NonZeroUsize, sync::Mutex};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {}
|
||||
|
||||
pub enum MappingSource {}
|
||||
pub struct MountOptions;
|
||||
pub struct UnmountOptions;
|
||||
pub struct DirectoryEntry;
|
||||
pub struct FileAttr;
|
||||
pub struct SpawnOptions;
|
||||
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 {}
|
||||
|
||||
impl FromSyscallResult for Result<(), Error> {
|
||||
fn from_syscall_result(value: usize) -> Self {
|
||||
if (value as isize) < 0 {
|
||||
todo!()
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSyscallResult for Result<usize, Error> {
|
||||
fn from_syscall_result(value: usize) -> Self {
|
||||
if (value as isize) < 0 {
|
||||
todo!()
|
||||
} else {
|
||||
Ok(value as _)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoSyscallArgument for Option<NonZeroUsize> {
|
||||
fn into_syscall_argument(self) -> usize {
|
||||
match self {
|
||||
Some(value) => value.into(),
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! syscall {
|
||||
($f:expr $(,)?) => {
|
||||
syscall0($f)
|
||||
};
|
||||
($f:expr, $a0:expr $(,)?) => {
|
||||
syscall1($f, $a0)
|
||||
};
|
||||
($f:expr, $a0:expr, $a1:expr $(,)?) => {
|
||||
syscall2($f, $a0, $a1)
|
||||
};
|
||||
($f:expr, $a0:expr, $a1:expr, $a2:expr $(,)?) => {
|
||||
syscall3($f, $a0, $a1, $a2)
|
||||
};
|
||||
($f:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr $(,)?) => {
|
||||
syscall4($f, $a0, $a1, $a2, $a3)
|
||||
};
|
||||
($f:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr $(,)?) => {
|
||||
syscall5($f, $a0, $a1, $a2, $a3, $a4)
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/generated_abi.rs"));
|
||||
|
||||
fn main() {}
|
141
example-abi/syscall.abi
Normal file
141
example-abi/syscall.abi
Normal file
@ -0,0 +1,141 @@
|
||||
// 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;
|
||||
};
|
||||
|
||||
newtype Fd(u32);
|
||||
newtype FileMode(u32);
|
||||
newtype ProcessId(u32);
|
||||
newtype ThreadId(u32);
|
||||
newtype ProcessGroupId(u32);
|
||||
|
||||
// TODO define these as enums
|
||||
newtype OpenOptions(u32);
|
||||
newtype FileMode(u32);
|
||||
newtype ExitCode(i32);
|
||||
newtype SeekFrom(u64);
|
||||
newtype Signal(u32);
|
||||
newtype PollControl(u32);
|
||||
newtype MessageDestination(u32);
|
||||
newtype SocketType(u32);
|
||||
|
||||
// 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>;
|
305
src/abi/mod.rs
Normal file
305
src/abi/mod.rs
Normal file
@ -0,0 +1,305 @@
|
||||
use std::{collections::HashMap, fs::File, io::Write, path::Path, rc::Rc};
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, TokenStreamExt};
|
||||
use syn::{punctuated::Punctuated, spanned::Spanned, token};
|
||||
|
||||
pub mod ty;
|
||||
|
||||
use crate::{
|
||||
abi::ty::{ComplexType, SimpleType, Type, TypeWidth},
|
||||
error::Error,
|
||||
parse::{parse_abi, ParseError},
|
||||
Syscall, TargetEnv,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Abi {
|
||||
pub types: HashMap<String, Rc<ComplexType>>,
|
||||
// Contains syscalls and their corresponding SyscallFunction enum variants
|
||||
pub syscalls: Vec<(syn::Ident, Syscall)>,
|
||||
}
|
||||
|
||||
pub struct AbiBuilder {
|
||||
abi: Abi,
|
||||
target_env: TargetEnv,
|
||||
emit_syscall_traits: bool,
|
||||
}
|
||||
|
||||
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(path)?;
|
||||
Ok(Self {
|
||||
abi,
|
||||
target_env,
|
||||
emit_syscall_traits: true,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write<P: AsRef<Path>>(self, path: P) -> Result<(), Error> {
|
||||
// Generate preamble
|
||||
let mut stream = TokenStream::new();
|
||||
|
||||
if self.emit_syscall_traits {
|
||||
stream.append_all(quote! {
|
||||
pub trait IntoSyscallArgument {
|
||||
fn into_syscall_argument(self) -> usize;
|
||||
}
|
||||
|
||||
pub trait FromSyscallArgument {
|
||||
fn from_syscall_argument(value: usize) -> Self;
|
||||
}
|
||||
|
||||
pub trait FromSyscallResult {
|
||||
fn from_syscall_result(value: usize) -> Self;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let syscall_discriminants = self
|
||||
.abi
|
||||
.syscalls
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, (variant, _))| {
|
||||
quote! { #variant = #index }
|
||||
})
|
||||
.collect::<Punctuated<_, token::Comma>>();
|
||||
|
||||
stream.append_all(quote! {
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)]
|
||||
#[repr(usize)]
|
||||
pub enum SyscallFunction {
|
||||
#syscall_discriminants
|
||||
}
|
||||
});
|
||||
|
||||
stream.append_all(self.abi.generate_user_side(&self.target_env));
|
||||
|
||||
let file = syn::parse_file(&stream.to_string())?;
|
||||
let out = prettyplease::unparse(&file);
|
||||
|
||||
let mut output = File::create(path)?;
|
||||
output.write_all(out.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Abi {
|
||||
pub fn read<P: AsRef<Path>>(path: P) -> Result<Self, ParseError> {
|
||||
let data = std::fs::read_to_string(path)?;
|
||||
parse_abi(&data)
|
||||
}
|
||||
|
||||
pub fn write<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
|
||||
// TODO read TargetEnv
|
||||
let target = TargetEnv {
|
||||
thin_pointer_width: TypeWidth::U64,
|
||||
fat_pointer_width: TypeWidth::U128,
|
||||
};
|
||||
let mut output = File::create(path)?;
|
||||
let stream = self.generate_user_side(&target);
|
||||
let file = syn::parse_file(&stream.to_string())?;
|
||||
let out = prettyplease::unparse(&file);
|
||||
output.write_all(out.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn generate_user_side(&self, env: &TargetEnv) -> TokenStream {
|
||||
let mut stream = TokenStream::new();
|
||||
|
||||
for (_, ty) in &self.types {
|
||||
let definition =
|
||||
if let ComplexType::Simple(SimpleType::Transparent { name, inner }) = ty.as_ref() {
|
||||
let ty = inner.as_rust_type();
|
||||
let mut res = quote! {
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct #name(pub(crate) #ty);
|
||||
};
|
||||
|
||||
if inner.width(env) < env.thin_pointer_width {
|
||||
res.append_all(quote! {
|
||||
impl IntoSyscallArgument for Option<#name> {
|
||||
fn into_syscall_argument(self) -> usize {
|
||||
match self {
|
||||
Some(value) => value.0 as usize,
|
||||
None => usize::MAX
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSyscallArgument for Option<#name> {
|
||||
fn from_syscall_argument(value: usize) -> Self {
|
||||
match value {
|
||||
usize::MAX => None,
|
||||
_ => Some(#name(value as _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSyscallResult for Result<#name, Error> {
|
||||
fn from_syscall_result(value: usize) -> Self {
|
||||
if (value as isize) < 0 {
|
||||
todo!()
|
||||
} else {
|
||||
Ok(#name(value as _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSyscallResult for #name {
|
||||
fn from_syscall_result(value: usize) -> Self {
|
||||
Self(value as _)
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
res
|
||||
} else if matches!(ty.as_ref(), ComplexType::Extern(_)) {
|
||||
continue;
|
||||
} else {
|
||||
todo!()
|
||||
};
|
||||
|
||||
stream.append_all(definition);
|
||||
}
|
||||
|
||||
for (variant, syscall) in &self.syscalls {
|
||||
stream.append_all(syscall.emit_stub(variant, env));
|
||||
}
|
||||
|
||||
stream
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{collections::HashMap, rc::Rc};
|
||||
|
||||
use proc_macro2::Span;
|
||||
use quote::quote;
|
||||
use syn::{Ident, Visibility};
|
||||
|
||||
use crate::{
|
||||
abi::ty::{AbiPrimitive, ComplexType, SimpleType, TypeWidth},
|
||||
Syscall, TargetEnv,
|
||||
};
|
||||
|
||||
use super::Abi;
|
||||
|
||||
#[test]
|
||||
fn generate_full_simple_abi() {
|
||||
let target = TargetEnv {
|
||||
thin_pointer_width: TypeWidth::U64,
|
||||
fat_pointer_width: TypeWidth::U128,
|
||||
};
|
||||
let fd = ComplexType::transparent(Ident::new("Fd", Span::call_site()), AbiPrimitive::U32);
|
||||
let read_buf_ty = ComplexType::Simple(SimpleType::Slice {
|
||||
mutable: true,
|
||||
element: Rc::new(ComplexType::primitive(AbiPrimitive::U8)),
|
||||
});
|
||||
let write_buf_ty = ComplexType::Simple(SimpleType::Slice {
|
||||
mutable: false,
|
||||
element: Rc::new(ComplexType::primitive(AbiPrimitive::U8)),
|
||||
});
|
||||
|
||||
let read_syscall = Syscall {
|
||||
name: Ident::new("read", Span::call_site()),
|
||||
visibility: Visibility::Inherited,
|
||||
args: vec![
|
||||
(Ident::new("fd", Span::call_site()), fd.clone()),
|
||||
(Ident::new("buf", Span::call_site()), read_buf_ty),
|
||||
],
|
||||
return_type: Some(ComplexType::SizeResult),
|
||||
};
|
||||
let write_syscall = Syscall {
|
||||
name: Ident::new("write", Span::call_site()),
|
||||
visibility: Visibility::Inherited,
|
||||
args: vec![
|
||||
(Ident::new("fd", Span::call_site()), fd.clone()),
|
||||
(Ident::new("buf", Span::call_site()), write_buf_ty),
|
||||
],
|
||||
return_type: Some(ComplexType::SizeResult),
|
||||
};
|
||||
|
||||
let abi = Abi {
|
||||
types: HashMap::from_iter([("Fd".to_owned(), fd)]),
|
||||
syscalls: vec![read_syscall, write_syscall],
|
||||
};
|
||||
|
||||
let result = abi.generate_user_side(&target);
|
||||
assert_eq!(
|
||||
result.to_string(),
|
||||
quote! {
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct Fd(pub(crate) u32);
|
||||
|
||||
impl IntoSyscallArgument for Option<Fd> {
|
||||
fn into_syscall_argument(self) -> usize {
|
||||
match self {
|
||||
Some(value) => value.0 as usize,
|
||||
None => usize::MAX
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSyscallArgument for Option<Fd> {
|
||||
fn from_syscall_argument(value: usize) -> Self {
|
||||
match value {
|
||||
usize::MAX => None,
|
||||
_ => Some(Fd(value as _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSyscallResult for Result<Fd, Error> {
|
||||
fn from_syscall_result(value: usize) -> Self {
|
||||
if (value as isize) < 0 {
|
||||
todo!()
|
||||
} else {
|
||||
Ok(Fd(value as _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn read(fd: Fd, buf: &mut [u8],) -> Result<usize, Error> {
|
||||
<Result<usize, Error> >::from_syscall_result(syscall!(
|
||||
SyscallFunction::read,
|
||||
fd.0 as usize,
|
||||
buf.as_mut_ptr().expose_addr(),
|
||||
buf.len(),
|
||||
))
|
||||
}
|
||||
|
||||
unsafe fn write(fd: Fd, buf: &[u8],) -> Result<usize, Error> {
|
||||
<Result<usize, Error> >::from_syscall_result(syscall!(
|
||||
SyscallFunction::write,
|
||||
fd.0 as usize,
|
||||
buf.as_ptr().expose_addr(),
|
||||
buf.len(),
|
||||
))
|
||||
}
|
||||
}
|
||||
.to_string()
|
||||
);
|
||||
}
|
||||
}
|
109
src/abi/ty/complex.rs
Normal file
109
src/abi/ty/complex.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use std::{fmt, rc::Rc};
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::Ident;
|
||||
|
||||
use crate::{error::Error, TargetEnv};
|
||||
|
||||
use super::{AbiPrimitive, SimpleType, Type, TypeKind, TypeWidth};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ComplexType {
|
||||
Simple(SimpleType),
|
||||
MaybeUninit(Rc<ComplexType>),
|
||||
Result(Rc<ComplexType>),
|
||||
Option(Rc<ComplexType>),
|
||||
Extern(syn::Type),
|
||||
}
|
||||
|
||||
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 to_simple_type(&self) -> Result<SimpleType, Error> {
|
||||
todo!()
|
||||
// if let Self::Simple(simple) = self {
|
||||
// Ok(simple)
|
||||
// } else {
|
||||
// todo!("{:?}", self)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
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::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::Result(ty) => {
|
||||
let ty = ty.as_rust_type();
|
||||
// TODO take from TargetEnv
|
||||
quote!(Result<#ty, Error>)
|
||||
}
|
||||
Self::Extern(ty) => quote!(#ty),
|
||||
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 {
|
||||
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_argument())
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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::Extern(ty) => f.debug_tuple("Extern").field("e!(#ty)).finish(),
|
||||
Self::MaybeUninit(ty) => f.debug_tuple("MaybeUninit").field(&ty).finish(),
|
||||
}
|
||||
}
|
||||
}
|
161
src/abi/ty/mod.rs
Normal file
161
src/abi/ty/mod.rs
Normal file
@ -0,0 +1,161 @@
|
||||
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(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 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()
|
||||
);
|
||||
}
|
||||
}
|
83
src/abi/ty/primitive.rs
Normal file
83
src/abi/ty/primitive.rs
Normal file
@ -0,0 +1,83 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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(()),
|
||||
}
|
||||
}
|
||||
}
|
104
src/abi/ty/simple.rs
Normal file
104
src/abi/ty/simple.rs
Normal file
@ -0,0 +1,104 @@
|
||||
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.0 as usize),
|
||||
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()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
22
src/error.rs
Normal file
22
src/error.rs
Normal 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),
|
||||
}
|
141
src/lib.rs
Normal file
141
src/lib.rs
Normal file
@ -0,0 +1,141 @@
|
||||
#![feature(if_let_guard, extend_one, proc_macro_span)]
|
||||
|
||||
use std::{fmt, rc::Rc};
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::Ident;
|
||||
|
||||
use crate::abi::ty::{ComplexType, SimpleType, Type, TypeWidth};
|
||||
|
||||
pub mod abi;
|
||||
pub mod error;
|
||||
pub mod parse;
|
||||
|
||||
pub struct Syscall {
|
||||
pub name: Ident,
|
||||
pub args: Vec<(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()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct TargetEnv {
|
||||
pub thin_pointer_width: TypeWidth,
|
||||
pub fat_pointer_width: TypeWidth,
|
||||
}
|
||||
|
||||
impl Syscall {
|
||||
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_result(#sys_call))
|
||||
}
|
||||
None => quote!(#sys_call;),
|
||||
};
|
||||
|
||||
let v = quote! {
|
||||
pub unsafe fn #name(#args) #return_type_arrow {
|
||||
#wrapped
|
||||
}
|
||||
};
|
||||
println!("{}", v);
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::rc::Rc;
|
||||
|
||||
use proc_macro2::Span;
|
||||
use quote::quote;
|
||||
use syn::Ident;
|
||||
|
||||
use crate::{
|
||||
abi::ty::{AbiPrimitive, ComplexType, SimpleType, TypeWidth},
|
||||
Syscall, TargetEnv,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn emit_syscall_stub() {
|
||||
let target = TargetEnv {
|
||||
thin_pointer_width: TypeWidth::U64,
|
||||
fat_pointer_width: TypeWidth::U128,
|
||||
};
|
||||
let syscall = Syscall {
|
||||
name: Ident::new("read", Span::call_site()),
|
||||
args: vec![
|
||||
(
|
||||
Ident::new("fd", Span::call_site()),
|
||||
ComplexType::Simple(SimpleType::Primitive(AbiPrimitive::U32)),
|
||||
),
|
||||
(
|
||||
Ident::new("buffer", Span::call_site()),
|
||||
ComplexType::Simple(SimpleType::Slice {
|
||||
mutable: true,
|
||||
element: Rc::new(ComplexType::Simple(SimpleType::Primitive(
|
||||
AbiPrimitive::U8,
|
||||
))),
|
||||
}),
|
||||
),
|
||||
],
|
||||
return_type: Some(ComplexType::Result(SimpleType::Primitive(
|
||||
AbiPrimitive::USize,
|
||||
))),
|
||||
};
|
||||
let res = syscall.emit_stub(&target);
|
||||
assert_eq!(
|
||||
res.to_string(),
|
||||
quote! {
|
||||
unsafe fn read(fd: u32, buffer: &mut [u8],) -> Result<usize, Error> {
|
||||
<Result<usize, Error> >::from_syscall_result(syscall!(SyscallFunction::read, fd as usize, buffer.as_mut_ptr().expose_addr(), buffer.len(),))
|
||||
}
|
||||
}
|
||||
.to_string()
|
||||
);
|
||||
}
|
||||
}
|
622
src/parse.rs
Normal file
622
src/parse.rs
Normal file
@ -0,0 +1,622 @@
|
||||
use std::{collections::HashMap, fmt, path::Path, rc::Rc, str::FromStr};
|
||||
|
||||
use proc_macro2::Span;
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
braced, parenthesized, parse::ParseStream, punctuated::Punctuated, spanned::Spanned, token,
|
||||
Expr, ExprLit, Ident, Lit, PathSegment, Token,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
abi::{
|
||||
syscall_enum_variant_identifier,
|
||||
ty::{AbiPrimitive, ComplexType, SimpleType},
|
||||
Abi,
|
||||
},
|
||||
Syscall,
|
||||
};
|
||||
|
||||
/*
|
||||
Document syntax:
|
||||
|
||||
newtype SomeType = u32;
|
||||
newtype Fd(u32);
|
||||
|
||||
syscall read(fd: Fd, buf: &mut [u8]) -> SizeResult;
|
||||
syscall write(fd: Fd, buf: &[u8]) -> SizeResult;
|
||||
|
||||
*/
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Document {
|
||||
newtypes: Vec<NewType>,
|
||||
syscalls: Vec<SyscallDefinition>,
|
||||
}
|
||||
|
||||
enum NewTypeDefinition {
|
||||
Alias(syn::Type),
|
||||
Transparent {
|
||||
repr: syn::Type,
|
||||
#[allow(dead_code)]
|
||||
sentinel: Option<syn::LitInt>,
|
||||
},
|
||||
Extern(syn::Type),
|
||||
}
|
||||
|
||||
struct SyscallDefinition {
|
||||
name: Ident,
|
||||
arguments: Punctuated<Argument, Token![,]>,
|
||||
return_type: Option<syn::Type>,
|
||||
}
|
||||
|
||||
struct Argument {
|
||||
name: Ident,
|
||||
ty: syn::Type,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct NewType {
|
||||
name: Ident,
|
||||
definition: NewTypeDefinition,
|
||||
}
|
||||
|
||||
struct ExternTypeItem {
|
||||
name: Ident,
|
||||
ty: syn::Type,
|
||||
}
|
||||
|
||||
enum TopLevel {
|
||||
NewType(NewType),
|
||||
ExternBlock(Punctuated<ExternTypeItem, Token![;]>),
|
||||
Syscall(SyscallDefinition),
|
||||
}
|
||||
|
||||
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(Argument::parse, Token![,])?;
|
||||
|
||||
let return_type = if input.peek(Token![->]) {
|
||||
input.parse::<Token![->]>()?;
|
||||
let ty = input.parse()?;
|
||||
Some(ty)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
name,
|
||||
arguments,
|
||||
return_type,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for Argument {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let name = input.parse()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
let ty = input.parse()?;
|
||||
|
||||
Ok(Self { name, ty })
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for NewType {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let name = input.parse()?;
|
||||
if input.peek(Token![=]) {
|
||||
let _token: Token![=] = input.parse()?;
|
||||
let ty = input.parse()?;
|
||||
|
||||
Ok(Self {
|
||||
name,
|
||||
definition: NewTypeDefinition::Alias(ty),
|
||||
})
|
||||
} else {
|
||||
// Parenthesized definition
|
||||
let content;
|
||||
parenthesized!(content in input);
|
||||
|
||||
// First value has to be a type
|
||||
let ty = content.parse()?;
|
||||
|
||||
Ok(Self {
|
||||
name,
|
||||
definition: NewTypeDefinition::Transparent {
|
||||
repr: ty,
|
||||
sentinel: None,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for ExternTypeItem {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
input.parse::<Token![type]>()?;
|
||||
let name: syn::Ident = input.parse()?;
|
||||
|
||||
let ty = if input.peek(Token![=]) {
|
||||
input.parse::<Token![=]>()?;
|
||||
input.parse()?
|
||||
} else {
|
||||
syn::Type::Path(syn::TypePath {
|
||||
qself: None,
|
||||
path: syn::Path {
|
||||
leading_colon: None,
|
||||
segments: Punctuated::from_iter([syn::PathSegment {
|
||||
arguments: syn::PathArguments::None,
|
||||
ident: name.clone(),
|
||||
}]),
|
||||
},
|
||||
})
|
||||
};
|
||||
|
||||
Ok(Self { name, ty })
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for TopLevel {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(Ident) {
|
||||
let ident: Ident = input.parse()?;
|
||||
match ident.to_string().as_str() {
|
||||
"newtype" => return NewType::parse(input).map(Self::NewType),
|
||||
"syscall" => return SyscallDefinition::parse(input).map(Self::Syscall),
|
||||
_ => (),
|
||||
}
|
||||
} else if lookahead.peek(token::Extern) {
|
||||
input.parse::<Token![extern]>()?;
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(Token![type]) {
|
||||
input.parse::<token::Type>()?;
|
||||
let name = input.parse()?;
|
||||
input.parse::<Token![=]>()?;
|
||||
let ty = input.parse()?;
|
||||
return Ok(Self::NewType(NewType {
|
||||
name,
|
||||
definition: NewTypeDefinition::Extern(ty),
|
||||
}));
|
||||
} else if lookahead.peek(token::Brace) {
|
||||
let content;
|
||||
braced!(content in input);
|
||||
let items = content.parse_terminated(ExternTypeItem::parse, Token![;])?;
|
||||
return Ok(Self::ExternBlock(items));
|
||||
} else {
|
||||
return Err(input.error(&format!(
|
||||
"Expected 'extern type' or 'extern {{ ... }}': {}",
|
||||
lookahead.error()
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
Err(input.error(&format!(
|
||||
"Expected 'extern', 'newtype' or 'syscall', got '{}'",
|
||||
lookahead.error()
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for Document {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let mut newtypes = vec![];
|
||||
let mut syscalls = vec![];
|
||||
|
||||
loop {
|
||||
if input.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
let toplevel = TopLevel::parse(input)?;
|
||||
input.parse::<Token![;]>()?;
|
||||
|
||||
match toplevel {
|
||||
TopLevel::NewType(ty) => newtypes.push(ty),
|
||||
TopLevel::ExternBlock(extys) => {
|
||||
for exty in extys {
|
||||
newtypes.push(NewType {
|
||||
name: exty.name,
|
||||
definition: NewTypeDefinition::Extern(exty.ty),
|
||||
});
|
||||
}
|
||||
}
|
||||
TopLevel::Syscall(syscall) => syscalls.push(syscall),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self { newtypes, syscalls })
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Argument {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let ty = &self.ty;
|
||||
f.debug_tuple("Argument")
|
||||
.field(&self.name)
|
||||
.field("e!(#ty))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SyscallDefinition {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let args: Vec<_> = self.arguments.iter().collect();
|
||||
let return_type = self.return_type.as_ref().map(|ty| quote!(#ty));
|
||||
|
||||
f.debug_struct("SyscallDefinition")
|
||||
.field("name", &self.name)
|
||||
.field("args", &args)
|
||||
.field("return_type", &return_type)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NewTypeDefinition {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Transparent { repr, .. } => f
|
||||
.debug_struct("NewTypeDefinition::Transparent")
|
||||
.field("repr", "e!(#repr))
|
||||
.finish(),
|
||||
Self::Alias(ty) => f
|
||||
.debug_tuple("NewTypeDefinition::Alias")
|
||||
.field("e!(#ty))
|
||||
.finish(),
|
||||
Self::Extern(ty) => f
|
||||
.debug_tuple("NewTypeDefinition::Extern")
|
||||
.field("e!(#ty))
|
||||
.finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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: &HashMap<String, Rc<ComplexType>>,
|
||||
) -> 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.get(name) => Ok(ty.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)))
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn process_newtype(
|
||||
t: &NewType,
|
||||
known_types: &HashMap<String, Rc<ComplexType>>,
|
||||
) -> Result<(Ident, Rc<ComplexType>), ParseError> {
|
||||
let ty = match &t.definition {
|
||||
NewTypeDefinition::Transparent { repr, .. } => {
|
||||
let ty = process_type(repr, known_types)?;
|
||||
if let ComplexType::Simple(SimpleType::Primitive(ty)) = ty.as_ref() {
|
||||
Rc::new(ComplexType::Simple(SimpleType::Transparent {
|
||||
name: t.name.clone(),
|
||||
inner: *ty,
|
||||
}))
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
NewTypeDefinition::Extern(ty) => Rc::new(ComplexType::Extern(ty.clone())),
|
||||
NewTypeDefinition::Alias(ty) => process_type(ty, known_types)?,
|
||||
};
|
||||
Ok((t.name.clone(), ty))
|
||||
}
|
||||
|
||||
pub fn parse_abi(input: &str) -> Result<Abi, ParseError> {
|
||||
let doc = parse_abi_document(input)?;
|
||||
let mut abi = Abi {
|
||||
types: HashMap::new(),
|
||||
syscalls: Vec::new(),
|
||||
};
|
||||
|
||||
ParseError::fold(
|
||||
(),
|
||||
doc.newtypes.iter().map(|nt| {
|
||||
let (name, value) = process_newtype(nt, &abi.types)?;
|
||||
abi.types.insert(name.to_string(), value);
|
||||
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)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use quote::quote;
|
||||
|
||||
use crate::{abi::ty::TypeWidth, parse::UnwrapFancy, TargetEnv};
|
||||
|
||||
use super::parse_abi;
|
||||
|
||||
const TARGET_64: TargetEnv = TargetEnv {
|
||||
thin_pointer_width: TypeWidth::U64,
|
||||
fat_pointer_width: TypeWidth::U128,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn emit_basic_syscall() {
|
||||
let abi = parse_abi(
|
||||
r#"
|
||||
extern type MyStruct = some_lib::MyStruct;
|
||||
newtype Fd(u32);
|
||||
|
||||
syscall read(fd: Fd, buf: &mut [u8]) -> SizeResult;
|
||||
syscall get_file_info(data: &mut MyStruct);
|
||||
"#,
|
||||
)
|
||||
.unwrap_fancy("");
|
||||
|
||||
assert_eq!(
|
||||
abi.syscalls[0].emit_stub(&TARGET_64).to_string(),
|
||||
quote! {
|
||||
unsafe fn read(fd: Fd, buf: &mut [u8],) -> Result<usize, Error> {
|
||||
<Result<usize, Error> >::from_syscall_result(syscall!(
|
||||
SyscallFunction::read,
|
||||
fd.0 as usize,
|
||||
buf.as_mut_ptr().expose_addr(),
|
||||
buf.len(),
|
||||
))
|
||||
}
|
||||
}
|
||||
.to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
abi.syscalls[1].emit_stub(&TARGET_64).to_string(),
|
||||
quote! {
|
||||
unsafe fn get_file_info(data: &mut some_lib::MyStruct,) {
|
||||
syscall!(SyscallFunction::get_file_info, (data as *mut _).expose_addr(),);
|
||||
}
|
||||
}
|
||||
.to_string()
|
||||
);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user