use std::ffi::CString;
use std::marker::PhantomData;
use std::mem;
use super::audio;
use super::wren;
use crate::unsafe_wrappers::dome as unsafe_dome;
use crate::Api;
type Result = std::result::Result<(), ()>;
pub(crate) type ForeignFn = wren::ForeignMethodFn;
pub(crate) type FinalizerFn = wren::FinalizerFn;
#[derive(Debug)]
#[repr(transparent)]
pub struct Context(pub(crate) unsafe_dome::Context);
impl Context {
#[inline]
pub fn register_module(&self, name: &str, source: &str) -> Result {
let name = CString::new(name).expect("Module name contains null byte(s).");
let source = CString::new(source).expect("Source contains null byte(s).");
(Api::dome().register_module)(self.0, name.as_ptr(), source.as_ptr()).into()
}
#[inline]
pub unsafe fn register_fn(&self, module: &str, signature: &str, method: ForeignFn) -> Result {
let module = CString::new(module).expect("Method name contains null byte(s).");
let signature = CString::new(signature).expect("Method signature contains null byte(s).");
(Api::dome().register_fn)(
self.0,
module.as_ptr(),
signature.as_ptr(),
mem::transmute(method),
)
.into()
}
#[inline]
pub unsafe fn register_class(
&self,
module_name: &str,
class_name: &str,
allocate: ForeignFn,
finalize: Option<FinalizerFn>,
) -> Result {
let module_name = CString::new(module_name).expect("Module name contains null byte(s).");
let class_name = CString::new(class_name).expect("Class name contains null byte(s).");
(Api::dome().register_class)(
self.0,
module_name.as_ptr(),
class_name.as_ptr(),
mem::transmute(allocate),
finalize,
)
.into()
}
#[inline]
pub fn lock_module(&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(&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! { @process_modules
ctx = [{ $ctx }]
modules = [{ $($modules)+ }]
}
}};
{ @process_modules
ctx = [{ $ctx:expr }]
modules = [{ $(
module $module_name:literal { $($module_contents:tt)* }
)+ }]
} => {
$(
$ctx.register_module(
$module_name,
$crate::register_modules! { @get_module_source
items = [{ $($module_contents)* }]
}
).unwrap();
$crate::register_modules! { @register_module_members
ctx = [{ $ctx }]
module = [{ $module_name }]
items = [{ $($module_contents)* }]
}
$ctx.lock_module($module_name);
)+
};
{ @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! { @get_class_source
items = [{ $($class_contents)* }]
},
"}\n",
$crate::register_modules! { @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! { @get_class_source
items = [{ $($class_contents)* }]
},
"}\n",
$crate::register_modules! { @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! { @get_class_source
items = [{ $($rest)* }]
},
)
};
{ @get_class_source
items = [{
foreign $name:ident = $method:ident
$($rest:tt)*
}]
} => {
concat!(
"foreign ", stringify!($name), "\n",
$crate::register_modules! { @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! { @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! { @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! { @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! { @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! { @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! { @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! { @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! { @get_class_source
items = [{ $($rest)* }]
},
)
};
{ @get_class_source
items = [{
$method:literal
$($rest:tt)*
}]
} => {
concat!(
$method, "\n",
$crate::register_modules! { @get_class_source
items = [{ $($rest)* }]
},
)
};
{ @register_module_members
ctx = [{ $ctx:expr }]
module = [{ $module:literal }]
items = [{ }]
} => { };
{ @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(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);
}
}
}
#[allow(unused)]
extern "C" fn __dome_cloomnik_class_finalize(data: *mut $crate::__c_void) {
let data = data as *mut $crate::__ForeignWrapper<$foreign_type>;
::std::panic::catch_unwind(|| 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
},
)
}
.unwrap();
$crate::register_modules! { @register_class_members
ctx = [{ $ctx }]
module = [{ $module }]
class = [{ $name }]
items = [{ $($class_contents)* }]
type = [{ $foreign_type }]
foreign_type = [{ $foreign_type }]
}
$crate::register_modules! { @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! { @register_class_members
ctx = [{ $ctx }]
module = [{ $module }]
class = [{ $name }]
items = [{ $($class_contents)* }]
type = [{ $type }]
}
$crate::register_modules! { @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)+ }])?
} => { };
{ @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(&vm));
}
unsafe {
$ctx.register_fn(
$module,
concat!("static ", stringify!($class), ".", stringify!($name)),
__dome_cloomnik_method,
)
}
.unwrap();
$crate::register_modules! { @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) },)?
&vm,
)
});
}
unsafe {
$ctx.register_fn(
$module,
concat!(stringify!($class), ".", stringify!($name)),
__dome_cloomnik_method,
)
}
.unwrap();
$crate::register_modules! { @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(&vm));
}
unsafe {
$ctx.register_fn(
$module,
concat!("static ", stringify!($class), ".", stringify!($name), "=(_)"),
__dome_cloomnik_method,
)
}
.unwrap();
$crate::register_modules! { @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) },)?
&vm,
)
});
}
unsafe {
$ctx.register_fn(
$module,
concat!(stringify!($class), ".", stringify!($name), "=(_)"),
__dome_cloomnik_method,
)
}
.unwrap();
$crate::register_modules! { @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(&vm));
}
unsafe {
$ctx.register_fn(
$module,
concat!("static ", stringify!($class), ".", stringify!($name), "(",
$(
$crate::register_modules! { @underscore $param0 },
$(",", $crate::register_modules! { @underscore $params },)*
)?
")"),
__dome_cloomnik_method,
)
}
.unwrap();
$crate::register_modules! { @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) },)?
&vm,
)
});
}
unsafe {
$ctx.register_fn(
$module,
concat!(stringify!($class), ".", stringify!($name), "(",
$(
$crate::register_modules! { @underscore $param0 },
$(",", $crate::register_modules! { @underscore $params },)*
)?
")"),
__dome_cloomnik_method,
)
}
.unwrap();
$crate::register_modules! { @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(&vm));
}
unsafe {
$ctx.register_fn(
$module,
concat!("static ", stringify!($class), ".[",
$crate::register_modules! { @underscore $param0 },
$(",", $crate::register_modules! { @underscore $params },)*
"]"),
__dome_cloomnik_method,
)
}
.unwrap();
$crate::register_modules! { @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) },)?
&vm,
)
});
}
unsafe {
$ctx.register_fn(
$module,
concat!(stringify!($class), ".[",
$crate::register_modules! { @underscore $param0 },
$(",", $crate::register_modules! { @underscore $params },)*
"]"),
__dome_cloomnik_method,
)
}
.unwrap();
$crate::register_modules! { @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(&vm));
}
unsafe {
$ctx.register_fn(
$module,
concat!("static ", stringify!($class), ".[",
$crate::register_modules! { @underscore $param0 },
$(",", $crate::register_modules! { @underscore $params },)*
"]=(_)"),
__dome_cloomnik_method,
)
}
.unwrap();
$crate::register_modules! { @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) },)?
&vm,
)
});
}
unsafe {
$ctx.register_fn(
$module,
concat!(stringify!($class), ".[",
$crate::register_modules! { @underscore $param0 },
$(",", $crate::register_modules! { @underscore $params },)*
"]=(_)"),
__dome_cloomnik_method,
)
}
.unwrap();
$crate::register_modules! { @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! { @register_class_members
ctx = [{ $ctx }]
module = [{ $module }]
class = [{ $class }]
items = [{ $($rest)* }]
type = [{ $($type)+ }]
$(foreign_type = [{ $($foreign_type)+ }])?
}
};
}