1use std::{
2 borrow::Cow,
3 collections::{BTreeMap, BTreeSet},
4 convert::Infallible,
5 f32::consts::E,
6 iter::once,
7 sync::{Arc, Mutex, OnceLock},
8};
9use wars::*;
10
11use pit_core::{Arg, Interface};
12use proc_macro2::{Span, TokenStream};
13use quasiquote::quasiquote;
14use quote::{format_ident, quote, ToTokens};
15use relooper::{reloop, BranchMode, ShapedBlock};
16use sha3::Digest;
17use syn::{Ident, Lifetime};
18use portal_pc_waffle::{
19 cfg::CFGInfo, entity::EntityRef, Block, BlockTarget, Export, ExportKind, Func, ImportKind,
20 Memory, Module, Operator, Signature, SignatureData, Type, Value,
21};
22#[derive(Default)]
23pub struct PitPlugin {
24 pub tpit: OnceLock<BTreeSet<pit_core::Interface>>,
25 pub extra: Vec<Arc<dyn PitPluginPlugin>>,
26}
27
28pub trait PitPluginPlugin {
29 fn pre(&self, pit: &PitPlugin, module: &mut Opts<Module<'static>>) -> anyhow::Result<()> {
30 return Ok(());
31 }
32 fn choose_type(&self, opts: &Opts<Module<'static>>) -> anyhow::Result<Option<TokenStream>>;
33 fn emit_method(
34 &self,
35 opts: &Opts<Module<'static>>,
36 idx: usize,
37 i: &Interface,
38 s: &str,
39 value: TokenStream,
40 params: &[TokenStream],
41 ) -> anyhow::Result<TokenStream>;
42 fn emit_drop(
43 &self,
44 opts: &Opts<Module<'static>>,
45 idx: usize,
46 value: TokenStream,
47 ) -> anyhow::Result<TokenStream> {
48 return Ok(quasiquote!(#{opts.fp()}::ret(Ok(()))));
49 }
50 fn post(
51 &self,
52 parent: &PitPlugin,
53 idx: usize,
54 opts: &Opts<Module<'static>>,
55 ) -> anyhow::Result<TokenStream>;
56}
57
58impl PitPlugin {
59 pub fn tpit(&self, opts: &Opts<Module<'static>>) -> &BTreeSet<Interface> {
60 return self.tpit.get_or_init(|| {
61 pit_patch::get_interfaces(&opts.module)
62 .unwrap()
63 .into_iter()
64 .collect()
65 });
66 }
67 pub fn host_tpit(&self, opts: &Opts<Module<'static>>) -> anyhow::Result<TokenStream> {
68 let mut a;
69 let root = &opts.crate_path;
70 a = quote! {#root::Infallible};
71 for e in self.extra.iter() {
72 if let Some(c) = e.choose_type(opts)? {
73 a = quote! {
74 #root::Either<#c,#a>
75 };
76 }
77 }
78 return Ok(a);
79 }
80 pub fn apply_host(
81 &self,
82 mut x: TokenStream,
83 opts: &Opts<Module<'static>>,
84 i: &Interface,
85 s: &str,
86 params: &[TokenStream],
87 ) -> anyhow::Result<TokenStream> {
88 let root = &opts.crate_path;
89 for (idx, e) in self.extra.iter().enumerate() {
90 if let Some(c) = e.choose_type(opts)? {
91 x = quasiquote! {
92 match host{
93 #root::Either::Right(host) => #x,
94 #root::Either::Left(host) => #{e.emit_method(opts,idx, i, s, quote! {host}, params)?},
95 }
96 };
97 }
98 }
99 return Ok(x);
100 }
101 pub fn apply_drop(
102 &self,
103 mut x: TokenStream,
104 opts: &Opts<Module<'static>>,
105 ) -> anyhow::Result<TokenStream> {
106 let root = &opts.crate_path;
107 for (idx, e) in self.extra.iter().enumerate() {
108 if let Some(c) = e.choose_type(opts)? {
109 x = quasiquote! {
110 match host{
111 #root::Either::Right(host) => #x,
112 #root::Either::Left(host) => #{e.emit_drop(opts,idx, quote! {host})?},
113 }
114 };
115 }
116 }
117 return Ok(x);
118 }
119 pub fn wrap(
120 &self,
121 opts: &Opts<Module<'static>>,
122 mut x: TokenStream,
123 ) -> anyhow::Result<TokenStream> {
124 let root = &opts.crate_path;
125 for e in self.extra.iter() {
126 if let Some(c) = e.choose_type(opts)? {
127 x = quasiquote! {
128 #root::Either::Right(#x)
129 };
130 }
131 }
132 return Ok(x);
133 }
134}
135impl Plugin for PitPlugin {
136 fn exref_bounds(&self, opts: &Opts<Module<'static>>) -> anyhow::Result<Option<TokenStream>> {
137 let root = &opts.crate_path;
138 Ok(Some(
139 quasiquote! {From<#root::Pit<Vec<#{opts.fp()}::Value<Self>>,#{self.host_tpit(opts)?}>> + TryInto<#root::Pit<Vec<#{opts.fp()}::Value<Self>>,#{self.host_tpit(opts)?}>>},
140 ))
141 }
142 fn pre(&self, module: &mut Opts<Module<'static>>) -> anyhow::Result<()> {
143 for p in self.extra.iter() {
144 p.pre(self, module)?;
145 }
146 Ok(())
147 }
148
149 fn import(
150 &self,
151 opts: &Opts<Module<'static>>,
152 module: &str,
153 name: &str,
154 params: Vec<TokenStream>,
155 ) -> anyhow::Result<Option<TokenStream>> {
156 let root = &opts.crate_path;
157 let mut params = params.into_iter();
158 if let Some(i) = module.strip_prefix("pit/") {
159 let x: [u8; 32] = hex::decode(i).unwrap().try_into().unwrap();
160 if let Some(s) = name.strip_prefix("~") {
161 let s = {
162 let mut h = sha3::Sha3_256::default();
163 h.update(s);
164 h.finalize()
165 };
166 return Ok(Some(quasiquote! {
167 #{opts.fp()}::ret(Ok(#{opts.fp()}::Value::<C>::ExternRef(#root::Pit::Guest{
168 id: [#(#x),*],
169 x: #{opts.fp()}::CoeVec::coe(#root::tuple_list::tuple_list!(#(#params),*)),
170 s: [#(#s),*],
171 }.into())))
172 }));
173 }
174
175 let mut f = params.next().unwrap();
176 let params = params.collect::<Vec<_>>();
179 let cases = opts
180 .module
181 .exports
182 .iter()
183 .filter_map(|x| x.name.strip_prefix(&format!("pit/{i}/~")))
184 .filter_map(|x| x.strip_suffix(&format!("/{name}")))
185 .map(|s| {
186 let id = format_ident!("{}", bindname(&format!("pit/{i}/~{s}/{name}")));
187 let s = {
188 let mut h = sha3::Sha3_256::default();
189 h.update(s);
190 h.finalize()
191 };
192 quasiquote! {
193 [#(#s),*] => {
194 let mut y = #{opts.fp()}::CoeVec::coe(#root::tuple_list::tuple_list!(#(#params),*));
195 y.extend(&mut x.clone());
196 ctx.#id(#{opts.fp()}::CoeVec::uncoe(y))
197 }
198 }
199 });
200 let interface = self.tpit(opts).iter().find(|a| a.rid() == x);
201 let meth = interface.and_then(|a| a.methods.get(name));
202 return Ok(Some(quasiquote! {
203 'a: {
204 let x = #f;
205 let #{opts.fp()}::Value::<C>::ExternRef(x) = x else{
206 break 'a #{opts.fp()}::ret(Err(#root::_rexport::anyhow::anyhow!("not an externref")))
207 };
208 let Ok(x) = x.try_into() else{
209 break 'a #{opts.fp()}::ret(Err(#root::_rexport::anyhow::anyhow!("not a pit externref")))
210 };
211 match x{
212 #root::Pit::Guest{s,x,id} => match s{
213 #(#cases),*,
214 _ => break 'a #{opts.fp()}::ret(Err(#root::_rexport::anyhow::anyhow!("invalid target")))
215 },
216 #root::Pit::Host{host} => #{let t = quote!{match host{}};self.apply_host(t,opts,interface.unwrap(),name,¶ms)?}
217 _ => todo!()
218 }
219 }
220 }));
221 }
222 if module == "pit" && name == "drop" {
223 let mut f = params.next().unwrap();
224 let cases = opts
225 .module
226 .exports
227 .iter()
228 .filter_map(|x| {
229 let x = x.name.as_str();
230 let x = x.strip_prefix("pit/")?;
231 let (a, x) = x.split_once("/~")?;
232 let s = x.strip_suffix(".drop")?;
233 return Some((a, s));
234 })
235 .map(|(a, s)| {
236 let x = hex::decode(a).unwrap();
237 let id = format_ident!("{}", bindname(&format!("pit/{a}/~{s}.drop")));
238 let s = {
239 let mut h = sha3::Sha3_256::default();
240 h.update(s);
241 h.finalize()
242 };
243 quasiquote!(
248 ([#(#x),*],[#(#s),*]) => ctx.#id(#{opts.fp()}::CoeVec::uncoe(x))
249 )
250 });
251 return Ok(Some(quasiquote! {
252 'a: {
253 let x = #f;
254 let #{opts.fp()}::Value::<C>::ExternRef(x) = x else{
255 break 'a #{opts.fp()}::ret(Ok(()));
256 };
257 if let Ok(x) = x.try_into(){
258 match x{
259 #root::Pit::Guest{s,x,id} => => break 'a match (id,s){
260 #(#cases),*,
261 _ => #{opts.fp()}::ret(Ok(()))
262 },
263 #root::Pit::Host{host} => break 'a #{self.apply_drop(quasiquote!(#{opts.fp()}::ret(Ok(()))),opts)?}
264 }
265 }else{
266 break 'a #{opts.fp()}::ret(Ok(()))
267 }
268 }
269 }));
270 };
271 return Ok(None);
272 }
273
274 fn post(&self, opts: &Opts<Module<'static>>) -> anyhow::Result<TokenStream> {
275 let root = &opts.crate_path;
276 let name = opts.name.clone();
277
278 let bs = self
279 .extra
280 .iter()
281 .enumerate()
282 .map(|(i, x)| x.post(self, i, opts))
283 .collect::<anyhow::Result<Vec<_>>>()?;
284 return Ok(quote! {
285 #(#bs)*
286 });
287 }
288}
289pub fn indexed_lookup(root: &TokenStream, i: usize, a: TokenStream) -> TokenStream {
290 if i == 0 {
291 return quote! {#root::Either::Right(#a)};
292 }
293 return quasiquote!(#root::Either::Left(#{indexed_lookup(root, i - 1, a)}));
294}
295pub struct Passthrough {}
296impl PitPluginPlugin for Passthrough {
297 fn choose_type(&self, opts: &Opts<Module<'static>>) -> anyhow::Result<Option<TokenStream>> {
298 Ok(Some(quasiquote!(
299 #{opts.host_tpit()}
300 )))
301 }
302
303 fn emit_method(
304 &self,
305 opts: &Opts<Module<'static>>,
306 idx: usize,
307 i: &Interface,
308 s: &str,
309 value: TokenStream,
310 params: &[TokenStream],
311 ) -> anyhow::Result<TokenStream> {
312 let meth = i.methods.get(s);
313 let i2 = i.rid_str();
314 let root = &opts.crate_path;
315 Ok(match opts.roots.get("tpit_rt") {
316 None => quote! {
317 match #value{
318
319 }
320 },
321 Some(r) => quasiquote! {
322 match #value{
323 host => {
324 let casted = unsafe{
325 host.cast::<Box<dyn #{format_ident!("R{}",i2)}>>()
326 };
327 let a = casted.#{format_ident!("{s}")}(#{
328 let p = params.iter().zip(meth.unwrap().params.iter()).map(|(x,y)|match y{
329 Arg::Resource { ty, nullable, take, ann } => quasiquote!{
330 Box::new(Shim{wrapped: ctx, x: #x}).into()
331 },
332 _ => quote!{
333 #x
334 }
335 });
336
337 quote!{
338 #(#p),*
339 }
340 });
341 break 'a #{opts.fp()}::ret(Ok(#root::tuple_list::tuple_list!(#{
342 let r = meth.unwrap().rets.iter().enumerate().map(|(i,r)|{
343 let i = syn::Index{index: i as u32, span: Span::call_site()};
344 let i = quote!{
345 a.#i
346 };
347 match r{
348 Arg::Resource { ty, nullable, take, ann } => quote!{
349 #{opts.fp()}::Value::<C>::ExternRef(#root::Pit::Host{host: #{indexed_lookup(root,idx,quasiquote!{
350 unsafe{i.cast()}
351 })}})
352 },
353 _ => i
354 }
355 });
356
357 quote!{
358 #(#r),*
359 }
360 })));
361 }
362 }
363 },
364 })
365 }
366
367 fn post(
368 &self,
369 parent: &PitPlugin,
370 idx: usize,
371 opts: &Opts<Module<'static>>,
372 ) -> anyhow::Result<TokenStream> {
373 let root = &opts.crate_path;
374 let name = opts.name.clone();
375 let a = match opts.roots.get("tpit_rt") {
376 None => quote! {},
377 Some(tpit_rt) => quasiquote! {
378 impl<T: #name + ?Sized> Into<#tpit_rt::Tpit<()>> for Box<Shim<T>>{
379 fn into(self) -> #tpit_rt::Tpit<()>{
380 if let #{opts.fp()}::Value::<T>::ExternRef(e) = *self{
381 if let Ok(a) = e.try_into(){
382 if let #root::Pit::Host{host} = a{
383 return host;
384 }
385 }
386 }
387 Default::default()
388 }
389 }
390 impl<T: #name + ?Sized> Drop for Shim<T>{
391 fn drop(&mut self){
392 let ctx = unsafe{
393 &mut *self.wrapped
394 };
395 #root::rexport::tramp::tramp(#{opts.import("pit","drop",once(quote!{
396 self.x.clone()
397 }))?})
398 }
399 }
400 #{
401 let a = parent.tpit(opts).iter().map(|i|{
402 let tname = format_ident!("R{}",i.rid_str());
403 let meths = i.methods.iter().map(|(a,b)|
404 Ok(quasiquote!{
405 fn #{format_ident!("{a}")}#{pit_rust_guest::render_sig(&pit_rust_guest::Opts { root: tpit_rt.clone(), salt: vec![], tpit: true },&tpit_rt.clone(),i,b,"e! {&mut self},false)}{
406 let ctx = unsafe{
407 &mut *self.wrapped
408 };
409 let res = #{opts.import(&format!("pit/{}",i.rid_str()),&format!("{a}"),once(Ok(quote!{self.x.clone()})).chain(b.params.iter().enumerate().map(|(i,p)|{
410 let i = format_ident!("p{i}");
411 Ok(match p{
412 Arg::Resource{ty,nullable,take,ann} => {
413 quote!{
414 #{opts.fp()}::Value::<C>::ExternRef(Pit::Host{host:#{indexed_lookup(opts,i,dxquasiquote!{
415 unsafe{
416 #i.cast()
417 }
418 })?}}.into())
419 }
420 }
421 _ => quote!{
422 #i
423 }
424 })
425 })).collect::<anyhow::Result<Vec<_>>>()?.into_iter())?};
426 let res = #root::rexport::tramp::tramp(res).unwrap().into_tuple()
427 ;
428 #{ let r = b.rets.iter().enumerate().map(|(i,r)|{
429 let i = syn::Index{index: i as u32, span: Span::call_site()};
430 let i = quote!{
431 res.#i
432 };
433 match r{
434 Arg::Resource { ty, nullable, take, ann } => quote!{
435 Box::new(Shim{wrapped:self.wrapped,x: #i}).into()
436 },
437 _ => i
438 }
439 });
440
441 quote!{
442 #(#r),*
443 }}
444 }
445 })).collect::<anyhow::Result<Vec<_>>>()?;
446 Ok(quote!{
447 impl<C: #name + ?Sized> #tname for Shim<C>{
448 #(#meths),*
449 }
450 })
451 }).collect::<anyhow::Result<Vec<_>>>()?;
452 quote!{
453 #(#a)*
454 }
455 }
456 },
457 };
458 Ok(a)
459 }
460}