From 39b8d10b93155e80c5757e644706bf27455cf96a Mon Sep 17 00:00:00 2001 From: Alex Good Date: Sat, 10 Jul 2021 15:12:41 +0100 Subject: [PATCH] Use rustc version of dylib to choose which proc macro ABI to use --- crates/proc_macro_srv/src/dylib.rs | 355 ++++++++++++++---- crates/proc_macro_srv/src/lib.rs | 7 + .../src/proc_macro_nightly/bridge/buffer.rs | 4 +- 3 files changed, 291 insertions(+), 75 deletions(-) diff --git a/crates/proc_macro_srv/src/dylib.rs b/crates/proc_macro_srv/src/dylib.rs index 5133e7c50b2..c5ac2c402cb 100644 --- a/crates/proc_macro_srv/src/dylib.rs +++ b/crates/proc_macro_srv/src/dylib.rs @@ -1,6 +1,8 @@ //! Handles dynamic library loading for proc macro use std::{ + convert::{TryFrom, TryInto}, + fmt, fs::File, io, path::{Path, PathBuf}, @@ -9,9 +11,16 @@ use std::{ use libloading::Library; use memmap2::Mmap; use object::Object; -use proc_macro_api::ProcMacroKind; +use proc_macro_api::{read_dylib_info, ProcMacroKind, RustCInfo}; -use crate::{proc_macro::bridge, rustc_server::TokenStream}; +use crate::{ + proc_macro::bridge::{self as stable_bridge, client::ProcMacro as StableProcMacro}, + rustc_server::TokenStream as StableTokenStream, +}; +use crate::{ + proc_macro_nightly::bridge::{self as nightly_bridge, client::ProcMacro as NightlyProcMacro}, + rustc_server_nightly::TokenStream as NightlyTokenStream, +}; const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_"; @@ -74,26 +83,117 @@ fn load_library(file: &Path) -> Result { unsafe { UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into()) } } -struct ProcMacroLibraryLibloading { - // Hold the dylib to prevent it from unloading - _lib: Library, - exported_macros: Vec, +enum ProcMacroABI { + Stable, + Nightly, +} + +impl TryFrom for ProcMacroABI { + type Error = LoadProcMacroDylibError; + + fn try_from(info: RustCInfo) -> Result { + if info.version.0 < 1 { + Err(LoadProcMacroDylibError::UnsupportedABI) + } else if info.version.1 < 47 { + Err(LoadProcMacroDylibError::UnsupportedABI) + } else if info.version.1 < 54 { + Ok(ProcMacroABI::Stable) + } else { + Ok(ProcMacroABI::Nightly) + } + } +} + +#[derive(Debug)] +pub enum LoadProcMacroDylibError { + Io(io::Error), + UnsupportedABI, +} + +impl fmt::Display for LoadProcMacroDylibError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Io(e) => e.fmt(f), + Self::UnsupportedABI => write!(f, "unsupported ABI version"), + } + } +} + +impl From for LoadProcMacroDylibError { + fn from(e: io::Error) -> Self { + LoadProcMacroDylibError::Io(e) + } +} + +enum ProcMacroLibraryLibloading { + StableProcMacroLibrary { + _lib: Library, + exported_macros: Vec, + }, + NightlyProcMacroLibrary { + _lib: Library, + exported_macros: Vec, + }, } impl ProcMacroLibraryLibloading { - fn open(file: &Path) -> io::Result { + fn open(file: &Path) -> Result { let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| { invalid_data_err(format!("Cannot find registrar symbol in file {}", file.display())) })?; - let lib = load_library(file).map_err(invalid_data_err)?; - let exported_macros = { - let macros: libloading::Symbol<&&[bridge::client::ProcMacro]> = - unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?; - macros.to_vec() - }; + let version_info = read_dylib_info(file)?; + let macro_abi: ProcMacroABI = version_info.try_into()?; - Ok(ProcMacroLibraryLibloading { _lib: lib, exported_macros }) + let lib = load_library(file).map_err(invalid_data_err)?; + match macro_abi { + ProcMacroABI::Stable => { + let macros: libloading::Symbol<&&[crate::proc_macro::bridge::client::ProcMacro]> = + unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?; + let macros_vec = macros.to_vec(); + Ok(ProcMacroLibraryLibloading::StableProcMacroLibrary { + _lib: lib, + exported_macros: macros_vec, + }) + } + ProcMacroABI::Nightly => { + let macros: libloading::Symbol< + &&[crate::proc_macro_nightly::bridge::client::ProcMacro], + > = unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?; + let macros_vec = macros.to_vec(); + Ok(ProcMacroLibraryLibloading::NightlyProcMacroLibrary { + _lib: lib, + exported_macros: macros_vec, + }) + } + } + } +} + +#[derive(Debug)] +pub enum PanicMessage { + Stable(stable_bridge::PanicMessage), + Nightly(nightly_bridge::PanicMessage), +} + +impl From for PanicMessage { + fn from(p: stable_bridge::PanicMessage) -> Self { + PanicMessage::Stable(p) + } +} + +impl From for PanicMessage { + fn from(p: nightly_bridge::PanicMessage) -> Self { + PanicMessage::Nightly(p) + } +} + +impl PanicMessage { + pub fn as_str(&self) -> Option<&str> { + match self { + Self::Stable(p) => p.as_str(), + Self::Nightly(p) => p.as_str(), + } } } @@ -102,7 +202,7 @@ pub struct Expander { } impl Expander { - pub fn new(lib: &Path) -> io::Result { + pub fn new(lib: &Path) -> Result { // Some libraries for dynamic loading require canonicalized path even when it is // already absolute let lib = lib.canonicalize()?; @@ -119,69 +219,28 @@ impl Expander { macro_name: &str, macro_body: &tt::Subtree, attributes: Option<&tt::Subtree>, - ) -> Result { - let parsed_body = TokenStream::with_subtree(macro_body.clone()); - - let parsed_attributes = attributes - .map_or(crate::rustc_server::TokenStream::new(), |attr| { - TokenStream::with_subtree(attr.clone()) - }); - - for proc_macro in &self.inner.exported_macros { - match proc_macro { - bridge::client::ProcMacro::CustomDerive { trait_name, client, .. } - if *trait_name == macro_name => - { - let res = client.run( - &crate::proc_macro::bridge::server::SameThread, - crate::rustc_server::Rustc::default(), - parsed_body, - false, - ); - return res.map(|it| it.into_subtree()); - } - bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => { - let res = client.run( - &crate::proc_macro::bridge::server::SameThread, - crate::rustc_server::Rustc::default(), - parsed_body, - false, - ); - return res.map(|it| it.into_subtree()); - } - bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => { - let res = client.run( - &crate::proc_macro::bridge::server::SameThread, - crate::rustc_server::Rustc::default(), - parsed_attributes, - parsed_body, - false, - ); - return res.map(|it| it.into_subtree()); - } - _ => continue, + ) -> Result { + match &self.inner { + ProcMacroLibraryLibloading::StableProcMacroLibrary { exported_macros, .. } => { + expand_stable(macro_name, macro_body, attributes, &exported_macros[..]) + .map_err(PanicMessage::from) + } + ProcMacroLibraryLibloading::NightlyProcMacroLibrary { exported_macros, .. } => { + expand_nightly(macro_name, macro_body, attributes, &exported_macros[..]) + .map_err(PanicMessage::from) } } - - Err(bridge::PanicMessage::String("Nothing to expand".to_string())) } pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { - self.inner - .exported_macros - .iter() - .map(|proc_macro| match proc_macro { - bridge::client::ProcMacro::CustomDerive { trait_name, .. } => { - (trait_name.to_string(), ProcMacroKind::CustomDerive) - } - bridge::client::ProcMacro::Bang { name, .. } => { - (name.to_string(), ProcMacroKind::FuncLike) - } - bridge::client::ProcMacro::Attr { name, .. } => { - (name.to_string(), ProcMacroKind::Attr) - } - }) - .collect() + match &self.inner { + ProcMacroLibraryLibloading::StableProcMacroLibrary { exported_macros, .. } => { + list_macros_stable(&exported_macros[..]) + } + ProcMacroLibraryLibloading::NightlyProcMacroLibrary { exported_macros, .. } => { + list_macros_nightly(&exported_macros[..]) + } + } } } @@ -217,3 +276,153 @@ fn ensure_file_with_lock_free_access(path: &Path) -> io::Result { fn ensure_file_with_lock_free_access(path: &Path) -> io::Result { Ok(path.to_path_buf()) } + +fn expand_nightly( + macro_name: &str, + macro_body: &tt::Subtree, + attributes: Option<&tt::Subtree>, + macros: &[NightlyProcMacro], +) -> Result { + let parsed_body = NightlyTokenStream::with_subtree(macro_body.clone()); + + let parsed_attributes = attributes + .map_or(crate::rustc_server_nightly::TokenStream::new(), |attr| { + NightlyTokenStream::with_subtree(attr.clone()) + }); + + for proc_macro in macros { + match proc_macro { + crate::proc_macro_nightly::bridge::client::ProcMacro::CustomDerive { + trait_name, + client, + .. + } if *trait_name == macro_name => { + let res = client.run( + &crate::proc_macro_nightly::bridge::server::SameThread, + crate::rustc_server_nightly::Rustc::default(), + parsed_body, + false, + ); + return res.map(|it| it.into_subtree()); + } + crate::proc_macro_nightly::bridge::client::ProcMacro::Bang { name, client } + if *name == macro_name => + { + let res = client.run( + &crate::proc_macro_nightly::bridge::server::SameThread, + crate::rustc_server_nightly::Rustc::default(), + parsed_body, + false, + ); + return res.map(|it| it.into_subtree()); + } + crate::proc_macro_nightly::bridge::client::ProcMacro::Attr { name, client } + if *name == macro_name => + { + let res = client.run( + &crate::proc_macro_nightly::bridge::server::SameThread, + crate::rustc_server_nightly::Rustc::default(), + parsed_attributes, + parsed_body, + false, + ); + return res.map(|it| it.into_subtree()); + } + _ => continue, + } + } + + Err(crate::proc_macro_nightly::bridge::PanicMessage::String("Nothing to expand".to_string())) +} + +fn expand_stable( + macro_name: &str, + macro_body: &tt::Subtree, + attributes: Option<&tt::Subtree>, + macros: &[StableProcMacro], +) -> Result { + let parsed_body = StableTokenStream::with_subtree(macro_body.clone()); + + let parsed_attributes = attributes.map_or(crate::rustc_server::TokenStream::new(), |attr| { + StableTokenStream::with_subtree(attr.clone()) + }); + + for proc_macro in macros { + match proc_macro { + crate::proc_macro::bridge::client::ProcMacro::CustomDerive { + trait_name, + client, + .. + } if *trait_name == macro_name => { + let res = client.run( + &crate::proc_macro::bridge::server::SameThread, + crate::rustc_server::Rustc::default(), + parsed_body, + false, + ); + return res.map(|it| it.into_subtree()); + } + crate::proc_macro::bridge::client::ProcMacro::Bang { name, client } + if *name == macro_name => + { + let res = client.run( + &crate::proc_macro::bridge::server::SameThread, + crate::rustc_server::Rustc::default(), + parsed_body, + false, + ); + return res.map(|it| it.into_subtree()); + } + crate::proc_macro::bridge::client::ProcMacro::Attr { name, client } + if *name == macro_name => + { + let res = client.run( + &crate::proc_macro::bridge::server::SameThread, + crate::rustc_server::Rustc::default(), + parsed_attributes, + parsed_body, + false, + ); + return res.map(|it| it.into_subtree()); + } + _ => continue, + } + } + + Err(crate::proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string())) +} + +pub fn list_macros_stable(macros: &[StableProcMacro]) -> Vec<(String, ProcMacroKind)> { + macros + .iter() + .map(|proc_macro| match proc_macro { + crate::proc_macro::bridge::client::ProcMacro::CustomDerive { trait_name, .. } => { + (trait_name.to_string(), ProcMacroKind::CustomDerive) + } + crate::proc_macro::bridge::client::ProcMacro::Bang { name, .. } => { + (name.to_string(), ProcMacroKind::FuncLike) + } + crate::proc_macro::bridge::client::ProcMacro::Attr { name, .. } => { + (name.to_string(), ProcMacroKind::Attr) + } + }) + .collect() +} + +pub fn list_macros_nightly(macros: &[NightlyProcMacro]) -> Vec<(String, ProcMacroKind)> { + macros + .iter() + .map(|proc_macro| match proc_macro { + crate::proc_macro_nightly::bridge::client::ProcMacro::CustomDerive { + trait_name, + .. + } => (trait_name.to_string(), ProcMacroKind::CustomDerive), + crate::proc_macro_nightly::bridge::client::ProcMacro::Bang { name, .. } => { + (name.to_string(), ProcMacroKind::FuncLike) + } + crate::proc_macro_nightly::bridge::client::ProcMacro::Attr { name, .. } => { + (name.to_string(), ProcMacroKind::Attr) + } + }) + .collect() +} diff --git a/crates/proc_macro_srv/src/lib.rs b/crates/proc_macro_srv/src/lib.rs index f54cbcd6153..deb1d1e526e 100644 --- a/crates/proc_macro_srv/src/lib.rs +++ b/crates/proc_macro_srv/src/lib.rs @@ -15,9 +15,16 @@ #[doc(hidden)] mod proc_macro; +#[allow(dead_code)] +#[doc(hidden)] +mod proc_macro_nightly; + #[doc(hidden)] mod rustc_server; +#[doc(hidden)] +mod rustc_server_nightly; + mod dylib; use proc_macro::bridge::client::TokenStream; diff --git a/crates/proc_macro_srv/src/proc_macro_nightly/bridge/buffer.rs b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/buffer.rs index a25feac2012..1565db187c6 100644 --- a/crates/proc_macro_srv/src/proc_macro_nightly/bridge/buffer.rs +++ b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/buffer.rs @@ -91,7 +91,7 @@ impl Buffer { self.len += xs.len(); } } - + pub(super) fn push(&mut self, v: T) { // The code here is taken from Vec::push, and we know that reserve() // will panic if we're exceeding isize::MAX bytes and so there's no need @@ -145,7 +145,7 @@ impl From> for Buffer { } } - extern "C" fn reserve(b: Buffer, additional: usize) -> Buffer { + extern "C" fn reserve(b: Buffer, additional: usize) -> Buffer { let mut v = to_vec(b); v.reserve(additional); Buffer::from(v)