diff options
Diffstat (limited to 'crates/ra_proc_macro/src/process.rs')
-rw-r--r-- | crates/ra_proc_macro/src/process.rs | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/crates/ra_proc_macro/src/process.rs b/crates/ra_proc_macro/src/process.rs new file mode 100644 index 000000000..e8c85be38 --- /dev/null +++ b/crates/ra_proc_macro/src/process.rs | |||
@@ -0,0 +1,196 @@ | |||
1 | //! Handle process life-time and message passing for proc-macro client | ||
2 | |||
3 | use crossbeam_channel::{bounded, Receiver, Sender}; | ||
4 | use ra_tt::Subtree; | ||
5 | |||
6 | use crate::msg::{ErrorCode, Message, Request, Response, ResponseError}; | ||
7 | use crate::rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind}; | ||
8 | |||
9 | use io::{BufRead, BufReader}; | ||
10 | use std::{ | ||
11 | convert::{TryFrom, TryInto}, | ||
12 | io::{self, Write}, | ||
13 | path::{Path, PathBuf}, | ||
14 | process::{Child, Command, Stdio}, | ||
15 | sync::{Arc, Weak}, | ||
16 | }; | ||
17 | |||
18 | #[derive(Debug, Default)] | ||
19 | pub(crate) struct ProcMacroProcessSrv { | ||
20 | inner: Option<Weak<Sender<Task>>>, | ||
21 | } | ||
22 | |||
23 | #[derive(Debug)] | ||
24 | pub(crate) struct ProcMacroProcessThread { | ||
25 | // XXX: drop order is significant | ||
26 | sender: Arc<Sender<Task>>, | ||
27 | handle: jod_thread::JoinHandle<()>, | ||
28 | } | ||
29 | |||
30 | struct Task { | ||
31 | req: Request, | ||
32 | result_tx: Sender<Option<Response>>, | ||
33 | } | ||
34 | |||
35 | struct Process { | ||
36 | path: PathBuf, | ||
37 | child: Child, | ||
38 | } | ||
39 | |||
40 | impl Drop for Process { | ||
41 | fn drop(&mut self) { | ||
42 | let _ = self.child.kill(); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | impl Process { | ||
47 | fn run(process_path: &Path) -> Result<Process, io::Error> { | ||
48 | let child = Command::new(process_path.clone()) | ||
49 | .stdin(Stdio::piped()) | ||
50 | .stdout(Stdio::piped()) | ||
51 | .stderr(Stdio::null()) | ||
52 | .spawn()?; | ||
53 | |||
54 | Ok(Process { path: process_path.into(), child }) | ||
55 | } | ||
56 | |||
57 | fn restart(&mut self) -> Result<(), io::Error> { | ||
58 | let _ = self.child.kill(); | ||
59 | self.child = Command::new(self.path.clone()) | ||
60 | .stdin(Stdio::piped()) | ||
61 | .stdout(Stdio::piped()) | ||
62 | .stderr(Stdio::null()) | ||
63 | .spawn()?; | ||
64 | Ok(()) | ||
65 | } | ||
66 | |||
67 | fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> { | ||
68 | let stdin = self.child.stdin.take()?; | ||
69 | let stdout = self.child.stdout.take()?; | ||
70 | let read = BufReader::new(stdout); | ||
71 | |||
72 | Some((stdin, read)) | ||
73 | } | ||
74 | } | ||
75 | |||
76 | impl ProcMacroProcessSrv { | ||
77 | pub fn run( | ||
78 | process_path: &Path, | ||
79 | ) -> Result<(ProcMacroProcessThread, ProcMacroProcessSrv), io::Error> { | ||
80 | let process = Process::run(process_path)?; | ||
81 | |||
82 | let (task_tx, task_rx) = bounded(0); | ||
83 | let handle = jod_thread::spawn(move || { | ||
84 | client_loop(task_rx, process); | ||
85 | }); | ||
86 | |||
87 | let task_tx = Arc::new(task_tx); | ||
88 | let srv = ProcMacroProcessSrv { inner: Some(Arc::downgrade(&task_tx)) }; | ||
89 | let thread = ProcMacroProcessThread { handle, sender: task_tx }; | ||
90 | |||
91 | Ok((thread, srv)) | ||
92 | } | ||
93 | |||
94 | pub fn find_proc_macros( | ||
95 | &self, | ||
96 | dylib_path: &Path, | ||
97 | ) -> Result<Vec<(String, ProcMacroKind)>, ra_tt::ExpansionError> { | ||
98 | let task = ListMacrosTask { lib: dylib_path.to_path_buf() }; | ||
99 | |||
100 | let result: ListMacrosResult = self.send_task(Request::ListMacro(task))?; | ||
101 | Ok(result.macros) | ||
102 | } | ||
103 | |||
104 | pub fn custom_derive( | ||
105 | &self, | ||
106 | dylib_path: &Path, | ||
107 | subtree: &Subtree, | ||
108 | derive_name: &str, | ||
109 | ) -> Result<Subtree, ra_tt::ExpansionError> { | ||
110 | let task = ExpansionTask { | ||
111 | macro_body: subtree.clone(), | ||
112 | macro_name: derive_name.to_string(), | ||
113 | attributes: None, | ||
114 | lib: dylib_path.to_path_buf(), | ||
115 | }; | ||
116 | |||
117 | let result: ExpansionResult = self.send_task(Request::ExpansionMacro(task))?; | ||
118 | Ok(result.expansion) | ||
119 | } | ||
120 | |||
121 | pub fn send_task<R>(&self, req: Request) -> Result<R, ra_tt::ExpansionError> | ||
122 | where | ||
123 | R: TryFrom<Response, Error = &'static str>, | ||
124 | { | ||
125 | let sender = match &self.inner { | ||
126 | None => return Err(ra_tt::ExpansionError::Unknown("No sender is found.".to_string())), | ||
127 | Some(it) => it, | ||
128 | }; | ||
129 | |||
130 | let (result_tx, result_rx) = bounded(0); | ||
131 | let sender = match sender.upgrade() { | ||
132 | None => { | ||
133 | return Err(ra_tt::ExpansionError::Unknown("Proc macro process is closed.".into())) | ||
134 | } | ||
135 | Some(it) => it, | ||
136 | }; | ||
137 | sender.send(Task { req: req.into(), result_tx }).unwrap(); | ||
138 | let res = result_rx | ||
139 | .recv() | ||
140 | .map_err(|_| ra_tt::ExpansionError::Unknown("Proc macro thread is closed.".into()))?; | ||
141 | |||
142 | match res { | ||
143 | Some(Response::Error(err)) => { | ||
144 | return Err(ra_tt::ExpansionError::ExpansionError(err.message)); | ||
145 | } | ||
146 | Some(res) => Ok(res.try_into().map_err(|err| { | ||
147 | ra_tt::ExpansionError::Unknown(format!( | ||
148 | "Fail to get response, reason : {:#?} ", | ||
149 | err | ||
150 | )) | ||
151 | })?), | ||
152 | None => Err(ra_tt::ExpansionError::Unknown("Empty result".into())), | ||
153 | } | ||
154 | } | ||
155 | } | ||
156 | |||
157 | fn client_loop(task_rx: Receiver<Task>, mut process: Process) { | ||
158 | let (mut stdin, mut stdout) = match process.stdio() { | ||
159 | None => return, | ||
160 | Some(it) => it, | ||
161 | }; | ||
162 | |||
163 | for task in task_rx { | ||
164 | let Task { req, result_tx } = task; | ||
165 | |||
166 | match send_request(&mut stdin, &mut stdout, req) { | ||
167 | Ok(res) => result_tx.send(res).unwrap(), | ||
168 | Err(_err) => { | ||
169 | let res = Response::Error(ResponseError { | ||
170 | code: ErrorCode::ServerErrorEnd, | ||
171 | message: "Server closed".into(), | ||
172 | }); | ||
173 | result_tx.send(res.into()).unwrap(); | ||
174 | // Restart the process | ||
175 | if process.restart().is_err() { | ||
176 | break; | ||
177 | } | ||
178 | let stdio = match process.stdio() { | ||
179 | None => break, | ||
180 | Some(it) => it, | ||
181 | }; | ||
182 | stdin = stdio.0; | ||
183 | stdout = stdio.1; | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | } | ||
188 | |||
189 | fn send_request( | ||
190 | mut writer: &mut impl Write, | ||
191 | mut reader: &mut impl BufRead, | ||
192 | req: Request, | ||
193 | ) -> Result<Option<Response>, io::Error> { | ||
194 | req.write(&mut writer)?; | ||
195 | Ok(Response::read(&mut reader)?) | ||
196 | } | ||