hyperlight_component_util/
hl.rs

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