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