5
5
//! is used to provide basic infrastructure for communication between two
6
6
//! processes: Client (RA itself), Server (the external program)
7
7
8
- mod rpc;
9
- mod process;
10
8
pub mod msg;
9
+ mod process;
10
+ mod rpc;
11
11
12
- use std:: {
13
- ffi:: OsStr ,
14
- io,
15
- path:: { Path , PathBuf } ,
16
- sync:: Arc ,
17
- } ;
12
+ use std:: { ffi:: OsStr , fs:: read as fsread, io:: { self , Read } , path:: { Path , PathBuf } , sync:: Arc } ;
18
13
19
14
use base_db:: { Env , ProcMacro } ;
20
15
use tt:: { SmolStr , Subtree } ;
@@ -23,6 +18,9 @@ use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread};
23
18
24
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
24
#[ derive( Debug , Clone ) ]
27
25
struct ProcMacroProcessExpander {
28
26
process : Arc < ProcMacroProcessSrv > ,
@@ -71,7 +69,10 @@ impl ProcMacroClient {
71
69
args : impl IntoIterator < Item = impl AsRef < OsStr > > ,
72
70
) -> io:: Result < ProcMacroClient > {
73
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
78
pub fn by_dylib_path ( & self , dylib_path : & Path ) -> Vec < ProcMacro > {
@@ -98,8 +99,69 @@ impl ProcMacroClient {
98
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
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
}
0 commit comments