aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock48
-rw-r--r--crates/ra_proc_macro_srv/Cargo.toml4
-rw-r--r--crates/ra_proc_macro_srv/src/dylib.rs220
-rw-r--r--crates/ra_proc_macro_srv/src/lib.rs2
-rw-r--r--crates/ra_proc_macro_srv/src/rustc_server.rs4
5 files changed, 277 insertions, 1 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 34f05e83a..5f7c52e0a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -425,6 +425,17 @@ dependencies = [
425] 425]
426 426
427[[package]] 427[[package]]
428name = "goblin"
429version = "0.2.1"
430source = "registry+https://github.com/rust-lang/crates.io-index"
431checksum = "ddd5e3132801a1ac34ac53b97acde50c4685414dd2f291b9ea52afa6f07468c8"
432dependencies = [
433 "log",
434 "plain",
435 "scroll",
436]
437
438[[package]]
428name = "heck" 439name = "heck"
429version = "0.3.1" 440version = "0.3.1"
430source = "registry+https://github.com/rust-lang/crates.io-index" 441source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -587,6 +598,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
587checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" 598checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0"
588 599
589[[package]] 600[[package]]
601name = "libloading"
602version = "0.6.0"
603source = "registry+https://github.com/rust-lang/crates.io-index"
604checksum = "2c979a19ffb457f0273965c333053f3d586bf759bf7b683fbebc37f9a9ebedc4"
605dependencies = [
606 "winapi 0.3.8",
607]
608
609[[package]]
590name = "linked-hash-map" 610name = "linked-hash-map"
591version = "0.5.2" 611version = "0.5.2"
592source = "registry+https://github.com/rust-lang/crates.io-index" 612source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -826,6 +846,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
826checksum = "3ad1f1b834a05d42dae330066e9699a173b28185b3bdc3dbf14ca239585de8cc" 846checksum = "3ad1f1b834a05d42dae330066e9699a173b28185b3bdc3dbf14ca239585de8cc"
827 847
828[[package]] 848[[package]]
849name = "plain"
850version = "0.2.3"
851source = "registry+https://github.com/rust-lang/crates.io-index"
852checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
853
854[[package]]
829name = "ppv-lite86" 855name = "ppv-lite86"
830version = "0.2.6" 856version = "0.2.6"
831source = "registry+https://github.com/rust-lang/crates.io-index" 857source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1081,6 +1107,8 @@ version = "0.1.0"
1081dependencies = [ 1107dependencies = [
1082 "cargo_metadata", 1108 "cargo_metadata",
1083 "difference", 1109 "difference",
1110 "goblin",
1111 "libloading",
1084 "ra_mbe", 1112 "ra_mbe",
1085 "ra_proc_macro", 1113 "ra_proc_macro",
1086 "ra_tt", 1114 "ra_tt",
@@ -1397,6 +1425,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1397checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1425checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
1398 1426
1399[[package]] 1427[[package]]
1428name = "scroll"
1429version = "0.10.1"
1430source = "registry+https://github.com/rust-lang/crates.io-index"
1431checksum = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1"
1432dependencies = [
1433 "scroll_derive",
1434]
1435
1436[[package]]
1437name = "scroll_derive"
1438version = "0.10.1"
1439source = "registry+https://github.com/rust-lang/crates.io-index"
1440checksum = "f8584eea9b9ff42825b46faf46a8c24d2cff13ec152fa2a50df788b87c07ee28"
1441dependencies = [
1442 "proc-macro2",
1443 "quote",
1444 "syn",
1445]
1446
1447[[package]]
1400name = "semver" 1448name = "semver"
1401version = "0.9.0" 1449version = "0.9.0"
1402source = "registry+https://github.com/rust-lang/crates.io-index" 1450source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/crates/ra_proc_macro_srv/Cargo.toml b/crates/ra_proc_macro_srv/Cargo.toml
index f08de5fc7..437b8f475 100644
--- a/crates/ra_proc_macro_srv/Cargo.toml
+++ b/crates/ra_proc_macro_srv/Cargo.toml
@@ -12,9 +12,11 @@ doctest = false
12ra_tt = { path = "../ra_tt" } 12ra_tt = { path = "../ra_tt" }
13ra_mbe = { path = "../ra_mbe" } 13ra_mbe = { path = "../ra_mbe" }
14ra_proc_macro = { path = "../ra_proc_macro" } 14ra_proc_macro = { path = "../ra_proc_macro" }
15goblin = "0.2.1"
16libloading = "0.6.0"
15 17
16[dev-dependencies] 18[dev-dependencies]
17cargo_metadata = "0.9.1" 19cargo_metadata = "0.9.1"
18difference = "2.0.0" 20difference = "2.0.0"
19# used as proc macro test target 21# used as proc macro test target
20serde_derive = "=1.0.104" \ No newline at end of file 22serde_derive = "=1.0.104"
diff --git a/crates/ra_proc_macro_srv/src/dylib.rs b/crates/ra_proc_macro_srv/src/dylib.rs
new file mode 100644
index 000000000..525c7ac7b
--- /dev/null
+++ b/crates/ra_proc_macro_srv/src/dylib.rs
@@ -0,0 +1,220 @@
1//! Handles dynamic library loading for proc macro
2
3use crate::{proc_macro::bridge, rustc_server::TokenStream};
4use std::fs::File;
5use std::io::Read;
6use std::path::Path;
7
8use goblin::{mach::Mach, Object};
9use libloading::Library;
10use ra_proc_macro::ProcMacroKind;
11
12static NEW_REGISTRAR_SYMBOL: &str = "__rustc_proc_macro_decls_";
13static _OLD_REGISTRAR_SYMBOL: &str = "__rustc_derive_registrar_";
14
15fn read_bytes(file: &Path) -> Option<Vec<u8>> {
16 let mut fd = File::open(file).ok()?;
17 let mut buffer = Vec::new();
18 fd.read_to_end(&mut buffer).ok()?;
19
20 Some(buffer)
21}
22
23fn get_symbols_from_lib(file: &Path) -> Option<Vec<String>> {
24 let buffer = read_bytes(file)?;
25 let object = Object::parse(&buffer).ok()?;
26
27 return match object {
28 Object::Elf(elf) => {
29 let symbols = elf.dynstrtab.to_vec().ok()?;
30 let names = symbols.iter().map(|s| s.to_string()).collect();
31
32 Some(names)
33 }
34
35 Object::PE(pe) => {
36 let symbol_names =
37 pe.exports.iter().flat_map(|s| s.name).map(|n| n.to_string()).collect();
38 Some(symbol_names)
39 }
40
41 Object::Mach(mach) => match mach {
42 Mach::Binary(binary) => {
43 let exports = binary.exports().ok()?;
44 let names = exports.iter().map(|s| s.name.clone()).collect();
45
46 Some(names)
47 }
48
49 Mach::Fat(_) => None,
50 },
51
52 Object::Archive(_) | Object::Unknown(_) => None,
53 };
54}
55
56fn is_derive_registrar_symbol(symbol: &str) -> bool {
57 symbol.contains(NEW_REGISTRAR_SYMBOL)
58}
59
60fn find_registrar_symbol(file: &Path) -> Option<String> {
61 let symbols = get_symbols_from_lib(file)?;
62
63 symbols.iter().find(|s| is_derive_registrar_symbol(s)).map(|s| s.to_string())
64}
65
66/// Loads dynamic library in platform dependent manner.
67///
68/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
69/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample)
70/// and [here](https://github.com/rust-lang/rust/issues/60593).
71///
72/// Usage of RTLD_DEEPBIND
73/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1)
74///
75/// It seems that on Windows that behaviour is default, so we do nothing in that case.
76#[cfg(windows)]
77fn load_library(file: &Path) -> Result<Library, libloading::Error> {
78 Library::new(file)
79}
80
81#[cfg(unix)]
82fn load_library(file: &Path) -> Result<Library, libloading::Error> {
83 use libloading::os::unix::Library as UnixLibrary;
84 use std::os::raw::c_int;
85
86 const RTLD_NOW: c_int = 0x00002;
87 const RTLD_DEEPBIND: c_int = 0x00008;
88
89 UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into())
90}
91
92struct ProcMacroLibraryLibloading {
93 // Hold the dylib to prevent it for unloadeding
94 #[allow(dead_code)]
95 lib: Library,
96 exported_macros: Vec<bridge::client::ProcMacro>,
97}
98
99impl ProcMacroLibraryLibloading {
100 fn open(file: &Path) -> Result<Self, String> {
101 let symbol_name = find_registrar_symbol(file)
102 .ok_or(format!("Cannot find registrar symbol in file {:?}", file))?;
103
104 let lib = load_library(file).map_err(|e| e.to_string())?;
105
106 let exported_macros = {
107 let macros: libloading::Symbol<&&[bridge::client::ProcMacro]> =
108 unsafe { lib.get(symbol_name.as_bytes()) }.map_err(|e| e.to_string())?;
109
110 macros.to_vec()
111 };
112
113 Ok(ProcMacroLibraryLibloading { lib, exported_macros })
114 }
115}
116
117type ProcMacroLibraryImpl = ProcMacroLibraryLibloading;
118
119pub struct Expander {
120 libs: Vec<ProcMacroLibraryImpl>,
121}
122
123impl Expander {
124 pub fn new<P: AsRef<Path>>(lib: &P) -> Result<Expander, String> {
125 let mut libs = vec![];
126
127 /* Some libraries for dynamic loading require canonicalized path (even when it is
128 already absolute
129 */
130 let lib =
131 lib.as_ref().canonicalize().expect(&format!("Cannot canonicalize {:?}", lib.as_ref()));
132
133 let library = ProcMacroLibraryImpl::open(&lib)?;
134 libs.push(library);
135
136 Ok(Expander { libs })
137 }
138
139 pub fn expand(
140 &self,
141 macro_name: &str,
142 macro_body: &ra_tt::Subtree,
143 attributes: Option<&ra_tt::Subtree>,
144 ) -> Result<ra_tt::Subtree, bridge::PanicMessage> {
145 let parsed_body = TokenStream::with_subtree(macro_body.clone());
146
147 let parsed_attributes = attributes
148 .map_or(crate::rustc_server::TokenStream::new(), |attr| {
149 TokenStream::with_subtree(attr.clone())
150 });
151
152 for lib in &self.libs {
153 for proc_macro in &lib.exported_macros {
154 match proc_macro {
155 bridge::client::ProcMacro::CustomDerive { trait_name, client, .. }
156 if *trait_name == macro_name =>
157 {
158 let res = client.run(
159 &crate::proc_macro::bridge::server::SameThread,
160 crate::rustc_server::Rustc::default(),
161 parsed_body,
162 );
163
164 return res.map(|it| it.subtree);
165 }
166
167 bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => {
168 let res = client.run(
169 &crate::proc_macro::bridge::server::SameThread,
170 crate::rustc_server::Rustc::default(),
171 parsed_body,
172 );
173
174 return res.map(|it| it.subtree);
175 }
176
177 bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => {
178 let res = client.run(
179 &crate::proc_macro::bridge::server::SameThread,
180 crate::rustc_server::Rustc::default(),
181 parsed_attributes,
182 parsed_body,
183 );
184
185 return res.map(|it| it.subtree);
186 }
187
188 _ => {
189 continue;
190 }
191 }
192 }
193 }
194
195 Err(bridge::PanicMessage::String("Nothing to expand".to_string()))
196 }
197
198 pub fn list_macros(&self) -> Result<Vec<(String, ProcMacroKind)>, bridge::PanicMessage> {
199 let mut result = vec![];
200
201 for lib in &self.libs {
202 for proc_macro in &lib.exported_macros {
203 let res = match proc_macro {
204 bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
205 (trait_name.to_string(), ProcMacroKind::CustomDerive)
206 }
207 bridge::client::ProcMacro::Bang { name, .. } => {
208 (name.to_string(), ProcMacroKind::FuncLike)
209 }
210 bridge::client::ProcMacro::Attr { name, .. } => {
211 (name.to_string(), ProcMacroKind::Attr)
212 }
213 };
214 result.push(res);
215 }
216 }
217
218 Ok(result)
219 }
220}
diff --git a/crates/ra_proc_macro_srv/src/lib.rs b/crates/ra_proc_macro_srv/src/lib.rs
index f376df236..f5a526dbf 100644
--- a/crates/ra_proc_macro_srv/src/lib.rs
+++ b/crates/ra_proc_macro_srv/src/lib.rs
@@ -17,6 +17,8 @@ mod proc_macro;
17#[doc(hidden)] 17#[doc(hidden)]
18mod rustc_server; 18mod rustc_server;
19 19
20mod dylib;
21
20use proc_macro::bridge::client::TokenStream; 22use proc_macro::bridge::client::TokenStream;
21use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask}; 23use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
22 24
diff --git a/crates/ra_proc_macro_srv/src/rustc_server.rs b/crates/ra_proc_macro_srv/src/rustc_server.rs
index 92d1fd989..ec0d35692 100644
--- a/crates/ra_proc_macro_srv/src/rustc_server.rs
+++ b/crates/ra_proc_macro_srv/src/rustc_server.rs
@@ -34,6 +34,10 @@ impl TokenStream {
34 TokenStream { subtree: Default::default() } 34 TokenStream { subtree: Default::default() }
35 } 35 }
36 36
37 pub fn with_subtree(subtree: tt::Subtree) -> Self {
38 TokenStream { subtree }
39 }
40
37 pub fn is_empty(&self) -> bool { 41 pub fn is_empty(&self) -> bool {
38 self.subtree.token_trees.is_empty() 42 self.subtree.token_trees.is_empty()
39 } 43 }