Skip to main content

hyperlight_component_util/
hl.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025 The Hyperlight Authors.
3
4use itertools::Itertools;
5use proc_macro2::{Ident, TokenStream};
6use quote::{format_ident, quote};
7
8use crate::emit::{ResolvedBoundVar, State, kebab_to_cons, kebab_to_flags_const, kebab_to_var};
9use crate::etypes::{self, Defined, Handleable, Tyvar, Value};
10use crate::rtypes;
11
12/// Construct a string that can be used "on the wire" to identify a
13/// given function between the guest/host.  This should be replaced
14/// with an integer index so that we can dispatch less dynamically in
15/// the future.
16pub fn emit_fn_hl_name(s: &State, kebab: &str) -> String {
17    s.mod_cursor
18        .iter()
19        .map(|x| x.to_string())
20        .chain(std::iter::once(kebab.to_string()))
21        .join("::")
22}
23
24/// Emit code to unmarshal a value into a toplevel type (i.e. types
25/// that cannot be represented inline in a valtype).
26/// - `id`: an ident of a slice that the code will unmarshal from; also
27///   used as the beginning of any other identifiers that this code
28///   declares (if only we had hygiene in stable rust...)
29/// - `tv`: the tyvar that we followed to get to this type
30/// - `vt`: the value type that we are unmarshalling
31///
32/// The token stream produced will be an expression which typechecks
33/// as a tuple whose first component is the Rust type (as defined by
34/// the [`crate::rtypes`] module) of the given value type and whose
35/// second component is an integer. The second component represents
36/// the number of bytes consumed from the `id` slice while
37/// unmarshalling.
38pub fn emit_hl_unmarshal_toplevel_value(
39    s: &mut State,
40    id: Ident,
41    tv: Tyvar,
42    vt: &Value,
43) -> TokenStream {
44    let tname = rtypes::emit_var_ref_value(s, &tv);
45    let mut s = s.clone();
46    let Tyvar::Bound(n) = tv else {
47        panic!("impossible tyvar")
48    };
49    s.var_offset += n as usize + 1;
50    let s = &mut s;
51    match vt {
52        Value::Record(rfs) => {
53            let cursor = format_ident!("{}_cursor", id);
54            let inid = format_ident!("{}_field", id);
55            let (decls, uses) = rfs
56                .iter()
57                .map(|rf| {
58                    let field_name = kebab_to_var(rf.name.name);
59                    let field_name_var = format_ident!("{}_field_{}", id, field_name);
60                    let vtun = emit_hl_unmarshal_value(s, inid.clone(), &rf.ty);
61                    (
62                        quote! {
63                            let #inid = &#id[#cursor..];
64                            let (#field_name_var, b) = { #vtun };
65                            #cursor += b;
66                        },
67                        quote! {
68                            #field_name: #field_name_var,
69                        },
70                    )
71                })
72                .unzip::<_, _, Vec<_>, Vec<_>>();
73            quote! {
74                let mut #cursor = 0;
75                #(#decls)*
76                (#tname { #(#uses)* }, #cursor)
77            }
78        }
79        Value::Flags(ns) => {
80            let bytes = usize::div_ceil(ns.len(), 8);
81            let result_var = format_ident!("{}_flags", id);
82            let fields = ns.iter().enumerate().map(|(i, n)| {
83                let byte_offset = i / 8;
84                let bit_offset = i % 8;
85                let is_set = quote! { (#id[#byte_offset] >> #bit_offset) & 0x1 == 1 };
86                if s.is_wasmtime_guest {
87                    let const_name = kebab_to_flags_const(n.name);
88                    quote! {
89                        if #is_set {
90                            #result_var |= #tname::#const_name;
91                        }
92                    }
93                } else {
94                    let fieldid = kebab_to_var(n.name);
95                    quote! {
96                        #fieldid: #is_set,
97                    }
98                }
99            });
100            if s.is_wasmtime_guest {
101                quote! {
102                    {
103                        let mut #result_var = #tname::empty();
104                        #(#fields)*
105                        (#result_var, #bytes)
106                    }
107                }
108            } else {
109                quote! {
110                    (#tname { #(#fields)* }, #bytes)
111                }
112            }
113        }
114        Value::Variant(vcs) => {
115            let inid = format_ident!("{}_body", id);
116            let vcs = vcs.iter().enumerate().map(|(i, vc)| {
117                let case_name = kebab_to_cons(vc.name.name);
118                let i = i as u32;
119                let case_name_var = format_ident!("{}_case_{}", id, case_name);
120                match &vc.ty {
121                    Some(ty) => {
122                        let vtun = emit_hl_unmarshal_value(s, inid.clone(), ty);
123                        quote! {
124                            #i => {
125                                let (#case_name_var, b) = { #vtun };
126                                (#tname::#case_name(#case_name_var), b + 4)
127                            }
128                        }
129                    }
130                    None => quote! {
131                        #i => (#tname::#case_name, 4)
132                    },
133                }
134            });
135            quote! {
136                let n = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
137                let #inid = &#id[4..];
138                match n {
139                    #(#vcs,)*
140                    _ => panic!("invalid value for variant"),
141                }
142            }
143        }
144        Value::Enum(ns) => {
145            let vcs = ns.iter().enumerate().map(|(i, n)| {
146                let case_name = kebab_to_cons(n.name);
147                let i = i as u32;
148                quote! { #i => ( #tname::#case_name, 4) }
149            });
150            quote! {
151                let n = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
152                match n {
153                    #(#vcs,)*
154                    _ => panic!("invalid value for enum"),
155                }
156            }
157        }
158        _ => emit_hl_unmarshal_value(s, id, vt),
159    }
160}
161
162/// Find the resource index that the given Handleable refers to.
163///
164/// Precondition: this type variable does refer to a resource type
165pub fn resolve_handleable_to_resource(s: &mut State, ht: &Handleable) -> u32 {
166    match ht {
167        Handleable::Var(Tyvar::Bound(vi)) => {
168            let ResolvedBoundVar::Resource { rtidx } = s.resolve_bound_var(*vi) else {
169                panic!("impossible: resource var is not resource");
170            };
171            rtidx
172        }
173        _ => panic!("impossible handleable in type"),
174    }
175}
176
177/// Emit code to unmarshal a value into an inline-able value type
178/// - `id`: an ident of a slice that the code will unmarshal from; also
179///   used as the beginning of any other identifiers that this code
180///   declares (if only we had hygiene in stable rust...)
181/// - `vt`: the value type that we are unmarshalling
182///
183/// The token stream produced will be an expression which typechecks
184/// as a tuple whose first component is the Rust type (as defined by
185/// the [`crate::rtypes`] module) of the given value type and whose
186/// second component is an integer. The second component represents
187/// the number of bytes consumed from the `id` slice while
188/// unmarshalling.
189pub fn emit_hl_unmarshal_value(s: &mut State, id: Ident, vt: &Value) -> TokenStream {
190    match vt {
191        Value::Bool => quote! { (#id[0] != 0, 1) },
192        Value::S(_) | Value::U(_) | Value::F(_) => {
193            let (tid, width) = rtypes::numeric_rtype(vt);
194            let blen = width as usize / 8;
195            quote! {
196                (#tid::from_ne_bytes(#id[0..#blen].try_into().unwrap()), #blen)
197            }
198        }
199        Value::Char => quote! {
200            (unsafe { char::from_u32_unchecked(u32::from_ne_bytes(
201                #id[0..4].try_into().unwrap())) }, 4)
202        },
203        Value::String => quote! {
204            let n = u32::from_ne_bytes(#id[0..4].try_into().unwrap()) as usize;
205            let s = ::alloc::string::ToString::to_string(::core::str::from_utf8(&#id[4..4 + n]).unwrap()); // todo: better error handling
206            (s, n + 4)
207        },
208        Value::List(vt) => {
209            let retid = format_ident!("{}_list", id);
210            let inid = format_ident!("{}_elem", id);
211            let vtun = emit_hl_unmarshal_value(s, inid.clone(), vt);
212            quote! {
213                let n = u32::from_ne_bytes(#id[0..4].try_into().unwrap()) as usize;
214                let mut #retid = alloc::vec::Vec::new();
215                let mut cursor = 4;
216                for i in 0..n {
217                    let #inid = &#id[cursor..];
218                    let (x, b) = { #vtun };
219                    cursor += b;
220                    #retid.push(x);
221                }
222                (#retid, cursor)
223            }
224        }
225        Value::FixList(vt, _) => {
226            let inid = format_ident!("{}_elem", id);
227            let vtun = emit_hl_unmarshal_value(s, inid.clone(), vt);
228            quote! {
229                let mut cursor = 0;
230                let arr = ::core::array::from_fn(|_i| {
231                    let #inid = &#id[cursor..];
232                    let (x, b) = { #vtun };
233                    cursor += b;
234                    x
235                });
236                (arr, cursor)
237            }
238        }
239        Value::Record(_) => panic!("record not at top level of valtype"),
240        Value::Tuple(vts) => {
241            let inid = format_ident!("{}_elem", id);
242            let len = format_ident!("{}_len", id);
243            let (ns, vtuns) = vts
244                .iter()
245                .enumerate()
246                .map(|(i, vt)| {
247                    let vtun = emit_hl_unmarshal_value(s, inid.clone(), vt);
248                    let retid = format_ident!("{}_elem{}", id, i);
249                    (
250                        retid.clone(),
251                        quote! {
252                            let (#retid, b) = { #vtun };
253                            #len += b;
254                            let #inid = &#inid[b..];
255                        },
256                    )
257                })
258                .unzip::<_, _, Vec<_>, Vec<_>>();
259            quote! {
260                let #inid = &#id[0..];
261                let mut #len = 0;
262                #(#vtuns)*
263                ((#(#ns),*), #len)
264            }
265        }
266        Value::Flags(_) => panic!("flags not at top level of valtype"),
267        Value::Variant(_) => panic!("variant not at top level of valtype"),
268        Value::Enum(_) => panic!("enum not at top level of valtype"),
269        Value::Option(vt) => {
270            let inid = format_ident!("{}_body", id);
271            let vtun = emit_hl_unmarshal_value(s, inid.clone(), vt);
272            quote! {
273                let n = u8::from_ne_bytes(#id[0..1].try_into().unwrap());
274                if n != 0 {
275                    let #inid = &#id[1..];
276                    let (x, b) = { #vtun };
277                    (::core::option::Option::Some(x), b + 1)
278                } else {
279                    (::core::option::Option::None, 1)
280                }
281            }
282        }
283        Value::Result(vt1, vt2) => {
284            let inid = format_ident!("{}_body", id);
285            let vtun1 = if let Some(ref vt1) = **vt1 {
286                emit_hl_unmarshal_value(s, inid.clone(), vt1)
287            } else {
288                quote! { ((), 0) }
289            };
290            let vtun2 = if let Some(ref vt2) = **vt2 {
291                emit_hl_unmarshal_value(s, inid.clone(), vt2)
292            } else {
293                quote! { ((), 0) }
294            };
295            quote! {
296                let i = u8::from_ne_bytes(#id[0..1].try_into().unwrap());
297                let #inid = &#id[1..];
298                if i == 0 {
299                    let (x, b) = { #vtun1 };
300                    (::core::result::Result::Ok(x), b + 1)
301                } else {
302                    let (x, b)= { #vtun2 };
303                    (::core::result::Result::Err(x), b +1)
304                }
305            }
306        }
307        Value::Own(ht) => {
308            let vi = resolve_handleable_to_resource(s, ht);
309            tracing::debug!("resolved ht to r (1) {:?} {:?}", ht, vi);
310            if s.is_guest {
311                let rid = format_ident!("HostResource{}", vi);
312                if s.is_wasmtime_guest {
313                    quote! {
314                        let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
315                        (::wasmtime::component::Resource::<#rid>::new_own(i), 4)
316                    }
317                } else {
318                    quote! {
319                        let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
320                        (#rid { rep: i }, 4)
321                    }
322                }
323            } else {
324                let rid = format_ident!("resource{}", vi);
325                quote! {
326                    let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
327                    let Some(v) = rts.#rid[i as usize].take() else {
328                        // todo: better error handling
329                        panic!("");
330                    };
331                    (v, 4)
332                }
333            }
334        }
335        Value::Borrow(ht) => {
336            let vi = resolve_handleable_to_resource(s, ht);
337            tracing::debug!("resolved ht to r (2) {:?} {:?}", ht, vi);
338            if s.is_guest {
339                let rid = format_ident!("HostResource{}", vi);
340                if s.is_wasmtime_guest {
341                    quote! {
342                        let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
343                        (::wasmtime::component::Resource::<#rid>::new_borrow(i), 4)
344                    }
345                } else {
346                    // TODO: When we add the Drop impl (#810), we need
347                    // to make sure it does not get called here
348                    //
349                    // If we tried to actually return a reference
350                    // here, rustc would get mad about the temporary
351                    // constructed here not living long enough, so
352                    // instead we return the temporary and construct
353                    // the reference elsewhere. It might be a bit more
354                    // principled to have a separate
355                    // HostResourceXXBorrow struct that implements
356                    // AsRef<HostResourceXX> or something in the
357                    // future...
358                    quote! {
359                        let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
360
361                        (#rid { rep: i }, 4)
362                    }
363                }
364            } else {
365                let rid = format_ident!("resource{}", vi);
366                quote! {
367                    let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
368                    let Some(v) = rts.#rid[i as usize].borrow() else {
369                        // todo: better error handling
370                        panic!("");
371                    };
372                    (v, 4)
373                }
374            }
375        }
376        Value::Var(tv, _) => {
377            let Some(Tyvar::Bound(n)) = tv else {
378                panic!("impossible tyvar")
379            };
380            let ResolvedBoundVar::Definite {
381                final_bound_var: n,
382                ty: Defined::Value(vt),
383            } = s.resolve_bound_var(*n)
384            else {
385                panic!("unresolvable tyvar (2)");
386            };
387            let vt = vt.clone();
388            emit_hl_unmarshal_toplevel_value(s, id, Tyvar::Bound(n), &vt)
389        }
390    }
391}
392
393/// Emit code to marshal a value from a toplevel type (i.e. types that
394/// cannot be represented inline in a valtype).
395/// - `id`: an ident of a Rust value of the Rust type (as defined by
396///   the [`crate::rtypes`] module) of the given value type that is
397///   being marshaled from
398/// - `tv`: the tyvar that we followed to get to this type
399/// - `vt`: the value type that we are marshaling
400///
401/// The token stream produced will be an expression which typechecks
402/// as `Vec<u8`>`.
403pub fn emit_hl_marshal_toplevel_value(
404    s: &mut State,
405    id: Ident,
406    tv: Tyvar,
407    vt: &Value,
408) -> TokenStream {
409    let tname = rtypes::emit_var_ref_value(s, &tv);
410    let mut s = s.clone();
411    let Tyvar::Bound(n) = tv else {
412        panic!("impossible tyvar")
413    };
414    s.var_offset += n as usize + 1;
415    let s = &mut s;
416    match vt {
417        Value::Record(rfs) => {
418            let retid = format_ident!("{}_record", id);
419            let fields = rfs
420                .iter()
421                .map(|rf| {
422                    let field_name = kebab_to_var(rf.name.name);
423                    let fieldid = format_ident!("{}_field_{}", id, field_name);
424                    let vtun = emit_hl_marshal_value(s, fieldid.clone(), &rf.ty);
425                    quote! {
426                        let #fieldid = #id.#field_name;
427                        #retid.extend({ #vtun });
428                    }
429                })
430                .collect::<Vec<_>>();
431            quote! {
432                let mut #retid = alloc::vec::Vec::new();
433                #(#fields)*
434                #retid
435            }
436        }
437        Value::Flags(ns) => {
438            let bytes = usize::div_ceil(ns.len(), 8);
439            let fields = ns
440                .iter()
441                .enumerate()
442                .map(|(i, n)| {
443                    let byte_offset = i / 8;
444                    let bit_offset = i % 8;
445                    let is_set = if s.is_wasmtime_guest {
446                        let const_name = kebab_to_flags_const(n.name);
447                        quote! { #id.contains(#tname::#const_name) }
448                    } else {
449                        let fieldid = kebab_to_var(n.name);
450                        quote! { #id.#fieldid }
451                    };
452                    quote! {
453                        bytes[#byte_offset] |= (if #is_set { 1 } else { 0 }) << #bit_offset;
454                    }
455                })
456                .collect::<Vec<_>>();
457            quote! {
458                let mut bytes = [0; #bytes];
459                #(#fields)*
460                alloc::vec::Vec::from(bytes)
461            }
462        }
463        Value::Variant(vcs) => {
464            let retid = format_ident!("{}_ret", id);
465            let bodyid = format_ident!("{}_body", id);
466            let vcs = vcs
467                .iter()
468                .enumerate()
469                .map(|(i, vc)| {
470                    let i = i as u32;
471                    let case_name = kebab_to_cons(vc.name.name);
472                    match &vc.ty {
473                        Some(ty) => {
474                            let vtun = emit_hl_marshal_value(s, bodyid.clone(), ty);
475                            quote! {
476                               #tname::#case_name(#bodyid) => {
477                                    #retid.extend(u32::to_ne_bytes(#i));
478                                    #retid.extend({ #vtun })
479                                }
480                            }
481                        }
482                        None => {
483                            quote! {
484                                #tname::#case_name => {
485                                    #retid.extend(u32::to_ne_bytes(#i));
486                                }
487                            }
488                        }
489                    }
490                })
491                .collect::<Vec<_>>();
492            quote! {
493                let mut #retid = alloc::vec::Vec::new();
494                match #id {
495                    #(#vcs)*
496                }
497                #retid
498            }
499        }
500        Value::Enum(ns) => {
501            let vcs = ns.iter().enumerate().map(|(i, n)| {
502                let case_name = kebab_to_cons(n.name);
503                let i = i as u32;
504                quote! { #tname::#case_name => #i }
505            });
506            quote! {
507                alloc::vec::Vec::from(u32::to_ne_bytes(match #id {
508                    #(#vcs,)*
509                }))
510            }
511        }
512        _ => emit_hl_marshal_value(s, id, vt),
513    }
514}
515
516/// Emit code to marshal a value from an inline-able value type
517/// - `id`: an ident of a Rust value of the Rust type (as defined by
518///   the [`crate::rtypes`] module) of the given value type that is
519///   being marshaled from
520/// - `vt`: the value type that we are marshaling
521///
522/// The token stream produced will be an expression which typechecks
523/// as `Vec<u8>`.
524pub fn emit_hl_marshal_value(s: &mut State, id: Ident, vt: &Value) -> TokenStream {
525    match vt {
526        Value::Bool => quote! {
527            alloc::vec![if #id { 1u8 } else { 0u8 }]
528        },
529        Value::S(_) | Value::U(_) | Value::F(_) => {
530            let (tid, _) = rtypes::numeric_rtype(vt);
531            quote! { alloc::vec::Vec::from(#tid::to_ne_bytes(#id)) }
532        }
533        Value::Char => quote! {
534            alloc::vec::Vec::from((#id as u32).to_ne_bytes())
535        },
536        Value::String => {
537            let retid = format_ident!("{}_string", id);
538            let bytesid = format_ident!("{}_bytes", id);
539            quote! {
540                let mut #retid = alloc::vec::Vec::new();
541                let #bytesid = #id.into_bytes();
542                #retid.extend(alloc::vec::Vec::from(u32::to_ne_bytes(#bytesid.len() as u32)));
543                #retid.extend(#bytesid);
544                #retid
545            }
546        }
547        Value::List(vt) => {
548            let retid = format_ident!("{}_list", id);
549            let inid = format_ident!("{}_elem", id);
550            let vtun = emit_hl_marshal_value(s, inid.clone(), vt);
551            quote! {
552                let mut #retid = alloc::vec::Vec::new();
553                let n = #id.len();
554                #retid.extend(alloc::vec::Vec::from(u32::to_ne_bytes(n as u32)));
555                for #inid in #id {
556                    #retid.extend({ #vtun })
557                }
558                #retid
559            }
560        }
561        Value::FixList(vt, _size) => {
562            let retid = format_ident!("{}_fixlist", id);
563            let inid = format_ident!("{}_elem", id);
564            let vtun = emit_hl_marshal_value(s, inid.clone(), vt);
565            quote! {
566                let mut #retid = alloc::vec::Vec::new();
567                for #inid in #id {
568                    #retid.extend({ #vtun })
569                }
570                #retid
571            }
572        }
573        Value::Record(_) => panic!("record not at top level of valtype"),
574        Value::Tuple(vts) => {
575            let retid = format_ident!("{}_tuple", id);
576            let inid = format_ident!("{}_elem", id);
577            let vtuns = vts.iter().enumerate().map(|(i, vt)| {
578                let i = syn::Index::from(i);
579                let vtun = emit_hl_marshal_value(s, inid.clone(), vt);
580                quote! {
581                    let #inid = #id.#i;
582                    #retid.extend({ #vtun });
583                }
584            });
585            quote! {
586                let mut #retid = alloc::vec::Vec::new();
587                #(#vtuns)*
588                #retid
589            }
590        }
591        Value::Flags(_) => panic!("flags not at top level of valtype"),
592        Value::Variant(_) => panic!("flags not at top level of valtype"),
593        Value::Enum(_) => panic!("flags not at top level of valtype"),
594        Value::Option(vt) => {
595            let bodyid = format_ident!("{}_body", id);
596            let retid = format_ident!("{}_ret", id);
597            let vtun = emit_hl_marshal_value(s, bodyid.clone(), vt);
598            quote! {
599                match #id {
600                    ::core::option::Option::Some(#bodyid) => {
601                        let mut #retid = alloc::vec::Vec::from(u8::to_ne_bytes(1));
602                        #retid.extend({ #vtun });
603                        #retid
604                    },
605                    ::core::option::Option::None => alloc::vec::Vec::from(u8::to_ne_bytes(0))
606                }
607            }
608        }
609        Value::Result(vt1, vt2) => {
610            let bodyid = format_ident!("{}_body", id);
611            let retid = format_ident!("{}_ret", id);
612            let vtun1 = if let Some(ref vt1) = **vt1 {
613                let vtun = emit_hl_marshal_value(s, bodyid.clone(), vt1);
614                quote! { #retid.extend({ #vtun }); }
615            } else {
616                quote! {}
617            };
618            let vtun2 = if let Some(ref vt2) = **vt2 {
619                let vtun = emit_hl_marshal_value(s, bodyid.clone(), vt2);
620                quote! { #retid.extend({ #vtun }); }
621            } else {
622                quote! {}
623            };
624            quote! {
625                match #id {
626                    ::core::result::Result::Ok(#bodyid) => {
627                        let mut #retid = alloc::vec::Vec::from(u8::to_ne_bytes(0));
628                        #vtun1
629                        #retid
630                    },
631                    ::core::result::Result::Err(#bodyid) => {
632                        let mut #retid = alloc::vec::Vec::from(u8::to_ne_bytes(1));
633                        #vtun2
634                        #retid
635                    },
636                }
637            }
638        }
639        Value::Own(ht) => {
640            let vi = resolve_handleable_to_resource(s, ht);
641            tracing::debug!("resolved ht to r (3) {:?} {:?}", ht, vi);
642            if s.is_guest {
643                let call = if s.is_wasmtime_guest {
644                    quote! { () }
645                } else {
646                    quote! {}
647                };
648                quote! {
649                    alloc::vec::Vec::from(u32::to_ne_bytes(#id.rep #call))
650                }
651            } else {
652                let rid = format_ident!("resource{}", vi);
653                quote! {
654                    let i = rts.#rid.len();
655                    rts.#rid.push_back(::hyperlight_common::resource::ResourceEntry::give(#id));
656                    alloc::vec::Vec::from(u32::to_ne_bytes(i as u32))
657                }
658            }
659        }
660        Value::Borrow(ht) => {
661            let vi = resolve_handleable_to_resource(s, ht);
662            tracing::debug!("resolved ht to r (6) {:?} {:?}", ht, vi);
663            if s.is_guest {
664                let call = if s.is_wasmtime_guest {
665                    quote! { () }
666                } else {
667                    quote! {}
668                };
669                quote! {
670                    alloc::vec::Vec::from(u32::to_ne_bytes(#id.rep #call))
671                }
672            } else {
673                let rid = format_ident!("resource{}", vi);
674                quote! {
675                    let i = rts.#rid.len();
676                    let (lrg, re) = ::hyperlight_common::resource::ResourceEntry::lend(#id);
677                    to_cleanup.push(Box::new(lrg));
678                    rts.#rid.push_back(re);
679                    alloc::vec::Vec::from(u32::to_ne_bytes(i as u32))
680                }
681            }
682        }
683        Value::Var(tv, _) => {
684            let Some(Tyvar::Bound(n)) = tv else {
685                panic!("impossible tyvar")
686            };
687            let ResolvedBoundVar::Definite {
688                final_bound_var: n,
689                ty: Defined::Value(vt),
690            } = s.resolve_bound_var(*n)
691            else {
692                panic!("unresolvable tyvar (2)");
693            };
694            let vt = vt.clone();
695            emit_hl_marshal_toplevel_value(s, id, Tyvar::Bound(n), &vt)
696        }
697    }
698}
699
700/// Emit code to unmarshal a parameter with value type `pt` from a
701/// slice named by `id`. The resultant token stream will be an
702/// expression which typechecks at the Rust type (as defined by the
703/// [`crate::rtypes`] module) of the given value type.
704pub fn emit_hl_unmarshal_param(s: &mut State, id: Ident, pt: &Value) -> TokenStream {
705    let toks = emit_hl_unmarshal_value(s, id, pt);
706    // Slight hack to avoid rust complaints about deserialised
707    // resource borrow lifetimes.
708    fn is_borrow(vt: &Value) -> bool {
709        match vt {
710            Value::Borrow(_) => true,
711            Value::Var(_, vt) => is_borrow(vt),
712            _ => false,
713        }
714    }
715    if s.is_guest && !s.is_wasmtime_guest && is_borrow(pt) {
716        quote! { &({ #toks }.0) }
717    } else {
718        quote! { { #toks }.0 }
719    }
720}
721
722/// Emit code to unmarshal the result of a function with result type
723/// `rt` from a slice named by `id`. The resultant token stream
724/// will be an expression which typechecks at the Rust type (as
725/// defined by the [`crate::rtypes`] module) of the unnamed type of
726/// the result, or unit if named results are used.
727///
728/// Precondition: the result type must only be a named result if there
729/// are no names in it (i.e. a unit type)
730pub fn emit_hl_unmarshal_result(s: &mut State, id: Ident, rt: &etypes::Result<'_>) -> TokenStream {
731    match rt {
732        Some(vt) => {
733            let toks = emit_hl_unmarshal_value(s, id, vt);
734            quote! { { #toks }.0 }
735        }
736        None => quote! { () },
737    }
738}
739
740/// Emit code to marshal a parameter with value type `pt` from a
741/// Rust value named by `id`. The resultant token stream will be an
742/// expression which typechecks as `Vec<u8>`.
743pub fn emit_hl_marshal_param(s: &mut State, id: Ident, pt: &Value) -> TokenStream {
744    let toks = emit_hl_marshal_value(s, id, pt);
745    quote! { { #toks } }
746}
747
748/// Emit code to marshal the result of a function with result type
749/// `rt` from a Rust value named by `id`. The resultant token stream
750/// will be an expression that which typechecks as `Vec<u8>`.
751///
752/// Precondition: the result type must only be a named result if there
753/// are no names in it (a unit type)
754pub fn emit_hl_marshal_result(s: &mut State, id: Ident, rt: &etypes::Result) -> TokenStream {
755    match rt {
756        None => quote! { ::alloc::vec::Vec::new() },
757        Some(vt) => {
758            let toks = emit_hl_marshal_value(s, id, vt);
759            quote! { { #toks } }
760        }
761    }
762}