1use 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
12pub 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
24pub 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
162pub 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
177pub 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()); (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 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 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 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
393pub 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
516pub 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
700pub fn emit_hl_unmarshal_param(s: &mut State, id: Ident, pt: &Value) -> TokenStream {
705 let toks = emit_hl_unmarshal_value(s, id, pt);
706 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
722pub 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
740pub 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
748pub 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}