1use proc_macro2::{Ident, TokenStream};
18use quote::{format_ident, quote};
19
20use crate::emit::{
21 FnName, ResourceItemName, State, WitName, kebab_to_exports_name, kebab_to_fn, kebab_to_getter,
22 kebab_to_imports_name, kebab_to_namespace, kebab_to_type, kebab_to_var, split_wit_name,
23};
24use crate::etypes::{Component, ExternDecl, ExternDesc, Instance, Tyvar};
25use crate::hl::{
26 emit_fn_hl_name, emit_hl_marshal_param, emit_hl_marshal_result, emit_hl_unmarshal_param,
27 emit_hl_unmarshal_result,
28};
29use crate::{resource, rtypes};
30
31fn emit_export_extern_decl<'a, 'b, 'c>(
35 s: &'c mut State<'a, 'b>,
36 ed: &'c ExternDecl<'b>,
37) -> TokenStream {
38 match &ed.desc {
39 ExternDesc::CoreModule(_) => panic!("core module (im/ex)ports are not supported"),
40 ExternDesc::Func(ft) => {
41 match kebab_to_fn(ed.kebab_name) {
42 FnName::Plain(n) => {
43 let param_decls = ft
44 .params
45 .iter()
46 .map(|p| rtypes::emit_func_param(s, p))
47 .collect::<Vec<_>>();
48 let result_decl = rtypes::emit_func_result(s, &ft.result);
49 let hln = emit_fn_hl_name(s, ed.kebab_name);
50 let ret = format_ident!("ret");
51 let marshal = ft
52 .params
53 .iter()
54 .map(|p| emit_hl_marshal_param(s, kebab_to_var(p.name.name), &p.ty))
55 .collect::<Vec<_>>();
56 let unmarshal = emit_hl_unmarshal_result(s, ret.clone(), &ft.result);
57 quote! {
58 fn #n(&mut self, #(#param_decls),*) -> #result_decl {
59 let #ret = ::hyperlight_host::sandbox::Callable::call::<::std::vec::Vec::<u8>>(&mut self.sb,
60 #hln,
61 (#(#marshal,)*)
62 );
63 let ::std::result::Result::Ok(#ret) = #ret else { panic!("bad return from guest {:?}", #ret) };
64 #[allow(clippy::unused_unit)]
65 #unmarshal
66 }
67 }
68 }
69 FnName::Associated(_, _) =>
70 {
73 panic!("guest resources are not currently supported")
74 }
75 }
76 }
77 ExternDesc::Type(_) => {
78 quote! {}
80 }
81 ExternDesc::Instance(it) => {
82 let wn = split_wit_name(ed.kebab_name);
83 emit_export_instance(s, wn.clone(), it);
84
85 let getter = kebab_to_getter(wn.name);
86 let tn = kebab_to_type(wn.name);
87 quote! {
88 type #tn = Self;
89 #[allow(refining_impl_trait)]
90 fn #getter<'a>(&'a mut self) -> &'a mut Self {
91 self
92 }
93 }
94 }
95 ExternDesc::Component(_) => {
96 panic!("nested components not yet supported in rust bindings");
97 }
98 }
99}
100
101fn emit_export_instance<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, it: &'c Instance<'b>) {
105 let mut s = s.with_cursor(wn.namespace_idents());
106 s.cur_helper_mod = Some(kebab_to_namespace(wn.name));
107
108 let exports = it
109 .exports
110 .iter()
111 .map(|ed| emit_export_extern_decl(&mut s, ed))
112 .collect::<Vec<_>>();
113
114 let ns = wn.namespace_path();
115 let nsi = wn.namespace_idents();
116 let trait_name = kebab_to_type(wn.name);
117 let r#trait = s.r#trait(&nsi, trait_name.clone());
118 let tvs = r#trait
119 .tvs
120 .iter()
121 .map(|(_, (tv, _))| tv.unwrap())
122 .collect::<Vec<_>>();
123 let tvs = tvs
124 .iter()
125 .map(|tv| rtypes::emit_var_ref(&mut s, &Tyvar::Bound(*tv)))
126 .collect::<Vec<_>>();
127 let (root_ns, root_base_name) = s.root_component_name.unwrap();
128 let wrapper_name = kebab_to_wrapper_name(root_base_name);
129 let imports_name = kebab_to_imports_name(root_base_name);
130 s.root_mod.items.extend(quote! {
131 impl<I: #root_ns::#imports_name, S: ::hyperlight_host::sandbox::Callable> #ns::#trait_name <#(#tvs),*> for #wrapper_name<I, S> {
132 #(#exports)*
133 }
134 });
135}
136
137#[derive(Clone)]
140struct SelfInfo {
141 orig_id: Ident,
142 type_id: Vec<Ident>,
143 outer_id: Ident,
144 inner_preamble: TokenStream,
145 inner_id: Ident,
146}
147impl SelfInfo {
148 fn new(orig_id: Ident) -> Self {
149 let outer_id = format_ident!("captured_{}", orig_id);
150 let inner_id = format_ident!("slf");
151 SelfInfo {
152 orig_id,
153 type_id: vec![format_ident!("I")],
154 inner_preamble: quote! {
155 let mut #inner_id = #outer_id.lock().unwrap();
156 let mut #inner_id = ::std::ops::DerefMut::deref_mut(&mut #inner_id);
157 },
158 outer_id,
159 inner_id,
160 }
161 }
162 fn with_getter(&self, tp: TokenStream, type_name: Ident, getter: Ident) -> Self {
165 let mut toks = self.inner_preamble.clone();
166 let id = self.inner_id.clone();
167 let mut type_id = self.type_id.clone();
168 toks.extend(quote! {
169 let mut #id = #tp::#getter(::std::borrow::BorrowMut::<#(#type_id)::*>::borrow_mut(&mut #id));
170 });
171 type_id.push(type_name);
172 SelfInfo {
173 orig_id: self.orig_id.clone(),
174 type_id,
175 outer_id: self.outer_id.clone(),
176 inner_preamble: toks,
177 inner_id: id,
178 }
179 }
180}
181
182fn emit_import_extern_decl<'a, 'b, 'c>(
189 s: &'c mut State<'a, 'b>,
190 get_self: SelfInfo,
191 ed: &'c ExternDecl<'b>,
192) -> TokenStream {
193 match &ed.desc {
194 ExternDesc::CoreModule(_) => panic!("core module (im/ex)ports are not supported"),
195 ExternDesc::Func(ft) => {
196 let hln = emit_fn_hl_name(s, ed.kebab_name);
197 log::debug!("providing host function {}", hln);
198 let (pds, pus) = ft
199 .params
200 .iter()
201 .map(|p| {
202 let id = kebab_to_var(p.name.name);
203 (
204 quote! { #id: ::std::vec::Vec<u8> },
205 emit_hl_unmarshal_param(s, id, &p.ty),
206 )
207 })
208 .unzip::<_, _, Vec<_>, Vec<_>>();
209 let tp = s.cur_trait_path();
210 let callname = match kebab_to_fn(ed.kebab_name) {
211 FnName::Plain(n) => quote! { #tp::#n },
212 FnName::Associated(r, m) => {
213 let hp = s.helper_path();
214 match m {
215 ResourceItemName::Constructor => quote! { #hp #r::new },
216 ResourceItemName::Method(mn) => quote! { #hp #r::#mn },
217 ResourceItemName::Static(mn) => quote! { #hp #r::#mn },
218 }
219 }
220 };
221 let SelfInfo {
222 orig_id,
223 type_id,
224 outer_id,
225 inner_preamble,
226 inner_id,
227 } = get_self;
228 let ret = format_ident!("ret");
229 let marshal_result = emit_hl_marshal_result(s, ret.clone(), &ft.result);
230 quote! {
231 let #outer_id = #orig_id.clone();
232 let captured_rts = rts.clone();
233 sb.register_host_function(#hln, move |#(#pds),*| {
234 let mut rts = captured_rts.lock().unwrap();
235 #inner_preamble
236 let #ret = #callname(
237 ::std::borrow::BorrowMut::<#(#type_id)::*>::borrow_mut(
238 &mut #inner_id
239 ),
240 #(#pus),*
241 );
242 Ok(#marshal_result)
243 })
244 .unwrap();
245 }
246 }
247 ExternDesc::Type(_) => {
248 quote! {}
250 }
251 ExternDesc::Instance(it) => {
252 let mut s = s.clone();
253 let wn = split_wit_name(ed.kebab_name);
254 let type_name = kebab_to_type(wn.name);
255 let getter = kebab_to_getter(wn.name);
256 let tp = s.cur_trait_path();
257 let get_self = get_self.with_getter(tp, type_name, getter); emit_import_instance(&mut s, get_self, wn.clone(), it)
259 }
260 ExternDesc::Component(_) => {
261 panic!("nested components not yet supported in rust bindings");
262 }
263 }
264}
265
266fn emit_import_instance<'a, 'b, 'c>(
274 s: &'c mut State<'a, 'b>,
275 get_self: SelfInfo,
276 wn: WitName,
277 it: &'c Instance<'b>,
278) -> TokenStream {
279 let mut s = s.with_cursor(wn.namespace_idents());
280 s.cur_helper_mod = Some(kebab_to_namespace(wn.name));
281 s.cur_trait = Some(kebab_to_type(wn.name));
282
283 let imports = it
284 .exports
285 .iter()
286 .map(|ed| emit_import_extern_decl(&mut s, get_self.clone(), ed))
287 .collect::<Vec<_>>();
288
289 quote! { #(#imports)* }
290}
291
292fn kebab_to_wrapper_name(trait_name: &str) -> Ident {
296 format_ident!("{}Sandbox", kebab_to_type(trait_name))
297}
298
299fn emit_component<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, ct: &'c Component<'b>) {
306 let mut s = s.with_cursor(wn.namespace_idents());
307 let ns = wn.namespace_path();
308 let r#trait = kebab_to_type(wn.name);
309 let import_trait = kebab_to_imports_name(wn.name);
310 let export_trait = kebab_to_exports_name(wn.name);
311 let wrapper_name = kebab_to_wrapper_name(wn.name);
312 let import_id = format_ident!("imports");
313
314 let rtsid = format_ident!("{}Resources", r#trait);
315 s.import_param_var = Some(format_ident!("I"));
316 resource::emit_tables(
317 &mut s,
318 rtsid.clone(),
319 quote! { #ns::#import_trait },
320 None,
321 false,
322 );
323
324 s.var_offset = ct.instance.evars.len();
325 s.cur_trait = Some(import_trait.clone());
326 let imports = ct
327 .imports
328 .iter()
329 .map(|ed| emit_import_extern_decl(&mut s, SelfInfo::new(import_id.clone()), ed))
330 .collect::<Vec<_>>();
331 s.var_offset = 0;
332
333 s.root_component_name = Some((ns.clone(), wn.name));
334 s.cur_trait = Some(export_trait.clone());
335 s.import_param_var = Some(format_ident!("I"));
336 let exports = ct
337 .instance
338 .unqualified
339 .exports
340 .iter()
341 .map(|ed| emit_export_extern_decl(&mut s, ed))
342 .collect::<Vec<_>>();
343
344 s.root_mod.items.extend(quote! {
345 pub struct #wrapper_name<T: #ns::#import_trait, S: ::hyperlight_host::sandbox::Callable> {
346 pub(crate) sb: S,
347 pub(crate) rt: ::std::sync::Arc<::std::sync::Mutex<#rtsid<T>>>,
348 }
349 pub(crate) fn register_host_functions<I: #ns::#import_trait + ::std::marker::Send + 'static, S: ::hyperlight_host::func::Registerable>(sb: &mut S, i: I) -> ::std::sync::Arc<::std::sync::Mutex<#rtsid<I>>> {
350 use ::hyperlight_host::sandbox_state::sandbox::EvolvableSandbox;
351 let rts = ::std::sync::Arc::new(::std::sync::Mutex::new(#rtsid::new()));
352 let #import_id = ::std::sync::Arc::new(::std::sync::Mutex::new(i));
353 #(#imports)*
354 rts
355 }
356 impl<I: #ns::#import_trait + ::std::marker::Send, S: ::hyperlight_host::sandbox::Callable> #ns::#export_trait<I> for #wrapper_name<I, S> {
357 #(#exports)*
358 }
359 impl #ns::#r#trait for ::hyperlight_host::sandbox::UninitializedSandbox {
360 type Exports<I: #ns::#import_trait + ::std::marker::Send> = #wrapper_name<I, ::hyperlight_host::func::call_ctx::MultiUseGuestCallContext>;
361 fn instantiate<I: #ns::#import_trait + ::std::marker::Send + 'static>(mut self, i: I) -> Self::Exports<I> {
362 let rts = register_host_functions(&mut self, i);
363 let noop = ::core::default::Default::default();
364 let sb = ::hyperlight_host::sandbox_state::sandbox::EvolvableSandbox::evolve(self, noop).unwrap();
365 let cc = ::hyperlight_host::func::call_ctx::MultiUseGuestCallContext::start(sb);
366 #wrapper_name {
367 sb: cc,
368 rt: rts,
369 }
370 }
371 }
372 });
373}
374
375pub fn emit_toplevel<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, n: &str, ct: &'c Component<'b>) {
377 s.is_impl = true;
378 log::debug!("\n\n=== starting host emit ===\n");
379 let wn = split_wit_name(n);
380 emit_component(s, wn, ct)
381}