diff options
author | Jay Somedon <[email protected]> | 2020-12-11 14:14:42 +0000 |
---|---|---|
committer | Edwin Cheng <[email protected]> | 2021-03-04 01:05:23 +0000 |
commit | 6608acef719ea201d8d6ce9df7d8907aa770c959 (patch) | |
tree | 77492b99ecc7dc57b34b4d2a9e4a9fd7f8a908d7 | |
parent | 7275750e42103b5877faeb0fcaff30c9f8a92ea1 (diff) |
Read version of rustc that compiled proc macro
With Jay Somedon <[email protected]>
-rw-r--r-- | Cargo.lock | 28 | ||||
-rw-r--r-- | crates/proc_macro_api/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/proc_macro_api/src/lib.rs | 82 |
3 files changed, 100 insertions, 12 deletions
diff --git a/Cargo.lock b/Cargo.lock index 799127891..b61fd2e98 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -80,7 +80,7 @@ dependencies = [ | |||
80 | "cfg-if", | 80 | "cfg-if", |
81 | "libc", | 81 | "libc", |
82 | "miniz_oxide", | 82 | "miniz_oxide", |
83 | "object", | 83 | "object 0.23.0", |
84 | "rustc-demangle", | 84 | "rustc-demangle", |
85 | ] | 85 | ] |
86 | 86 | ||
@@ -1010,6 +1010,16 @@ dependencies = [ | |||
1010 | 1010 | ||
1011 | [[package]] | 1011 | [[package]] |
1012 | name = "object" | 1012 | name = "object" |
1013 | version = "0.22.0" | ||
1014 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1015 | checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" | ||
1016 | dependencies = [ | ||
1017 | "flate2", | ||
1018 | "wasmparser", | ||
1019 | ] | ||
1020 | |||
1021 | [[package]] | ||
1022 | name = "object" | ||
1013 | version = "0.23.0" | 1023 | version = "0.23.0" |
1014 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1024 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1015 | checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" | 1025 | checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" |
@@ -1154,8 +1164,10 @@ dependencies = [ | |||
1154 | "crossbeam-channel", | 1164 | "crossbeam-channel", |
1155 | "jod-thread", | 1165 | "jod-thread", |
1156 | "log", | 1166 | "log", |
1167 | "object 0.22.0", | ||
1157 | "serde", | 1168 | "serde", |
1158 | "serde_json", | 1169 | "serde_json", |
1170 | "snap", | ||
1159 | "stdx", | 1171 | "stdx", |
1160 | "tt", | 1172 | "tt", |
1161 | ] | 1173 | ] |
@@ -1168,7 +1180,7 @@ dependencies = [ | |||
1168 | "libloading", | 1180 | "libloading", |
1169 | "mbe", | 1181 | "mbe", |
1170 | "memmap2", | 1182 | "memmap2", |
1171 | "object", | 1183 | "object 0.23.0", |
1172 | "proc_macro_api", | 1184 | "proc_macro_api", |
1173 | "proc_macro_test", | 1185 | "proc_macro_test", |
1174 | "serde_derive", | 1186 | "serde_derive", |
@@ -1541,6 +1553,12 @@ dependencies = [ | |||
1541 | ] | 1553 | ] |
1542 | 1554 | ||
1543 | [[package]] | 1555 | [[package]] |
1556 | name = "snap" | ||
1557 | version = "1.0.4" | ||
1558 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1559 | checksum = "dc725476a1398f0480d56cd0ad381f6f32acf2642704456f8f59a35df464b59a" | ||
1560 | |||
1561 | [[package]] | ||
1544 | name = "socket2" | 1562 | name = "socket2" |
1545 | version = "0.3.19" | 1563 | version = "0.3.19" |
1546 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1564 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1881,6 +1899,12 @@ dependencies = [ | |||
1881 | ] | 1899 | ] |
1882 | 1900 | ||
1883 | [[package]] | 1901 | [[package]] |
1902 | name = "wasmparser" | ||
1903 | version = "0.57.0" | ||
1904 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1905 | checksum = "32fddd575d477c6e9702484139cf9f23dcd554b06d185ed0f56c857dd3a47aa6" | ||
1906 | |||
1907 | [[package]] | ||
1884 | name = "winapi" | 1908 | name = "winapi" |
1885 | version = "0.3.9" | 1909 | version = "0.3.9" |
1886 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1910 | source = "registry+https://github.com/rust-lang/crates.io-index" |
diff --git a/crates/proc_macro_api/Cargo.toml b/crates/proc_macro_api/Cargo.toml index f09726223..a72ae236a 100644 --- a/crates/proc_macro_api/Cargo.toml +++ b/crates/proc_macro_api/Cargo.toml | |||
@@ -19,3 +19,5 @@ jod-thread = "0.1.1" | |||
19 | tt = { path = "../tt", version = "0.0.0" } | 19 | tt = { path = "../tt", version = "0.0.0" } |
20 | base_db = { path = "../base_db", version = "0.0.0" } | 20 | base_db = { path = "../base_db", version = "0.0.0" } |
21 | stdx = { path = "../stdx", version = "0.0.0" } | 21 | stdx = { path = "../stdx", version = "0.0.0" } |
22 | snap = "1" | ||
23 | object = "0.22.0" | ||
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 | } |