diff options
-rw-r--r-- | Cargo.lock | 48 | ||||
-rw-r--r-- | crates/ra_proc_macro_srv/Cargo.toml | 4 | ||||
-rw-r--r-- | crates/ra_proc_macro_srv/src/dylib.rs | 220 | ||||
-rw-r--r-- | crates/ra_proc_macro_srv/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_proc_macro_srv/src/rustc_server.rs | 4 |
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]] |
428 | name = "goblin" | ||
429 | version = "0.2.1" | ||
430 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
431 | checksum = "ddd5e3132801a1ac34ac53b97acde50c4685414dd2f291b9ea52afa6f07468c8" | ||
432 | dependencies = [ | ||
433 | "log", | ||
434 | "plain", | ||
435 | "scroll", | ||
436 | ] | ||
437 | |||
438 | [[package]] | ||
428 | name = "heck" | 439 | name = "heck" |
429 | version = "0.3.1" | 440 | version = "0.3.1" |
430 | source = "registry+https://github.com/rust-lang/crates.io-index" | 441 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -587,6 +598,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
587 | checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" | 598 | checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" |
588 | 599 | ||
589 | [[package]] | 600 | [[package]] |
601 | name = "libloading" | ||
602 | version = "0.6.0" | ||
603 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
604 | checksum = "2c979a19ffb457f0273965c333053f3d586bf759bf7b683fbebc37f9a9ebedc4" | ||
605 | dependencies = [ | ||
606 | "winapi 0.3.8", | ||
607 | ] | ||
608 | |||
609 | [[package]] | ||
590 | name = "linked-hash-map" | 610 | name = "linked-hash-map" |
591 | version = "0.5.2" | 611 | version = "0.5.2" |
592 | source = "registry+https://github.com/rust-lang/crates.io-index" | 612 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -826,6 +846,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
826 | checksum = "3ad1f1b834a05d42dae330066e9699a173b28185b3bdc3dbf14ca239585de8cc" | 846 | checksum = "3ad1f1b834a05d42dae330066e9699a173b28185b3bdc3dbf14ca239585de8cc" |
827 | 847 | ||
828 | [[package]] | 848 | [[package]] |
849 | name = "plain" | ||
850 | version = "0.2.3" | ||
851 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
852 | checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" | ||
853 | |||
854 | [[package]] | ||
829 | name = "ppv-lite86" | 855 | name = "ppv-lite86" |
830 | version = "0.2.6" | 856 | version = "0.2.6" |
831 | source = "registry+https://github.com/rust-lang/crates.io-index" | 857 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1081,6 +1107,8 @@ version = "0.1.0" | |||
1081 | dependencies = [ | 1107 | dependencies = [ |
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" | |||
1397 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" | 1425 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" |
1398 | 1426 | ||
1399 | [[package]] | 1427 | [[package]] |
1428 | name = "scroll" | ||
1429 | version = "0.10.1" | ||
1430 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1431 | checksum = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1" | ||
1432 | dependencies = [ | ||
1433 | "scroll_derive", | ||
1434 | ] | ||
1435 | |||
1436 | [[package]] | ||
1437 | name = "scroll_derive" | ||
1438 | version = "0.10.1" | ||
1439 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1440 | checksum = "f8584eea9b9ff42825b46faf46a8c24d2cff13ec152fa2a50df788b87c07ee28" | ||
1441 | dependencies = [ | ||
1442 | "proc-macro2", | ||
1443 | "quote", | ||
1444 | "syn", | ||
1445 | ] | ||
1446 | |||
1447 | [[package]] | ||
1400 | name = "semver" | 1448 | name = "semver" |
1401 | version = "0.9.0" | 1449 | version = "0.9.0" |
1402 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1450 | source = "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 | |||
12 | ra_tt = { path = "../ra_tt" } | 12 | ra_tt = { path = "../ra_tt" } |
13 | ra_mbe = { path = "../ra_mbe" } | 13 | ra_mbe = { path = "../ra_mbe" } |
14 | ra_proc_macro = { path = "../ra_proc_macro" } | 14 | ra_proc_macro = { path = "../ra_proc_macro" } |
15 | goblin = "0.2.1" | ||
16 | libloading = "0.6.0" | ||
15 | 17 | ||
16 | [dev-dependencies] | 18 | [dev-dependencies] |
17 | cargo_metadata = "0.9.1" | 19 | cargo_metadata = "0.9.1" |
18 | difference = "2.0.0" | 20 | difference = "2.0.0" |
19 | # used as proc macro test target | 21 | # used as proc macro test target |
20 | serde_derive = "=1.0.104" \ No newline at end of file | 22 | serde_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 | |||
3 | use crate::{proc_macro::bridge, rustc_server::TokenStream}; | ||
4 | use std::fs::File; | ||
5 | use std::io::Read; | ||
6 | use std::path::Path; | ||
7 | |||
8 | use goblin::{mach::Mach, Object}; | ||
9 | use libloading::Library; | ||
10 | use ra_proc_macro::ProcMacroKind; | ||
11 | |||
12 | static NEW_REGISTRAR_SYMBOL: &str = "__rustc_proc_macro_decls_"; | ||
13 | static _OLD_REGISTRAR_SYMBOL: &str = "__rustc_derive_registrar_"; | ||
14 | |||
15 | fn 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 | |||
23 | fn 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 | |||
56 | fn is_derive_registrar_symbol(symbol: &str) -> bool { | ||
57 | symbol.contains(NEW_REGISTRAR_SYMBOL) | ||
58 | } | ||
59 | |||
60 | fn 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)] | ||
77 | fn load_library(file: &Path) -> Result<Library, libloading::Error> { | ||
78 | Library::new(file) | ||
79 | } | ||
80 | |||
81 | #[cfg(unix)] | ||
82 | fn 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 | |||
92 | struct 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 | |||
99 | impl 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 | |||
117 | type ProcMacroLibraryImpl = ProcMacroLibraryLibloading; | ||
118 | |||
119 | pub struct Expander { | ||
120 | libs: Vec<ProcMacroLibraryImpl>, | ||
121 | } | ||
122 | |||
123 | impl 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)] |
18 | mod rustc_server; | 18 | mod rustc_server; |
19 | 19 | ||
20 | mod dylib; | ||
21 | |||
20 | use proc_macro::bridge::client::TokenStream; | 22 | use proc_macro::bridge::client::TokenStream; |
21 | use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask}; | 23 | use 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 | } |