diff options
author | SoniEx2 <endermoneymod@gmail.com> | 2022-06-20 00:19:56 -0300 |
---|---|---|
committer | SoniEx2 <endermoneymod@gmail.com> | 2022-06-20 00:19:56 -0300 |
commit | 342d0b3ba53512c98cc5540313f874fe543d4b9b (patch) | |
tree | 4de8305590a3ebd80f003f5fb4ed7938b6168e4e | |
parent | 890d8bbef986d8b85d6eaaddd4c8895c5f61c09c (diff) |
Begin the great refactoring
-rw-r--r-- | Cargo.toml | 4 | ||||
-rw-r--r-- | src/internals.rs | 137 | ||||
-rw-r--r-- | src/lib.rs | 186 | ||||
-rw-r--r-- | src/mock/mod.rs | 465 |
4 files changed, 471 insertions, 321 deletions
diff --git a/Cargo.toml b/Cargo.toml index a88a2b1..cde21f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,5 +18,5 @@ nightly_tests = ["dep:selfref"] [dependencies] libc = "0.2" impl_trait = "0.1" -ltptr = "0.1.1" -selfref = { version = "0.1", optional = true } +ltptr = "0.1.2" +selfref = { version = "0.1.3", optional = true } diff --git a/src/internals.rs b/src/internals.rs index d1b714f..ec96299 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -20,30 +20,39 @@ //! This also includes the hexchat_plugin struct, from hexchat-plugin.h. Note that we use the //! struct even on non-Windows platforms because it's a lot easier that way. Should be safe, tho. +use std::marker::PhantomData; +use std::marker::PhantomPinned; + use libc; -// apparently this is the right way to do these -#[repr(i8)] -pub enum HexchatList { - __One, - __Two, +use ltptr::*; + +// apparently this is the new right way to do these +#[repr(C)] +pub struct HexchatList { + _data: libc::c_void, + // remove Send/Sync/etc + _marker: PhantomData<(*mut u8, PhantomPinned)> } -#[repr(i8)] -pub enum HexchatHook { - __One, - __Two, +#[repr(C)] +pub struct HexchatHook { + _data: libc::c_void, + // remove Send/Sync/etc + _marker: PhantomData<(*mut u8, PhantomPinned)> } -#[repr(i8)] -pub enum HexchatContext { - __One, - __Two, +#[repr(C)] +pub struct HexchatContext { + _data: libc::c_void, + // remove Send/Sync/etc + _marker: PhantomData<(*mut u8, PhantomPinned)> } // not in hexchat-plugin.h -#[repr(i8)] -pub enum PluginGuiHandle { - __One, - __Two, +#[repr(C)] +pub struct PluginGuiHandle { + _data: libc::c_void, + // remove Send/Sync/etc + _marker: PhantomData<(*mut u8, PhantomPinned)> } #[repr(C)] @@ -51,147 +60,143 @@ pub struct HexchatEventAttrs { pub server_time_utc: libc::time_t, } -pub type HexchatPlugin = Ph; - #[repr(C)] -pub struct Ph { - pub hexchat_hook_command: unsafe extern "C" fn(ph: *mut HexchatPlugin, +pub struct Ph<'ph> { + pub(crate) hexchat_hook_command: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, name: *const libc::c_char, pri: libc::c_int, /* CALLBACK */ callback: unsafe extern "C" fn(word: *const *const libc::c_char, word_eol: *const *const libc::c_char, user_data: *mut libc::c_void) -> libc::c_int, help_text: *const libc::c_char, userdata: *mut libc::c_void) -> *const HexchatHook, - pub hexchat_hook_server: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_hook_server: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, name: *const libc::c_char, pri: libc::c_int, /* CALLBACK */ callback: unsafe extern "C" fn(word: *const *const libc::c_char, word_eol: *const *const libc::c_char, user_data: *mut libc::c_void) -> libc::c_int, userdata: *mut libc::c_void) -> *const HexchatHook, - pub hexchat_hook_print: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_hook_print: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, name: *const libc::c_char, pri: libc::c_int, /* CALLBACK */ callback: unsafe extern "C" fn(word: *const *const libc::c_char, user_data: *mut libc::c_void) -> libc::c_int, userdata: *mut libc::c_void) -> *const HexchatHook, - pub hexchat_hook_timer: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_hook_timer: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, timeout: libc::c_int, /* CALLBACK */ callback: unsafe extern "C" fn(user_data: *mut libc::c_void) -> libc::c_int, userdata: *mut libc::c_void) -> *const HexchatHook, - pub hexchat_hook_fd: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_hook_fd: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, fd: libc::c_int, flags: libc::c_int, /* CALLBACK */ callback: unsafe extern "C" fn(fd: libc::c_int, flags: libc::c_int, user_data: *mut libc::c_void) -> libc::c_int, userdata: *mut libc::c_void) -> *const HexchatHook, - pub hexchat_unhook: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_unhook: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, hook: *const HexchatHook) -> *const libc::c_void, - pub hexchat_print: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_print: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, text: *const libc::c_char), // this is VERY NAUGHTY. + // NOTE this is an *owned* pointer, so {Const,Mut}LtPtr don't apply here. // TODO see if hexchat's gonna provide a proper userdata field at some point. - // it appears this function isn't used anywhere by hexchat so we reuse its pointer. - // on linux, it's a dummy anyway. - // another option would've been to use one of the printf functions. // TODO test this on platforms hexchat doesn't build on, like AVR. - pub userdata: *mut libc::c_void, - /*pub hexchat_printf_do_not_use: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) userdata: *mut crate::PhUserdata<'ph>, + /*pub(crate) hexchat_printf_do_not_use: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, format: *const libc::c_char, ...),*/ - pub hexchat_command: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_command: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, command: *const libc::c_char), - pub hexchat_commandf_do_not_use: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_commandf_do_not_use: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, format: *const libc::c_char, ...), - pub hexchat_nickcmp: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_nickcmp: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, s1: *const libc::c_char, s2: *const libc::c_char) -> libc::c_int, - pub hexchat_set_context: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_set_context: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, ctx: *const HexchatContext) -> libc::c_int, - pub hexchat_find_context: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_find_context: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, servname: *const libc::c_char, channel: *const libc::c_char) -> *const HexchatContext, - pub hexchat_get_context: unsafe extern "C" fn(ph: *mut HexchatPlugin) -> *const HexchatContext, - pub hexchat_get_info: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_get_context: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>) -> *const HexchatContext, + pub(crate) hexchat_get_info: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, id: *const libc::c_char) -> *const libc::c_char, - pub hexchat_get_prefs: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_get_prefs: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, name: *const libc::c_char, string: *mut *const libc::c_char, integer: *mut libc::c_int) -> libc::c_int, - pub hexchat_list_get: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_list_get: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, name: *const libc::c_char) -> *mut HexchatList, - pub hexchat_list_free: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_list_free: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, xlist: *const HexchatList), - pub hexchat_list_fields: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_list_fields: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, name: *const libc::c_char) -> *const *const libc::c_char, - pub hexchat_list_next: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_list_next: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, xlist: *const HexchatList) -> libc::c_int, - pub hexchat_list_str: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_list_str: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, xlist: *const HexchatList, name: *const libc::c_char) -> *const libc::c_char, - pub hexchat_list_int: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_list_int: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, xlist: *const HexchatList, name: *const libc::c_char) -> libc::c_int, - pub hexchat_plugingui_add: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_plugingui_add: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, filename: *const libc::c_char, name: *const libc::c_char, desc: *const libc::c_char, version: *const libc::c_char, reserved: *mut char) -> *const PluginGuiHandle, - pub hexchat_plugingui_remove: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_plugingui_remove: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, handle: *const PluginGuiHandle), - pub hexchat_emit_print: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_emit_print: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, event_name: *const libc::c_char, ...) -> libc::c_int, - pub hexchat_read_fd: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_read_fd: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, src: *const libc::c_void, buf: *mut char, len: *mut libc::c_int) -> libc::c_int, - pub hexchat_list_time: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_list_time: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, xlist: *const HexchatList, name: *const libc::c_char) -> libc::time_t, - pub hexchat_gettext: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_gettext: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, msgid: *const libc::c_char) -> *const libc::c_char, - pub hexchat_send_modes: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_send_modes: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, targets: *mut *const libc::c_char, ntargets: libc::c_int, modes_per_line: libc::c_int, sign: libc::c_char, mode: libc::c_char), - pub hexchat_strip: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_strip: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, string: *const libc::c_char, len: libc::c_int, flags: libc::c_int) -> *const libc::c_char, - pub hexchat_free: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_free: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, ptr: *const libc::c_void), - pub hexchat_pluginpref_set_str: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_pluginpref_set_str: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, var: *const libc::c_char, value: *const libc::c_char) -> libc::c_int, - pub hexchat_pluginpref_get_str: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_pluginpref_get_str: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, var: *const libc::c_char, dest: *mut char) -> libc::c_int, - pub hexchat_pluginpref_set_int: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_pluginpref_set_int: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, var: *const libc::c_char, value: libc::c_int) -> libc::c_int, - pub hexchat_pluginpref_get_int: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_pluginpref_get_int: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, var: *const libc::c_char) -> libc::c_int, - pub hexchat_pluginpref_delete: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_pluginpref_delete: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, var: *const libc::c_char) -> libc::c_int, - pub hexchat_pluginpref_list: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_pluginpref_list: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, dest: *mut char) -> libc::c_int, - pub hexchat_hook_server_attrs: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_hook_server_attrs: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, name: *const libc::c_char, pri: libc::c_int, /* CALLBACK */ callback: unsafe extern "C" fn(word: *const *const libc::c_char, word_eol: *const *const libc::c_char, attrs: *const HexchatEventAttrs, user_data: *mut libc::c_void) -> libc::c_int, userdata: *mut libc::c_void) -> *const HexchatHook, - pub hexchat_hook_print_attrs: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_hook_print_attrs: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, name: *const libc::c_char, pri: libc::c_int, /* CALLBACK */ callback: unsafe extern "C" fn(word: *const *const libc::c_char, attrs: *const HexchatEventAttrs, user_data: *mut libc::c_void) -> libc::c_int, userdata: *mut libc::c_void) -> *const HexchatHook, - pub hexchat_emit_print_attrs: unsafe extern "C" fn(ph: *mut HexchatPlugin, attrs: *const HexchatEventAttrs, + pub(crate) hexchat_emit_print_attrs: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, attrs: *const HexchatEventAttrs, event_name: *const libc::c_char, ...) -> libc::c_int, - pub hexchat_event_attrs_create: unsafe extern "C" fn(ph: *mut HexchatPlugin) -> *mut HexchatEventAttrs, - pub hexchat_event_attrs_free: unsafe extern "C" fn(ph: *mut HexchatPlugin, + pub(crate) hexchat_event_attrs_create: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>) -> *mut HexchatEventAttrs, + pub(crate) hexchat_event_attrs_free: unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>, attrs: *mut HexchatEventAttrs), } diff --git a/src/lib.rs b/src/lib.rs index 308559a..51b4459 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -196,18 +196,16 @@ extern crate impl_trait; #[doc(hidden)] pub extern crate libc; -extern crate ltptr; - // private macros /// Calls a function on a PluginHandle struct. macro_rules! ph_call { ($f:ident($ph:expr, $($args:expr),*)) => { - ((*$ph.data.ph).$f)($ph.plugin, $($args),*) + ((*$ph.data.as_raw()).$f)($ph.get_plugin(), $($args),*) }; ($f:ident($ph:expr $(,)?)) => { - ((*$ph.data.ph).$f)($ph.plugin) + ((*$ph.data.as_raw()).$f)($ph.get_plugin()) }; } @@ -229,7 +227,8 @@ pub use crate::strip::*; pub use crate::word::*; use crate::internals::HexchatEventAttrs as RawAttrs; -use crate::internals::Ph as RawPh; +#[doc(hidden)] +pub use crate::internals::Ph as RawPh; use crate::pluginfo::PluginInfo; use std::borrow::Cow; @@ -254,6 +253,8 @@ use std::time::{SystemTime, UNIX_EPOCH, Duration}; #[doc(hidden)] pub use libc::{c_char, c_int, c_void, time_t}; +#[doc(hidden)] +pub use ltptr::MutLtPtr; // ****** // // PUBLIC // @@ -310,21 +311,6 @@ pub unsafe trait Plugin<'ph> { // Structs -/// A `*mut RawPh` with a lifetime bolted to it. -/// -/// This allows us to enforce a non-`'static` lifetime on the `Plugin`. -// this is NOT public API -#[doc(hidden)] -#[repr(transparent)] -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)] -pub struct LtPhPtr<'ph> { - ph: *mut RawPh, - // 'ph has to be invariant because RawPh is self-referential. - // "ideally" we'd want `&'ph mut RawPh<'ph>`, tho the `*mut` above would've - // had the same effect if `RawPh` were `RawPh<'ph>`. - _lt: PhantomData<fn(&'ph ()) -> &'ph ()>, -} - /// A hexchat plugin handle, used to register hooks and interact with hexchat. /// /// # Examples @@ -337,8 +323,8 @@ pub struct LtPhPtr<'ph> { /// } /// ``` pub struct PluginHandle<'ph> { - data: LtPhPtr<'ph>, - plugin: *mut RawPh, + data: MutLtPtr<'ph, RawPh<'ph>>, + guiplugin: *const internals::PluginGuiHandle, contexts: Contexts, // Used for registration info: PluginInfo, @@ -423,7 +409,7 @@ pub struct EventAttrs<'a> { /// Likes to get caught on stuff. Unhooks when dropped. #[must_use = "Hooks must be stored somewhere and are automatically unhooked on Drop"] pub struct HookHandle<'ph, 'f> where 'f: 'ph { - ph: LtPhPtr<'ph>, + ph: MutLtPtr<'ph, RawPh<'ph>>, hh: *const internals::HexchatHook, freed: Rc<Cell<bool>>, // this does actually store an Rc<...>, but on the other side of the FFI. @@ -433,7 +419,7 @@ pub struct HookHandle<'ph, 'f> where 'f: 'ph { /// A virtual plugin. #[must_use = "Virtual plugins must be stored somewhere and are automatically unregistered on Drop"] pub struct PluginEntryHandle<'ph> { - ph: LtPhPtr<'ph>, + ph: MutLtPtr<'ph, RawPh<'ph>>, entry: *const internals::PluginGuiHandle, } @@ -442,7 +428,7 @@ pub struct PluginEntryHandle<'ph> { pub struct Context<'ph> { contexts: Contexts, ctx: RcWeak<*const internals::HexchatContext>, - _ph: PhantomData<&'ph RawPh>, + _ph: PhantomData<MutLtPtr<'ph, RawPh<'ph>>>, } /// The error returned by [`PluginHandle::with_context`] when the context is @@ -618,7 +604,7 @@ impl<'ph, 'f> Drop for HookHandle<'ph, 'f> where 'f: 'ph { } self.freed.set(true); unsafe { - let b = ((*self.ph.ph).hexchat_unhook)(self.ph.ph, self.hh) as *mut HookUd<'f>; + let b = ((*self.ph.as_raw()).hexchat_unhook)(self.ph, self.hh) as *mut HookUd<'f>; // we assume b is not null. this should be safe. // just drop it drop(Rc::from_raw(b)); @@ -629,7 +615,7 @@ impl<'ph, 'f> Drop for HookHandle<'ph, 'f> where 'f: 'ph { impl<'ph> Drop for PluginEntryHandle<'ph> { fn drop(&mut self) { unsafe { - ((*self.ph.ph).hexchat_plugingui_remove)(self.ph.ph, self.entry); + ((*self.ph.as_raw()).hexchat_plugingui_remove)(self.ph, self.entry); } } } @@ -655,7 +641,10 @@ impl_trait! { /// # Safety /// /// `ph` must be a valid pointer (see `std::ptr::read`). -unsafe fn log_panic(ph: *mut RawPh, e: Box<dyn std::any::Any + Send + 'static>) { +unsafe fn log_panic<'ph>( + ph: MutLtPtr<'ph, RawPh<'ph>>, + e: Box<dyn std::any::Any + Send + 'static> +) { // if it's a &str or String, just print it if let Some(s) = e.downcast_ref::<&str>() { hexchat_print_str(ph, s, false); @@ -667,7 +656,11 @@ unsafe fn log_panic(ph: *mut RawPh, e: Box<dyn std::any::Any + Send + 'static>) hexchat_print_str(ph, "couldn't log panic message", false); if let Err(e) = catch_unwind(AssertUnwindSafe(|| drop(e))) { // eprintln panics, hexchat_print_str doesn't. - hexchat_print_str(ph, "ERROR: panicked while trying to log panic!", false); + hexchat_print_str( + ph, + "ERROR: panicked while trying to log panic!", + false + ); mem::forget(e); std::process::abort(); } @@ -679,10 +672,11 @@ unsafe fn log_panic(ph: *mut RawPh, e: Box<dyn std::any::Any + Send + 'static>) /// # Safety /// /// `ph` must be a valid pointer (see `std::ptr::read`). -unsafe fn call_hook_protected<F: FnOnce() -> Eat + UnwindSafe>( - ph: *mut RawPh, +unsafe fn call_hook_protected<'ph, F>( + ph: MutLtPtr<'ph, RawPh<'ph>>, f: F -) -> Eat { +) -> Eat +where F: FnOnce() -> Eat + UnwindSafe{ match catch_unwind(f) { Result::Ok(v @ _) => v, Result::Err(e @ _) => { @@ -698,13 +692,28 @@ impl<'ph> PluginHandle<'ph> { /// # Safety /// /// `ph` must be a valid pointer (see `std::ptr::read`). - unsafe fn new(data: LtPhPtr<'ph>, info: PluginInfo, contexts: Contexts) -> PluginHandle<'ph> { + unsafe fn new( + data: MutLtPtr<'ph, RawPh<'ph>>, + info: PluginInfo, + contexts: Contexts + ) -> PluginHandle<'ph> { PluginHandle { - data, plugin: data.ph, info, contexts + data, guiplugin: ptr::null(), info, contexts + } + } + + pub(crate) fn get_plugin(&self) -> MutLtPtr<'ph, RawPh<'ph>> { + if self.guiplugin.is_null() { + self.data + } else { + unsafe { + MutLtPtr::<'ph>::from_raw(self.guiplugin as *mut RawPh<'ph>) + } } } - /// Registers this hexchat plugin. This must be called exactly once when the plugin is loaded. + /// Registers this hexchat plugin. This must be called exactly once when + /// the plugin is loaded. /// /// # Panics /// @@ -720,7 +729,7 @@ impl<'ph> PluginHandle<'ph> { /// } /// ``` pub fn register(&mut self, name: &str, desc: &str, ver: &str) { - assert_eq!(self.data.ph, self.plugin, "PluginEntryHandle can't be registered"); + assert!(self.guiplugin.is_null(), "PluginEntryHandle can't be registered"); unsafe { let info = self.info; if !(*info.name).is_null() || !(*info.desc).is_null() || !(*info.vers).is_null() { @@ -742,7 +751,7 @@ impl<'ph> PluginHandle<'ph> { /// /// This function panics if this plugin is not registered. pub fn get_name(&self) -> &str { - assert_eq!(self.data.ph, self.plugin, "PluginEntryHandle can't be registered"); + assert!(self.guiplugin.is_null(), "PluginEntryHandle can't be registered"); unsafe { let info = self.info; if !(*info.name).is_null() || !(*info.desc).is_null() || !(*info.vers).is_null() { @@ -759,7 +768,7 @@ impl<'ph> PluginHandle<'ph> { /// /// This function panics if this plugin is not registered. pub fn get_description(&self) -> &str { - assert_eq!(self.data.ph, self.plugin, "PluginEntryHandle can't be registered"); + assert!(self.guiplugin.is_null(), "PluginEntryHandle can't be registered"); unsafe { let info = self.info; if !(*info.name).is_null() || !(*info.desc).is_null() || !(*info.vers).is_null() { @@ -776,7 +785,7 @@ impl<'ph> PluginHandle<'ph> { /// /// This function panics if this plugin is not registered. pub fn get_version(&self) -> &str { - assert_eq!(self.data.ph, self.plugin, "PluginEntryHandle can't be registered"); + assert!(self.guiplugin.is_null(), "PluginEntryHandle can't be registered"); unsafe { let info = self.info; if !(*info.name).is_null() || !(*info.desc).is_null() || !(*info.vers).is_null() { @@ -829,7 +838,7 @@ impl<'ph> PluginHandle<'ph> { let info = self.info; let contexts = Rc::clone(&self.contexts); let mut handle = unsafe { Self::new(data, info, contexts) }; - handle.plugin = entry.entry as *mut RawPh; + handle.guiplugin = entry.entry; handle.set_context(&self.get_context()); f(&mut handle) } @@ -911,21 +920,22 @@ impl<'ph> PluginHandle<'ph> { /// } /// ``` pub fn hook_command<'f, F>(&self, cmd: &str, pri: i32, help: Option<&str>, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word, WordEol) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { - assert_eq!(self.data.ph, self.plugin, "PluginEntryHandle can't have hooks"); + assert!(self.guiplugin.is_null(), "PluginEntryHandle can't have hooks"); unsafe extern "C" fn callback(word: *const *const c_char, word_eol: *const *const c_char, ud: *mut c_void) -> c_int { let f: Rc<HookUd> = rc_clone_from_raw(ud as *const HookUd); (f)(word, word_eol, ptr::null()).do_eat as c_int } let b: Rc<HookUd> = { - let ph = self.data; + let ph = AssertUnwindSafe(self.data); let info = self.info; let contexts = Rc::clone(&self.contexts); Rc::new(Box::new(move |word, word_eol, _| { let cb = &cb; let contexts = Rc::clone(&contexts); + let ph = AssertUnwindSafe(*ph); unsafe { - call_hook_protected(ph.ph, move || { - let mut ph = PluginHandle::new(ph, info, contexts); + call_hook_protected(*ph, move || { + let mut ph = PluginHandle::new(*ph, info, contexts); let word = Word::new(word); let word_eol = WordEol::new(word_eol); cb(&mut ph, word, word_eol) @@ -961,26 +971,27 @@ impl<'ph> PluginHandle<'ph> { /// } /// ``` pub fn hook_server<'f, F>(&self, cmd: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word, WordEol) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { - assert_eq!(self.data.ph, self.plugin, "PluginEntryHandle can't have hooks"); + assert!(self.guiplugin.is_null(), "PluginEntryHandle can't have hooks"); self.hook_server_attrs(cmd, pri, move |ph, w, we, _| cb(ph, w, we)) } /// Sets a server hook, with attributes. pub fn hook_server_attrs<'f, F>(&self, cmd: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word, WordEol, EventAttrs) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { - assert_eq!(self.data.ph, self.plugin, "PluginEntryHandle can't have hooks"); + assert!(self.guiplugin.is_null(), "PluginEntryHandle can't have hooks"); unsafe extern "C" fn callback(word: *const *const c_char, word_eol: *const *const c_char, attrs: *const RawAttrs, ud: *mut c_void) -> c_int { let f: Rc<HookUd> = rc_clone_from_raw(ud as *const HookUd); (f)(word, word_eol, attrs).do_eat as c_int } let b: Rc<HookUd> = { - let ph = self.data; + let ph = AssertUnwindSafe(self.data); let info = self.info; let contexts = Rc::clone(&self.contexts); Rc::new(Box::new(move |word, word_eol, attrs| { let cb = &cb; let contexts = Rc::clone(&contexts); + let ph = AssertUnwindSafe(*ph); unsafe { - call_hook_protected(ph.ph, move || { - let mut ph = PluginHandle::new(ph, info, contexts); + call_hook_protected(*ph, move || { + let mut ph = PluginHandle::new(*ph, info, contexts); let word = Word::new(word); let word_eol = WordEol::new(word_eol); let attrs = (&*attrs).into(); @@ -1018,7 +1029,7 @@ impl<'ph> PluginHandle<'ph> { /// } /// ``` pub fn hook_print<'f, F>(&self, name: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { - assert_eq!(self.data.ph, self.plugin, "PluginEntryHandle can't have hooks"); + assert!(self.guiplugin.is_null(), "PluginEntryHandle can't have hooks"); // hmm, is there any way to avoid this code duplication? // hook_print is special because dummy prints (keypresses, Close Context) are handled // through here, but never through hook_print_attrs. :/ @@ -1034,15 +1045,16 @@ impl<'ph> PluginHandle<'ph> { // we still do our best to be well-behaved. let suppress_eat = name.eq_ignore_ascii_case("Close Context"); let b: Rc<HookUd> = { - let ph = self.data; + let ph = AssertUnwindSafe(self.data); let info = self.info; let contexts = Rc::clone(&self.contexts); Rc::new(Box::new(move |word, _, _| { let cb = &cb; let contexts = Rc::clone(&contexts); + let ph = AssertUnwindSafe(*ph); unsafe { - call_hook_protected(ph.ph, move || { - let mut ph = PluginHandle::new(ph, info, contexts); + call_hook_protected(*ph, move || { + let mut ph = PluginHandle::new(*ph, info, contexts); let word = Word::new(word); match cb(&mut ph, word) { _ if suppress_eat => EAT_NONE, @@ -1081,21 +1093,22 @@ impl<'ph> PluginHandle<'ph> { /// } /// ``` pub fn hook_print_attrs<'f, F>(&self, name: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word, EventAttrs) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { - assert_eq!(self.data.ph, self.plugin, "PluginEntryHandle can't have hooks"); + assert!(self.guiplugin.is_null(), "PluginEntryHandle can't have hooks"); unsafe extern "C" fn callback(word: *const *const c_char, attrs: *const RawAttrs, ud: *mut c_void) -> c_int { let f: Rc<HookUd> = rc_clone_from_raw(ud as *const HookUd); (f)(word, ptr::null(), attrs).do_eat as c_int } let b: Rc<HookUd> = { - let ph = self.data; + let ph = AssertUnwindSafe(self.data); let info = self.info; let contexts = Rc::clone(&self.contexts); Rc::new(Box::new(move |word, _, attrs| { let cb = &cb; let contexts = Rc::clone(&contexts); + let ph = AssertUnwindSafe(*ph); unsafe { - call_hook_protected(ph.ph, move || { - let mut ph = PluginHandle::new(ph, info, contexts); + call_hook_protected(*ph, move || { + let mut ph = PluginHandle::new(*ph, info, contexts); let word = Word::new(word); let attrs = (&*attrs).into(); cb(&mut ph, word, attrs) @@ -1128,7 +1141,7 @@ impl<'ph> PluginHandle<'ph> { /// } /// ``` pub fn hook_timer<'f, F>(&self, timeout: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>) -> bool + 'f + RefUnwindSafe, 'f: 'ph { - assert_eq!(self.data.ph, self.plugin, "PluginEntryHandle can't have hooks"); + assert!(self.guiplugin.is_null(), "PluginEntryHandle can't have hooks"); unsafe extern "C" fn callback(ud: *mut c_void) -> c_int { let f: Rc<HookUd> = rc_clone_from_raw(ud as *const HookUd); (f)(ptr::null(), ptr::null(), ptr::null()).do_eat as c_int @@ -1137,7 +1150,7 @@ impl<'ph> PluginHandle<'ph> { // helps us clean up the thing when returning false let dropper = Rc::new(Cell::new(None)); let b: Rc<HookUd> = { - let ph = self.data; + let ph = AssertUnwindSafe(self.data); let info = self.info; let contexts = Rc::clone(&self.contexts); let freed = AssertUnwindSafe(Rc::clone(&freed)); @@ -1146,8 +1159,9 @@ impl<'ph> PluginHandle<'ph> { let cb = &cb; let contexts = Rc::clone(&contexts); let res = unsafe { - call_hook_protected(ph.ph, move || { - let mut ph = PluginHandle::new(ph, info, contexts); + let ph = AssertUnwindSafe(*ph); + call_hook_protected(*ph, move || { + let mut ph = PluginHandle::new(*ph, info, contexts); if cb(&mut ph) { EAT_HEXCHAT } else { @@ -1163,7 +1177,7 @@ impl<'ph> PluginHandle<'ph> { // but we must not panic // (kinda silly to abuse call_hook_protected here // but hey, it works and it helps with stuff) - call_hook_protected(ph.ph, || { + call_hook_protected(*ph, || { drop(Rc::from_raw(dropper.take().unwrap())); EAT_NONE }); @@ -1187,7 +1201,7 @@ impl<'ph> PluginHandle<'ph> { pub fn print<T: ToString>(&self, s: T) { let s = s.to_string(); unsafe { - hexchat_print_str(self.data.ph, &*s, true); + hexchat_print_str(self.data, &*s, true); } } @@ -1214,7 +1228,7 @@ impl<'ph> PluginHandle<'ph> { // "fast" path. hexchat_print_str still has to allocate, and // hexchat is slow af. unsafe { - hexchat_print_str(self.data.ph, s, true); + hexchat_print_str(self.data, s, true); } } else { self.print(fmt); @@ -2091,11 +2105,11 @@ unsafe fn wrap_context<'ph>(ph: &PluginHandle<'ph>, ctx: *const internals::Hexch /// # Panics /// /// Panics if panic_on_nul is true and the string contains embedded nuls. -unsafe fn hexchat_print_str(ph: *mut RawPh, s: &str, panic_on_nul: bool) { +unsafe fn hexchat_print_str<'ph>(ph: MutLtPtr<'ph, RawPh<'ph>>, s: &str, panic_on_nul: bool) { match CString::new(s) { Result::Ok(cs @ _) => { let csr: &CStr = &cs; - ((*ph).hexchat_print)(ph, csr.as_ptr()) + ((*ph.as_raw()).hexchat_print)(ph, csr.as_ptr()) }, e @ _ => if panic_on_nul {e.unwrap();}, // TODO nul_position? } @@ -2163,8 +2177,8 @@ struct PhUserdata<'ph> { /// This function is unsafe because it doesn't check if the pointer is valid. /// /// Improper use of this function can leak memory. -unsafe fn put_userdata<'ph>(ph: LtPhPtr<'ph>, ud: Box<PhUserdata<'ph>>) { - (*ph.ph).userdata = Box::into_raw(ud) as *mut c_void; +unsafe fn put_userdata<'ph>(ph: MutLtPtr<'ph, RawPh<'ph>>, ud: Box<PhUserdata<'ph>>) { + (*ph.as_raw()).userdata = Box::into_raw(ud); } /// Pops the userdata from the plugin handle. @@ -2172,8 +2186,8 @@ unsafe fn put_userdata<'ph>(ph: LtPhPtr<'ph>, ud: Box<PhUserdata<'ph>>) { /// # Safety /// /// This function is unsafe because it doesn't check if the pointer is valid. -unsafe fn pop_userdata<'ph>(ph: LtPhPtr<'ph>) -> Box<PhUserdata<'ph>> { - Box::from_raw(mem::replace(&mut (*ph.ph).userdata, ptr::null_mut()) as *mut PhUserdata<'ph>) +unsafe fn pop_userdata<'ph>(ph: MutLtPtr<'ph, RawPh<'ph>>) -> Box<PhUserdata<'ph>> { + Box::from_raw(mem::replace(&mut (*ph.as_raw()).userdata, ptr::null_mut())) } fn test_pluginpref_var(var: &[u8]) -> Result<(), PluginPrefError> { @@ -2202,22 +2216,21 @@ fn check_pluginpref_var(var: impl Into<Vec<u8>>) -> Result<CString, PluginPrefEr // *********************** // #[doc(hidden)] -pub unsafe fn hexchat_plugin_init<'ph, T>(plugin_handle: LtPhPtr<'ph>, +pub unsafe fn hexchat_plugin_init<'ph, T>(ph: MutLtPtr<'ph, RawPh<'ph>>, plugin_name: *mut *const c_char, plugin_desc: *mut *const c_char, plugin_version: *mut *const c_char, arg: *const c_char) -> c_int where T: Plugin<'ph> + Default + 'ph { - if plugin_handle.ph.is_null() || plugin_name.is_null() || plugin_desc.is_null() || plugin_version.is_null() { + if ph.as_raw().is_null() || plugin_name.is_null() || plugin_desc.is_null() || plugin_version.is_null() { // we can't really do anything here. just hope this doesn't panic. eprintln!("hexchat_plugin_init called with a null pointer that shouldn't be null - broken hexchat"); std::process::abort(); } - let ph = plugin_handle.ph as *mut RawPh; // clear the "userdata" field first thing - if the deinit function gets called (wrong hexchat // version, other issues), we don't wanna try to drop the hexchat_dummy or hexchat_read_fd // function as if it were a Box! - (*ph).userdata = ptr::null_mut(); + (*ph.as_raw()).userdata = ptr::null_mut(); // read the filename so we can pass it on later. let filename = if !(*plugin_name).is_null() { if let Ok(fname) = CStr::from_ptr(*plugin_name).to_owned().into_string() { @@ -2239,7 +2252,7 @@ pub unsafe fn hexchat_plugin_init<'ph, T>(plugin_handle: LtPhPtr<'ph>, // NOTE: calling hexchat functions with null plugin_name, plugin_desc, plugin_version is a bit // dangerous. this particular case is "ok". { - let ver = ((*ph).hexchat_get_info)(ph, cstr(b"version\0")); // this shouldn't panic + let ver = ((*ph.as_raw()).hexchat_get_info)(ph, cstr(b"version\0")); // this shouldn't panic let cstr = CStr::from_ptr(ver); if let Ok(ver) = cstr.to_str() { let mut iter = ver.split('.'); @@ -2262,10 +2275,11 @@ pub unsafe fn hexchat_plugin_init<'ph, T>(plugin_handle: LtPhPtr<'ph>, return 0; }; let r: thread::Result<Option<Box<_>>> = { + let ausph = AssertUnwindSafe(ph); catch_unwind(move || { // AssertUnwindSafe not Default at the time of writing this let contexts = Rc::new(AssertUnwindSafe(Default::default())); - let mut pluginhandle = PluginHandle::new(plugin_handle, pluginfo, contexts); + let mut pluginhandle = PluginHandle::new(*ausph, pluginfo, contexts); let contexts = Rc::clone(&pluginhandle.contexts); // must register this before the plugin registers anything else! let context_hook = pluginhandle.hook_print("Close Context", c_int::min_value(), move |ph, _| { @@ -2293,7 +2307,7 @@ pub unsafe fn hexchat_plugin_init<'ph, T>(plugin_handle: LtPhPtr<'ph>, }; match r { Result::Ok(Option::Some(plug @ _)) => { - put_userdata(plugin_handle, plug); + put_userdata(ph, plug); 1 }, r @ _ => { @@ -2306,24 +2320,24 @@ pub unsafe fn hexchat_plugin_init<'ph, T>(plugin_handle: LtPhPtr<'ph>, } #[doc(hidden)] -pub unsafe fn hexchat_plugin_deinit<'ph, T>(plugin_handle: LtPhPtr<'ph>) -> c_int where T: Plugin<'ph> { +pub unsafe extern "C" fn hexchat_plugin_deinit<'ph, T>(ph: MutLtPtr<'ph, RawPh<'ph>>) -> c_int where T: Plugin<'ph> { let mut safe_to_unload = 1; // plugin_handle should never be null, but just in case. - if !plugin_handle.ph.is_null() { - let ph = plugin_handle.ph as *mut RawPh; + if !ph.as_raw().is_null() { // userdata should also never be null. - if !(*ph).userdata.is_null() { + if !(*ph.as_raw()).userdata.is_null() { { let mut info: Option<PluginInfo> = None; { + let ausph = AssertUnwindSafe(ph); let mut ausinfo = AssertUnwindSafe(&mut info); safe_to_unload = match catch_unwind(move || { - let mut userdata = pop_userdata(plugin_handle); + let mut userdata = pop_userdata(*ausph); let pluginfo = userdata.pluginfo; if let Err(e) = catch_unwind(AssertUnwindSafe(|| { userdata.plug.as_mut().deinit(&mut { PluginHandle::new( - plugin_handle, + *ausph, pluginfo, Rc::clone(&userdata.contexts) ) @@ -2332,7 +2346,7 @@ pub unsafe fn hexchat_plugin_deinit<'ph, T>(plugin_handle: LtPhPtr<'ph>) -> c_in // panics in deinit may be retried. // however, one may need to close hexchat if that // happens. - put_userdata(plugin_handle, userdata); + put_userdata(*ausph, userdata); std::panic::resume_unwind(e); } **ausinfo = Some(pluginfo); @@ -2366,7 +2380,7 @@ pub unsafe fn hexchat_plugin_deinit<'ph, T>(plugin_handle: LtPhPtr<'ph>) -> c_in macro_rules! hexchat_plugin { ($l:lifetime, $t:ty) => { #[no_mangle] - pub unsafe extern "C" fn hexchat_plugin_init<$l>(plugin_handle: $crate::LtPhPtr<$l>, + pub unsafe extern "C" fn hexchat_plugin_init<$l>(plugin_handle: $crate::MutLtPtr<$l, $crate::RawPh<$l>>, plugin_name: *mut *const $crate::c_char, plugin_desc: *mut *const $crate::c_char, plugin_version: *mut *const $crate::c_char, @@ -2374,7 +2388,7 @@ macro_rules! hexchat_plugin { $crate::hexchat_plugin_init::<$l, $t>(plugin_handle, plugin_name, plugin_desc, plugin_version, arg) } #[no_mangle] - pub unsafe extern "C" fn hexchat_plugin_deinit<$l>(plugin_handle: $crate::LtPhPtr<$l>) -> $crate::c_int { + pub unsafe extern "C" fn hexchat_plugin_deinit<$l>(plugin_handle: $crate::MutLtPtr<$l, $crate::RawPh<$l>>) -> $crate::c_int { $crate::hexchat_plugin_deinit::<$l, $t>(plugin_handle) } // unlike what the documentation states, there's no need to define hexchat_plugin_get_info. diff --git a/src/mock/mod.rs b/src/mock/mod.rs index 92de66c..0e4ba03 100644 --- a/src/mock/mod.rs +++ b/src/mock/mod.rs @@ -38,8 +38,11 @@ //! //! This API is not stable. Do not rely on this API to be stable. +use crate::internals::Ph; + use std::cell::Cell; use std::cell::RefCell; +use std::cell::UnsafeCell; use std::ffi::CStr; use std::ffi::CString; use std::marker::PhantomData; @@ -48,24 +51,40 @@ use std::pin::Pin; use std::ptr; use std::rc::Rc; -use libc::c_char; +use ::libc::{c_char, c_int}; use ::ltptr::*; -use ::selfref::{Holder, Opaque, SelfRef, opaque}; +use ::selfref::{Holder, NewWith, OperateIn, opaque}; + +struct Context { +} -// MUST be repr(C) +// MUST be repr(C), despite having repr(Rust) types inside it. +/// A loaded plugin/complete plugin handle, including private fields. #[repr(C)] -struct MockPlugin<'dangling, 'env> { +struct MockPlugin<'ph, 'env: 'ph> { // NOTE: MUST be first thing in the struct. - methods: crate::internals::Ph, - filename: *const c_char, - plugin_name: Cell<*const c_char>, - plugin_desc: Cell<*const c_char>, - plugin_vers: Cell<*const c_char>, + // we actually pass the whole MockPlugin to the plugin, but we pretend it's + // just the Ph. + methods: Ph<'ph>, + filename: CString, + plugin_name: *const c_char, + plugin_desc: *const c_char, + plugin_vers: *const c_char, free_strings: bool, - env: &'env PluginEnvironment<'dangling, 'env>, - _pin: PhantomPinned, + env: Pin<&'env PluginEnvironment<'env>>, + deinit: Option<unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>) -> c_int>, + context: Option<Rc<Context>>, +} + +struct MockPluginK<'env> { + _p: PhantomData<&'env PluginEnvironment<'env>>, +} +opaque! { + impl['env] Opaque for MockPluginK<'env> { + type Kind<'ph> = UnsafeCell<MockPlugin<'ph, 'env>>; + } } enum MockCallback { @@ -96,9 +115,9 @@ enum MockCallback { HookDeleted, } -struct MockHook<'dangling, 'env> { - //pl: Rc<Holder<'dangling, MockPluginK<'dangling, 'env>>>, - pl: Rc<MockPlugin<'dangling, 'env>>, +struct MockHook<'env> { + pl: Rc<Holder<'env, MockPluginK<'env>>>, + //pl: Rc<MockPlugin<'env>>, cb: RefCell<MockCallback>, name: Option<Box<str>>, pri_or_fd: libc::c_int, @@ -106,32 +125,39 @@ struct MockHook<'dangling, 'env> { _pin: PhantomPinned, } -pub struct PluginEnvironment<'dangling, 'env> { - //plugins: RefCell<Vec<Pin<Rc<Holder<'dangling, MockPluginK<'dangling, 'env>>>>>>, - plugins: RefCell<Vec<Pin<Rc<MockPlugin<'dangling, 'env>>>>>, - // priority-based hooks - //hooks: RefCell<Vec<Pin<Rc<Holder<'dangling, MockHookK<'dangling, 'env>>>>>>, - hooks: RefCell<Vec<Pin<Rc<MockHook<'dangling, 'env>>>>>, - // fd/timer hooks - //extra_hooks: RefCell<Vec<Pin<Rc<Holder<'dangling, MockHookK<'dangling, 'env>>>>>>, - extra_hooks: RefCell<Vec<Pin<Rc<MockHook<'dangling, 'env>>>>>, - depth: Cell<usize>, +struct MockHookK<'env> { + _p: PhantomData<&'env PluginEnvironment<'env>>, +} +opaque! { + impl['env] Opaque for MockHookK<'env> { + type Kind<'ph> = MockHook<'env>; + } } -impl<'dangling, 'env> SelfRef<'env> for PluginEnvironment<'dangling, 'env> {} -pub struct PluginEnvironmentK<'dangling> { - _p: PhantomData<&'dangling ()>, +/// A plugin environment. An emulated hexchat instance. +// TODO: maybe make this a full plugin host, with <'app>? +pub struct PluginEnvironment<'env> { + /// The loaded plugins. + plugins: RefCell<Vec<Pin<Rc<Holder<'env, MockPluginK<'env>>>>>>, + /// Priority-based hooks. + hooks: RefCell<Vec<Pin<Rc<Holder<'env, MockHookK<'env>>>>>>, + /// Timer and fd hooks. + extra_hooks: RefCell<Vec<Pin<Rc<Holder<'env, MockHookK<'env>>>>>>, + /// Hook recursion depth. + depth: Cell<usize>, + /// Contexts. + contexts: RefCell<Vec<Rc<Context>>>, } + +pub struct PluginEnvironmentK; opaque! { - impl['dangling] Opaque for PluginEnvironmentK<'dangling> { - type Kind<'env> = PluginEnvironment<'dangling, 'env>; + impl Opaque for PluginEnvironmentK { + type Kind<'env> = PluginEnvironment<'env>; } } -pub type GlobalPluginEnvironment<'dangling> = Holder<'dangling, PluginEnvironmentK<'dangling>>; - -unsafe extern "C" fn hexchat_hook_command( - ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_hook_command<'ph>( + ph: MutLtPtr<'ph, Ph<'ph>>, name: *const libc::c_char, pri: libc::c_int, /* CALLBACK */ @@ -139,10 +165,10 @@ unsafe extern "C" fn hexchat_hook_command( help_text: *const libc::c_char, userdata: *mut libc::c_void ) -> *const crate::internals::HexchatHook { - let ph = ph as *mut MockPlugin; + let ph = MutLtPtr::from_raw(ph.as_raw() as *mut MockPlugin<'ph, '_>); todo!(); } -unsafe extern "C" fn hexchat_hook_server(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_hook_server<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, name: *const libc::c_char, pri: libc::c_int, /* CALLBACK */ @@ -150,7 +176,7 @@ unsafe extern "C" fn hexchat_hook_server(ph: *mut crate::internals::HexchatPlugi userdata: *mut libc::c_void) -> *const crate::internals::HexchatHook { todo!(); } -unsafe extern "C" fn hexchat_hook_print(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_hook_print<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, name: *const libc::c_char, pri: libc::c_int, /* CALLBACK */ @@ -158,14 +184,14 @@ unsafe extern "C" fn hexchat_hook_print(ph: *mut crate::internals::HexchatPlugin userdata: *mut libc::c_void) -> *const crate::internals::HexchatHook { todo!(); } -unsafe extern "C" fn hexchat_hook_timer(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_hook_timer<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, timeout: libc::c_int, /* CALLBACK */ callback: unsafe extern "C" fn(user_data: *mut libc::c_void) -> libc::c_int, userdata: *mut libc::c_void) -> *const crate::internals::HexchatHook { todo!(); } -unsafe extern "C" fn hexchat_hook_fd(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_hook_fd<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, fd: libc::c_int, flags: libc::c_int, /* CALLBACK */ @@ -173,80 +199,102 @@ unsafe extern "C" fn hexchat_hook_fd(ph: *mut crate::internals::HexchatPlugin, userdata: *mut libc::c_void) -> *const crate::internals::HexchatHook { todo!(); } -unsafe extern "C" fn hexchat_unhook(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_unhook<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, hook: *const crate::internals::HexchatHook) -> *const libc::c_void { todo!(); } -unsafe extern "C" fn hexchat_print(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_print<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, text: *const libc::c_char) { todo!(); } -pub unsafe extern "C" fn hexchat_printf(ph: *mut crate::internals::HexchatPlugin, +pub unsafe extern "C" fn hexchat_printf<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, format: *const libc::c_char, ...) { unimplemented!(); } -unsafe extern "C" fn hexchat_command(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_command<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, command: *const libc::c_char) { todo!(); } -unsafe extern "C" fn hexchat_commandf(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_commandf<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, format: *const libc::c_char, ...) { unimplemented!(); } -unsafe extern "C" fn hexchat_nickcmp(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_nickcmp<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, s1: *const libc::c_char, s2: *const libc::c_char) -> libc::c_int { todo!(); } -unsafe extern "C" fn hexchat_set_context(ph: *mut crate::internals::HexchatPlugin, - ctx: *const crate::internals::HexchatContext) -> libc::c_int { - todo!(); +unsafe extern "C" fn hexchat_set_context<'ph>( + ph: MutLtPtr<'ph, Ph<'ph>>, + ctx: *const crate::internals::HexchatContext, +) -> libc::c_int { + let ctx = ctx as *const Context; + let ph: *mut MockPlugin<'_, '_> = ph.as_raw() as *mut _; + if ctx.is_null() { return 0; } + // in C, ctx must NOT be dangling, so we let miri catch it + let _ = &*ctx; + // in addition to that we also panic if ctx isn't on the list, + // because you're supposed to use "Close Context" for cleanup. + // if your plugin makes it this far, you're literally passing in garbage. + let env: Pin<&PluginEnvironment<'_>> = (*ph).env; + if env.contexts.borrow().iter().any(|e| ptr::eq(&**e, ctx)) { + (*ph).context = Some(crate::rc_clone_from_raw(ctx)); + return 1; + } else { + panic!("garbage in hexchat_set_context - broken plugin") + } } -unsafe extern "C" fn hexchat_find_context(ph: *mut crate::internals::HexchatPlugin, - servname: *const libc::c_char, - channel: *const libc::c_char) -> *const crate::internals::HexchatContext { +unsafe extern "C" fn hexchat_find_context<'ph>( + ph: MutLtPtr<'ph, Ph<'ph>>, + servname: *const libc::c_char, + channel: *const libc::c_char, +) -> *const crate::internals::HexchatContext { todo!(); } -unsafe extern "C" fn hexchat_get_context(ph: *mut crate::internals::HexchatPlugin) -> *const crate::internals::HexchatContext { - todo!(); +unsafe extern "C" fn hexchat_get_context<'ph>( + ph: MutLtPtr<'ph, Ph<'ph>>, +) -> *const crate::internals::HexchatContext { + let ph: *mut MockPlugin<'_, '_> = ph.as_raw() as *mut _; + let ctx = (*ph).context.as_ref().map(|rc| Rc::as_ptr(rc)); + ctx.unwrap_or(ptr::null()) as *const _ } -unsafe extern "C" fn hexchat_get_info(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_get_info<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, id: *const libc::c_char) -> *const libc::c_char { todo!(); } -unsafe extern "C" fn hexchat_get_prefs(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_get_prefs<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, name: *const libc::c_char, string: *mut *const libc::c_char, integer: *mut libc::c_int) -> libc::c_int { todo!(); } -unsafe extern "C" fn hexchat_list_get(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_list_get<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, name: *const libc::c_char) -> *mut crate::internals::HexchatList { todo!(); } -unsafe extern "C" fn hexchat_list_free(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_list_free<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, xlist: *const crate::internals::HexchatList) { todo!(); } -unsafe extern "C" fn hexchat_list_fields(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_list_fields<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, name: *const libc::c_char) -> *const *const libc::c_char { todo!(); } -unsafe extern "C" fn hexchat_list_next(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_list_next<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, xlist: *const crate::internals::HexchatList) -> libc::c_int { todo!(); } -unsafe extern "C" fn hexchat_list_str(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_list_str<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, xlist: *const crate::internals::HexchatList, name: *const libc::c_char) -> *const libc::c_char { todo!(); } -unsafe extern "C" fn hexchat_list_int(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_list_int<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, xlist: *const crate::internals::HexchatList, name: *const libc::c_char) -> libc::c_int { todo!(); } -unsafe extern "C" fn hexchat_plugingui_add(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_plugingui_add<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, filename: *const libc::c_char, name: *const libc::c_char, desc: *const libc::c_char, @@ -254,30 +302,30 @@ unsafe extern "C" fn hexchat_plugingui_add(ph: *mut crate::internals::HexchatPlu reserved: *mut char) -> *const crate::internals::PluginGuiHandle { todo!(); } -unsafe extern "C" fn hexchat_plugingui_remove(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_plugingui_remove<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, handle: *const crate::internals::PluginGuiHandle) { todo!(); } -unsafe extern "C" fn hexchat_emit_print(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_emit_print<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, event_name: *const libc::c_char, ...) -> libc::c_int { todo!(); } -unsafe extern "C" fn hexchat_read_fd(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_read_fd<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, src: *const libc::c_void, buf: *mut char, len: *mut libc::c_int) -> libc::c_int { todo!(); } -unsafe extern "C" fn hexchat_list_time(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_list_time<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, xlist: *const crate::internals::HexchatList, name: *const libc::c_char) -> libc::time_t { todo!(); } -unsafe extern "C" fn hexchat_gettext(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_gettext<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, msgid: *const libc::c_char) -> *const libc::c_char { todo!(); } -unsafe extern "C" fn hexchat_send_modes(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_send_modes<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, targets: *mut *const libc::c_char, ntargets: libc::c_int, modes_per_line: libc::c_int, @@ -285,44 +333,44 @@ unsafe extern "C" fn hexchat_send_modes(ph: *mut crate::internals::HexchatPlugin mode: libc::c_char) { todo!(); } -unsafe extern "C" fn hexchat_strip(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_strip<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, string: *const libc::c_char, len: libc::c_int, flags: libc::c_int) -> *const libc::c_char { todo!(); } -unsafe extern "C" fn hexchat_free(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_free<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, ptr: *const libc::c_void) { todo!(); } -unsafe extern "C" fn hexchat_pluginpref_set_str(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_pluginpref_set_str<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, var: *const libc::c_char, value: *const libc::c_char) -> libc::c_int { todo!(); } -unsafe extern "C" fn hexchat_pluginpref_get_str(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_pluginpref_get_str<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, var: *const libc::c_char, dest: *mut char) -> libc::c_int { todo!(); } -unsafe extern "C" fn hexchat_pluginpref_set_int(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_pluginpref_set_int<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, var: *const libc::c_char, value: libc::c_int) -> libc::c_int { todo!(); } -unsafe extern "C" fn hexchat_pluginpref_get_int(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_pluginpref_get_int<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, var: *const libc::c_char) -> libc::c_int { todo!(); } -unsafe extern "C" fn hexchat_pluginpref_delete(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_pluginpref_delete<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, var: *const libc::c_char) -> libc::c_int { todo!(); } -unsafe extern "C" fn hexchat_pluginpref_list(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_pluginpref_list<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, dest: *mut char) -> libc::c_int { todo!(); } -unsafe extern "C" fn hexchat_hook_server_attrs(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_hook_server_attrs<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, name: *const libc::c_char, pri: libc::c_int, /* CALLBACK */ @@ -330,7 +378,7 @@ unsafe extern "C" fn hexchat_hook_server_attrs(ph: *mut crate::internals::Hexcha userdata: *mut libc::c_void) -> *const crate::internals::HexchatHook { todo!(); } -unsafe extern "C" fn hexchat_hook_print_attrs(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_hook_print_attrs<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, name: *const libc::c_char, pri: libc::c_int, /* CALLBACK */ @@ -338,106 +386,189 @@ unsafe extern "C" fn hexchat_hook_print_attrs(ph: *mut crate::internals::Hexchat userdata: *mut libc::c_void) -> *const crate::internals::HexchatHook { todo!(); } -unsafe extern "C" fn hexchat_emit_print_attrs(ph: *mut crate::internals::HexchatPlugin, attrs: *const crate::internals::HexchatEventAttrs, +unsafe extern "C" fn hexchat_emit_print_attrs<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, attrs: *const crate::internals::HexchatEventAttrs, event_name: *const libc::c_char, ...) -> libc::c_int { todo!(); } -unsafe extern "C" fn hexchat_event_attrs_create(ph: *mut crate::internals::HexchatPlugin) -> *mut crate::internals::HexchatEventAttrs { +unsafe extern "C" fn hexchat_event_attrs_create<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>) -> *mut crate::internals::HexchatEventAttrs { todo!(); } -unsafe extern "C" fn hexchat_event_attrs_free(ph: *mut crate::internals::HexchatPlugin, +unsafe extern "C" fn hexchat_event_attrs_free<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, attrs: *mut crate::internals::HexchatEventAttrs) { todo!(); } -impl<'dangling, 'env> PluginEnvironment<'dangling, 'env> { - pub fn new() -> Self { - Self { - plugins: Default::default(), - hooks: Default::default(), - extra_hooks: Default::default(), - depth: Default::default(), +impl<'env> PluginEnvironment<'env> { + /// Creates a new plugin environment. + pub fn new() -> Holder<'static, PluginEnvironmentK> { + struct NewEnv; + impl<'k> NewWith<'k, PluginEnvironmentK> for NewEnv { + fn new_with<'env>(self) -> PluginEnvironment<'env> { + PluginEnvironment { + plugins: Default::default(), + hooks: Default::default(), + extra_hooks: Default::default(), + depth: Default::default(), + contexts: Default::default(), + } + } + } + Holder::new_with(NewEnv) + } + + /// Loads a plugin. + pub fn load_plugin<T: PluginFactory>( + self: Pin<&'env Self>, + filename: CString, + arg: Option<&CStr>, + ) -> Result<(), ()> { + let plug = MockPlugin::new(self, filename); + // add the plugin + self.plugins.borrow_mut().push(plug.clone()); + + struct MockPluginInit<'arg, T> { + arg: Option<&'arg CStr>, + _p: PhantomData<T> + } + impl<'k, 'env: 'k, 'arg, T> OperateIn<'k, MockPluginK<'env>> + for MockPluginInit<'arg, T> + where T: PluginFactory { + type Out = bool; + fn operate_in<'ph>( + self, + ph: Pin<&'ph UnsafeCell<MockPlugin<'ph, 'env>>>, + ) -> bool where 'k: 'ph { + let ptr = ph.get(); + let arg = self.arg; + unsafe { + // setup deinit function + (*ptr).deinit = Some( + crate::hexchat_plugin_deinit::<'ph, T::Plugin<'ph>> + ); + crate::hexchat_plugin_init::<'ph, T::Plugin<'ph>>( + MutLtPtr::from_raw(ptr as *mut _), + ptr::addr_of_mut!((*ptr).plugin_name), + ptr::addr_of_mut!((*ptr).plugin_desc), + ptr::addr_of_mut!((*ptr).plugin_vers), + arg.map(|arg| arg.as_ptr()).unwrap_or(ptr::null()), + ) != 0 + } + } + } + if plug.as_ref().operate_in(MockPluginInit::<T> { + arg, + _p: PhantomData, + }) { + Ok(()) + } else { + // remove the plugin - aka retain every plugin that isn't this one + self.plugins.borrow_mut().retain(|e| { + !ptr::eq::<Holder<_>>(&**e, &*plug) + }); + Err(()) } } } -// -//impl MockPlugin { -// pub fn new<T: for<'ph> crate::Plugin<'ph> + Default>( -// env: Pin<&PluginEnvironment>, -// filename: CString, -// arg: Option<&CStr>, -// ) -> Option<Pin<Box<MockPlugin>>> { -// let filename = filename.into_raw(); -// let mut plug = Box::pin(MockPlugin { -// methods: crate::internals::Ph { -// hexchat_hook_command, -// hexchat_hook_server, -// hexchat_hook_print, -// hexchat_hook_timer, -// hexchat_hook_fd, -// hexchat_unhook, -// hexchat_print, -// userdata: hexchat_printf as *mut libc::c_void, -// hexchat_command, -// hexchat_commandf_do_not_use: hexchat_commandf, -// hexchat_nickcmp, -// hexchat_set_context, -// hexchat_find_context, -// hexchat_get_context, -// hexchat_get_info, -// hexchat_get_prefs, -// hexchat_list_get, -// hexchat_list_free, -// hexchat_list_fields, -// hexchat_list_next, -// hexchat_list_str, -// hexchat_list_int, -// hexchat_plugingui_add, -// hexchat_plugingui_remove, -// hexchat_emit_print, -// hexchat_read_fd, -// hexchat_list_time, -// hexchat_gettext, -// hexchat_send_modes, -// hexchat_strip, -// hexchat_free, -// hexchat_pluginpref_set_str, -// hexchat_pluginpref_get_str, -// hexchat_pluginpref_set_int, -// hexchat_pluginpref_get_int, -// hexchat_pluginpref_delete, -// hexchat_pluginpref_list, -// hexchat_hook_server_attrs, -// hexchat_hook_print_attrs, -// hexchat_emit_print_attrs, -// hexchat_event_attrs_create, -// hexchat_event_attrs_free, -// }, -// filename: filename, -// plugin_name: filename, -// plugin_desc: ptr::null(), -// plugin_vers: ptr::null(), -// free_strings: false, -// env: &*env, -// _pin: PhantomPinned, -// }); -// if unsafe { -// let ptr: *mut MockPlugin = &mut *plug; -// crate::hexchat_plugin_init::<'_, T>( -// crate::LtPhPtr { -// ph: ptr as *mut crate::internals::Ph, -// _lt: PhantomData, -// }, -// ptr::addr_of_mut!((*ptr).plugin_name), -// ptr::addr_of_mut!((*ptr).plugin_desc), -// ptr::addr_of_mut!((*ptr).plugin_vers), -// arg.map(|arg| arg.as_ptr()).unwrap_or(ptr::null()), -// ) != 0 -// } { -// Some(plug) -// } else { -// None -// } -// } -//} +/// Helper for making [`MockPlugin`]s from [`crate::Plugin`]s. +pub trait PluginFactory: 'static { + /// The plugin type. + type Plugin<'a>: crate::Plugin<'a> + Default; +} + +impl<'ph, 'env> MockPlugin<'ph, 'env> { + /// Creates a MockPlugin, but does not load it (yet). + fn new( + env: Pin<&'env PluginEnvironment<'env>>, + filename: CString, + ) -> Pin<Rc<Holder<'env, MockPluginK<'env>>>> { + struct MockPluginNewWith<'env> { + filename: CString, + env: Pin<&'env PluginEnvironment<'env>> + } + impl<'k, 'env> NewWith<'k, MockPluginK<'env>> + for MockPluginNewWith<'env> + where 'env: 'k { + fn new_with<'ph>(self) -> UnsafeCell<MockPlugin<'ph, 'env>> + where 'k: 'ph { + let filename = self.filename; + let env = self.env; + UnsafeCell::new(MockPlugin { + methods: Ph { + hexchat_hook_command, + hexchat_hook_server, + hexchat_hook_print, + hexchat_hook_timer, + hexchat_hook_fd, + hexchat_unhook, + hexchat_print, + userdata: hexchat_printf as *mut _, + hexchat_command, + hexchat_commandf_do_not_use: hexchat_commandf, + hexchat_nickcmp, + hexchat_set_context, + hexchat_find_context, + hexchat_get_context, + hexchat_get_info, + hexchat_get_prefs, + hexchat_list_get, + hexchat_list_free, + hexchat_list_fields, + hexchat_list_next, + hexchat_list_str, + hexchat_list_int, + hexchat_plugingui_add, + hexchat_plugingui_remove, + hexchat_emit_print, + hexchat_read_fd, + hexchat_list_time, + hexchat_gettext, + hexchat_send_modes, + hexchat_strip, + hexchat_free, + hexchat_pluginpref_set_str, + hexchat_pluginpref_get_str, + hexchat_pluginpref_set_int, + hexchat_pluginpref_get_int, + hexchat_pluginpref_delete, + hexchat_pluginpref_list, + hexchat_hook_server_attrs, + hexchat_hook_print_attrs, + hexchat_emit_print_attrs, + hexchat_event_attrs_create, + hexchat_event_attrs_free, + }, + filename: filename, + plugin_name: ptr::null(), // not initialized yet! + plugin_desc: ptr::null(), + plugin_vers: ptr::null(), + free_strings: false, + env: env, + deinit: None, + context: None, + }) + } + } + let plug = Rc::pin(Holder::new_with(MockPluginNewWith { + filename, + env, + })); + struct MockPluginPreInit; + impl<'k, 'env: 'k> OperateIn<'k, MockPluginK<'env>> + for MockPluginPreInit { + type Out = (); + fn operate_in<'ph>( + self, + ph: Pin<&'ph UnsafeCell<MockPlugin<'ph, 'env>>>, + ) where 'k: 'ph { + let ptr = ph.get(); + unsafe { + // initialize plugin_name from filename(!) + (*ptr).plugin_name = (*ptr).filename.as_ptr(); + } + } + } + plug.as_ref().operate_in(MockPluginPreInit); + plug + } +} |