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:
commit
d9b70439b8
1
tool/abi-generator/.gitignore
vendored
Normal file
1
tool/abi-generator/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
110
tool/abi-generator/Cargo.lock
generated
Normal file
110
tool/abi-generator/Cargo.lock
generated
Normal 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"
|
13
tool/abi-generator/Cargo.toml
Normal file
13
tool/abi-generator/Cargo.toml
Normal 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"]
|
15
tool/abi-generator/abi-lib/Cargo.toml
Normal file
15
tool/abi-generator/abi-lib/Cargo.toml
Normal 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",
|
||||||
|
]
|
19
tool/abi-generator/abi-lib/src/lib.rs
Normal file
19
tool/abi-generator/abi-lib/src/lib.rs
Normal 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 {}
|
56
tool/abi-generator/abi-lib/src/primitive_impls.rs
Normal file
56
tool/abi-generator/abi-lib/src/primitive_impls.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
95
tool/abi-generator/abi-lib/src/wrapper_impls.rs
Normal file
95
tool/abi-generator/abi-lib/src/wrapper_impls.rs
Normal 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!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
tool/abi-generator/example-abi/Cargo.toml
Normal file
11
tool/abi-generator/example-abi/Cargo.toml
Normal 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"
|
34
tool/abi-generator/example-abi/build.rs
Normal file
34
tool/abi-generator/example-abi/build.rs
Normal 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();
|
||||||
|
}
|
152
tool/abi-generator/example-abi/src/main.rs
Normal file
152
tool/abi-generator/example-abi/src/main.rs
Normal 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() {}
|
201
tool/abi-generator/example-abi/syscall.abi
Normal file
201
tool/abi-generator/example-abi/syscall.abi
Normal 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>;
|
513
tool/abi-generator/src/abi/mod.rs
Normal file
513
tool/abi-generator/src/abi/mod.rs
Normal 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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
124
tool/abi-generator/src/abi/syscall.rs
Normal file
124
tool/abi-generator/src/abi/syscall.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
181
tool/abi-generator/src/abi/ty/complex.rs
Normal file
181
tool/abi-generator/src/abi/ty/complex.rs
Normal 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("e!(#alias)).finish(),
|
||||||
|
Self::MaybeUninit(ty) => f.debug_tuple("MaybeUninit").field(&ty).finish(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
167
tool/abi-generator/src/abi/ty/mod.rs
Normal file
167
tool/abi-generator/src/abi/ty/mod.rs
Normal 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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
98
tool/abi-generator/src/abi/ty/primitive.rs
Normal file
98
tool/abi-generator/src/abi/ty/primitive.rs
Normal 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(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
158
tool/abi-generator/src/abi/ty/simple.rs
Normal file
158
tool/abi-generator/src/abi/ty/simple.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
tool/abi-generator/src/error.rs
Normal file
22
tool/abi-generator/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),
|
||||||
|
}
|
13
tool/abi-generator/src/lib.rs
Normal file
13
tool/abi-generator/src/lib.rs
Normal 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,
|
||||||
|
}
|
120
tool/abi-generator/src/syntax/bitfield_type.rs
Normal file
120
tool/abi-generator/src/syntax/bitfield_type.rs
Normal 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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
66
tool/abi-generator/src/syntax/common.rs
Normal file
66
tool/abi-generator/src/syntax/common.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
135
tool/abi-generator/src/syntax/document.rs
Normal file
135
tool/abi-generator/src/syntax/document.rs
Normal 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
99
tool/abi-generator/src/syntax/enum_type.rs
Normal file
99
tool/abi-generator/src/syntax/enum_type.rs
Normal 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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
103
tool/abi-generator/src/syntax/error.rs
Normal file
103
tool/abi-generator/src/syntax/error.rs
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
70
tool/abi-generator/src/syntax/extern_block.rs
Normal file
70
tool/abi-generator/src/syntax/extern_block.rs
Normal 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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
329
tool/abi-generator/src/syntax/mod.rs
Normal file
329
tool/abi-generator/src/syntax/mod.rs
Normal 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)
|
||||||
|
}
|
50
tool/abi-generator/src/syntax/newtype.rs
Normal file
50
tool/abi-generator/src/syntax/newtype.rs
Normal 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;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
82
tool/abi-generator/src/syntax/syscall.rs
Normal file
82
tool/abi-generator/src/syntax/syscall.rs
Normal 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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user