summary refs log tree commit diff stats
path: root/src/lib.rs
diff options
context:
space:
mode:
authorSoniEx2 <endermoneymod@gmail.com>2022-06-20 00:19:56 -0300
committerSoniEx2 <endermoneymod@gmail.com>2022-06-20 00:19:56 -0300
commit342d0b3ba53512c98cc5540313f874fe543d4b9b (patch)
tree4de8305590a3ebd80f003f5fb4ed7938b6168e4e /src/lib.rs
parent890d8bbef986d8b85d6eaaddd4c8895c5f61c09c (diff)
Begin the great refactoring
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs186
1 files changed, 100 insertions, 86 deletions
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.