1use proc_macro2::{Ident, TokenStream};
5use quote::{ToTokens, format_ident, quote};
6
7use crate::emit::{
8 FnName, ResourceItemName, State, WitName, find_colliding_import_names, import_member_names,
9 kebab_to_exports_name, kebab_to_fn, kebab_to_getter, kebab_to_imports_name, kebab_to_namespace,
10 kebab_to_type, kebab_to_var, split_wit_name,
11};
12use crate::etypes::{Component, ExternDecl, ExternDesc, Instance, Tyvar};
13use crate::hl::{
14 emit_fn_hl_name, emit_hl_marshal_param, emit_hl_marshal_result, emit_hl_unmarshal_param,
15 emit_hl_unmarshal_result,
16};
17use crate::{resource, rtypes};
18
19fn emit_export_extern_decl<'a, 'b, 'c>(
23 s: &'c mut State<'a, 'b>,
24 ed: &'c ExternDecl<'b>,
25) -> TokenStream {
26 match &ed.desc {
27 ExternDesc::CoreModule(_) => panic!("core module (im/ex)ports are not supported"),
28 ExternDesc::Func(ft) => {
29 match kebab_to_fn(ed.kebab_name) {
30 FnName::Plain(n) => {
31 let param_decls = ft
32 .params
33 .iter()
34 .map(|p| rtypes::emit_func_param(s, p))
35 .collect::<Vec<_>>();
36 let result_decl = rtypes::emit_func_result(s, &ft.result);
37 let hln = emit_fn_hl_name(s, ed.kebab_name);
38 let ret = format_ident!("ret");
39 let marshal = ft
40 .params
41 .iter()
42 .map(|p| emit_hl_marshal_param(s, kebab_to_var(p.name.name), &p.ty))
43 .collect::<Vec<_>>();
44 let unmarshal = emit_hl_unmarshal_result(s, ret.clone(), &ft.result);
45 quote! {
46 fn #n(&mut self, #(#param_decls),*) -> #result_decl {
47 let mut to_cleanup = Vec::<Box<dyn Drop>>::new();
48 let marshalled = {
49 let mut rts = self.rt
50 .lock()
51 .map_err(<::hyperlight_host::error::HyperlightError as From<_>>::from)?;
52 #[allow(clippy::unused_unit)]
53 (#(#marshal,)*)
54 };
55 let #ret = ::hyperlight_host::sandbox::Callable::call::<::std::vec::Vec::<u8>>(&mut self.sb,
56 #hln,
57 marshalled,
58 )?;
59 #[allow(clippy::unused_unit)]
60 let mut rts = self.rt
61 .lock()
62 .map_err(<::hyperlight_host::error::HyperlightError as From<_>>::from)?;
63 #[allow(clippy::unused_unit)]
64 ::std::result::Result::Ok(#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 let trait_path = if ns.is_empty() {
130 quote! { #trait_name }
131 } else {
132 quote! { #ns::#trait_name }
133 };
134 s.root_mod.items.extend(quote! {
135 impl<I: #root_ns::#imports_name<::hyperlight_common::component::Negative>, S: ::hyperlight_host::sandbox::Callable> #trait_path <::hyperlight_common::component::Positive #(,#tvs)*> for #wrapper_name<I, S> {
136 #(#exports)*
137 }
138 });
139}
140
141#[derive(Clone)]
144struct SelfInfo {
145 orig_id: Ident,
146 type_id: Vec<(Ident, TokenStream)>,
148 outer_id: Ident,
149 inner_preamble: TokenStream,
150 inner_id: Ident,
151}
152impl SelfInfo {
153 fn new(orig_id: Ident, imports_trait_bound: TokenStream) -> Self {
154 let outer_id = format_ident!("captured_{}", orig_id);
155 let inner_id = format_ident!("slf");
156 SelfInfo {
157 orig_id,
158 type_id: vec![(format_ident!("I"), imports_trait_bound)],
159 inner_preamble: quote! {
160 let mut #inner_id = #outer_id.lock()
161 .map_err(<::hyperlight_host::error::HyperlightError as From<_>>::from)?;
162 let mut #inner_id = ::std::ops::DerefMut::deref_mut(&mut #inner_id);
163 },
164 outer_id,
165 inner_id,
166 }
167 }
168 fn type_inst(&self) -> TokenStream {
169 self.type_id[1..]
170 .iter()
171 .fold(
172 (
173 self.type_id[0].0.to_token_stream() as TokenStream,
174 &self.type_id[0].1,
175 ),
176 |(toks, last_bound), (tid, next_bound)| {
177 (quote! { <#toks as #last_bound>::#tid }, next_bound)
178 },
179 )
180 .0
181 }
182 fn with_getter(
185 &self,
186 tp: TokenStream,
187 type_name: Ident,
188 type_bound: TokenStream,
189 getter: Ident,
190 ) -> Self {
191 let mut toks = self.inner_preamble.clone();
192 let id = self.inner_id.clone();
193 let type_inst = self.type_inst();
194 toks.extend(quote! {
195 let mut #id = #tp::#getter(::std::borrow::BorrowMut::<#type_inst>::borrow_mut(&mut #id));
196 });
197 let mut type_id = self.type_id.clone();
198 type_id.push((type_name, type_bound));
199 SelfInfo {
200 orig_id: self.orig_id.clone(),
201 type_id,
202 outer_id: self.outer_id.clone(),
203 inner_preamble: toks,
204 inner_id: id,
205 }
206 }
207}
208
209fn emit_import_extern_decl<'a, 'b, 'c>(
216 s: &'c mut State<'a, 'b>,
217 get_self: SelfInfo,
218 ed: &'c ExternDecl<'b>,
219) -> TokenStream {
220 match &ed.desc {
221 ExternDesc::CoreModule(_) => panic!("core module (im/ex)ports are not supported"),
222 ExternDesc::Func(ft) => {
223 let hln = emit_fn_hl_name(s, ed.kebab_name);
224 tracing::debug!("providing host function {}", hln);
225 let (pds, pus) = ft
226 .params
227 .iter()
228 .map(|p| {
229 let id = kebab_to_var(p.name.name);
230 (
231 quote! { #id: ::std::vec::Vec<u8> },
232 emit_hl_unmarshal_param(s, id, &p.ty),
233 )
234 })
235 .unzip::<_, _, Vec<_>, Vec<_>>();
236 let tp = s.cur_trait_path();
237 let callname = match kebab_to_fn(ed.kebab_name) {
238 FnName::Plain(n) => quote! { #tp::#n },
239 FnName::Associated(r, m) => {
240 let hp = s.helper_path();
241 match m {
242 ResourceItemName::Constructor => quote! { #hp #r::new },
243 ResourceItemName::Method(mn) => quote! { #hp #r::#mn },
244 ResourceItemName::Static(mn) => quote! { #hp #r::#mn },
245 }
246 }
247 };
248 let type_inst = get_self.type_inst();
249 let SelfInfo {
250 orig_id,
251 outer_id,
252 inner_preamble,
253 inner_id,
254 ..
255 } = get_self;
256 let ret = format_ident!("ret");
257 let marshal_result = emit_hl_marshal_result(s, ret.clone(), &ft.result);
258 quote! {
259 let #outer_id = #orig_id.clone();
260 let captured_rts = rts.clone();
261 sb.register_host_function(#hln, move |#(#pds),*| {
262 let mut rts = captured_rts.lock()
263 .map_err(<::hyperlight_host::error::HyperlightError as From<_>>::from)?;
264 #inner_preamble
265 let #ret = #callname(
266 ::std::borrow::BorrowMut::<#type_inst>::borrow_mut(
267 &mut #inner_id
268 ),
269 #(#pus),*
270 );
271 Ok(#marshal_result)
272 })?;
273 }
274 }
275 ExternDesc::Type(_) => {
276 quote! {}
278 }
279 ExternDesc::Instance(it) => {
280 let mut s = s.clone();
281 let wn = split_wit_name(ed.kebab_name);
282 let (type_name, getter) = import_member_names(&wn, &s.colliding_import_names);
283 let tp = s.cur_trait_path();
284 let trait_path = wn
285 .namespace_idents()
286 .iter()
287 .chain(&[kebab_to_type(wn.name)])
288 .cloned()
289 .collect::<Vec<_>>();
290 let trait_ref =
291 rtypes::trait_ref(&mut s, rtypes::EmitPositivity::Opposite, true, &trait_path);
292 let get_self = get_self.with_getter(tp, type_name, trait_ref, getter);
293 emit_import_instance(&mut s, get_self, wn.clone(), it)
294 }
295 ExternDesc::Component(_) => {
296 panic!("nested components not yet supported in rust bindings");
297 }
298 }
299}
300
301fn emit_import_instance<'a, 'b, 'c>(
309 s: &'c mut State<'a, 'b>,
310 get_self: SelfInfo,
311 wn: WitName,
312 it: &'c Instance<'b>,
313) -> TokenStream {
314 let mut s = s.with_cursor(wn.namespace_idents());
315 s.cur_helper_mod = Some(kebab_to_namespace(wn.name));
316 s.cur_trait = Some(kebab_to_type(wn.name));
317
318 let imports = it
319 .exports
320 .iter()
321 .map(|ed| emit_import_extern_decl(&mut s, get_self.clone(), ed))
322 .collect::<Vec<_>>();
323
324 quote! { #(#imports)* }
325}
326
327fn kebab_to_wrapper_name(trait_name: &str) -> Ident {
331 format_ident!("{}Sandbox", kebab_to_type(trait_name))
332}
333
334fn emit_component<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, ct: &'c Component<'b>) {
341 let mut s = s.with_cursor(wn.namespace_idents());
342 let ns = wn.namespace_path();
343 let r#trait = kebab_to_type(wn.name);
344 let import_trait = kebab_to_imports_name(wn.name);
345 let export_trait = kebab_to_exports_name(wn.name);
346 let wrapper_name = kebab_to_wrapper_name(wn.name);
347 let import_id = format_ident!("imports");
348
349 let rtsid = format_ident!("{}Resources", r#trait);
350 s.import_param_var = Some(format_ident!("I"));
351 s.positivity_param = Some(quote! { ::hyperlight_common::component::Positive });
352 s.colliding_import_names = find_colliding_import_names(&ct.imports);
353 resource::emit_tables(
354 &mut s,
355 rtsid.clone(),
356 quote! { #ns::#import_trait<::hyperlight_common::component::Negative> },
357 None,
358 false,
359 );
360
361 s.var_offset = ct.instance.evars.len();
362 s.cur_trait = Some(import_trait.clone());
363 let imports = ct
364 .imports
365 .iter()
366 .map(|ed| {
367 emit_import_extern_decl(
368 &mut s,
369 SelfInfo::new(
370 import_id.clone(),
371 quote! { #ns::#import_trait<::hyperlight_common::component::Negative> },
372 ),
373 ed,
374 )
375 })
376 .collect::<Vec<_>>();
377 s.var_offset = 0;
378
379 s.root_component_name = Some((ns.clone(), wn.name));
380 s.cur_trait = Some(export_trait.clone());
381 s.import_param_var = Some(format_ident!("I"));
382 s.positivity_param = Some(quote! { ::hyperlight_common::component::Positive });
383 s.self_param_var =
386 Some(quote! { <Self as #ns::#export_trait<I, ::hyperlight_common::component::Negative>> });
387
388 let exports = ct
389 .instance
390 .unqualified
391 .exports
392 .iter()
393 .map(|ed| emit_export_extern_decl(&mut s, ed))
394 .collect::<Vec<_>>();
395
396 s.root_mod.items.extend(quote! {
397 pub struct #wrapper_name<T: #ns::#import_trait<::hyperlight_common::component::Negative>, S: ::hyperlight_host::sandbox::Callable> {
398 pub(crate) sb: S,
399 pub(crate) rt: ::std::sync::Arc<::std::sync::Mutex<#rtsid<T>>>,
400 }
401 pub(crate) fn register_host_functions<I: #ns::#import_trait<::hyperlight_common::component::Negative> + ::std::marker::Send + 'static, S: ::hyperlight_host::func::Registerable>(sb: &mut S, i: I) -> <::hyperlight_common::component::Positive as ::hyperlight_common::component::Positivity>::CallResult<::std::sync::Arc<::std::sync::Mutex<#rtsid<I>>>> {
402 let rts = ::std::sync::Arc::new(::std::sync::Mutex::new(#rtsid::new()));
403 let #import_id = ::std::sync::Arc::new(::std::sync::Mutex::new(i));
404 #(#imports)*
405 Ok(rts)
406 }
407 impl<I: #ns::#import_trait<::hyperlight_common::component::Negative> + ::std::marker::Send, S: ::hyperlight_host::sandbox::Callable> #ns::#export_trait<::hyperlight_common::component::Positive, I> for #wrapper_name<I, S> {
408 #(#exports)*
409 }
410 impl #ns::#r#trait<::hyperlight_common::component::Positive> for ::hyperlight_host::sandbox::UninitializedSandbox {
411 type Exports<I: #ns::#import_trait<::hyperlight_common::component::Negative> + ::std::marker::Send> = #wrapper_name<I, ::hyperlight_host::sandbox::initialized_multi_use::MultiUseSandbox>;
412 fn instantiate<I: #ns::#import_trait<::hyperlight_common::component::Negative> + ::std::marker::Send + 'static>(mut self, i: I) -> <::hyperlight_common::component::Positive as ::hyperlight_common::component::Positivity>::CallResult<Self::Exports<I>> {
413 let rts = register_host_functions(&mut self, i)?;
414 let sb = self.evolve()?;
415 Ok(#wrapper_name {
416 sb,
417 rt: rts,
418 })
419 }
420 }
421 });
422}
423
424pub fn emit_toplevel<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, n: &str, ct: &'c Component<'b>) {
426 s.is_impl = true;
427 tracing::debug!("\n\n=== starting host emit ===\n");
428 let wn = split_wit_name(n);
429 emit_component(s, wn, ct)
430}