Use rustc version of dylib to choose which proc macro ABI to use

This commit is contained in:
Alex Good 2021-07-10 15:12:41 +01:00
parent a8b0a91011
commit 39b8d10b93
3 changed files with 291 additions and 75 deletions

View File

@ -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 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<Library, libloading::Error> {
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<bridge::client::ProcMacro>,
enum ProcMacroABI {
Stable,
Nightly,
}
impl TryFrom<RustCInfo> for ProcMacroABI {
type Error = LoadProcMacroDylibError;
fn try_from(info: RustCInfo) -> Result<Self, Self::Error> {
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<io::Error> for LoadProcMacroDylibError {
fn from(e: io::Error) -> Self {
LoadProcMacroDylibError::Io(e)
}
}
enum ProcMacroLibraryLibloading {
StableProcMacroLibrary {
_lib: Library,
exported_macros: Vec<crate::proc_macro::bridge::client::ProcMacro>,
},
NightlyProcMacroLibrary {
_lib: Library,
exported_macros: Vec<crate::proc_macro_nightly::bridge::client::ProcMacro>,
},
}
impl ProcMacroLibraryLibloading {
fn open(file: &Path) -> io::Result<Self> {
fn open(file: &Path) -> Result<Self, LoadProcMacroDylibError> {
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<stable_bridge::PanicMessage> for PanicMessage {
fn from(p: stable_bridge::PanicMessage) -> Self {
PanicMessage::Stable(p)
}
}
impl From<nightly_bridge::PanicMessage> 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<Expander> {
pub fn new(lib: &Path) -> Result<Expander, LoadProcMacroDylibError> {
// Some libraries for dynamic loading require canonicalized path even when it is
// already absolute
let lib = lib.canonicalize()?;
@ -119,69 +219,28 @@ pub fn expand(
macro_name: &str,
macro_body: &tt::Subtree,
attributes: Option<&tt::Subtree>,
) -> Result<tt::Subtree, bridge::PanicMessage> {
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<tt::Subtree, PanicMessage> {
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<PathBuf> {
fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> {
Ok(path.to_path_buf())
}
fn expand_nightly(
macro_name: &str,
macro_body: &tt::Subtree,
attributes: Option<&tt::Subtree>,
macros: &[NightlyProcMacro],
) -> Result<tt::Subtree, crate::proc_macro_nightly::bridge::PanicMessage> {
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<tt::Subtree, crate::proc_macro::bridge::PanicMessage> {
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()
}

View File

@ -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;

View File

@ -91,7 +91,7 @@ pub(super) fn extend_from_slice(&mut self, xs: &[T]) {
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 @@ fn to_vec<T: Copy>(b: Buffer<T>) -> Vec<T> {
}
}
extern "C" fn reserve<T: Copy>(b: Buffer<T>, additional: usize) -> Buffer<T> {
extern "C" fn reserve<T: Copy>(b: Buffer<T>, additional: usize) -> Buffer<T> {
let mut v = to_vec(b);
v.reserve(additional);
Buffer::from(v)