diff options
Diffstat (limited to 'crates/proc_macro_api/src')
-rw-r--r-- | crates/proc_macro_api/src/lib.rs | 82 |
1 files changed, 72 insertions, 10 deletions
diff --git a/crates/proc_macro_api/src/lib.rs b/crates/proc_macro_api/src/lib.rs index 2ea456fb0..cec854746 100644 --- a/crates/proc_macro_api/src/lib.rs +++ b/crates/proc_macro_api/src/lib.rs | |||
@@ -5,16 +5,11 @@ | |||
5 | //! is used to provide basic infrastructure for communication between two | 5 | //! is used to provide basic infrastructure for communication between two |
6 | //! processes: Client (RA itself), Server (the external program) | 6 | //! processes: Client (RA itself), Server (the external program) |
7 | 7 | ||
8 | mod rpc; | ||
9 | mod process; | ||
10 | pub mod msg; | 8 | pub mod msg; |
9 | mod process; | ||
10 | mod rpc; | ||
11 | 11 | ||
12 | use std::{ | 12 | use std::{ffi::OsStr, fs::read as fsread, io::{self, Read}, path::{Path, PathBuf}, sync::Arc}; |
13 | ffi::OsStr, | ||
14 | io, | ||
15 | path::{Path, PathBuf}, | ||
16 | sync::Arc, | ||
17 | }; | ||
18 | 13 | ||
19 | use base_db::{Env, ProcMacro}; | 14 | use base_db::{Env, ProcMacro}; |
20 | use tt::{SmolStr, Subtree}; | 15 | use tt::{SmolStr, Subtree}; |
@@ -23,6 +18,9 @@ use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread}; | |||
23 | 18 | ||
24 | pub use rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind}; | 19 | pub use rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind}; |
25 | 20 | ||
21 | use object::read::{File as BinaryFile, Object, ObjectSection}; | ||
22 | use snap::read::FrameDecoder as SnapDecoder; | ||
23 | |||
26 | #[derive(Debug, Clone)] | 24 | #[derive(Debug, Clone)] |
27 | struct ProcMacroProcessExpander { | 25 | struct ProcMacroProcessExpander { |
28 | process: Arc<ProcMacroProcessSrv>, | 26 | process: Arc<ProcMacroProcessSrv>, |
@@ -71,7 +69,10 @@ impl ProcMacroClient { | |||
71 | args: impl IntoIterator<Item = impl AsRef<OsStr>>, | 69 | args: impl IntoIterator<Item = impl AsRef<OsStr>>, |
72 | ) -> io::Result<ProcMacroClient> { | 70 | ) -> io::Result<ProcMacroClient> { |
73 | let (thread, process) = ProcMacroProcessSrv::run(process_path, args)?; | 71 | let (thread, process) = ProcMacroProcessSrv::run(process_path, args)?; |
74 | Ok(ProcMacroClient { process: Arc::new(process), thread }) | 72 | Ok(ProcMacroClient { |
73 | process: Arc::new(process), | ||
74 | thread, | ||
75 | }) | ||
75 | } | 76 | } |
76 | 77 | ||
77 | pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<ProcMacro> { | 78 | pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<ProcMacro> { |
@@ -98,8 +99,69 @@ impl ProcMacroClient { | |||
98 | dylib_path: dylib_path.into(), | 99 | dylib_path: dylib_path.into(), |
99 | }); | 100 | }); |
100 | 101 | ||
101 | ProcMacro { name, kind, expander } | 102 | ProcMacro { |
103 | name, | ||
104 | kind, | ||
105 | expander, | ||
106 | } | ||
102 | }) | 107 | }) |
103 | .collect() | 108 | .collect() |
104 | } | 109 | } |
110 | |||
111 | // This is used inside self.read_version() to locate the ".rustc" section | ||
112 | // from a proc macro crate's binary file. | ||
113 | fn read_section<'a>(&self, dylib_binary: &'a [u8], section_name: &str) -> &'a [u8] { | ||
114 | BinaryFile::parse(dylib_binary) | ||
115 | .unwrap() | ||
116 | .section_by_name(section_name) | ||
117 | .unwrap() | ||
118 | .data() | ||
119 | .unwrap() | ||
120 | } | ||
121 | |||
122 | // Check the version of rustc that was used to compile a proc macro crate's | ||
123 | // binary file. | ||
124 | // A proc macro crate binary's ".rustc" section has following byte layout: | ||
125 | // * [b'r',b'u',b's',b't',0,0,0,5] is the first 8 bytes | ||
126 | // * ff060000 734e6150 is followed, it's the snappy format magic bytes, | ||
127 | // means bytes from here(including this sequence) are compressed in | ||
128 | // snappy compression format. Version info is here inside, so decompress | ||
129 | // this. | ||
130 | // The bytes you get after decompressing the snappy format portion has | ||
131 | // following layout: | ||
132 | // * [b'r',b'u',b's',b't',0,0,0,5] is the first 8 bytes(again) | ||
133 | // * [crate root bytes] next 4 bytes is to store crate root position, | ||
134 | // according to rustc's source code comment | ||
135 | // * [length byte] next 1 byte tells us how many bytes we should read next | ||
136 | // for the version string's utf8 bytes | ||
137 | // * [version string bytes encoded in utf8] <- GET THIS BOI | ||
138 | // * [some more bytes that we don really care but still there] :-) | ||
139 | // Check this issue for more about the bytes layout: | ||
140 | // https://github.com/rust-analyzer/rust-analyzer/issues/6174 | ||
141 | fn read_version(&self, dylib_path: &Path) -> String { | ||
142 | let dylib_binary = fsread(dylib_path).unwrap(); | ||
143 | |||
144 | let dot_rustc = self.read_section(&dylib_binary, ".rustc"); | ||
145 | |||
146 | let snappy_portion = &dot_rustc[8..]; | ||
147 | |||
148 | let mut snappy_decoder = SnapDecoder::new(snappy_portion); | ||
149 | |||
150 | // the bytes before version string bytes, so this basically is: | ||
151 | // 8 bytes for [b'r',b'u',b's',b't',0,0,0,5] | ||
152 | // 4 bytes for [crate root bytes] | ||
153 | // 1 byte for length of version string | ||
154 | // so 13 bytes in total, and we should check the 13th byte | ||
155 | // to know the length | ||
156 | let mut bytes_before_version = [0u8; 13]; | ||
157 | snappy_decoder | ||
158 | .read_exact(&mut bytes_before_version) | ||
159 | .unwrap(); | ||
160 | let length = bytes_before_version[12]; // what? can't use -1 indexing? | ||
161 | |||
162 | let mut version_string_utf8 = vec![0u8; length as usize]; | ||
163 | snappy_decoder.read_exact(&mut version_string_utf8).unwrap(); | ||
164 | let version_string = String::from_utf8(version_string_utf8).unwrap(); | ||
165 | version_string | ||
166 | } | ||
105 | } | 167 | } |