use std::{error, ptr::NonNull};
#[doc(hidden)]
pub mod __private {
pub use linkme;
}
use linkme::distributed_slice;
use crate::{compile::Compiler, runtime::vm::Builder};
#[derive(Copy, Clone)]
pub struct Version {
pub major: u32,
pub minor: u32,
pub patch: u32,
}
pub type Error = Box<dyn error::Error + 'static>;
pub trait Extension: Send + Sync + 'static {
type Error: error::Error + 'static;
const NAME: &str;
const DESCRIPTION: &str;
const VERSION: Version;
fn apply_compiler<'a>(&self, compiler: &mut Compiler<'a>) -> Result<(), Self::Error>;
fn apply_vm<'v>(&self, builder: &mut Builder<'v>) -> Result<(), Self::Error>;
}
#[doc(hidden)]
pub struct Vtbl {
name: &'static str,
description: &'static str,
version: Version,
apply_compiler: unsafe fn(this: NonNull<()>, compiler: &mut Compiler) -> Result<(), Error>,
apply_vm: for<'v> unsafe fn(this: NonNull<()>, builder: &mut Builder<'v>) -> Result<(), Error>,
}
#[doc(hidden)]
pub struct Erased {
vtbl: Vtbl,
ext: NonNull<()>,
}
unsafe impl Send for Erased {}
unsafe impl Sync for Erased {}
#[doc(hidden)]
impl Vtbl {
pub const fn erase<T: Extension>(ext: &'static T) -> Erased {
Erased {
vtbl: Vtbl {
name: T::NAME,
description: T::DESCRIPTION,
version: T::VERSION,
apply_compiler: |this, compiler| unsafe {
this.cast::<T>()
.as_ref()
.apply_compiler(compiler)
.map_err(|e| e.into())
},
apply_vm: |this, builder| unsafe {
this.cast::<T>()
.as_ref()
.apply_vm(builder)
.map_err(|e| e.into())
},
},
ext: NonNull::from_ref(ext).cast(),
}
}
}
#[doc(hidden)]
#[distributed_slice]
pub static EXTENSIONS: [Erased];
#[macro_export]
macro_rules! extension {
($expr: expr) => {
#[$crate::extension::__private::linkme::distributed_slice($crate::extension::EXTENSIONS)]
#[linkme(crate = $crate::extension::__private::linkme)]
static _EXTENSION: $crate::extension::Erased = $crate::extension::Vtbl::erase(&$expr);
};
}
pub struct CompilerExtension {
vtbl: &'static Vtbl,
ext: NonNull<()>,
}
impl CompilerExtension {
pub fn name(&self) -> &str {
self.vtbl.name
}
pub fn description(&self) -> &str {
self.vtbl.description
}
pub fn version(&self) -> Version {
self.vtbl.version
}
pub fn apply(&self, compiler: &mut Compiler) -> Result<(), Error> {
unsafe { (self.vtbl.apply_compiler)(self.ext, compiler) }
}
}
pub trait CompilerExt {
fn extensions(&mut self) -> impl Iterator<Item = CompilerExtension> + 'static;
}
impl<'a> CompilerExt for Compiler<'a> {
fn extensions(&mut self) -> impl Iterator<Item = CompilerExtension> + 'static {
EXTENSIONS
.iter()
.map(|Erased { vtbl, ext }| CompilerExtension { vtbl, ext: *ext })
}
}
pub struct VmExtension {
vtbl: &'static Vtbl,
ext: NonNull<()>,
}
impl VmExtension {
pub fn name(&self) -> &str {
self.vtbl.name
}
pub fn description(&self) -> &str {
self.vtbl.description
}
pub fn version(&self) -> Version {
self.vtbl.version
}
pub fn apply<'v>(&self, builder: &mut Builder<'v>) -> Result<(), Error> {
unsafe { (self.vtbl.apply_vm)(self.ext, builder) }
}
}
pub trait VmExt {
fn extensions(&self) -> impl Iterator<Item = VmExtension> + 'static;
}
impl<'a> VmExt for Builder<'a> {
fn extensions(&self) -> impl Iterator<Item = VmExtension> + 'static {
EXTENSIONS
.iter()
.map(|Erased { vtbl, ext }| VmExtension { vtbl, ext: *ext })
}
}