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