Skip to main content

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