use std::ffi::CString;
use std::marker::PhantomData;
use std::mem;
use super::audio;
use super::wren;
use crate::errors::{Error, Result};
use crate::unsafe_wrappers::dome as unsafe_dome;
use crate::Api;
pub(crate) type ForeignFn = wren::ForeignMethodFn;
pub(crate) type FinalizerFn = wren::FinalizerFn;
#[derive(Debug)]
#[repr(transparent)]
pub struct Context<'a>(
pub(crate) unsafe_dome::Context,
pub(crate) PhantomData<&'a ()>,
);
impl Context<'_> {
#[inline]
pub fn register_module(&mut self, name: &str, source: &str) -> Result {
let c_name = CString::new(name).expect("Module name contains null byte(s).");
let c_source = CString::new(source).expect("Source contains null byte(s).");
(Api::dome().register_module)(self.0, c_name.as_ptr(), c_source.as_ptr()).to_result(|| {
Error::ModuleRegistrationFailed {
module_name: name.to_owned(),
}
})
}
#[inline]
pub unsafe fn register_fn(
&mut self,
module: &str,
signature: &str,
method: ForeignFn,
) -> Result {
let c_module = CString::new(module).expect("Method name contains null byte(s).");
let c_signature = CString::new(signature).expect("Method signature contains null byte(s).");
(Api::dome().register_fn)(
self.0,
c_module.as_ptr(),
c_signature.as_ptr(),
mem::transmute(method),
)
.to_result(|| Error::MethodRegistrationFailed {
module_name: module.to_owned(),
method_signature: signature.to_owned(),
})
}
#[inline]
pub unsafe fn register_class(
&mut self,
module_name: &str,
class_name: &str,
allocate: ForeignFn,
finalize: Option<FinalizerFn>,
) -> Result {
let c_module_name = CString::new(module_name).expect("Module name contains null byte(s).");
let c_class_name = CString::new(class_name).expect("Class name contains null byte(s).");
(Api::dome().register_class)(
self.0,
c_module_name.as_ptr(),
c_class_name.as_ptr(),
mem::transmute(allocate),
finalize,
)
.to_result(|| Error::ClassRegistrationFailed {
module_name: module_name.to_owned(),
class_name: class_name.to_owned(),
})
}
#[inline]
pub fn lock_module(&mut self, name: &str) {
let name = CString::new(name).expect("Module name contains null byte(s).");
(Api::dome().lock_module)(self.0, name.as_ptr())
}
#[inline]
pub fn log(&mut self, text: &str) {
let fmt = CString::new("%s").unwrap();
let text = CString::new(text).expect("Text contains null byte(s).");
unsafe { (Api::dome().log)(self.0, fmt.as_ptr(), text.as_ptr()) }
}
#[inline]
pub fn create_channel<T: Send + Sync>(
&self,
mix: audio::ChannelMix<T>,
update: audio::ChannelUpdate<T>,
user_data: T,
) -> audio::Channel<T> {
let data = Box::into_raw(Box::new(audio::ChannelData::new(mix, update, user_data)));
audio::Channel(
(Api::audio().channel_create)(
self.0,
audio::mix,
audio::update,
if mem::needs_drop::<T>() {
audio::finish
} else {
audio::finish_no_drop
},
data as *mut _,
),
PhantomData,
)
}
}
#[macro_export]
macro_rules! register_modules {
{ $ctx:expr, $($modules:tt)+ } => {
$crate::__register_modules_impl! { $ctx, $($modules)+ }
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __register_modules_impl {
{ $ctx:expr, $($modules:tt)+ } => {{
$crate::__register_modules_impl! { @process_modules
ctx = [{ $ctx }]
modules = [{ $($modules)+ }]
}
}};
{ @process_modules
ctx = [{ $ctx:expr }]
modules = [{ $(
module $module_name:literal { $($module_contents:tt)* }
)+ }]
} => {{
let mut result: $crate::Result = Ok(());
$(
result = result.and_then(|()| {
$ctx.register_module(
$module_name,
$crate::__register_modules_impl! { @get_module_source
items = [{ $($module_contents)* }]
}
)
})
.and_then(|()| {
let result = $crate::__register_modules_impl! { @register_module_members
ctx = [{ $ctx }]
module = [{ $module_name }]
items = [{ $($module_contents)* }]
};
$ctx.lock_module($module_name);
result
});
)+
result
}};
{ @get_module_source
items = [{ }]
} => { "" };
{ @get_module_source
items = [{
foreign class $name:ident $(is $superclass:literal)? = $constructor:ident of $foreign_type:ty {
$($class_contents:tt)*
}
$($rest:tt)*
}]
} => {
concat!(
"foreign class ", stringify!($name), $(" is (", $superclass, ")",)? " {\n",
$crate::__register_modules_impl! { @get_class_source
items = [{ $($class_contents)* }]
},
"}\n",
$crate::__register_modules_impl! { @get_module_source
items = [{ $($rest)* }]
},
)
};
{ @get_module_source
items = [{
class $name:ident $(is $superclass:literal)? = $type:ty { $($class_contents:tt)* }
$($rest:tt)*
}]
} => {
concat!(
"class ", stringify!($name), $(" is (", $superclass, ")",)? " {\n",
$crate::__register_modules_impl! { @get_class_source
items = [{ $($class_contents)* }]
},
"}\n",
$crate::__register_modules_impl! { @get_module_source
items = [{ $($rest)* }]
},
)
};
{ @get_module_source
items = [{
$code:literal
$($rest:tt)*
}]
} => {
concat!(
"\n", $code, "\n",
$crate::__register_modules_impl! { @get_module_source
items = [{ $($rest)* }]
},
)
};
{ @get_class_source
items = [{ }]
} => { "" };
{ @get_class_source
items = [{
foreign static $name:ident = $method:ident
$($rest:tt)*
}]
} => {
concat!(
"foreign static ", stringify!($name), "\n",
$crate::__register_modules_impl! { @get_class_source
items = [{ $($rest)* }]
},
)
};
{ @get_class_source
items = [{
foreign $name:ident = $method:ident
$($rest:tt)*
}]
} => {
concat!(
"foreign ", stringify!($name), "\n",
$crate::__register_modules_impl! { @get_class_source
items = [{ $($rest)* }]
},
)
};
{ @get_class_source
items = [{
foreign static $name:ident=($value:ident) = $method:ident
$($rest:tt)*
}]
} => {
concat!(
"foreign static ", stringify!($name), "=(", stringify!($value), ")\n",
$crate::__register_modules_impl! { @get_class_source
items = [{ $($rest)* }]
},
)
};
{ @get_class_source
items = [{
foreign $name:ident=($value:ident) = $method:ident
$($rest:tt)*
}]
} => {
concat!(
"foreign ", stringify!($name), "=(", stringify!($value), ")\n",
$crate::__register_modules_impl! { @get_class_source
items = [{ $($rest)* }]
},
)
};
{ @get_class_source
items = [{
foreign static $name:ident($($param0:ident $(, $params:ident)*)?) = $method:ident
$($rest:tt)*
}]
} => {
concat!(
"foreign static ", stringify!($name), "(",
$(stringify!($param0), $(",", stringify!($params),)*)?
")\n",
$crate::__register_modules_impl! { @get_class_source
items = [{ $($rest)* }]
},
)
};
{ @get_class_source
items = [{
foreign $name:ident($($param0:ident $(, $params:ident)*)?) = $method:ident
$($rest:tt)*
}]
} => {
concat!(
"foreign ", stringify!($name), "(",
$(stringify!($param0), $(",", stringify!($params),)*)?
")\n",
$crate::__register_modules_impl! { @get_class_source
items = [{ $($rest)* }]
},
)
};
{ @get_class_source
items = [{
foreign static [$param0:ident $(, $params:ident)*] = $method:ident
$($rest:tt)*
}]
} => {
concat!(
"foreign static [", stringify!($param0), $(",", stringify!($params),)* "]\n",
$crate::__register_modules_impl! { @get_class_source
items = [{ $($rest)* }]
},
)
};
{ @get_class_source
items = [{
foreign [$param0:ident $(, $params:ident)*] = $method:ident
$($rest:tt)*
}]
} => {
concat!(
"foreign [", stringify!($param0), $(",", stringify!($params),)* "]\n",
$crate::__register_modules_impl! { @get_class_source
items = [{ $($rest)* }]
},
)
};
{ @get_class_source
items = [{
foreign static [$param0:ident $(, $params:ident)*]=($value:ident) = $method:ident
$($rest:tt)*
}]
} => {
concat!(
"foreign static [",
stringify!($param0), $(",", stringify!($params),)*
"]=(", stringify!($value), ")\n",
$crate::__register_modules_impl! { @get_class_source
items = [{ $($rest)* }]
},
)
};
{ @get_class_source
items = [{
foreign [$param0:ident $(, $params:ident)*]=($value:ident) = $method:ident
$($rest:tt)*
}]
} => {
concat!(
"foreign [",
stringify!($param0), $(",", stringify!($params),)*
"]=(", stringify!($value), ")\n",
$crate::__register_modules_impl! { @get_class_source
items = [{ $($rest)* }]
},
)
};
{ @get_class_source
items = [{
$method:literal
$($rest:tt)*
}]
} => {
concat!(
$method, "\n",
$crate::__register_modules_impl! { @get_class_source
items = [{ $($rest)* }]
},
)
};
{ @register_module_members
ctx = [{ $ctx:expr }]
module = [{ $module:literal }]
items = [{ }]
} => { Ok(()) };
{ @register_module_members
ctx = [{ $ctx:expr }]
module = [{ $module:literal }]
items = [{
foreign class $name:ident $(is $superclass:literal)? = $constructor:ident of $foreign_type:ty {
$($class_contents:tt)*
}
$($rest:tt)*
}]
} => {{
extern "C" fn __dome_cloomnik_class_allocate(mut vm: $crate::WrenVM) {
if let Some(instance) = $crate::__catch_panic_from_foreign(&vm, || {
<$foreign_type>::$constructor(&vm)
}) {
unsafe {
vm.set_slot_new_foreign_unchecked(0, 0, instance);
}
}
}
extern "C" fn __dome_cloomnik_class_finalize(data: *mut $crate::__c_void) {
let _ = ::std::panic::catch_unwind(|| {
let data = data as *mut $crate::__ForeignWrapper<$foreign_type>;
unsafe { ::std::ptr::drop_in_place(data) };
});
}
unsafe {
$ctx.register_class(
$module,
stringify!($name),
__dome_cloomnik_class_allocate,
if ::std::mem::needs_drop::<$crate::__ForeignWrapper<$foreign_type>>() {
Some(__dome_cloomnik_class_finalize)
} else {
None
},
)
}
.and_then(|()| {
$crate::__register_modules_impl! { @register_class_members
ctx = [{ $ctx }]
module = [{ $module }]
class = [{ $name }]
items = [{ $($class_contents)* }]
type = [{ $foreign_type }]
foreign_type = [{ $foreign_type }]
}
})
.and_then(|()| {
$crate::__register_modules_impl! { @register_module_members
ctx = [{ $ctx }]
module = [{ $module }]
items = [{ $($rest)* }]
}
})
}};
{ @register_module_members
ctx = [{ $ctx:expr }]
module = [{ $module:literal }]
items = [{
class $name:ident $(is $superclass:literal)? = $type:ty { $($class_contents:tt)* }
$($rest:tt)*
}]
} => {
$crate::__register_modules_impl! { @register_class_members
ctx = [{ $ctx }]
module = [{ $module }]
class = [{ $name }]
items = [{ $($class_contents)* }]
type = [{ $type }]
}
.and_then(|()| {
$crate::__register_modules_impl! { @register_module_members
ctx = [{ $ctx }]
module = [{ $module }]
items = [{ $($rest)* }]
}
})
};
{ @register_module_members
ctx = [{ $ctx:expr }]
module = [{ $module:literal }]
items = [{
$code:literal
$($rest:tt)*
}]
} => {
$crate::__register_modules_impl! { @register_module_members
ctx = [{ $ctx }]
module = [{ $module }]
items = [{ $($rest)* }]
}
};
{ @underscore $($t:tt)* } => { "_" };
{ @register_class_members
ctx = [{ $ctx:expr }]
module = [{ $module:literal }]
class = [{ $class:ident }]
items = [{ }]
type = [{ $($type:tt)+ }]
$(foreign_type = [{ $($foreign_type:tt)+ }])?
} => { Ok(()) };
{ @register_class_members
ctx = [{ $ctx:expr }]
module = [{ $module:literal }]
class = [{ $class:ident }]
items = [{
foreign static $name:ident = $method:ident
$($rest:tt)*
}]
type = [{ $($type:tt)+ }]
$(foreign_type = [{ $($foreign_type:tt)+ }])?
} => {{
extern "C" fn __dome_cloomnik_method(vm: $crate::WrenVM) {
$crate::__catch_panic_from_foreign(&vm, || <$($type)+>::$method(&mut vm));
}
unsafe {
$ctx.register_fn(
$module,
concat!("static ", stringify!($class), ".", stringify!($name)),
__dome_cloomnik_method,
)
}
.and_then(|()| {
$crate::__register_modules_impl! { @register_class_members
ctx = [{ $ctx }]
module = [{ $module }]
class = [{ $class }]
items = [{ $($rest)* }]
type = [{ $($type)+ }]
$(foreign_type = [{ $($foreign_type)+ }])?
}
})
}};
{ @register_class_members
ctx = [{ $ctx:expr }]
module = [{ $module:literal }]
class = [{ $class:ident }]
items = [{
foreign $name:ident = $method:ident
$($rest:tt)*
}]
type = [{ $($type:tt)+ }]
$(foreign_type = [{ $($foreign_type:tt)+ }])?
} => {{
extern "C" fn __dome_cloomnik_method(vm: $crate::WrenVM) {
$crate::__catch_panic_from_foreign(&vm, || {
<$($type)+>::$method(
$(unsafe { vm.get_slot_foreign_unchecked::<$($foreign_type)+>(0) },)?
&mut unsafe { $crate::__clone_vm(&vm) },
)
});
}
unsafe {
$ctx.register_fn(
$module,
concat!(stringify!($class), ".", stringify!($name)),
__dome_cloomnik_method,
)
}
.and_then(|()| {
$crate::__register_modules_impl! { @register_class_members
ctx = [{ $ctx }]
module = [{ $module }]
class = [{ $class }]
items = [{ $($rest)* }]
type = [{ $($type)+ }]
$(foreign_type = [{ $($foreign_type)+ }])?
}
})
}};
{ @register_class_members
ctx = [{ $ctx:expr }]
module = [{ $module:literal }]
class = [{ $class:ident }]
items = [{
foreign static $name:ident=($value:ident) = $method:ident
$($rest:tt)*
}]
type = [{ $($type:tt)+ }]
$(foreign_type = [{ $($foreign_type:tt)+ }])?
} => {{
extern "C" fn __dome_cloomnik_method(vm: $crate::WrenVM) {
$crate::__catch_panic_from_foreign(&vm, || <$($type)+>::$method(&mut vm));
}
unsafe {
$ctx.register_fn(
$module,
concat!("static ", stringify!($class), ".", stringify!($name), "=(_)"),
__dome_cloomnik_method,
)
}
.and_then(|()| {
$crate::__register_modules_impl! { @register_class_members
ctx = [{ $ctx }]
module = [{ $module }]
class = [{ $class }]
items = [{ $($rest)* }]
type = [{ $($type)+ }]
$(foreign_type = [{ $($foreign_type)+ }])?
}
})
}};
{ @register_class_members
ctx = [{ $ctx:expr }]
module = [{ $module:literal }]
class = [{ $class:ident }]
items = [{
foreign $name:ident=($value:ident) = $method:ident
$($rest:tt)*
}]
type = [{ $($type:tt)+ }]
$(foreign_type = [{ $($foreign_type:tt)+ }])?
} => {{
extern "C" fn __dome_cloomnik_method(vm: $crate::WrenVM) {
$crate::__catch_panic_from_foreign(&vm, || {
<$($type)+>::$method(
$(unsafe { vm.get_slot_foreign_unchecked::<$($foreign_type)+>(0) },)?
&mut unsafe { $crate::__clone_vm(&vm) },
)
});
}
unsafe {
$ctx.register_fn(
$module,
concat!(stringify!($class), ".", stringify!($name), "=(_)"),
__dome_cloomnik_method,
)
}
.and_then(|()| {
$crate::__register_modules_impl! { @register_class_members
ctx = [{ $ctx }]
module = [{ $module }]
class = [{ $class }]
items = [{ $($rest)* }]
type = [{ $($type)+ }]
$(foreign_type = [{ $($foreign_type)+ }])?
}
})
}};
{ @register_class_members
ctx = [{ $ctx:expr }]
module = [{ $module:literal }]
class = [{ $class:ident }]
items = [{
foreign static $name:ident($($param0:ident $(, $params:ident)*)?) = $method:ident
$($rest:tt)*
}]
type = [{ $($type:tt)+ }]
$(foreign_type = [{ $($foreign_type:tt)+ }])?
} => {{
extern "C" fn __dome_cloomnik_method(vm: $crate::WrenVM) {
$crate::__catch_panic_from_foreign(&vm, || <$($type)+>::$method(&mut vm));
}
unsafe {
$ctx.register_fn(
$module,
concat!("static ", stringify!($class), ".", stringify!($name), "(",
$(
$crate::__register_modules_impl! { @underscore $param0 },
$(",", $crate::__register_modules_impl! { @underscore $params },)*
)?
")"),
__dome_cloomnik_method,
)
}
.and_then(|()| {
$crate::__register_modules_impl! { @register_class_members
ctx = [{ $ctx }]
module = [{ $module }]
class = [{ $class }]
items = [{ $($rest)* }]
type = [{ $($type)+ }]
$(foreign_type = [{ $($foreign_type)+ }])?
}
})
}};
{ @register_class_members
ctx = [{ $ctx:expr }]
module = [{ $module:literal }]
class = [{ $class:ident }]
items = [{
foreign $name:ident($($param0:ident $(, $params:ident)*)?) = $method:ident
$($rest:tt)*
}]
type = [{ $($type:tt)+ }]
$(foreign_type = [{ $($foreign_type:tt)+ }])?
} => {{
extern "C" fn __dome_cloomnik_method(vm: $crate::WrenVM) {
$crate::__catch_panic_from_foreign(&vm, || {
<$($type)+>::$method(
$(unsafe { vm.get_slot_foreign_unchecked::<$($foreign_type)+>(0) },)?
&mut unsafe { $crate::__clone_vm(&vm) },
)
});
}
unsafe {
$ctx.register_fn(
$module,
concat!(stringify!($class), ".", stringify!($name), "(",
$(
$crate::__register_modules_impl! { @underscore $param0 },
$(",", $crate::__register_modules_impl! { @underscore $params },)*
)?
")"),
__dome_cloomnik_method,
)
}
.and_then(|()| {
$crate::__register_modules_impl! { @register_class_members
ctx = [{ $ctx }]
module = [{ $module }]
class = [{ $class }]
items = [{ $($rest)* }]
type = [{ $($type)+ }]
$(foreign_type = [{ $($foreign_type)+ }])?
}
})
}};
{ @register_class_members
ctx = [{ $ctx:expr }]
module = [{ $module:literal }]
class = [{ $class:ident }]
items = [{
foreign static [$param0:ident $(, $params:ident)*] = $method:ident
$($rest:tt)*
}]
type = [{ $($type:tt)+ }]
$(foreign_type = [{ $($foreign_type:tt)+ }])?
} => {{
extern "C" fn __dome_cloomnik_method(vm: $crate::WrenVM) {
$crate::__catch_panic_from_foreign(&vm, || <$($type)+>::$method(&mut vm));
}
unsafe {
$ctx.register_fn(
$module,
concat!("static ", stringify!($class), ".[",
$crate::__register_modules_impl! { @underscore $param0 },
$(",", $crate::__register_modules_impl! { @underscore $params },)*
"]"),
__dome_cloomnik_method,
)
}
.and_then(|()| {
$crate::__register_modules_impl! { @register_class_members
ctx = [{ $ctx }]
module = [{ $module }]
class = [{ $class }]
items = [{ $($rest)* }]
type = [{ $($type)+ }]
$(foreign_type = [{ $($foreign_type)+ }])?
}
})
}};
{ @register_class_members
ctx = [{ $ctx:expr }]
module = [{ $module:literal }]
class = [{ $class:ident }]
items = [{
foreign [$param0:ident $(, $params:ident)*] = $method:ident
$($rest:tt)*
}]
type = [{ $($type:tt)+ }]
$(foreign_type = [{ $($foreign_type:tt)+ }])?
} => {{
extern "C" fn __dome_cloomnik_method(vm: $crate::WrenVM) {
$crate::__catch_panic_from_foreign(&vm, || {
<$($type)+>::$method(
$(unsafe { vm.get_slot_foreign_unchecked::<$($foreign_type)+>(0) },)?
&mut unsafe { $crate::__clone_vm(&vm) },
)
});
}
unsafe {
$ctx.register_fn(
$module,
concat!(stringify!($class), ".[",
$crate::__register_modules_impl! { @underscore $param0 },
$(",", $crate::__register_modules_impl! { @underscore $params },)*
"]"),
__dome_cloomnik_method,
)
}
.and_then(|()| {
$crate::__register_modules_impl! { @register_class_members
ctx = [{ $ctx }]
module = [{ $module }]
class = [{ $class }]
items = [{ $($rest)* }]
type = [{ $($type)+ }]
$(foreign_type = [{ $($foreign_type)+ }])?
}
})
}};
{ @register_class_members
ctx = [{ $ctx:expr }]
module = [{ $module:literal }]
class = [{ $class:ident }]
items = [{
foreign static [$param0:ident $(, $params:ident)*]=($value:ident) = $method:ident
$($rest:tt)*
}]
type = [{ $($type:tt)+ }]
$(foreign_type = [{ $($foreign_type:tt)+ }])?
} => {{
extern "C" fn __dome_cloomnik_method(vm: $crate::WrenVM) {
$crate::__catch_panic_from_foreign(&vm, || <$($type)+>::$method(&mut vm));
}
unsafe {
$ctx.register_fn(
$module,
concat!("static ", stringify!($class), ".[",
$crate::__register_modules_impl! { @underscore $param0 },
$(",", $crate::__register_modules_impl! { @underscore $params },)*
"]=(_)"),
__dome_cloomnik_method,
)
}
.and_then(|()| {
$crate::__register_modules_impl! { @register_class_members
ctx = [{ $ctx }]
module = [{ $module }]
class = [{ $class }]
items = [{ $($rest)* }]
type = [{ $($type)+ }]
$(foreign_type = [{ $($foreign_type)+ }])?
}
})
}};
{ @register_class_members
ctx = [{ $ctx:expr }]
module = [{ $module:literal }]
class = [{ $class:ident }]
items = [{
foreign [$param0:ident $(, $params:ident)*]=($value:ident) = $method:ident
$($rest:tt)*
}]
type = [{ $($type:tt)+ }]
$(foreign_type = [{ $($foreign_type:tt)+ }])?
} => {{
extern "C" fn __dome_cloomnik_method(vm: $crate::WrenVM) {
$crate::__catch_panic_from_foreign(&vm, || {
<$($type)+>::$method(
$(unsafe { vm.get_slot_foreign_unchecked::<$($foreign_type)+>(0) },)?
&mut unsafe { $crate::__clone_vm(&vm) },
)
});
}
unsafe {
$ctx.register_fn(
$module,
concat!(stringify!($class), ".[",
$crate::__register_modules_impl! { @underscore $param0 },
$(",", $crate::__register_modules_impl! { @underscore $params },)*
"]=(_)"),
__dome_cloomnik_method,
)
}
.and_then(|()| {
$crate::__register_modules_impl! { @register_class_members
ctx = [{ $ctx }]
module = [{ $module }]
class = [{ $class }]
items = [{ $($rest)* }]
type = [{ $($type)+ }]
$(foreign_type = [{ $($foreign_type)+ }])?
}
})
}};
{ @register_class_members
ctx = [{ $ctx:expr }]
module = [{ $module:literal }]
class = [{ $class:ident }]
items = [{
$method:literal
$($rest:tt)*
}]
type = [{ $($type:tt)+ }]
$(foreign_type = [{ $($foreign_type:tt)+ }])?
} => {
$crate::__register_modules_impl! { @register_class_members
ctx = [{ $ctx }]
module = [{ $module }]
class = [{ $class }]
items = [{ $($rest)* }]
type = [{ $($type)+ }]
$(foreign_type = [{ $($foreign_type)+ }])?
}
};
}