Skip to content

marker_adapter: Support non-utf8 paths #108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions marker_adapter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ license = "MIT OR Apache-2.0"
marker_api = { path = "../marker_api" }

libloading = "0.7.3"
cfg-if = "1.0.0"
2 changes: 1 addition & 1 deletion marker_adapter/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use marker_api::{
/// change and calling functions on them would require a stable ABI which Rust
/// doesn't provide.
///
/// In this case, the `DriverContextWrapper` will be passed as a `*const ()`
/// In this case, the [`DriverContextWrapper`] will be passed as a `*const ()`
/// pointer to [`DriverCallbacks`] which will do nothing with this data other
/// than giving it back to functions declared in this module. Since the `&dyn`
/// object is created, only used here and everything is compiled during the same
Expand Down
65 changes: 55 additions & 10 deletions marker_adapter/src/loader.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,46 @@
use cfg_if::cfg_if;
use libloading::Library;

use marker_api::context::AstContext;
use marker_api::lint::Lint;
use marker_api::LintPass;

use std::ffi::{OsStr, OsString};

/// Splits [`OsStr`] by an ascii character
fn split_os_str(s: &OsStr, c: u8) -> Vec<OsString> {
cfg_if! {
if #[cfg(unix)] {
unix_split_os_str(s, c)
} else if #[cfg(windows)] {
windows_split_os_str(s, c)
} else {
unimplemented!("`split_os_str` currently works only on unix and windows")
}
}
}

#[cfg(unix)]
#[doc(hidden)]
fn unix_split_os_str(s: &OsStr, c: u8) -> Vec<OsString> {
use std::os::unix::ffi::OsStrExt;

s.as_bytes()
.split(|byte| *byte == c)
.map(|bytes| OsStr::from_bytes(bytes).into())
.collect()
}

#[cfg(windows)]
#[doc(hidden)]
fn windows_split_os_str(s: &OsStr, c: u8) -> Vec<OsString> {
use std::os::windows::ffi::{OsStrExt, OsStringExt};

let bytes: Vec<u16> = s.encode_wide().collect();

bytes.split(|v| *v == u16::from(c)).map(OsString::from_wide).collect()
}

/// This struct loads external lint crates into memory and provides a safe API
/// to call the respective methods on all of them.
#[derive(Default)]
Expand All @@ -15,17 +52,16 @@ impl<'ast> LintCrateRegistry<'ast> {
/// # Errors
/// This can return errors if the library couldn't be found or if the
/// required symbols weren't provided.
fn load_external_lib(&mut self, lib_path: &str) -> Result<(), LoadingError> {
fn load_external_lib(lib_path: &OsStr) -> Result<LoadedLintCrate<'ast>, LoadingError> {
let lib: &'static Library = Box::leak(Box::new(
unsafe { Library::new(lib_path) }.map_err(|_| LoadingError::FileNotFound)?,
));

let pass = LoadedLintCrate::try_from_lib(lib)?;

self.passes.push(pass);
// FIXME: Create issue for lifetimes and fix droping and pointer decl stuff

Ok(())
Ok(pass)
}

/// # Panics
Expand All @@ -34,12 +70,21 @@ impl<'ast> LintCrateRegistry<'ast> {
pub fn new_from_env() -> Self {
let mut new_self = Self::default();

if let Ok(lint_crates_lst) = std::env::var("MARKER_LINT_CRATES") {
for lint_crate in lint_crates_lst.split(';') {
if let Err(err) = new_self.load_external_lib(lint_crate) {
panic!("Unable to load `{lint_crate}`, reason: {err:?}");
}
let Some((_, lint_crates_lst)) = std::env::vars_os().find(|(name, _val)| name == "MARKER_LINT_CRATES") else {
panic!("Adapter tried to find `MARKER_LINT_CRATES` env variable, but it was not present");
};

for lib in split_os_str(&lint_crates_lst, b';') {
if lib.is_empty() {
continue;
}

let lib = match Self::load_external_lib(&lib) {
Ok(v) => v,
Err(err) => panic!("Unable to load `{}`, reason: {err:?}", lib.to_string_lossy()),
};

new_self.passes.push(lib);
}

new_self
Expand Down Expand Up @@ -113,7 +158,7 @@ macro_rules! gen_LoadedLintCrate {
}

let set_ast_context = unsafe {
lib.get::<for<'ast> unsafe extern "C" fn(&'ast AstContext<'ast>) -> ()>(b"set_ast_context\0")
lib.get::<for<'ast> unsafe extern "C" fn(&'ast AstContext<'ast>)>(b"set_ast_context\0")
.map_err(|_| LoadingError::MissingLintDeclaration)?
};

Expand Down Expand Up @@ -144,7 +189,7 @@ macro_rules! gen_LoadedLintCrate {

// safe wrapper to external functions
$(
fn $fn_name<'ast>(& $($mut_)* self $(, $arg_name: $arg_ty)*) -> $ret_ty {
fn $fn_name<'ast>(&self $(, $arg_name: $arg_ty)*) -> $ret_ty {
unsafe {
(self.$fn_name)($($arg_name,)*)
}
Expand Down