2020-03-18 07:56:46 -05:00
//! Client-side Proc-Macro crate
//!
//! We separate proc-macro expanding logic to an extern program to allow
//! different implementations (e.g. wasm or dylib loading). And this crate
2020-04-20 13:26:10 -05:00
//! is used to provide basic infrastructure for communication between two
2020-03-25 21:49:23 -05:00
//! processes: Client (RA itself), Server (the external program)
2020-03-18 07:56:46 -05:00
2020-03-26 15:26:34 -05:00
pub mod msg ;
2020-12-11 08:14:42 -06:00
mod process ;
mod rpc ;
2020-03-26 15:26:34 -05:00
2020-12-11 07:57:50 -06:00
use base_db ::{ Env , ProcMacro } ;
2020-12-23 07:24:53 -06:00
use std ::{
ffi ::OsStr ,
fs ::File ,
io ::{ self , Read } ,
path ::{ Path , PathBuf } ,
sync ::Arc ,
} ;
2020-08-12 09:46:20 -05:00
use tt ::{ SmolStr , Subtree } ;
use crate ::process ::{ ProcMacroProcessSrv , ProcMacroProcessThread } ;
2020-04-01 00:11:26 -05:00
pub use rpc ::{ ExpansionResult , ExpansionTask , ListMacrosResult , ListMacrosTask , ProcMacroKind } ;
2020-03-26 15:26:34 -05:00
2020-12-23 07:24:53 -06:00
use memmap ::Mmap ;
2020-12-11 08:14:42 -06:00
use object ::read ::{ File as BinaryFile , Object , ObjectSection } ;
use snap ::read ::FrameDecoder as SnapDecoder ;
2020-03-26 15:26:34 -05:00
#[ derive(Debug, Clone) ]
2020-12-07 07:11:17 -06:00
struct ProcMacroProcessExpander {
2020-03-25 21:49:23 -05:00
process : Arc < ProcMacroProcessSrv > ,
2020-03-26 15:26:34 -05:00
dylib_path : PathBuf ,
2020-03-26 11:41:44 -05:00
name : SmolStr ,
2020-03-18 07:56:46 -05:00
}
2020-03-26 15:26:34 -05:00
impl Eq for ProcMacroProcessExpander { }
impl PartialEq for ProcMacroProcessExpander {
fn eq ( & self , other : & Self ) -> bool {
self . name = = other . name
& & self . dylib_path = = other . dylib_path
& & Arc ::ptr_eq ( & self . process , & other . process )
}
}
2020-12-11 07:24:02 -06:00
impl base_db ::ProcMacroExpander for ProcMacroProcessExpander {
2020-03-26 11:41:44 -05:00
fn expand (
2020-03-18 07:56:46 -05:00
& self ,
2020-03-26 15:26:34 -05:00
subtree : & Subtree ,
2020-12-07 06:55:41 -06:00
attr : Option < & Subtree > ,
2020-12-11 07:57:50 -06:00
env : & Env ,
2020-08-12 09:46:20 -05:00
) -> Result < Subtree , tt ::ExpansionError > {
2020-12-07 06:55:41 -06:00
let task = ExpansionTask {
macro_body : subtree . clone ( ) ,
macro_name : self . name . to_string ( ) ,
attributes : attr . cloned ( ) ,
lib : self . dylib_path . to_path_buf ( ) ,
2020-12-11 07:57:50 -06:00
env : env . iter ( ) . map ( | ( k , v ) | ( k . to_string ( ) , v . to_string ( ) ) ) . collect ( ) ,
2020-12-07 06:55:41 -06:00
} ;
let result : ExpansionResult = self . process . send_task ( msg ::Request ::ExpansionMacro ( task ) ) ? ;
Ok ( result . expansion )
2020-03-18 07:56:46 -05:00
}
}
2020-03-26 23:55:58 -05:00
#[ derive(Debug) ]
2020-03-26 15:26:34 -05:00
pub struct ProcMacroClient {
2020-12-07 10:16:50 -06:00
process : Arc < ProcMacroProcessSrv > ,
thread : ProcMacroProcessThread ,
2020-03-26 15:26:34 -05:00
}
2020-03-18 07:56:46 -05:00
impl ProcMacroClient {
2020-04-20 13:26:10 -05:00
pub fn extern_process (
process_path : PathBuf ,
args : impl IntoIterator < Item = impl AsRef < OsStr > > ,
) -> io ::Result < ProcMacroClient > {
2020-04-16 08:13:57 -05:00
let ( thread , process ) = ProcMacroProcessSrv ::run ( process_path , args ) ? ;
2020-12-11 08:14:42 -06:00
Ok ( ProcMacroClient {
process : Arc ::new ( process ) ,
thread ,
} )
2020-03-18 07:56:46 -05:00
}
2020-12-07 10:06:14 -06:00
pub fn by_dylib_path ( & self , dylib_path : & Path ) -> Vec < ProcMacro > {
2020-12-07 10:16:50 -06:00
let macros = match self . process . find_proc_macros ( dylib_path ) {
Err ( err ) = > {
eprintln! ( " Failed to find proc macros. Error: {:#?} " , err ) ;
return vec! [ ] ;
2020-03-26 15:26:34 -05:00
}
2020-12-07 10:16:50 -06:00
Ok ( macros ) = > macros ,
} ;
macros
. into_iter ( )
. map ( | ( name , kind ) | {
let name = SmolStr ::new ( & name ) ;
let kind = match kind {
ProcMacroKind ::CustomDerive = > base_db ::ProcMacroKind ::CustomDerive ,
ProcMacroKind ::FuncLike = > base_db ::ProcMacroKind ::FuncLike ,
ProcMacroKind ::Attr = > base_db ::ProcMacroKind ::Attr ,
} ;
2020-12-11 07:24:02 -06:00
let expander = Arc ::new ( ProcMacroProcessExpander {
2020-12-07 10:16:50 -06:00
process : self . process . clone ( ) ,
name : name . clone ( ) ,
dylib_path : dylib_path . into ( ) ,
} ) ;
2020-12-11 08:14:42 -06:00
ProcMacro {
name ,
kind ,
expander ,
}
2020-12-07 10:16:50 -06:00
} )
. collect ( )
2020-03-18 04:47:59 -05:00
}
2020-12-11 08:14:42 -06:00
// This is used inside self.read_version() to locate the ".rustc" section
// from a proc macro crate's binary file.
2020-12-23 07:24:53 -06:00
fn read_section < ' a > ( & self , dylib_binary : & ' a [ u8 ] , section_name : & str ) -> io ::Result < & ' a [ u8 ] > {
2020-12-11 08:14:42 -06:00
BinaryFile ::parse ( dylib_binary )
2020-12-23 07:24:53 -06:00
. map_err ( | e | io ::Error ::new ( io ::ErrorKind ::InvalidData , e ) ) ?
2020-12-11 08:14:42 -06:00
. section_by_name ( section_name )
2020-12-23 07:24:53 -06:00
. ok_or_else ( | | io ::Error ::new ( io ::ErrorKind ::InvalidData , " section read error " ) ) ?
2020-12-11 08:14:42 -06:00
. data ( )
2020-12-23 07:24:53 -06:00
. map_err ( | e | io ::Error ::new ( io ::ErrorKind ::InvalidData , e ) )
2020-12-11 08:14:42 -06:00
}
// Check the version of rustc that was used to compile a proc macro crate's
// binary file.
// A proc macro crate binary's ".rustc" section has following byte layout:
// * [b'r',b'u',b's',b't',0,0,0,5] is the first 8 bytes
// * ff060000 734e6150 is followed, it's the snappy format magic bytes,
// means bytes from here(including this sequence) are compressed in
// snappy compression format. Version info is here inside, so decompress
// this.
// The bytes you get after decompressing the snappy format portion has
// following layout:
// * [b'r',b'u',b's',b't',0,0,0,5] is the first 8 bytes(again)
// * [crate root bytes] next 4 bytes is to store crate root position,
// according to rustc's source code comment
// * [length byte] next 1 byte tells us how many bytes we should read next
// for the version string's utf8 bytes
// * [version string bytes encoded in utf8] <- GET THIS BOI
// * [some more bytes that we don really care but still there] :-)
// Check this issue for more about the bytes layout:
// https://github.com/rust-analyzer/rust-analyzer/issues/6174
2020-12-23 07:24:53 -06:00
#[ allow(unused) ]
fn read_version ( & self , dylib_path : & Path ) -> io ::Result < String > {
let dylib_file = File ::open ( dylib_path ) ? ;
let dylib_mmaped = unsafe { Mmap ::map ( & dylib_file ) } ? ;
let dot_rustc = self . read_section ( & dylib_mmaped , " .rustc " ) ? ;
2020-12-11 08:14:42 -06:00
2020-12-23 07:24:53 -06:00
let header = & dot_rustc [ .. 8 ] ;
const EXPECTED_HEADER : [ u8 ; 8 ] = [ b 'r' , b 'u' , b 's' , b 't' , 0 , 0 , 0 , 5 ] ;
// check if header is valid
if ! ( header = = EXPECTED_HEADER ) {
return Err ( io ::Error ::new ( io ::ErrorKind ::InvalidData , format! ( " .rustc section should start with header {:?} ; header {:?} is actually presented. " , EXPECTED_HEADER , header ) ) ) ;
}
2020-12-11 08:14:42 -06:00
let snappy_portion = & dot_rustc [ 8 .. ] ;
let mut snappy_decoder = SnapDecoder ::new ( snappy_portion ) ;
// the bytes before version string bytes, so this basically is:
// 8 bytes for [b'r',b'u',b's',b't',0,0,0,5]
// 4 bytes for [crate root bytes]
// 1 byte for length of version string
// so 13 bytes in total, and we should check the 13th byte
// to know the length
let mut bytes_before_version = [ 0 u8 ; 13 ] ;
2020-12-23 07:24:53 -06:00
snappy_decoder . read_exact ( & mut bytes_before_version ) ? ;
2020-12-11 08:14:42 -06:00
let length = bytes_before_version [ 12 ] ; // what? can't use -1 indexing?
let mut version_string_utf8 = vec! [ 0 u8 ; length as usize ] ;
2020-12-23 07:24:53 -06:00
snappy_decoder . read_exact ( & mut version_string_utf8 ) ? ;
let version_string = String ::from_utf8 ( version_string_utf8 ) ;
version_string . map_err ( | e | io ::Error ::new ( io ::ErrorKind ::InvalidData , e ) )
2020-12-11 08:14:42 -06:00
}
2020-03-18 04:47:59 -05:00
}