Skip to main content

proc_macro/bridge/
client.rs

1//! Client-side types.
2
3use std::cell::RefCell;
4use std::marker::PhantomData;
5
6use super::*;
7
8pub(crate) struct TokenStream {
9    handle: handle::Handle,
10}
11
12impl !Send for TokenStream {}
13impl !Sync for TokenStream {}
14
15// Forward `Drop::drop` to the inherent `drop` method.
16impl Drop for TokenStream {
17    fn drop(&mut self) {
18        Methods::ts_drop(TokenStream { handle: self.handle });
19    }
20}
21
22impl<S> Encode<S> for TokenStream {
23    #[inline]
24    fn encode(self, w: &mut Buffer, s: &mut S) {
25        mem::ManuallyDrop::new(self).handle.encode(w, s);
26    }
27}
28
29impl<S> Encode<S> for &TokenStream {
30    #[inline]
31    fn encode(self, w: &mut Buffer, s: &mut S) {
32        self.handle.encode(w, s);
33    }
34}
35
36impl<S> Decode<'_, '_, S> for TokenStream {
37    #[inline]
38    fn decode(r: &mut &[u8], s: &mut S) -> Self {
39        TokenStream { handle: handle::Handle::decode(r, s) }
40    }
41}
42
43impl Encode<()> for crate::TokenStream {
44    #[inline]
45    fn encode(self, w: &mut Buffer, s: &mut ()) {
46        self.0.encode(w, s)
47    }
48}
49
50impl Decode<'_, '_, ()> for crate::TokenStream {
51    #[inline]
52    fn decode(r: &mut &[u8], s: &mut ()) -> Self {
53        crate::TokenStream(Some(Decode::decode(r, s)))
54    }
55}
56
57#[derive(Copy, Clone, PartialEq, Eq, Hash)]
58pub(crate) struct Span {
59    handle: handle::Handle,
60}
61
62impl !Send for Span {}
63impl !Sync for Span {}
64
65impl<S> Encode<S> for Span {
66    #[inline]
67    fn encode(self, w: &mut Buffer, s: &mut S) {
68        self.handle.encode(w, s);
69    }
70}
71
72impl<S> Decode<'_, '_, S> for Span {
73    #[inline]
74    fn decode(r: &mut &[u8], s: &mut S) -> Self {
75        Span { handle: handle::Handle::decode(r, s) }
76    }
77}
78
79impl Clone for TokenStream {
80    fn clone(&self) -> Self {
81        Methods::ts_clone(self)
82    }
83}
84
85impl Span {
86    pub(crate) fn def_site() -> Span {
87        Bridge::with(|bridge| bridge.globals.def_site)
88    }
89
90    pub(crate) fn call_site() -> Span {
91        Bridge::with(|bridge| bridge.globals.call_site)
92    }
93
94    pub(crate) fn mixed_site() -> Span {
95        Bridge::with(|bridge| bridge.globals.mixed_site)
96    }
97}
98
99impl fmt::Debug for Span {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        f.write_str(&Methods::span_debug(*self))
102    }
103}
104
105pub(crate) use super::Methods;
106pub(crate) use super::symbol::Symbol;
107
108macro_rules! define_client_side {
109    (
110        $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
111    ) => {
112        impl Methods {
113            $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)? {
114                Bridge::with(|bridge| {
115                    let mut buf = bridge.cached_buffer.take();
116
117                    buf.clear();
118                    ApiTags::$method.encode(&mut buf, &mut ());
119                    $($arg.encode(&mut buf, &mut ());)*
120
121                    buf = bridge.dispatch.call(buf);
122
123                    let r = Result::<_, PanicMessage>::decode(&mut &buf[..], &mut ());
124
125                    bridge.cached_buffer = buf;
126
127                    r.unwrap_or_else(|e| panic::resume_unwind(e.into()))
128                })
129            })*
130        }
131    }
132}
133with_api!(define_client_side, TokenStream, Span, Symbol);
134
135struct Bridge<'a> {
136    /// Reusable buffer (only `clear`-ed, never shrunk), primarily
137    /// used for making requests.
138    cached_buffer: Buffer,
139
140    /// Server-side function that the client uses to make requests.
141    dispatch: closure::Closure<'a>,
142
143    /// Provided globals for this macro expansion.
144    globals: ExpnGlobals<Span>,
145}
146
147impl<'a> !Send for Bridge<'a> {}
148impl<'a> !Sync for Bridge<'a> {}
149
150#[allow(unsafe_code)]
151mod state {
152    use std::cell::{Cell, RefCell};
153    use std::ptr;
154
155    use super::Bridge;
156
157    thread_local! {
158        static BRIDGE_STATE: Cell<*const ()> = const { Cell::new(ptr::null()) };
159    }
160
161    pub(super) fn set<'bridge, R>(state: &RefCell<Bridge<'bridge>>, f: impl FnOnce() -> R) -> R {
162        struct RestoreOnDrop(*const ());
163        impl Drop for RestoreOnDrop {
164            fn drop(&mut self) {
165                BRIDGE_STATE.set(self.0);
166            }
167        }
168
169        let inner = ptr::from_ref(state).cast();
170        let outer = BRIDGE_STATE.replace(inner);
171        let _restore = RestoreOnDrop(outer);
172
173        f()
174    }
175
176    pub(super) fn with<R>(
177        f: impl for<'bridge> FnOnce(Option<&RefCell<Bridge<'bridge>>>) -> R,
178    ) -> R {
179        let state = BRIDGE_STATE.get();
180        // SAFETY: the only place where the pointer is set is in `set`. It puts
181        // back the previous value after the inner call has returned, so we know
182        // that as long as the pointer is not null, it came from a reference to
183        // a `RefCell<Bridge>` that outlasts the call to this function. Since `f`
184        // works the same for any lifetime of the bridge, including the actual
185        // one, we can lie here and say that the lifetime is `'static` without
186        // anyone noticing.
187        let bridge = unsafe { state.cast::<RefCell<Bridge<'static>>>().as_ref() };
188        f(bridge)
189    }
190}
191
192impl Bridge<'_> {
193    fn with<R>(f: impl FnOnce(&mut Bridge<'_>) -> R) -> R {
194        state::with(|state| {
195            let bridge = state.expect("procedural macro API is used outside of a procedural macro");
196            let mut bridge = bridge
197                .try_borrow_mut()
198                .expect("procedural macro API is used while it's already in use");
199            f(&mut bridge)
200        })
201    }
202}
203
204pub(crate) fn is_available() -> bool {
205    state::with(|s| s.is_some())
206}
207
208/// A client-side RPC entry-point, which may be using a different `proc_macro`
209/// from the one used by the server, but can be invoked compatibly.
210///
211/// Note that the (phantom) `I` ("input") and `O` ("output") type parameters
212/// decorate the `Client<I, O>` with the RPC "interface" of the entry-point, but
213/// do not themselves participate in ABI, at all, only facilitate type-checking.
214///
215/// E.g. `Client<TokenStream, TokenStream>` is the common proc macro interface,
216/// used for `#[proc_macro] fn foo(input: TokenStream) -> TokenStream`,
217/// indicating that the RPC input and output will be serialized token streams,
218/// and forcing the use of APIs that take/return `S::TokenStream`, server-side.
219#[repr(C)]
220pub struct Client<I, O> {
221    pub(super) run: extern "C" fn(BridgeConfig<'_>) -> Buffer,
222
223    pub(super) _marker: PhantomData<fn(I) -> O>,
224}
225
226impl<I, O> Copy for Client<I, O> {}
227impl<I, O> Clone for Client<I, O> {
228    fn clone(&self) -> Self {
229        *self
230    }
231}
232
233fn maybe_install_panic_hook(force_show_panics: bool) {
234    // Hide the default panic output within `proc_macro` expansions.
235    // NB. the server can't do this because it may use a different std.
236    static HIDE_PANICS_DURING_EXPANSION: Once = Once::new();
237    HIDE_PANICS_DURING_EXPANSION.call_once(|| {
238        let prev = panic::take_hook();
239        panic::set_hook(Box::new(move |info| {
240            // We normally report panics by catching unwinds and passing the payload from the
241            // unwind back to the compiler, but if the panic doesn't unwind we'll abort before
242            // the compiler has a chance to print an error. So we special-case PanicInfo where
243            // can_unwind is false.
244            if force_show_panics || !is_available() || !info.can_unwind() {
245                prev(info)
246            }
247        }));
248    });
249}
250
251/// Client-side helper for handling client panics, entering the bridge,
252/// deserializing input and serializing output.
253fn run_client<A: for<'a, 's> Decode<'a, 's, ()>>(
254    config: BridgeConfig<'_>,
255    f: impl FnOnce(A) -> crate::TokenStream,
256) -> Buffer {
257    let BridgeConfig { input: mut buf, dispatch, force_show_panics, .. } = config;
258
259    let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
260        maybe_install_panic_hook(force_show_panics);
261
262        // Make sure the symbol store is empty before decoding inputs.
263        Symbol::invalidate_all();
264
265        let reader = &mut &buf[..];
266        let (globals, input) = <(ExpnGlobals<Span>, A)>::decode(reader, &mut ());
267
268        // Put the buffer we used for input back in the `Bridge` for requests.
269        let state = RefCell::new(Bridge { cached_buffer: buf.take(), dispatch, globals });
270
271        let output = state::set(&state, || f(input));
272
273        // Take the `cached_buffer` back out, for the output value.
274        buf = RefCell::into_inner(state).cached_buffer;
275
276        output
277    }));
278
279    // Serialize response of type `Result<R, PanicMessage>`.
280    buf.clear();
281    res.map_err(PanicMessage::from).encode(&mut buf, &mut ());
282
283    // Now that a response has been serialized, invalidate all symbols
284    // registered with the interner.
285    Symbol::invalidate_all();
286    buf
287}
288
289impl Client<crate::TokenStream, crate::TokenStream> {
290    pub const fn expand1(f: impl Fn(crate::TokenStream) -> crate::TokenStream + Copy) -> Self {
291        Client {
292            run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| {
293                run_client(bridge, |input| f(input))
294            }),
295            _marker: PhantomData,
296        }
297    }
298}
299
300impl Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream> {
301    pub const fn expand2(
302        f: impl Fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream + Copy,
303    ) -> Self {
304        Client {
305            run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| {
306                run_client(bridge, |(input, input2)| f(input, input2))
307            }),
308            _marker: PhantomData,
309        }
310    }
311}
312
313#[repr(C)]
314#[derive(Copy, Clone)]
315pub enum ProcMacro {
316    CustomDerive {
317        trait_name: &'static str,
318        attributes: &'static [&'static str],
319        client: Client<crate::TokenStream, crate::TokenStream>,
320    },
321
322    Attr {
323        name: &'static str,
324        client: Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream>,
325    },
326
327    Bang {
328        name: &'static str,
329        client: Client<crate::TokenStream, crate::TokenStream>,
330    },
331}
332
333impl ProcMacro {
334    pub fn name(&self) -> &'static str {
335        match self {
336            ProcMacro::CustomDerive { trait_name, .. } => trait_name,
337            ProcMacro::Attr { name, .. } => name,
338            ProcMacro::Bang { name, .. } => name,
339        }
340    }
341
342    pub const fn custom_derive(
343        trait_name: &'static str,
344        attributes: &'static [&'static str],
345        expand: impl Fn(crate::TokenStream) -> crate::TokenStream + Copy,
346    ) -> Self {
347        ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) }
348    }
349
350    pub const fn attr(
351        name: &'static str,
352        expand: impl Fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream + Copy,
353    ) -> Self {
354        ProcMacro::Attr { name, client: Client::expand2(expand) }
355    }
356
357    pub const fn bang(
358        name: &'static str,
359        expand: impl Fn(crate::TokenStream) -> crate::TokenStream + Copy,
360    ) -> Self {
361        ProcMacro::Bang { name, client: Client::expand1(expand) }
362    }
363}