aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_proc_macro_srv/src/proc_macro/bridge/client.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_proc_macro_srv/src/proc_macro/bridge/client.rs')
-rw-r--r--crates/ra_proc_macro_srv/src/proc_macro/bridge/client.rs472
1 files changed, 472 insertions, 0 deletions
diff --git a/crates/ra_proc_macro_srv/src/proc_macro/bridge/client.rs b/crates/ra_proc_macro_srv/src/proc_macro/bridge/client.rs
new file mode 100644
index 000000000..4b5dc7fd0
--- /dev/null
+++ b/crates/ra_proc_macro_srv/src/proc_macro/bridge/client.rs
@@ -0,0 +1,472 @@
1//! lib-proc-macro Client-side types.
2//!
3//! Copy from https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/client.rs
4//! augmented with removing unstable features
5
6use super::*;
7
8macro_rules! define_handles {
9 (
10 'owned: $($oty:ident,)*
11 'interned: $($ity:ident,)*
12 ) => {
13 #[repr(C)]
14 #[allow(non_snake_case)]
15 pub struct HandleCounters {
16 $($oty: AtomicUsize,)*
17 $($ity: AtomicUsize,)*
18 }
19
20 impl HandleCounters {
21 // FIXME(eddyb) use a reference to the `static COUNTERS`, intead of
22 // a wrapper `fn` pointer, once `const fn` can reference `static`s.
23 extern "C" fn get() -> &'static Self {
24 static COUNTERS: HandleCounters = HandleCounters {
25 $($oty: AtomicUsize::new(1),)*
26 $($ity: AtomicUsize::new(1),)*
27 };
28 &COUNTERS
29 }
30 }
31
32 // FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`.
33 #[repr(C)]
34 #[allow(non_snake_case)]
35 pub(super) struct HandleStore<S: server::Types> {
36 $($oty: handle::OwnedStore<S::$oty>,)*
37 $($ity: handle::InternedStore<S::$ity>,)*
38 }
39
40 impl<S: server::Types> HandleStore<S> {
41 pub(super) fn new(handle_counters: &'static HandleCounters) -> Self {
42 HandleStore {
43 $($oty: handle::OwnedStore::new(&handle_counters.$oty),)*
44 $($ity: handle::InternedStore::new(&handle_counters.$ity),)*
45 }
46 }
47 }
48
49 $(
50 #[repr(C)]
51 pub struct $oty(pub(crate) handle::Handle);
52 // impl !Send for $oty {}
53 // impl !Sync for $oty {}
54
55 // Forward `Drop::drop` to the inherent `drop` method.
56 impl Drop for $oty {
57 fn drop(&mut self) {
58 $oty(self.0).drop();
59 }
60 }
61
62 impl<S> Encode<S> for $oty {
63 fn encode(self, w: &mut Writer, s: &mut S) {
64 let handle = self.0;
65 mem::forget(self);
66 handle.encode(w, s);
67 }
68 }
69
70 impl<S: server::Types> DecodeMut<'_, '_, HandleStore<server::MarkedTypes<S>>>
71 for Marked<S::$oty, $oty>
72 {
73 fn decode(r: &mut Reader<'_>, s: &mut HandleStore<server::MarkedTypes<S>>) -> Self {
74 s.$oty.take(handle::Handle::decode(r, &mut ()))
75 }
76 }
77
78 impl<S> Encode<S> for &$oty {
79 fn encode(self, w: &mut Writer, s: &mut S) {
80 self.0.encode(w, s);
81 }
82 }
83
84 impl<'s, S: server::Types,> Decode<'_, 's, HandleStore<server::MarkedTypes<S>>>
85 for &'s Marked<S::$oty, $oty>
86 {
87 fn decode(r: &mut Reader<'_>, s: &'s HandleStore<server::MarkedTypes<S>>) -> Self {
88 &s.$oty[handle::Handle::decode(r, &mut ())]
89 }
90 }
91
92 impl<S> Encode<S> for &mut $oty {
93 fn encode(self, w: &mut Writer, s: &mut S) {
94 self.0.encode(w, s);
95 }
96 }
97
98 impl<'s, S: server::Types> DecodeMut<'_, 's, HandleStore<server::MarkedTypes<S>>>
99 for &'s mut Marked<S::$oty, $oty>
100 {
101 fn decode(
102 r: &mut Reader<'_>,
103 s: &'s mut HandleStore<server::MarkedTypes<S>>
104 ) -> Self {
105 &mut s.$oty[handle::Handle::decode(r, &mut ())]
106 }
107 }
108
109 impl<S: server::Types> Encode<HandleStore<server::MarkedTypes<S>>>
110 for Marked<S::$oty, $oty>
111 {
112 fn encode(self, w: &mut Writer, s: &mut HandleStore<server::MarkedTypes<S>>) {
113 s.$oty.alloc(self).encode(w, s);
114 }
115 }
116
117 impl<S> DecodeMut<'_, '_, S> for $oty {
118 fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
119 $oty(handle::Handle::decode(r, s))
120 }
121 }
122 )*
123
124 $(
125 #[repr(C)]
126 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
127 pub(crate) struct $ity(handle::Handle);
128 // impl !Send for $ity {}
129 // impl !Sync for $ity {}
130
131 impl<S> Encode<S> for $ity {
132 fn encode(self, w: &mut Writer, s: &mut S) {
133 self.0.encode(w, s);
134 }
135 }
136
137 impl<S: server::Types> DecodeMut<'_, '_, HandleStore<server::MarkedTypes<S>>>
138 for Marked<S::$ity, $ity>
139 {
140 fn decode(r: &mut Reader<'_>, s: &mut HandleStore<server::MarkedTypes<S>>) -> Self {
141 s.$ity.copy(handle::Handle::decode(r, &mut ()))
142 }
143 }
144
145 impl<S: server::Types> Encode<HandleStore<server::MarkedTypes<S>>>
146 for Marked<S::$ity, $ity>
147 {
148 fn encode(self, w: &mut Writer, s: &mut HandleStore<server::MarkedTypes<S>>) {
149 s.$ity.alloc(self).encode(w, s);
150 }
151 }
152
153 impl<S> DecodeMut<'_, '_, S> for $ity {
154 fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
155 $ity(handle::Handle::decode(r, s))
156 }
157 }
158 )*
159 }
160}
161define_handles! {
162 'owned:
163 TokenStream,
164 TokenStreamBuilder,
165 TokenStreamIter,
166 Group,
167 Literal,
168 SourceFile,
169 MultiSpan,
170 Diagnostic,
171
172 'interned:
173 Punct,
174 Ident,
175 Span,
176}
177
178// FIXME(eddyb) generate these impls by pattern-matching on the
179// names of methods - also could use the presence of `fn drop`
180// to distinguish between 'owned and 'interned, above.
181// Alternatively, special 'modes" could be listed of types in with_api
182// instead of pattern matching on methods, here and in server decl.
183
184impl Clone for TokenStream {
185 fn clone(&self) -> Self {
186 self.clone()
187 }
188}
189
190impl Clone for TokenStreamIter {
191 fn clone(&self) -> Self {
192 self.clone()
193 }
194}
195
196impl Clone for Group {
197 fn clone(&self) -> Self {
198 self.clone()
199 }
200}
201
202impl Clone for Literal {
203 fn clone(&self) -> Self {
204 self.clone()
205 }
206}
207
208// FIXME(eddyb) `Literal` should not expose internal `Debug` impls.
209impl fmt::Debug for Literal {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 f.write_str(&self.debug())
212 }
213}
214
215impl Clone for SourceFile {
216 fn clone(&self) -> Self {
217 self.clone()
218 }
219}
220
221impl fmt::Debug for Span {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 f.write_str(&self.debug())
224 }
225}
226
227macro_rules! define_client_side {
228 ($($name:ident {
229 $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
230 }),* $(,)?) => {
231 $(impl $name {
232 #[allow(unused)]
233 $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* {
234 panic!("hello");
235 // Bridge::with(|bridge| {
236 // let mut b = bridge.cached_buffer.take();
237
238 // b.clear();
239 // api_tags::Method::$name(api_tags::$name::$method).encode(&mut b, &mut ());
240 // reverse_encode!(b; $($arg),*);
241
242 // b = bridge.dispatch.call(b);
243
244 // let r = Result::<_, PanicMessage>::decode(&mut &b[..], &mut ());
245
246 // bridge.cached_buffer = b;
247
248 // r.unwrap_or_else(|e| panic::resume_unwind(e.into()))
249 // })
250 })*
251 })*
252 }
253}
254with_api!(self, self, define_client_side);
255
256enum BridgeState<'a> {
257 /// No server is currently connected to this client.
258 NotConnected,
259
260 /// A server is connected and available for requests.
261 Connected(Bridge<'a>),
262
263 /// Access to the bridge is being exclusively acquired
264 /// (e.g., during `BridgeState::with`).
265 InUse,
266}
267
268enum BridgeStateL {}
269
270impl<'a> scoped_cell::ApplyL<'a> for BridgeStateL {
271 type Out = BridgeState<'a>;
272}
273
274thread_local! {
275 static BRIDGE_STATE: scoped_cell::ScopedCell<BridgeStateL> =
276 scoped_cell::ScopedCell::new(BridgeState::NotConnected);
277}
278
279impl BridgeState<'_> {
280 /// Take exclusive control of the thread-local
281 /// `BridgeState`, and pass it to `f`, mutably.
282 /// The state will be restored after `f` exits, even
283 /// by panic, including modifications made to it by `f`.
284 ///
285 /// N.B., while `f` is running, the thread-local state
286 /// is `BridgeState::InUse`.
287 fn with<R>(f: impl FnOnce(&mut BridgeState<'_>) -> R) -> R {
288 BRIDGE_STATE.with(|state| {
289 state.replace(BridgeState::InUse, |mut state| {
290 // FIXME(#52812) pass `f` directly to `replace` when `RefMutL` is gone
291 f(&mut *state)
292 })
293 })
294 }
295}
296
297impl Bridge<'_> {
298 fn enter<R>(self, f: impl FnOnce() -> R) -> R {
299 // Hide the default panic output within `proc_macro` expansions.
300 // NB. the server can't do this because it may use a different libstd.
301 static HIDE_PANICS_DURING_EXPANSION: Once = Once::new();
302 HIDE_PANICS_DURING_EXPANSION.call_once(|| {
303 let prev = panic::take_hook();
304 panic::set_hook(Box::new(move |info| {
305 let hide = BridgeState::with(|state| match state {
306 BridgeState::NotConnected => false,
307 BridgeState::Connected(_) | BridgeState::InUse => true,
308 });
309 if !hide {
310 prev(info)
311 }
312 }));
313 });
314
315 BRIDGE_STATE.with(|state| state.set(BridgeState::Connected(self), f))
316 }
317
318 fn with<R>(f: impl FnOnce(&mut Bridge<'_>) -> R) -> R {
319 BridgeState::with(|state| match state {
320 BridgeState::NotConnected => {
321 panic!("procedural macro API is used outside of a procedural macro");
322 }
323 BridgeState::InUse => {
324 panic!("procedural macro API is used while it's already in use");
325 }
326 BridgeState::Connected(bridge) => f(bridge),
327 })
328 }
329}
330
331/// A client-side "global object" (usually a function pointer),
332/// which may be using a different `proc_macro` from the one
333/// used by the server, but can be interacted with compatibly.
334///
335/// N.B., `F` must have FFI-friendly memory layout (e.g., a pointer).
336/// The call ABI of function pointers used for `F` doesn't
337/// need to match between server and client, since it's only
338/// passed between them and (eventually) called by the client.
339#[repr(C)]
340#[derive(Copy, Clone)]
341pub struct Client<F> {
342 // FIXME(eddyb) use a reference to the `static COUNTERS`, intead of
343 // a wrapper `fn` pointer, once `const fn` can reference `static`s.
344 pub(super) get_handle_counters: extern "C" fn() -> &'static HandleCounters,
345 pub(super) run: extern "C" fn(Bridge<'_>, F) -> Buffer<u8>,
346 pub(super) f: F,
347}
348
349/// Client-side helper for handling client panics, entering the bridge,
350/// deserializing input and serializing output.
351// FIXME(eddyb) maybe replace `Bridge::enter` with this?
352fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
353 mut bridge: Bridge<'_>,
354 f: impl FnOnce(A) -> R,
355) -> Buffer<u8> {
356 // The initial `cached_buffer` contains the input.
357 let mut b = bridge.cached_buffer.take();
358
359 panic::catch_unwind(panic::AssertUnwindSafe(|| {
360 bridge.enter(|| {
361 let reader = &mut &b[..];
362 let input = A::decode(reader, &mut ());
363
364 // Put the `cached_buffer` back in the `Bridge`, for requests.
365 Bridge::with(|bridge| bridge.cached_buffer = b.take());
366
367 let output = f(input);
368
369 // Take the `cached_buffer` back out, for the output value.
370 b = Bridge::with(|bridge| bridge.cached_buffer.take());
371
372 // HACK(eddyb) Separate encoding a success value (`Ok(output)`)
373 // from encoding a panic (`Err(e: PanicMessage)`) to avoid
374 // having handles outside the `bridge.enter(|| ...)` scope, and
375 // to catch panics that could happen while encoding the success.
376 //
377 // Note that panics should be impossible beyond this point, but
378 // this is defensively trying to avoid any accidental panicking
379 // reaching the `extern "C"` (which should `abort` but may not
380 // at the moment, so this is also potentially preventing UB).
381 b.clear();
382 Ok::<_, ()>(output).encode(&mut b, &mut ());
383 })
384 }))
385 .map_err(PanicMessage::from)
386 .unwrap_or_else(|e| {
387 b.clear();
388 Err::<(), _>(e).encode(&mut b, &mut ());
389 });
390 b
391}
392
393impl Client<fn(crate::TokenStream) -> crate::TokenStream> {
394 pub fn expand1(f: fn(crate::TokenStream) -> crate::TokenStream) -> Self {
395 extern "C" fn run(
396 bridge: Bridge<'_>,
397 f: impl FnOnce(crate::TokenStream) -> crate::TokenStream,
398 ) -> Buffer<u8> {
399 run_client(bridge, |input| f(crate::TokenStream(input)).0)
400 }
401 Client { get_handle_counters: HandleCounters::get, run, f }
402 }
403}
404
405impl Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream> {
406 pub fn expand2(f: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream) -> Self {
407 extern "C" fn run(
408 bridge: Bridge<'_>,
409 f: impl FnOnce(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
410 ) -> Buffer<u8> {
411 run_client(bridge, |(input, input2)| {
412 f(crate::TokenStream(input), crate::TokenStream(input2)).0
413 })
414 }
415 Client { get_handle_counters: HandleCounters::get, run, f }
416 }
417}
418
419#[repr(C)]
420#[derive(Copy, Clone)]
421pub enum ProcMacro {
422 CustomDerive {
423 trait_name: &'static str,
424 attributes: &'static [&'static str],
425 client: Client<fn(crate::TokenStream) -> crate::TokenStream>,
426 },
427
428 Attr {
429 name: &'static str,
430 client: Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream>,
431 },
432
433 Bang {
434 name: &'static str,
435 client: Client<fn(crate::TokenStream) -> crate::TokenStream>,
436 },
437}
438
439impl std::fmt::Debug for ProcMacro {
440 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
441 write!(f, "ProcMacro {{ name: {} }}", self.name())
442 }
443}
444
445impl ProcMacro {
446 pub fn name(&self) -> &'static str {
447 match self {
448 ProcMacro::CustomDerive { trait_name, .. } => trait_name,
449 ProcMacro::Attr { name, .. } => name,
450 ProcMacro::Bang { name, .. } => name,
451 }
452 }
453
454 pub fn custom_derive(
455 trait_name: &'static str,
456 attributes: &'static [&'static str],
457 expand: fn(crate::TokenStream) -> crate::TokenStream,
458 ) -> Self {
459 ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) }
460 }
461
462 pub fn attr(
463 name: &'static str,
464 expand: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
465 ) -> Self {
466 ProcMacro::Attr { name, client: Client::expand2(expand) }
467 }
468
469 pub fn bang(name: &'static str, expand: fn(crate::TokenStream) -> crate::TokenStream) -> Self {
470 ProcMacro::Bang { name, client: Client::expand1(expand) }
471 }
472}