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