1use 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
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
154fn 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}
166pub 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
178pub 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()); (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 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 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
370pub 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
487pub 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
665pub 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
674pub 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
692pub 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
700pub 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}