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