1use 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
11pub 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
23pub 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
161pub 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
176pub 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()); (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 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 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 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
392pub 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
515pub 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
699pub fn emit_hl_unmarshal_param(s: &mut State, id: Ident, pt: &Value) -> TokenStream {
704 let toks = emit_hl_unmarshal_value(s, id, pt);
705 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
721pub 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
739pub 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
747pub 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}