c_wrapper.rs
Branches > main > b8d45fc > files / nvim / pack / plugins / opt / parinfer-rust / src / c_wrapper.rs
use super::*;
use libc::c_char;
use std::cell::RefCell;
use std::ffi::{CStr, CString};
use std::panic;
use types::*;
/// On unix, Vim loads and unloads the library for every call. On Mac, and
/// possibly other unices, each load creates a new tlv key, and there is a
/// maximum number allowed per process. When we run out, dlopen() aborts
/// our process.
///
/// Here we reference ourselves and throw the handle away to prevent
/// ourselves from being unloaded (and also set RTLD_NODELETE and
/// RTLD_GLOBAL to make extra sure).
#[cfg(unix)]
mod reference_hack {
use libc::Dl_info;
use libc::{c_void, dladdr, dlerror, dlopen};
use std::ffi::CStr;
use std::ptr;
pub static mut INITIALIZED: bool = false;
#[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
mod netbsdlike {
use libc::{RTLD_GLOBAL, RTLD_LAZY};
pub fn first_attempt_flags() -> i32 {
RTLD_LAZY | RTLD_GLOBAL
}
pub fn second_attempt_flags() -> i32 {
RTLD_LAZY | RTLD_GLOBAL
}
}
#[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
use self::netbsdlike::*;
#[cfg(not(any(target_os = "netbsd", target_os = "openbsd")))]
mod default {
use libc::{RTLD_GLOBAL, RTLD_LAZY, RTLD_NODELETE, RTLD_NOLOAD};
pub fn first_attempt_flags() -> i32 {
RTLD_LAZY | RTLD_NOLOAD | RTLD_GLOBAL | RTLD_NODELETE
}
pub fn second_attempt_flags() -> i32 {
RTLD_LAZY | RTLD_GLOBAL | RTLD_NODELETE
}
}
#[cfg(not(any(target_os = "netbsd", target_os = "openbsd")))]
use self::default::*;
pub unsafe fn initialize() {
if INITIALIZED {
return;
}
let mut info: Dl_info = Dl_info {
dli_fname: ptr::null(),
dli_fbase: ptr::null_mut(),
dli_sname: ptr::null(),
dli_saddr: ptr::null_mut(),
};
let initialize_ptr: *const c_void = initialize as *const c_void;
if dladdr(initialize_ptr, &mut info) == 0 {
panic!("Could not get parinfer library path.");
}
// First, try to use RTLD_NOLOAD to promote the existing object. If
// this fails, it could be because we don't think we are already
// loaded (this happens when running the tests under Linux, but not
// Mac). dlerror() is unhelfully NULL at that point, so try to
// *really* load ourselves, then report if that fails.
let handle = dlopen(info.dli_fname, first_attempt_flags());
if handle.is_null() {
let handle = dlopen(info.dli_fname, second_attempt_flags());
if handle.is_null() {
let error = dlerror();
if error.is_null() {
panic!(
"Could not reference parinfer_rust library {:?}.",
CStr::from_ptr(info.dli_fname)
);
} else {
panic!(
"Could not reference parinfer_rust library {:?}: {:?}.",
CStr::from_ptr(info.dli_fname),
CStr::from_ptr(error)
);
}
}
}
INITIALIZED = true;
}
}
#[cfg(windows)]
mod reference_hack {
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use std::ptr;
use winapi::um::libloaderapi::{
GetModuleFileNameW, GetModuleHandleExW, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
GET_MODULE_HANDLE_EX_FLAG_PIN,
};
use winapi::um::winnt::LPCWSTR;
pub static mut INITIALIZED: bool = false;
pub unsafe fn initialize() {
let mut out = ptr::null_mut();
if GetModuleHandleExW(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN,
initialize as LPCWSTR,
&mut out,
) == 0
{
panic!("Could not pin parinfer_rust DLL.")
} else {
let mut buf = Vec::with_capacity(512);
let len = GetModuleFileNameW(out, buf.as_mut_ptr(), 512 as u32) as usize;
if len > 0 {
buf.set_len(len);
let filename = OsString::from_wide(&buf)
.into_string()
.expect("expect a string");
if filename.ends_with(".dll") {
} else {
panic!("parinfer_rust: reference_hack failed to find DLL.");
}
} else {
panic!("parinfer_rust: could not get DLL filename");
}
}
}
}
#[cfg(all(not(windows), not(unix)))]
mod reference_hack {
pub static mut INITIALIZED: bool = true;
pub fn initialize() {}
}
pub use self::reference_hack::INITIALIZED;
unsafe fn unwrap_c_pointers(json: *const c_char) -> Result<CString, Error> {
let json_str = CStr::from_ptr(json).to_str()?;
let response = common_wrapper::internal_run(json_str)?;
Ok(CString::new(response)?)
}
thread_local!(static BUFFER: RefCell<Option<CString>> = const { RefCell::new(None) });
#[cfg(not(target_arch = "wasm32"))]
#[no_mangle]
pub unsafe extern "C" fn run_parinfer(json: *const c_char) -> *const c_char {
reference_hack::initialize();
let output = match panic::catch_unwind(|| unwrap_c_pointers(json)) {
Ok(Ok(result)) => result,
Ok(Err(e)) => {
let out = serde_json::to_string(&Answer::from(e)).unwrap();
CString::new(out).unwrap()
}
Err(_) => {
let out = common_wrapper::panic_result();
CString::new(out).unwrap()
}
};
BUFFER.with(|buffer| {
buffer.replace(Some(output));
buffer.borrow().as_ref().unwrap().as_ptr()
})
}