1use itertools::Itertools;
18use proc_macro2::{Ident, TokenStream};
19use quote::{format_ident, quote};
20
21use crate::emit::{ResolvedBoundVar, State, kebab_to_cons, kebab_to_var};
22use crate::etypes::{self, Defined, Handleable, Tyvar, Value};
23use crate::rtypes;
24
25pub 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
37pub 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
154pub fn resolve_handleable_to_resource(s: &mut State, ht: &Handleable) -> u32 {
158 match ht {
159 Handleable::Var(Tyvar::Bound(vi)) => {
160 let ResolvedBoundVar::Resource { rtidx } = s.resolve_bound_var(*vi) else {
161 panic!("impossible: resource var is not resource");
162 };
163 rtidx
164 }
165 _ => panic!("impossible handleable in type"),
166 }
167}
168
169pub fn emit_hl_unmarshal_value(s: &mut State, id: Ident, vt: &Value) -> TokenStream {
182 match vt {
183 Value::Bool => quote! { (#id[0] != 0, 1) },
184 Value::S(_) | Value::U(_) | Value::F(_) => {
185 let (tid, width) = rtypes::numeric_rtype(vt);
186 let blen = width as usize / 8;
187 quote! {
188 (#tid::from_ne_bytes(#id[0..#blen].try_into().unwrap()), #blen)
189 }
190 }
191 Value::Char => quote! {
192 (unsafe { char::from_u32_unchecked(u32::from_ne_bytes(
193 #id[0..4].try_into().unwrap())) }, 4)
194 },
195 Value::String => quote! {
196 let n = u32::from_ne_bytes(#id[0..4].try_into().unwrap()) as usize;
197 let s = ::alloc::string::ToString::to_string(::core::str::from_utf8(&#id[4..4 + n]).unwrap()); (s, n + 4)
199 },
200 Value::List(vt) => {
201 let retid = format_ident!("{}_list", id);
202 let inid = format_ident!("{}_elem", id);
203 let vtun = emit_hl_unmarshal_value(s, inid.clone(), vt);
204 quote! {
205 let n = u32::from_ne_bytes(#id[0..4].try_into().unwrap()) as usize;
206 let mut #retid = alloc::vec::Vec::new();
207 let mut cursor = 4;
208 for i in 0..n {
209 let #inid = &#id[cursor..];
210 let (x, b) = { #vtun };
211 cursor += b;
212 #retid.push(x);
213 }
214 (#retid, cursor)
215 }
216 }
217 Value::FixList(vt, _) => {
218 let inid = format_ident!("{}_elem", id);
219 let vtun = emit_hl_unmarshal_value(s, inid.clone(), vt);
220 quote! {
221 let mut cursor = 0;
222 let arr = ::core::array::from_fn(|_i| {
223 let #inid = &#id[cursor..];
224 let (x, b) = { #vtun };
225 cursor += b;
226 x
227 });
228 (arr, cursor)
229 }
230 }
231 Value::Record(_) => panic!("record not at top level of valtype"),
232 Value::Tuple(vts) => {
233 let inid = format_ident!("{}_elem", id);
234 let len = format_ident!("{}_len", id);
235 let (ns, vtuns) = vts
236 .iter()
237 .enumerate()
238 .map(|(i, vt)| {
239 let vtun = emit_hl_unmarshal_value(s, inid.clone(), vt);
240 let retid = format_ident!("{}_elem{}", id, i);
241 (
242 retid.clone(),
243 quote! {
244 let (#retid, b) = { #vtun };
245 #len += b;
246 let #inid = &#inid[b..];
247 },
248 )
249 })
250 .unzip::<_, _, Vec<_>, Vec<_>>();
251 quote! {
252 let #inid = &#id[0..];
253 let mut #len = 0;
254 #(#vtuns)*
255 ((#(#ns),*), #len)
256 }
257 }
258 Value::Flags(_) => panic!("flags not at top level of valtype"),
259 Value::Variant(_) => panic!("variant not at top level of valtype"),
260 Value::Enum(_) => panic!("enum not at top level of valtype"),
261 Value::Option(vt) => {
262 let inid = format_ident!("{}_body", id);
263 let vtun = emit_hl_unmarshal_value(s, inid.clone(), vt);
264 quote! {
265 let n = u8::from_ne_bytes(#id[0..1].try_into().unwrap());
266 if n != 0 {
267 let #inid = &#id[1..];
268 let (x, b) = { #vtun };
269 (::core::option::Option::Some(x), b + 1)
270 } else {
271 (::core::option::Option::None, 1)
272 }
273 }
274 }
275 Value::Result(vt1, vt2) => {
276 let inid = format_ident!("{}_body", id);
277 let vtun1 = if let Some(ref vt1) = **vt1 {
278 emit_hl_unmarshal_value(s, inid.clone(), vt1)
279 } else {
280 quote! { ((), 0) }
281 };
282 let vtun2 = if let Some(ref vt2) = **vt2 {
283 emit_hl_unmarshal_value(s, inid.clone(), vt2)
284 } else {
285 quote! { ((), 0) }
286 };
287 quote! {
288 let i = u8::from_ne_bytes(#id[0..1].try_into().unwrap());
289 let #inid = &#id[1..];
290 if i == 0 {
291 let (x, b) = { #vtun1 };
292 (::core::result::Result::Ok(x), b + 1)
293 } else {
294 let (x, b)= { #vtun2 };
295 (::core::result::Result::Err(x), b +1)
296 }
297 }
298 }
299 Value::Own(ht) => {
300 let vi = resolve_handleable_to_resource(s, ht);
301 log::debug!("resolved ht to r (1) {:?} {:?}", ht, vi);
302 if s.is_guest {
303 let rid = format_ident!("HostResource{}", vi);
304 if s.is_wasmtime_guest {
305 quote! {
306 let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
307 (::wasmtime::component::Resource::<#rid>::new_own(i), 4)
308 }
309 } else {
310 quote! {
311 let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
312 (#rid { rep: i }, 4)
313 }
314 }
315 } else {
316 let rid = format_ident!("resource{}", vi);
317 quote! {
318 let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
319 let Some(v) = rts.#rid[i as usize].take() else {
320 panic!("");
322 };
323 (v, 4)
324 }
325 }
326 }
327 Value::Borrow(ht) => {
328 let vi = resolve_handleable_to_resource(s, ht);
329 log::debug!("resolved ht to r (2) {:?} {:?}", ht, vi);
330 if s.is_guest {
331 let rid = format_ident!("HostResource{}", vi);
332 if s.is_wasmtime_guest {
333 quote! {
334 let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
335 (::wasmtime::component::Resource::<#rid>::new_borrow(i), 4)
336 }
337 } else {
338 quote! {
351 let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
352
353 (#rid { rep: i }, 4)
354 }
355 }
356 } else {
357 let rid = format_ident!("resource{}", vi);
358 quote! {
359 let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
360 let Some(v) = rts.#rid[i as usize].borrow() else {
361 panic!("");
363 };
364 (v, 4)
365 }
366 }
367 }
368 Value::Var(tv, _) => {
369 let Some(Tyvar::Bound(n)) = tv else {
370 panic!("impossible tyvar")
371 };
372 let ResolvedBoundVar::Definite {
373 final_bound_var: n,
374 ty: Defined::Value(vt),
375 } = s.resolve_bound_var(*n)
376 else {
377 panic!("unresolvable tyvar (2)");
378 };
379 let vt = vt.clone();
380 emit_hl_unmarshal_toplevel_value(s, id, Tyvar::Bound(n), &vt)
381 }
382 }
383}
384
385pub fn emit_hl_marshal_toplevel_value(
396 s: &mut State,
397 id: Ident,
398 tv: Tyvar,
399 vt: &Value,
400) -> TokenStream {
401 let tname = rtypes::emit_var_ref_value(s, &tv);
402 let mut s = s.clone();
403 let Tyvar::Bound(n) = tv else {
404 panic!("impossible tyvar")
405 };
406 s.var_offset += n as usize + 1;
407 let s = &mut s;
408 match vt {
409 Value::Record(rfs) => {
410 let retid = format_ident!("{}_record", id);
411 let fields = rfs
412 .iter()
413 .map(|rf| {
414 let field_name = kebab_to_var(rf.name.name);
415 let fieldid = format_ident!("{}_field_{}", id, field_name);
416 let vtun = emit_hl_marshal_value(s, fieldid.clone(), &rf.ty);
417 quote! {
418 let #fieldid = #id.#field_name;
419 #retid.extend({ #vtun });
420 }
421 })
422 .collect::<Vec<_>>();
423 quote! {
424 let mut #retid = alloc::vec::Vec::new();
425 #(#fields)*
426 #retid
427 }
428 }
429 Value::Flags(ns) => {
430 let bytes = usize::div_ceil(ns.len(), 8);
431 let fields = ns
432 .iter()
433 .enumerate()
434 .map(|(i, n)| {
435 let byte_offset = i / 8;
436 let bit_offset = i % 8;
437 let fieldid = kebab_to_var(n.name);
438 quote! {
439 bytes[#byte_offset] |= (if #id.#fieldid { 1 } else { 0 }) << #bit_offset;
440 }
441 })
442 .collect::<Vec<_>>();
443 quote! {
444 let mut bytes = [0; #bytes];
445 #(#fields)*
446 alloc::vec::Vec::from(bytes)
447 }
448 }
449 Value::Variant(vcs) => {
450 let retid = format_ident!("{}_ret", id);
451 let bodyid = format_ident!("{}_body", id);
452 let vcs = vcs
453 .iter()
454 .enumerate()
455 .map(|(i, vc)| {
456 let i = i as u32;
457 let case_name = kebab_to_cons(vc.name.name);
458 match &vc.ty {
459 Some(ty) => {
460 let vtun = emit_hl_marshal_value(s, bodyid.clone(), ty);
461 quote! {
462 #tname::#case_name(#bodyid) => {
463 #retid.extend(u32::to_ne_bytes(#i));
464 #retid.extend({ #vtun })
465 }
466 }
467 }
468 None => {
469 quote! {
470 #tname::#case_name => {
471 #retid.extend(u32::to_ne_bytes(#i));
472 }
473 }
474 }
475 }
476 })
477 .collect::<Vec<_>>();
478 quote! {
479 let mut #retid = alloc::vec::Vec::new();
480 match #id {
481 #(#vcs)*
482 }
483 #retid
484 }
485 }
486 Value::Enum(ns) => {
487 let vcs = ns.iter().enumerate().map(|(i, n)| {
488 let case_name = kebab_to_cons(n.name);
489 let i = i as u32;
490 quote! { #tname::#case_name => #i }
491 });
492 quote! {
493 alloc::vec::Vec::from(u32::to_ne_bytes(match #id {
494 #(#vcs,)*
495 }))
496 }
497 }
498 _ => emit_hl_marshal_value(s, id, vt),
499 }
500}
501
502pub fn emit_hl_marshal_value(s: &mut State, id: Ident, vt: &Value) -> TokenStream {
511 match vt {
512 Value::Bool => quote! {
513 alloc::vec![if #id { 1u8 } else { 0u8 }]
514 },
515 Value::S(_) | Value::U(_) | Value::F(_) => {
516 let (tid, _) = rtypes::numeric_rtype(vt);
517 quote! { alloc::vec::Vec::from(#tid::to_ne_bytes(#id)) }
518 }
519 Value::Char => quote! {
520 alloc::vec::Vec::from((#id as u32).to_ne_bytes())
521 },
522 Value::String => {
523 let retid = format_ident!("{}_string", id);
524 let bytesid = format_ident!("{}_bytes", id);
525 quote! {
526 let mut #retid = alloc::vec::Vec::new();
527 let #bytesid = #id.into_bytes();
528 #retid.extend(alloc::vec::Vec::from(u32::to_ne_bytes(#bytesid.len() as u32)));
529 #retid.extend(#bytesid);
530 #retid
531 }
532 }
533 Value::List(vt) => {
534 let retid = format_ident!("{}_list", id);
535 let inid = format_ident!("{}_elem", id);
536 let vtun = emit_hl_marshal_value(s, inid.clone(), vt);
537 quote! {
538 let mut #retid = alloc::vec::Vec::new();
539 let n = #id.len();
540 #retid.extend(alloc::vec::Vec::from(u32::to_ne_bytes(n as u32)));
541 for #inid in #id {
542 #retid.extend({ #vtun })
543 }
544 #retid
545 }
546 }
547 Value::FixList(vt, _size) => {
548 let retid = format_ident!("{}_fixlist", 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 for #inid in #id {
554 #retid.extend({ #vtun })
555 }
556 #retid
557 }
558 }
559 Value::Record(_) => panic!("record not at top level of valtype"),
560 Value::Tuple(vts) => {
561 let retid = format_ident!("{}_tuple", id);
562 let inid = format_ident!("{}_elem", id);
563 let vtuns = vts.iter().enumerate().map(|(i, vt)| {
564 let i = syn::Index::from(i);
565 let vtun = emit_hl_marshal_value(s, inid.clone(), vt);
566 quote! {
567 let #inid = #id.#i;
568 #retid.extend({ #vtun });
569 }
570 });
571 quote! {
572 let mut #retid = alloc::vec::Vec::new();
573 #(#vtuns)*
574 #retid
575 }
576 }
577 Value::Flags(_) => panic!("flags not at top level of valtype"),
578 Value::Variant(_) => panic!("flags not at top level of valtype"),
579 Value::Enum(_) => panic!("flags not at top level of valtype"),
580 Value::Option(vt) => {
581 let bodyid = format_ident!("{}_body", id);
582 let retid = format_ident!("{}_ret", id);
583 let vtun = emit_hl_marshal_value(s, bodyid.clone(), vt);
584 quote! {
585 match #id {
586 ::core::option::Option::Some(#bodyid) => {
587 let mut #retid = alloc::vec::Vec::from(u8::to_ne_bytes(1));
588 #retid.extend({ #vtun });
589 #retid
590 },
591 ::core::option::Option::None => alloc::vec::Vec::from(u8::to_ne_bytes(0))
592 }
593 }
594 }
595 Value::Result(vt1, vt2) => {
596 let bodyid = format_ident!("{}_body", id);
597 let retid = format_ident!("{}_ret", id);
598 let vtun1 = if let Some(ref vt1) = **vt1 {
599 let vtun = emit_hl_marshal_value(s, bodyid.clone(), vt1);
600 quote! { #retid.extend({ #vtun }); }
601 } else {
602 quote! {}
603 };
604 let vtun2 = if let Some(ref vt2) = **vt2 {
605 let vtun = emit_hl_marshal_value(s, bodyid.clone(), vt2);
606 quote! { #retid.extend({ #vtun }); }
607 } else {
608 quote! {}
609 };
610 quote! {
611 match #id {
612 ::core::result::Result::Ok(#bodyid) => {
613 let mut #retid = alloc::vec::Vec::from(u8::to_ne_bytes(0));
614 #vtun1
615 #retid
616 },
617 ::core::result::Result::Err(#bodyid) => {
618 let mut #retid = alloc::vec::Vec::from(u8::to_ne_bytes(1));
619 #vtun2
620 #retid
621 },
622 }
623 }
624 }
625 Value::Own(ht) => {
626 let vi = resolve_handleable_to_resource(s, ht);
627 log::debug!("resolved ht to r (3) {:?} {:?}", ht, vi);
628 if s.is_guest {
629 let call = if s.is_wasmtime_guest {
630 quote! { () }
631 } else {
632 quote! {}
633 };
634 quote! {
635 alloc::vec::Vec::from(u32::to_ne_bytes(#id.rep #call))
636 }
637 } else {
638 let rid = format_ident!("resource{}", vi);
639 quote! {
640 let i = rts.#rid.len();
641 rts.#rid.push_back(::hyperlight_common::resource::ResourceEntry::give(#id));
642 alloc::vec::Vec::from(u32::to_ne_bytes(i as u32))
643 }
644 }
645 }
646 Value::Borrow(ht) => {
647 let vi = resolve_handleable_to_resource(s, ht);
648 log::debug!("resolved ht to r (6) {:?} {:?}", ht, vi);
649 if s.is_guest {
650 let call = if s.is_wasmtime_guest {
651 quote! { () }
652 } else {
653 quote! {}
654 };
655 quote! {
656 alloc::vec::Vec::from(u32::to_ne_bytes(#id.rep #call))
657 }
658 } else {
659 let rid = format_ident!("resource{}", vi);
660 quote! {
661 let i = rts.#rid.len();
662 let (lrg, re) = ::hyperlight_common::resource::ResourceEntry::lend(#id);
663 to_cleanup.push(Box::new(lrg));
664 rts.#rid.push_back(re);
665 alloc::vec::Vec::from(u32::to_ne_bytes(i as u32))
666 }
667 }
668 }
669 Value::Var(tv, _) => {
670 let Some(Tyvar::Bound(n)) = tv else {
671 panic!("impossible tyvar")
672 };
673 let ResolvedBoundVar::Definite {
674 final_bound_var: n,
675 ty: Defined::Value(vt),
676 } = s.resolve_bound_var(*n)
677 else {
678 panic!("unresolvable tyvar (2)");
679 };
680 let vt = vt.clone();
681 emit_hl_marshal_toplevel_value(s, id, Tyvar::Bound(n), &vt)
682 }
683 }
684}
685
686pub fn emit_hl_unmarshal_param(s: &mut State, id: Ident, pt: &Value) -> TokenStream {
691 let toks = emit_hl_unmarshal_value(s, id, pt);
692 fn is_borrow(vt: &Value) -> bool {
695 match vt {
696 Value::Borrow(_) => true,
697 Value::Var(_, vt) => is_borrow(vt),
698 _ => false,
699 }
700 }
701 if s.is_guest && !s.is_wasmtime_guest && is_borrow(pt) {
702 quote! { &({ #toks }.0) }
703 } else {
704 quote! { { #toks }.0 }
705 }
706}
707
708pub fn emit_hl_unmarshal_result(s: &mut State, id: Ident, rt: &etypes::Result<'_>) -> TokenStream {
717 match rt {
718 Some(vt) => {
719 let toks = emit_hl_unmarshal_value(s, id, vt);
720 quote! { { #toks }.0 }
721 }
722 None => quote! { () },
723 }
724}
725
726pub fn emit_hl_marshal_param(s: &mut State, id: Ident, pt: &Value) -> TokenStream {
730 let toks = emit_hl_marshal_value(s, id, pt);
731 quote! { { #toks } }
732}
733
734pub fn emit_hl_marshal_result(s: &mut State, id: Ident, rt: &etypes::Result) -> TokenStream {
741 match rt {
742 None => quote! { ::alloc::vec::Vec::new() },
743 Some(vt) => {
744 let toks = emit_hl_marshal_value(s, id, vt);
745 quote! { { #toks } }
746 }
747 }
748}