aboutsummaryrefslogtreecommitdiff
path: root/crates/proc_macro_srv/src/proc_macro/bridge/client.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/proc_macro_srv/src/proc_macro/bridge/client.rs')
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/client.rs478
1 files changed, 478 insertions, 0 deletions
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/client.rs b/crates/proc_macro_srv/src/proc_macro/bridge/client.rs
new file mode 100644
index 000000000..cb4b3bdb0
--- /dev/null
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/client.rs
@@ -0,0 +1,478 @@
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`, instead 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
208impl fmt::Debug for Literal {
209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 f.debug_struct("Literal")
211 // format the kind without quotes, as in `kind: Float`
212 // .field("kind", &format_args!("{}", &self.debug_kind()))
213 .field("symbol", &self.symbol())
214 // format `Some("...")` on one line even in {:#?} mode
215 // .field("suffix", &format_args!("{:?}", &self.suffix()))
216 .field("span", &self.span())
217 .finish()
218 }
219}
220
221impl Clone for SourceFile {
222 fn clone(&self) -> Self {
223 self.clone()
224 }
225}
226
227impl fmt::Debug for Span {
228 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229 f.write_str(&self.debug())
230 }
231}
232
233macro_rules! define_client_side {
234 ($($name:ident {
235 $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
236 }),* $(,)?) => {
237 $(impl $name {
238 #[allow(unused)]
239 $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* {
240 panic!("hello");
241 // Bridge::with(|bridge| {
242 // let mut b = bridge.cached_buffer.take();
243
244 // b.clear();
245 // api_tags::Method::$name(api_tags::$name::$method).encode(&mut b, &mut ());
246 // reverse_encode!(b; $($arg),*);
247
248 // b = bridge.dispatch.call(b);
249
250 // let r = Result::<_, PanicMessage>::decode(&mut &b[..], &mut ());
251
252 // bridge.cached_buffer = b;
253
254 // r.unwrap_or_else(|e| panic::resume_unwind(e.into()))
255 // })
256 })*
257 })*
258 }
259}
260with_api!(self, self, define_client_side);
261
262enum BridgeState<'a> {
263 /// No server is currently connected to this client.
264 NotConnected,
265
266 /// A server is connected and available for requests.
267 Connected(Bridge<'a>),
268
269 /// Access to the bridge is being exclusively acquired
270 /// (e.g., during `BridgeState::with`).
271 InUse,
272}
273
274enum BridgeStateL {}
275
276impl<'a> scoped_cell::ApplyL<'a> for BridgeStateL {
277 type Out = BridgeState<'a>;
278}
279
280thread_local! {
281 static BRIDGE_STATE: scoped_cell::ScopedCell<BridgeStateL> =
282 scoped_cell::ScopedCell::new(BridgeState::NotConnected);
283}
284
285impl BridgeState<'_> {
286 /// Take exclusive control of the thread-local
287 /// `BridgeState`, and pass it to `f`, mutably.
288 /// The state will be restored after `f` exits, even
289 /// by panic, including modifications made to it by `f`.
290 ///
291 /// N.B., while `f` is running, the thread-local state
292 /// is `BridgeState::InUse`.
293 fn with<R>(f: impl FnOnce(&mut BridgeState<'_>) -> R) -> R {
294 BRIDGE_STATE.with(|state| {
295 state.replace(BridgeState::InUse, |mut state| {
296 // FIXME(#52812) pass `f` directly to `replace` when `RefMutL` is gone
297 f(&mut *state)
298 })
299 })
300 }
301}
302
303impl Bridge<'_> {
304 fn enter<R>(self, f: impl FnOnce() -> R) -> R {
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 hide = BridgeState::with(|state| match state {
312 BridgeState::NotConnected => false,
313 BridgeState::Connected(_) | BridgeState::InUse => true,
314 });
315 if !hide {
316 prev(info)
317 }
318 }));
319 });
320
321 BRIDGE_STATE.with(|state| state.set(BridgeState::Connected(self), f))
322 }
323
324 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)]
347pub 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?
358fn 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 may 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
399impl Client<fn(crate::TokenStream) -> crate::TokenStream> {
400 pub fn expand1(f: fn(crate::TokenStream) -> crate::TokenStream) -> Self {
401 extern "C" fn run(
402 bridge: Bridge<'_>,
403 f: impl FnOnce(crate::TokenStream) -> crate::TokenStream,
404 ) -> Buffer<u8> {
405 run_client(bridge, |input| f(crate::TokenStream(input)).0)
406 }
407 Client { get_handle_counters: HandleCounters::get, run, f }
408 }
409}
410
411impl Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream> {
412 pub fn expand2(f: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream) -> Self {
413 extern "C" fn run(
414 bridge: Bridge<'_>,
415 f: impl FnOnce(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
416 ) -> Buffer<u8> {
417 run_client(bridge, |(input, input2)| {
418 f(crate::TokenStream(input), crate::TokenStream(input2)).0
419 })
420 }
421 Client { get_handle_counters: HandleCounters::get, run, f }
422 }
423}
424
425#[repr(C)]
426#[derive(Copy, Clone)]
427pub enum ProcMacro {
428 CustomDerive {
429 trait_name: &'static str,
430 attributes: &'static [&'static str],
431 client: Client<fn(crate::TokenStream) -> crate::TokenStream>,
432 },
433
434 Attr {
435 name: &'static str,
436 client: Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream>,
437 },
438
439 Bang {
440 name: &'static str,
441 client: Client<fn(crate::TokenStream) -> crate::TokenStream>,
442 },
443}
444
445impl std::fmt::Debug for ProcMacro {
446 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447 write!(f, "ProcMacro {{ name: {} }}", self.name())
448 }
449}
450
451impl ProcMacro {
452 pub fn name(&self) -> &'static str {
453 match self {
454 ProcMacro::CustomDerive { trait_name, .. } => trait_name,
455 ProcMacro::Attr { name, .. } => name,
456 ProcMacro::Bang { name, .. } => name,
457 }
458 }
459
460 pub fn custom_derive(
461 trait_name: &'static str,
462 attributes: &'static [&'static str],
463 expand: fn(crate::TokenStream) -> crate::TokenStream,
464 ) -> Self {
465 ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) }
466 }
467
468 pub fn attr(
469 name: &'static str,
470 expand: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
471 ) -> Self {
472 ProcMacro::Attr { name, client: Client::expand2(expand) }
473 }
474
475 pub fn bang(name: &'static str, expand: fn(crate::TokenStream) -> crate::TokenStream) -> Self {
476 ProcMacro::Bang { name, client: Client::expand1(expand) }
477 }
478}