Skip to content

Commit 0affbd4

Browse files
committed
Cleanup proc-macro dylib handling
1 parent d4b53f5 commit 0affbd4

File tree

3 files changed

+74
-74
lines changed

3 files changed

+74
-74
lines changed

src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs

Lines changed: 55 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -9,37 +9,7 @@ use libloading::Library;
99
use object::Object;
1010
use paths::{Utf8Path, Utf8PathBuf};
1111

12-
use crate::{proc_macros::ProcMacroKind, ProcMacroSrvSpan};
13-
14-
const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";
15-
16-
fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error {
17-
io::Error::new(io::ErrorKind::InvalidData, e)
18-
}
19-
20-
fn is_derive_registrar_symbol(symbol: &str) -> bool {
21-
symbol.contains(NEW_REGISTRAR_SYMBOL)
22-
}
23-
24-
fn find_registrar_symbol(obj: &object::File<'_>) -> object::Result<Option<String>> {
25-
Ok(obj
26-
.exports()?
27-
.into_iter()
28-
.map(|export| export.name())
29-
.filter_map(|sym| String::from_utf8(sym.into()).ok())
30-
.find(|sym| is_derive_registrar_symbol(sym))
31-
.map(|sym| {
32-
// From MacOS docs:
33-
// https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
34-
// Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
35-
// prepended with an underscore.
36-
if cfg!(target_os = "macos") && sym.starts_with('_') {
37-
sym[1..].to_owned()
38-
} else {
39-
sym
40-
}
41-
}))
42-
}
12+
use crate::{proc_macros::ProcMacros, ProcMacroKind, ProcMacroSrvSpan};
4313

4414
/// Loads dynamic library in platform dependent manner.
4515
///
@@ -99,13 +69,14 @@ impl From<libloading::Error> for LoadProcMacroDylibError {
9969
}
10070
}
10171

102-
struct ProcMacroLibraryLibloading {
72+
struct ProcMacroLibrary {
73+
// 'static is actually the lifetime of library, so make sure this drops before _lib
74+
proc_macros: &'static ProcMacros,
10375
// Hold on to the library so it doesn't unload
10476
_lib: Library,
105-
proc_macros: crate::proc_macros::ProcMacros,
10677
}
10778

108-
impl ProcMacroLibraryLibloading {
79+
impl ProcMacroLibrary {
10980
fn open(path: &Utf8Path) -> Result<Self, LoadProcMacroDylibError> {
11081
let file = fs::File::open(path)?;
11182
let file = unsafe { memmap2::Mmap::map(&file) }?;
@@ -118,27 +89,22 @@ impl ProcMacroLibraryLibloading {
11889
})?;
11990

12091
let lib = load_library(path).map_err(invalid_data_err)?;
121-
let proc_macros = crate::proc_macros::ProcMacros::from_lib(
122-
&lib,
123-
symbol_name,
124-
&version_info.version_string,
125-
)?;
126-
Ok(ProcMacroLibraryLibloading { _lib: lib, proc_macros })
127-
}
128-
}
129-
130-
struct RemoveFileOnDrop(Utf8PathBuf);
131-
impl Drop for RemoveFileOnDrop {
132-
fn drop(&mut self) {
133-
#[cfg(windows)]
134-
std::fs::remove_file(&self.0).unwrap();
135-
_ = self.0;
92+
let proc_macros = unsafe {
93+
// SAFETY: We extend the lifetime here to avoid referential borrow problems
94+
// We never reveal proc_macros to the outside and drop it before _lib
95+
std::mem::transmute::<&ProcMacros, &'static ProcMacros>(ProcMacros::from_lib(
96+
&lib,
97+
symbol_name,
98+
&version_info.version_string,
99+
)?)
100+
};
101+
Ok(ProcMacroLibrary { _lib: lib, proc_macros })
136102
}
137103
}
138104

139105
// Drop order matters as we can't remove the dylib before the library is unloaded
140106
pub(crate) struct Expander {
141-
inner: ProcMacroLibraryLibloading,
107+
inner: ProcMacroLibrary,
142108
_remove_on_drop: RemoveFileOnDrop,
143109
modified_time: SystemTime,
144110
}
@@ -151,7 +117,7 @@ impl Expander {
151117
let modified_time = fs::metadata(&lib).and_then(|it| it.modified())?;
152118

153119
let path = ensure_file_with_lock_free_access(&lib)?;
154-
let library = ProcMacroLibraryLibloading::open(path.as_ref())?;
120+
let library = ProcMacroLibrary::open(path.as_ref())?;
155121

156122
Ok(Expander { inner: library, _remove_on_drop: RemoveFileOnDrop(path), modified_time })
157123
}
@@ -184,6 +150,44 @@ impl Expander {
184150
}
185151
}
186152

