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 mut to_cleanup = Vec::<Box<dyn Drop>>::new();
60 let marshalled = {
61 let mut rts = self.rt.lock().unwrap();
62 #[allow(clippy::unused_unit)]
63 (#(#marshal,)*)
64 };
65 let #ret = ::hyperlight_host::sandbox::Callable::call::<::std::vec::Vec::<u8>>(&mut self.sb,
66 #hln,
67 marshalled,
68 );
69 let ::std::result::Result::Ok(#ret) = #ret else { panic!("bad return from guest {:?}", #ret) };
70 #[allow(clippy::unused_unit)]
71 let mut rts = self.rt.lock().unwrap();
72 #[allow(clippy::unused_unit)]
73 #unmarshal
74 }
75 }
76 }
77 FnName::Associated(_, _) =>
78 {
81 panic!("guest resources are not currently supported")
82 }
83 }
84 }
85 ExternDesc::Type(_) => {
86 quote! {}
88 }
89 ExternDesc::Instance(it) => {
90 let wn = split_wit_name(ed.kebab_name);
91 emit_export_instance(s, wn.clone(), it);
92
93 let getter = kebab_to_getter(wn.name);
94 let tn = kebab_to_type(wn.name);
95 quote! {
96 type #tn = Self;
97 #[allow(refining_impl_trait)]
98 fn #getter<'a>(&'a mut self) -> &'a mut Self {
99 self
100 }
101 }
102 }
103 ExternDesc::Component(_) => {
104 panic!("nested components not yet supported in rust bindings");
105 }
106 }
107}
108
109fn emit_export_instance<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, it: &'c Instance<'b>) {
113 let mut s = s.with_cursor(wn.namespace_idents());
114 s.cur_helper_mod = Some(kebab_to_namespace(wn.name));
115
116 let exports = it
117 .exports
118 .iter()
119 .map(|ed| emit_export_extern_decl(&mut s, ed))
120 .collect::<Vec<_>>();
121
122 let ns = wn.namespace_path();
123 let nsi = wn.namespace_idents();
124 let trait_name = kebab_to_type(wn.name);
125 let r#trait = s.r#trait(&nsi, trait_name.clone());
126 let tvs = r#trait
127 .tvs
128 .iter()
129 .map(|(_, (tv, _))| tv.unwrap())
130 .collect::<Vec<_>>();
131 let tvs = tvs
132 .iter()
133 .map(|tv| rtypes::emit_var_ref(&mut s, &Tyvar::Bound(*tv)))
134 .collect::<Vec<_>>();
135 let (root_ns, root_base_name) = s.root_component_name.unwrap();
136 let wrapper_name = kebab_to_wrapper_name(root_base_name);
137 let imports_name = kebab_to_imports_name(root_base_name);
138 s.root_mod.items.extend(quote! {
139 impl<I: #root_ns::#imports_name, S: ::hyperlight_host::sandbox::Callable> #ns::#trait_name <#(#tvs),*> for #wrapper_name<I, S> {
140 #(#exports)*
141 }
142 });
143}
144
145#[derive(Clone)]
148struct SelfInfo {
149 orig_id: Ident,
150 type_id: Vec<Ident>,
151 outer_id: Ident,
152 inner_preamble: TokenStream,
153 inner_id: Ident,
154}
155impl SelfInfo {
156 fn new(orig_id: Ident) -> Self {
157 let outer_id = format_ident!("captured_{}", orig_id);
158 let inner_id = format_ident!("slf");
159 SelfInfo {
160 orig_id,
161 type_id: vec![format_ident!("I")],
162 inner_preamble: quote! {
163 let mut #inner_id = #outer_id.lock().unwrap();
164 let mut #inner_id = ::std::ops::DerefMut::deref_mut(&mut #inner_id);
165 },
166 outer_id,
167 inner_id,
168 }
169 }
170 fn with_getter(&self, tp: TokenStream, type_name: Ident, getter: Ident) -> Self {
173 let mut toks = self.inner_preamble.clone();
174 let id = self.inner_id.clone();
175 let mut type_id = self.type_id.clone();
176 toks.extend(quote! {
177 let mut #id = #tp::#getter(::std::borrow::BorrowMut::<#(#type_id)::*>::borrow_mut(&mut #id));
178 });
179 type_id.push(type_name);
180 SelfInfo {
181 orig_id: self.orig_id.clone(),
182 type_id,
183 outer_id: self.outer_id.clone(),
184 inner_preamble: toks,
185 inner_id: id,
186 }
187 }
188}
189
190fn emit_import_extern_decl<'a, 'b, 'c>(
197 s: &'c mut State<'a, 'b>,
198 get_self: SelfInfo,
199 ed: &'c ExternDecl<'b>,
200) -> TokenStream {
201 match &ed.desc {
202 ExternDesc::CoreModule(_) => panic!("core module (im/ex)ports are not supported"),
203 ExternDesc::Func(ft) => {
204 let hln = emit_fn_hl_name(s, ed.kebab_name);
205 log::debug!("providing host function {}", hln);
206 let (pds, pus) = ft
207 .params
208 .iter()
209 .map(|p| {
210 let id = kebab_to_var(p.name.name);
211 (
212 quote! { #id: ::std::vec::Vec<u8> },
213 emit_hl_unmarshal_param(s, id, &p.ty),
214 )
215 })
216 .unzip::<_, _, Vec<_>, Vec<_>>();
217 let tp = s.cur_trait_path();
218 let callname = match kebab_to_fn(ed.kebab_name) {
219 FnName::Plain(n) => quote! { #tp::#n },
220 FnName::Associated(r, m) => {
221 let hp = s.helper_path();
222 match m {
223 ResourceItemName::Constructor => quote! { #hp #r::new },
224 ResourceItemName::Method(mn) => quote! { #hp #r::#mn },
225 ResourceItemName::Static(mn) => quote! { #hp #r::#mn },
226 }
227 }
228 };
229 let SelfInfo {
230 orig_id,
231 type_id,
232 outer_id,
233 inner_preamble,
234 inner_id,
235 } = get_self;
236 let ret = format_ident!("ret");
237 let marshal_result = emit_hl_marshal_result(s, ret.clone(), &ft.result);
238 quote! {
239 let #outer_id = #orig_id.clone();
240 let captured_rts = rts.clone();
241 sb.register_host_function(#hln, move |#(#pds),*| {
242 let mut rts = captured_rts.lock().unwrap();
243 #inner_preamble
244 let #ret = #callname(
245 ::std::borrow::BorrowMut::<#(#type_id)::*>::borrow_mut(
246 &mut #inner_id
247 ),
248 #(#pus),*
249 );
250 Ok(#marshal_result)
251 })
252 .unwrap();
253 }
254 }
255 ExternDesc::Type(_) => {
256 quote! {}
258 }
259 ExternDesc::Instance(it) => {
260 let mut s = s.clone();
261 let wn = split_wit_name(ed.kebab_name);
262 let type_name = kebab_to_type(wn.name);
263 let getter = kebab_to_getter(wn.name);
264 let tp = s.cur_trait_path();
265 let get_self = get_self.with_getter(tp, type_name, getter); emit_import_instance(&mut s, get_self, wn.clone(), it)
267 }
268 ExternDesc::Component(_) => {
269 panic!("nested components not yet supported in rust bindings");
270 }
271 }
272}
273
274fn emit_import_instance<'a, 'b, 'c>(
282 s: &'c mut State<'a, 'b>,
283 get_self: SelfInfo,
284 wn: WitName,
285 it: &'c Instance<'b>,
286) -> TokenStream {
287 let mut s = s.with_cursor(wn.namespace_idents());
288 s.cur_helper_mod = Some(kebab_to_namespace(wn.name));
289 s.cur_trait = Some(kebab_to_type(wn.name));
290
291 let imports = it
292 .exports
293 .iter()
294 .map(|ed| emit_import_extern_decl(&mut s, get_self.clone(), ed))
295 .collect::<Vec<_>>();
296
297 quote! { #(#imports)* }
298}
299
300fn kebab_to_wrapper_name(trait_name: &str) -> Ident {
304 format_ident!("{}Sandbox", kebab_to_type(trait_name))
305}
306
307fn emit_component<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, ct: &'c Component<'b>) {
314 let mut s = s.with_cursor(wn.namespace_idents());
315 let ns = wn.namespace_path();
316 let r#trait = kebab_to_type(wn.name);
317 let import_trait = kebab_to_imports_name(wn.name);
318 let export_trait = kebab_to_exports_name(wn.name);
319 let wrapper_name = kebab_to_wrapper_name(wn.name);
320 let import_id = format_ident!("imports");
321
322 let rtsid = format_ident!("{}Resources", r#trait);
323 s.import_param_var = Some(format_ident!("I"));
324 resource::emit_tables(
325 &mut s,
326 rtsid.clone(),
327 quote! { #ns::#import_trait },
328 None,
329 false,
330 );
331
332 s.var_offset = ct.instance.evars.len();
333 s.cur_trait = Some(import_trait.clone());
334 let imports = ct
335 .imports
336 .iter()
337 .map(|ed| emit_import_extern_decl(&mut s, SelfInfo::new(import_id.clone()), ed))
338 .collect::<Vec<_>>();
339 s.var_offset = 0;
340
341 s.root_component_name = Some((ns.clone(), wn.name));
342 s.cur_trait = Some(export_trait.clone());
343 s.import_param_var = Some(format_ident!("I"));
344 s.is_export = true;
345
346 let exports = ct
347 .instance
348 .unqualified
349 .exports
350 .iter()
351 .map(|ed| emit_export_extern_decl(&mut s, ed))
352 .collect::<Vec<_>>();
353
354 s.root_mod.items.extend(quote! {
355 pub struct #wrapper_name<T: #ns::#import_trait, S: ::hyperlight_host::sandbox::Callable> {
356 pub(crate) sb: S,
357 pub(crate) rt: ::std::sync::Arc<::std::sync::Mutex<#rtsid<T>>>,
358 }
359 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>>> {
360 let rts = ::std::sync::Arc::new(::std::sync::Mutex::new(#rtsid::new()));
361 let #import_id = ::std::sync::Arc::new(::std::sync::Mutex::new(i));
362 #(#imports)*
363 rts
364 }
365 impl<I: #ns::#import_trait + ::std::marker::Send, S: ::hyperlight_host::sandbox::Callable> #ns::#export_trait<I> for #wrapper_name<I, S> {
366 #(#exports)*
367 }
368 impl #ns::#r#trait for ::hyperlight_host::sandbox::UninitializedSandbox {
369 type Exports<I: #ns::#import_trait + ::std::marker::Send> = #wrapper_name<I, ::hyperlight_host::sandbox::initialized_multi_use::MultiUseSandbox>;
370 fn instantiate<I: #ns::#import_trait + ::std::marker::Send + 'static>(mut self, i: I) -> Self::Exports<I> {
371 let rts = register_host_functions(&mut self, i);
372 let sb = self.evolve().unwrap();
373 #wrapper_name {
374 sb,
375 rt: rts,
376 }
377 }
378 }
379 });
380}
381
382pub fn emit_toplevel<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, n: &str, ct: &'c Component<'b>) {
384 s.is_impl = true;
385 log::debug!("\n\n=== starting host emit ===\n");
386 let wn = split_wit_name(n);
387 emit_component(s, wn, ct)
388}