Skip to main content

hyperlight_component_util/
resource.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025 The Hyperlight Authors.
3
4use proc_macro2::{Ident, TokenStream};
5use quote::{format_ident, quote};
6
7use crate::emit::State;
8use crate::etypes::{TypeBound, Tyvar};
9use crate::rtypes::emit_var_ref;
10
11/// Emit a structure definition for a resource table that keeps track
12/// of resources lent/borrowed/given/taken to/from the other side of
13/// the Hyperlight boundary.
14/// - `rtsid`: The name of the struct to create
15/// - `bound`: a bound to be used for a phantom type variable that
16///   records the fact that these resource tables are only valid for a
17///   component that has been instantiated with a particular
18///   implementation of its imports
19/// - `sv`: optionally a bound to be used for a phantom type variable
20///   that records the fact that these resource tables are only valid
21///   for a particular implementation of a component
22pub fn emit_tables<'a, 'b, 'c>(
23    s: &'c mut State<'a, 'b>,
24    rtsid: Ident,
25    bound: TokenStream,
26    sv: Option<TokenStream>,
27    is_guest: bool,
28) {
29    let vs = s.bound_vars.clone();
30    let (fields, inits) = vs
31        .iter()
32        .enumerate()
33        .map(|(i, v)| {
34            let field_name = format_ident!("resource{}", i);
35            let alloc_ns = if s.is_guest {
36                quote! { ::alloc }
37            } else {
38                quote! { ::std }
39            };
40            match v.bound {
41                TypeBound::Eq(_) => (quote! { #field_name: () }, quote! { #field_name: () }),
42                TypeBound::SubResource => {
43                    if v.origin.is_imported() ^ is_guest {
44                        let t = emit_var_ref(s, &Tyvar::Bound(i as u32));
45                        (
46                            quote! {
47                                #field_name: #alloc_ns::collections::VecDeque<
48                                ::hyperlight_common::resource::ResourceEntry<#t>
49                                >
50                            },
51                            quote! { #field_name: #alloc_ns::collections::VecDeque::new() },
52                        )
53                    } else {
54                        // we don't need to keep track of anything for
55                        // resources owned by the other side
56                        (
57                            quote! {
58                                #field_name: ()
59                            },
60                            quote! { #field_name: () },
61                        )
62                    }
63                }
64            }
65        })
66        .unzip::<_, _, Vec<_>, Vec<_>>();
67    let (sv, svs, sphantom, sphantominit) = if let Some(sv) = sv {
68        (
69            quote! { , S: #sv },
70            quote! { , S },
71            quote! { _phantomS: ::core::marker::PhantomData<S>, },
72            quote! { _phantomS: ::core::marker::PhantomData, },
73        )
74    } else {
75        (
76            TokenStream::new(),
77            TokenStream::new(),
78            TokenStream::new(),
79            TokenStream::new(),
80        )
81    };
82    s.root_mod.items.extend(quote! {
83        pub(crate) struct #rtsid<I: #bound #sv> {
84            #(#fields,)*
85            _phantomI: ::core::marker::PhantomData<I>,
86            #sphantom
87        }
88        impl<I: #bound #sv> #rtsid<I #svs> {
89            fn new() -> Self {
90                #rtsid {
91                    #(#inits,)*
92                    _phantomI: ::core::marker::PhantomData,
93                    #sphantominit
94                }
95            }
96        }
97    });
98}