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::FixList(vt, _) => {
227            let inid = format_ident!("{}_elem", id);
228            let vtun = emit_hl_unmarshal_value(s, inid.clone(), vt);
229            quote! {
230                let mut cursor = 0;
231                let arr = ::core::array::from_fn(|_i| {
232                    let #inid = &#id[cursor..];
233                    let (x, b) = { #vtun };
234                    cursor += b;
235                    x
236                });
237                (arr, cursor)
238            }
239        }
240        Value::Record(_) => panic!("record not at top level of valtype"),
241        Value::Tuple(vts) => {
242            let inid = format_ident!("{}_elem", id);
243            let len = format_ident!("{}_len", id);
244            let (ns, vtuns) = vts
245                .iter()
246                .enumerate()
247                .map(|(i, vt)| {
248                    let vtun = emit_hl_unmarshal_value(s, inid.clone(), vt);
249                    let retid = format_ident!("{}_elem{}", id, i);
250                    (
251                        retid.clone(),
252                        quote! {
253                            let (#retid, b) = { #vtun };
254                            #len += b;
255                            let #inid = &#inid[b..];
256                        },
257                    )
258                })
259                .unzip::<_, _, Vec<_>, Vec<_>>();
260            quote! {
261                let #inid = &#id[0..];
262                let mut #len = 0;
263                #(#vtuns)*
264                ((#(#ns),*), #len)
265            }
266        }
267        Value::Flags(_) => panic!("flags not at top level of valtype"),
268        Value::Variant(_) => panic!("variant not at top level of valtype"),
269        Value::Enum(_) => panic!("enum not at top level of valtype"),
270        Value::Option(vt) => {
271            let inid = format_ident!("{}_body", id);
272            let vtun = emit_hl_unmarshal_value(s, inid.clone(), vt);
273            quote! {
274                let n = u8::from_ne_bytes(#id[0..1].try_into().unwrap());
275                if n != 0 {
276                    let #inid = &#id[1..];
277                    let (x, b) = { #vtun };
278                    (::core::option::Option::Some(x), b + 1)
279                } else {
280                    (::core::option::Option::None, 1)
281                }
282            }
283        }
284        Value::Result(vt1, vt2) => {
285            let inid = format_ident!("{}_body", id);
286            let vtun1 = if let Some(ref vt1) = **vt1 {
287                emit_hl_unmarshal_value(s, inid.clone(), vt1)
288            } else {
289                quote! { ((), 0) }
290            };
291            let vtun2 = if let Some(ref vt2) = **vt2 {
292                emit_hl_unmarshal_value(s, inid.clone(), vt2)
293            } else {
294                quote! { ((), 0) }
295            };
296            quote! {
297                let i = u8::from_ne_bytes(#id[0..1].try_into().unwrap());
298                let #inid = &#id[1..];
299                if i == 0 {
300                    let (x, b) = { #vtun1 };
301                    (::core::result::Result::Ok(x), b + 1)
302                } else {
303                    let (x, b)= { #vtun2 };
304                    (::core::result::Result::Err(x), b +1)
305                }
306            }
307        }
308        Value::Own(ht) => {
309            let vi = resolve_handleable_to_resource(s, ht);
310            log::debug!("resolved ht to r (1) {:?} {:?}", ht, vi);
311            if s.is_guest {
312                let rid = format_ident!("HostResource{}", vi);
313                if s.is_wasmtime_guest {
314                    quote! {
315                        let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
316                        (::wasmtime::component::Resource::<#rid>::new_own(i), 4)
317                    }
318                } else {
319                    quote! {
320                        let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
321                        (#rid { rep: i }, 4)
322                    }
323                }
324            } else {
325                let rid = format_ident!("resource{}", vi);
326                quote! {
327                    let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
328                    let Some(v) = rts.#rid[i as usize].take() else {
329                        // todo: better error handling
330                        panic!("");
331                    };
332                    (v, 4)
333                }
334            }
335        }
336        Value::Borrow(ht) => {
337            let vi = resolve_handleable_to_resource(s, ht);
338            log::debug!("resolved ht to r (2) {:?} {:?}", ht, vi);
339            if s.is_guest {
340                let rid = format_ident!("HostResource{}", vi);
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                let rid = format_ident!("resource{}", vi);
347                quote! {
348                    let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
349                    let Some(v) = rts.#rid[i as usize].borrow() else {
350                        // todo: better error handling
351                        panic!("");
352                    };
353                    (v, 4)
354                }
355            }
356        }
357        Value::Var(tv, _) => {
358            let Some(Tyvar::Bound(n)) = tv else {
359                panic!("impossible tyvar")
360            };
361            let (n, Some(Defined::Value(vt))) = s.resolve_tv(*n) else {
362                panic!("unresolvable tyvar (2)");
363            };
364            let vt = vt.clone();
365            emit_hl_unmarshal_toplevel_value(s, id, Tyvar::Bound(n), &vt)
366        }
367    }
368}
369
370/// Emit code to marshal a value from a toplevel type (i.e. types that
371/// cannot be represented inline in a valtype).
372/// - `id`: an ident of a Rust value of the Rust type (as defined by
373///   the [`crate::rtypes`] module) of the given value type that is
374///   being marshaled from
375/// - `tv`: the tyvar that we followed to get to this type
376/// - `vt`: the value type that we are marshaling
377///
378/// The token stream produced will be an expression which typechecks
379/// as `Vec<u8`>`.
380pub fn emit_hl_marshal_toplevel_value(
381    s: &mut State,
382    id: Ident,
383    tv: Tyvar,
384    vt: &Value,
385) -> TokenStream {
386    let tname = rtypes::emit_var_ref_value(s, &tv);
387    let mut s = s.clone();
388    let Tyvar::Bound(n) = tv else {
389        panic!("impossible tyvar")
390    };
391    s.var_offset += n as usize + 1;
392    let s = &mut s;
393    match vt {
394        Value::Record(rfs) => {
395            let retid = format_ident!("{}_record", id);
396            let fields = rfs
397                .iter()
398                .map(|rf| {
399                    let field_name = kebab_to_var(rf.name.name);
400                    let fieldid = format_ident!("{}_field_{}", id, field_name);
401                    let vtun = emit_hl_marshal_value(s, fieldid.clone(), &rf.ty);
402                    quote! {
403                        let #fieldid = #id.#field_name;
404                        #retid.extend({ #vtun });
405                    }
406                })
407                .collect::<Vec<_>>();
408            quote! {
409                let mut #retid = alloc::vec::Vec::new();
410                #(#fields)*
411                #retid
412            }
413        }
414        Value::Flags(ns) => {
415            let bytes = usize::div_ceil(ns.len(), 8);
416            let fields = ns
417                .iter()
418                .enumerate()
419                .map(|(i, n)| {
420                    let byte_offset = i / 8;
421                    let bit_offset = i % 8;
422                    let fieldid = kebab_to_var(n.name);
423                    quote! {
424                        bytes[#byte_offset] |= (if #id.#fieldid { 1 } else { 0 }) << #bit_offset;
425                    }
426                })
427                .collect::<Vec<_>>();
428            quote! {
429                let mut bytes = [0; #bytes];
430                #(#fields)*
431                alloc::vec::Vec::from(bytes)
432            }
433        }
434        Value::Variant(vcs) => {
435            let retid = format_ident!("{}_ret", id);
436            let bodyid = format_ident!("{}_body", id);
437            let vcs = vcs
438                .iter()
439                .enumerate()
440                .map(|(i, vc)| {
441                    let i = i as u32;
442                    let case_name = kebab_to_cons(vc.name.name);
443                    match &vc.ty {
444                        Some(ty) => {
445                            let vtun = emit_hl_marshal_value(s, bodyid.clone(), ty);
446                            quote! {
447                               #tname::#case_name(#bodyid) => {
448                                    #retid.extend(u32::to_ne_bytes(#i));
449                                    #retid.extend({ #vtun })
450                                }
451                            }
452                        }
453                        None => {
454                            quote! {
455                                #tname::#case_name => {
456                                    #retid.extend(u32::to_ne_bytes(#i));
457                                }
458                            }
459                        }
460                    }
461                })
462                .collect::<Vec<_>>();
463            quote! {
464                let mut #retid = alloc::vec::Vec::new();
465                match #id {
466                    #(#vcs)*
467                }
468                #retid
469            }
470        }
471        Value::Enum(ns) => {
472            let vcs = ns.iter().enumerate().map(|(i, n)| {
473                let case_name = kebab_to_cons(n.name);
474                let i = i as u32;
475                quote! { #tname::#case_name => #i }
476            });
477            quote! {
478                alloc::vec::Vec::from(u32::to_ne_bytes(match #id {
479                    #(#vcs,)*
480                }))
481            }
482        }
483        _ => emit_hl_marshal_value(s, id, vt),
484    }
485}
486
487/// Emit code to marshal a value from an inline-able value type
488/// - `id`: an ident of a Rust value of the Rust type (as defined by
489///   the [`crate::rtypes`] module) of the given value type that is
490///   being marshaled from
491/// - `vt`: the value type that we are marshaling
492///
493/// The token stream produced will be an expression which typechecks
494/// as `Vec<u8>`.
495pub fn emit_hl_marshal_value(s: &mut State, id: Ident, vt: &Value) -> TokenStream {
496    match vt {
497        Value::Bool => quote! {
498            alloc::vec![if #id { 1u8 } else { 0u8 }]
499        },
500        Value::S(_) | Value::U(_) | Value::F(_) => {
501            let (tid, _) = rtypes::numeric_rtype(vt);
502            quote! { alloc::vec::Vec::from(#tid::to_ne_bytes(#id)) }
503        }
504        Value::Char => quote! {
505            alloc::vec::Vec::from((#id as u32).to_ne_bytes())
506        },
507        Value::String => {
508            let retid = format_ident!("{}_string", id);
509            let bytesid = format_ident!("{}_bytes", id);
510            quote! {
511                let mut #retid = alloc::vec::Vec::new();
512                let #bytesid = #id.into_bytes();
513                #retid.extend(alloc::vec::Vec::from(u32::to_ne_bytes(#bytesid.len() as u32)));
514                #retid.extend(#bytesid);
515                #retid
516            }
517        }
518        Value::List(vt) => {
519            let retid = format_ident!("{}_list", id);
520            let inid = format_ident!("{}_elem", id);
521            let vtun = emit_hl_marshal_value(s, inid.clone(), vt);
522            quote! {
523                let mut #retid = alloc::vec::Vec::new();
524                let n = #id.len();
525                #retid.extend(alloc::vec::Vec::from(u32::to_ne_bytes(n as u32)));
526                for #inid in #id {
527                    #retid.extend({ #vtun })
528                }
529                #retid
530            }
531        }
532        Value::FixList(vt, _size) => {
533            let retid = format_ident!("{}_fixlist", id);
534            let inid = format_ident!("{}_elem", id);
535            let vtun = emit_hl_marshal_value(s, inid.clone(), vt);
536            quote! {
537                let mut #retid = alloc::vec::Vec::new();
538                for #inid in #id {
539                    #retid.extend({ #vtun })
540                }
541                #retid
542            }
543        }
544        Value::Record(_) => panic!("record not at top level of valtype"),
545        Value::Tuple(vts) => {
546            let retid = format_ident!("{}_tuple", id);
547            let inid = format_ident!("{}_elem", id);
548            let vtuns = vts.iter().enumerate().map(|(i, vt)| {
549                let i = syn::Index::from(i);
550                let vtun = emit_hl_marshal_value(s, inid.clone(), vt);
551                quote! {
552                    let #inid = #id.#i;
553                    #retid.extend({ #vtun });
554                }
555            });
556            quote! {
557                let mut #retid = alloc::vec::Vec::new();
558                #(#vtuns)*
559                #retid
560            }
561        }
562        Value::Flags(_) => panic!("flags not at top level of valtype"),
563        Value::Variant(_) => panic!("flags not at top level of valtype"),
564        Value::Enum(_) => panic!("flags not at top level of valtype"),
565        Value::Option(vt) => {
566            let bodyid = format_ident!("{}_body", id);
567            let retid = format_ident!("{}_ret", id);
568            let vtun = emit_hl_marshal_value(s, bodyid.clone(), vt);
569            quote! {
570                match #id {
571                    ::core::option::Option::Some(#bodyid) => {
572                        let mut #retid = alloc::vec::Vec::from(u8::to_ne_bytes(1));
573                        #retid.extend({ #vtun });
574                        #retid
575                    },
576                    ::core::option::Option::None => alloc::vec::Vec::from(u8::to_ne_bytes(0))
577                }
578            }
579        }
580        Value::Result(vt1, vt2) => {
581            let bodyid = format_ident!("{}_body", id);
582            let retid = format_ident!("{}_ret", id);
583            let vtun1 = if let Some(ref vt1) = **vt1 {
584                let vtun = emit_hl_marshal_value(s, bodyid.clone(), vt1);
585                quote! { #retid.extend({ #vtun }); }
586            } else {
587                quote! {}
588            };
589            let vtun2 = if let Some(ref vt2) = **vt2 {
590                let vtun = emit_hl_marshal_value(s, bodyid.clone(), vt2);
591                quote! { #retid.extend({ #vtun }); }
592            } else {
593                quote! {}
594            };
595            quote! {
596                match #id {
597                    ::core::result::Result::Ok(#bodyid) => {
598                        let mut #retid = alloc::vec::Vec::from(u8::to_ne_bytes(0));
599                        #vtun1
600                        #retid
601                    },
602                    ::core::result::Result::Err(#bodyid) => {
603                        let mut #retid = alloc::vec::Vec::from(u8::to_ne_bytes(1));
604                        #vtun2
605                        #retid
606                    },
607                }
608            }
609        }
610        Value::Own(ht) => {
611            let vi = resolve_handleable_to_resource(s, ht);
612            log::debug!("resolved ht to r (3) {:?} {:?}", ht, vi);
613            if s.is_guest {
614                let call = if s.is_wasmtime_guest {
615                    quote! { () }
616                } else {
617                    quote! {}
618                };
619                quote! {
620                    alloc::vec::Vec::from(u32::to_ne_bytes(#id.rep #call))
621                }
622            } else {
623                let rid = format_ident!("resource{}", vi);
624                quote! {
625                    let i = rts.#rid.len();
626                    rts.#rid.push_back(::hyperlight_common::resource::ResourceEntry::give(#id));
627                    alloc::vec::Vec::from(u32::to_ne_bytes(i as u32))
628                }
629            }
630        }
631        Value::Borrow(ht) => {
632            let vi = resolve_handleable_to_resource(s, ht);
633            log::debug!("resolved ht to r (6) {:?} {:?}", ht, vi);
634            if s.is_guest {
635                let call = if s.is_wasmtime_guest {
636                    quote! { () }
637                } else {
638                    quote! {}
639                };
640                quote! {
641                    alloc::vec::Vec::from(u32::to_ne_bytes(#id.rep #call))
642                }
643            } else {
644                let rid = format_ident!("resource{}", vi);
645                quote! {
646                    let i = rts.#rid.len();
647                    rts.#rid.push_back(::hyperlight_common::resource::ResourceEntry::lend(#id));
648                    alloc::vec::Vec::from(u32::to_ne_bytes(i as u32))
649                }
650            }
651        }
652        Value::Var(tv, _) => {
653            let Some(Tyvar::Bound(n)) = tv else {
654                panic!("impossible tyvar")
655            };
656            let (n, Some(Defined::Value(vt))) = s.resolve_tv(*n) else {
657                panic!("unresolvable tyvar (2)");
658            };
659            let vt = vt.clone();
660            emit_hl_marshal_toplevel_value(s, id, Tyvar::Bound(n), &vt)
661        }
662    }
663}
664
665/// Emit code to unmarshal a parameter with value type `pt` from a
666/// slice named by `id`. The resultant token stream will be an
667/// expression which typechecks at the Rust type (as defined by the
668/// [`crate::rtypes`] module) of the given value type.
669pub fn emit_hl_unmarshal_param(s: &mut State, id: Ident, pt: &Value) -> TokenStream {
670    let toks = emit_hl_unmarshal_value(s, id, pt);
671    quote! { { #toks }.0 }
672}
673
674/// Emit code to unmarshal the result of a function with result type
675/// `rt` from a slice named by `id`. The resultant token stream
676/// will be an expression which typechecks at the Rust type (as
677/// defined by the [`crate::rtypes`] module) of the unnamed type of
678/// the result, or unit if named results are used.
679///
680/// Precondition: the result type must only be a named result if there
681/// are no names in it (i.e. a unit type)
682pub fn emit_hl_unmarshal_result(s: &mut State, id: Ident, rt: &etypes::Result<'_>) -> TokenStream {
683    match rt {
684        Some(vt) => {
685            let toks = emit_hl_unmarshal_value(s, id, vt);
686            quote! { { #toks }.0 }
687        }
688        None => quote! { () },
689    }
690}
691
692/// Emit code to marshal a parameter with value type `pt` from a
693/// Rust value named by `id`. The resultant token stream will be an
694/// expression which typechecks as `Vec<u8>`.
695pub fn emit_hl_marshal_param(s: &mut State, id: Ident, pt: &Value) -> TokenStream {
696    let toks = emit_hl_marshal_value(s, id, pt);
697    quote! { { #toks } }
698}
699
700/// Emit code to marshal the result of a function with result type
701/// `rt` from a Rust value named by `id`. The resultant token stream
702/// will be an expression that which typechecks as `Vec<u8>`.
703///
704/// Precondition: the result type must only be a named result if there
705/// are no names in it (a unit type)
706pub fn emit_hl_marshal_result(s: &mut State, id: Ident, rt: &etypes::Result) -> TokenStream {
707    match rt {
708        None => quote! { ::alloc::vec::Vec::new() },
709        Some(vt) => {
710            let toks = emit_hl_marshal_value(s, id, vt);
711            quote! { { #toks } }
712        }
713    }
714}