153+
fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error {
154+
io::Error::new(io::ErrorKind::InvalidData, e)
155+
}
156+
157+
fn is_derive_registrar_symbol(symbol: &str) -> bool {
158+
const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";
159+
symbol.contains(NEW_REGISTRAR_SYMBOL)
160+
}
161+
162+
fn find_registrar_symbol(obj: &object::File<'_>) -> object::Result<Option<String>> {
163+
Ok(obj
164+
.exports()?
165+
.into_iter()
166+
.map(|export| export.name())
167+
.filter_map(|sym| String::from_utf8(sym.into()).ok())
168+
.find(|sym| is_derive_registrar_symbol(sym))
169+
.map(|sym| {
170+
// From MacOS docs:
171+
// https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
172+
// Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
173+
// prepended with an underscore.
174+
if cfg!(target_os = "macos") && sym.starts_with('_') {
175+
sym[1..].to_owned()
176+
} else {
177+
sym
178+
}
179+
}))
180+
}
181+
182+
struct RemoveFileOnDrop(Utf8PathBuf);
183+
impl Drop for RemoveFileOnDrop {
184+
fn drop(&mut self) {
185+
#[cfg(windows)]
186+
std::fs::remove_file(&self.0).unwrap();
187+
_ = self.0;
188+
}
189+
}
190+
187191
/// Copy the dylib to temp directory to prevent locking in Windows
188192
#[cfg(windows)]
189193
fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result<Utf8PathBuf> {

src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,12 @@ use span::{Span, TokenId};
4343

4444
use crate::server_impl::TokenStream;
4545

46-
pub use crate::proc_macros::ProcMacroKind;
46+
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
47+
pub enum ProcMacroKind {
48+
CustomDerive,
49+
Attr,
50+
Bang,
51+
}
4752

4853
pub const RUSTC_VERSION_STRING: &str = env!("RUSTC_VERSION");
4954

src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,10 @@ use proc_macro::bridge;
44

55
use libloading::Library;
66

7-
use crate::{dylib::LoadProcMacroDylibError, ProcMacroSrvSpan};
7+
use crate::{dylib::LoadProcMacroDylibError, ProcMacroKind, ProcMacroSrvSpan};
88

9-
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
10-
pub enum ProcMacroKind {
11-
CustomDerive,
12-
Attr,
13-
Bang,
14-
}
15-
16-
pub(crate) struct ProcMacros {
17-
exported_macros: Vec<bridge::client::ProcMacro>,
18-
}
9+
#[repr(transparent)]
10+
pub(crate) struct ProcMacros([bridge::client::ProcMacro]);
1911

2012
impl From<bridge::PanicMessage> for crate::PanicMessage {
2113
fn from(p: bridge::PanicMessage) -> Self {
@@ -33,18 +25,17 @@ impl ProcMacros {
3325
/// *`info` - RustCInfo about the compiler that was used to compile the
3426
/// macro crate. This is the information we use to figure out
3527
/// which ABI to return
36-
pub(crate) fn from_lib(
37-
lib: &Library,
28+
pub(crate) fn from_lib<'l>(
29+
lib: &'l Library,
3830
symbol_name: String,
3931
version_string: &str,
40-
) -> Result<ProcMacros, LoadProcMacroDylibError> {
41-
if version_string == crate::RUSTC_VERSION_STRING {
42-
let macros =
43-
unsafe { lib.get::<&&[bridge::client::ProcMacro]>(symbol_name.as_bytes()) }?;
44-
45-
return Ok(Self { exported_macros: macros.to_vec() });
32+
) -> Result<&'l ProcMacros, LoadProcMacroDylibError> {
33+
if version_string != crate::RUSTC_VERSION_STRING {
34+
return Err(LoadProcMacroDylibError::AbiMismatch(version_string.to_owned()));
4635
}
47-
Err(LoadProcMacroDylibError::AbiMismatch(version_string.to_owned()))
36+
unsafe { lib.get::<&'l &'l ProcMacros>(symbol_name.as_bytes()) }
37+
.map(|it| **it)
38+
.map_err(Into::into)
4839
}
4940

5041
pub(crate) fn expand<S: ProcMacroSrvSpan>(
@@ -63,7 +54,7 @@ impl ProcMacros {
6354
crate::server_impl::TokenStream::with_subtree(attr)
6455
});
6556

66-
for proc_macro in &self.exported_macros {
57+
for proc_macro in &self.0 {
6758
match proc_macro {
6859
bridge::client::ProcMacro::CustomDerive { trait_name, client, .. }
6960
if *trait_name == macro_name =>
@@ -109,7 +100,7 @@ impl ProcMacros {
109100
}
110101

111102
pub(crate) fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
112-
self.exported_macros
103+
self.0
113104
.iter()
114105
.map(|proc_macro| match proc_macro {
115106
bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {

0 commit comments

Comments
 (0)