aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/proc_macro_api/Cargo.toml2
-rw-r--r--crates/proc_macro_api/src/lib.rs82
2 files changed, 74 insertions, 10 deletions
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"
19tt = { path = "../tt", version = "0.0.0" } 19tt = { path = "../tt", version = "0.0.0" }
20base_db = { path = "../base_db", version = "0.0.0" } 20base_db = { path = "../base_db", version = "0.0.0" }
21stdx = { path = "../stdx", version = "0.0.0" } 21stdx = { path = "../stdx", version = "0.0.0" }
22snap = "1"
23object = "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
8mod rpc;
9mod process;
10pub mod msg; 8pub mod msg;
9mod process;
10mod rpc;
11 11
12use std::{ 12use 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
19use base_db::{Env, ProcMacro}; 14use base_db::{Env, ProcMacro};
20use tt::{SmolStr, Subtree}; 15use tt::{SmolStr, Subtree};
@@ -23,6 +18,9 @@ use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread};
23 18
24pub use rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind}; 19pub use rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind};
25 20
21use object::read::{File as BinaryFile, Object, ObjectSection};
22use snap::read::FrameDecoder as SnapDecoder;
23
26#[derive(Debug, Clone)] 24#[derive(Debug, Clone)]
27struct ProcMacroProcessExpander { 25struct 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}