1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
pub mod ffi_extern;
pub mod ffi_intern;
use libloading::Symbol;
use std::{
ffi::{CStr, OsStr},
sync::Arc,
};
use zsplg_core::Wrapper as FFIWrapper;
pub struct Plugin {
user_data: FFIWrapper,
modname: Vec<u8>,
dlh: libloading::Library,
}
pub struct Handle {
user_data: FFIWrapper,
parent: Arc<Plugin>,
}
impl Drop for Plugin {
fn drop(&mut self) {
self.user_data.call_dtor();
}
}
impl Drop for Handle {
fn drop(&mut self) {
self.user_data.call_dtor();
}
}
impl Plugin {
fn get_fn<T>(&self, prefix: &[u8], name: &[u8]) -> Result<Symbol<'_, T>, std::io::Error> {
let mut real_name: Vec<u8> =
Vec::with_capacity(self.modname.len() + prefix.len() + name.len() + 2);
real_name.extend(self.modname.iter().copied());
real_name.push(b'_');
real_name.extend(prefix.iter().copied());
real_name.extend(name.iter().copied());
real_name.push(b'\0');
unsafe { self.dlh.get(&real_name[..]) }
}
pub fn new(file: Option<&OsStr>, modname: &CStr) -> Result<Plugin, std::io::Error> {
let mut ret = Plugin {
user_data: FFIWrapper::null(),
modname: modname.to_bytes().to_owned(),
dlh: match file {
Some(file) => libloading::Library::new(file)?,
None => {
#[cfg(not(unix))]
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"selfexe-referential plugins aren't supported on this platform",
));
#[cfg(unix)]
libloading::os::unix::Library::this().into()
}
},
};
ret.user_data = (ret.get_fn::<extern "C" fn() -> FFIWrapper>(b"", b"init")?)();
Ok(ret)
}
pub fn create_handle(this: &Arc<Self>, args: &[FFIWrapper]) -> Result<Handle, std::io::Error> {
let hcfn: Symbol<extern "C" fn(*const FFIWrapper, usize, *const FFIWrapper) -> FFIWrapper> =
this.get_fn(b"", b"hcreate")?;
Ok(Handle {
user_data: hcfn(&this.user_data, args.len(), args.as_ptr()),
parent: Arc::clone(this),
})
}
fn call_intern(
&self,
hsel: Option<&FFIWrapper>,
fname: &CStr,
args: &[FFIWrapper],
) -> Result<FFIWrapper, std::io::Error> {
let xfn: Symbol<extern "C" fn(*const FFIWrapper, usize, *const FFIWrapper) -> FFIWrapper> =
self.get_fn(if hsel.is_some() { b"h_" } else { b"_" }, fname.to_bytes())?;
Ok(xfn(
hsel.unwrap_or(&self.user_data),
args.len(),
args.as_ptr(),
))
}
}
pub trait RTMultiFn {
fn call(&self, fname: &CStr, args: &[FFIWrapper]) -> Result<FFIWrapper, std::io::Error>;
}
impl RTMultiFn for Plugin {
#[inline]
fn call(&self, fname: &CStr, args: &[FFIWrapper]) -> Result<FFIWrapper, std::io::Error> {
self.call_intern(None, fname, args)
}
}
impl RTMultiFn for Handle {
#[inline]
fn call(&self, fname: &CStr, args: &[FFIWrapper]) -> Result<FFIWrapper, std::io::Error> {
self.parent.call_intern(Some(&self.user_data), fname, args)
}
}
impl<T> RTMultiFn for zsplg_core::WrapSized<T>
where
T: RTMultiFn,
{
#[inline]
fn call(&self, fname: &CStr, args: &[FFIWrapper]) -> Result<FFIWrapper, std::io::Error> {
self.0.call(fname, args)
}
}