wasmtime_internal_component_macro/
bindgen.rs1use proc_macro2::{Span, TokenStream};
2use quote::ToTokens;
3use std::collections::HashMap;
4use std::env;
5use std::path::{Path, PathBuf};
6use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
7use syn::parse::{Error, Parse, ParseStream, Result};
8use syn::punctuated::Punctuated;
9use syn::{Token, braced, token};
10use wasmtime_wit_bindgen::{
11 FunctionConfig, FunctionFilter, FunctionFlags, Opts, Ownership, TrappableError,
12};
13use wit_parser::{PackageId, Resolve, UnresolvedPackageGroup, WorldId};
14
15pub struct Config {
16 opts: Opts,
17 resolve: Resolve,
18 world: WorldId,
19 files: Vec<PathBuf>,
20 include_generated_code_from_file: bool,
21}
22
23pub fn expand(input: &Config) -> Result<TokenStream> {
24 let mut src = match input.opts.generate(&input.resolve, input.world) {
25 Ok(s) => s,
26 Err(e) => return Err(Error::new(Span::call_site(), e.to_string())),
27 };
28
29 if input.opts.stringify {
30 return Ok(quote::quote!(#src));
31 }
32
33 if input.include_generated_code_from_file
38 || input.opts.debug
39 || std::env::var("WASMTIME_DEBUG_BINDGEN").is_ok()
40 {
41 static INVOCATION: AtomicUsize = AtomicUsize::new(0);
42 let root = Path::new(env!("DEBUG_OUTPUT_DIR"));
43 let world_name = &input.resolve.worlds[input.world].name;
44 let n = INVOCATION.fetch_add(1, Relaxed);
45 let path = root.join(format!("{world_name}{n}.rs"));
46
47 std::fs::write(&path, &src).unwrap();
48
49 drop(
51 std::process::Command::new("rustfmt")
52 .arg(&path)
53 .arg("--edition=2021")
54 .output(),
55 );
56
57 src = format!("include!({path:?});");
58 }
59 let mut contents = src.parse::<TokenStream>().unwrap();
60
61 for file in input.files.iter() {
64 contents.extend(
65 format!("const _: &str = include_str!(r#\"{}\"#);\n", file.display())
66 .parse::<TokenStream>()
67 .unwrap(),
68 );
69 }
70
71 Ok(contents)
72}
73
74impl Parse for Config {
75 fn parse(input: ParseStream<'_>) -> Result<Self> {
76 let call_site = Span::call_site();
77 let mut opts = Opts::default();
78 let mut world = None;
79 let mut inline = None;
80 let mut paths = Vec::new();
81 let mut imports_configured = false;
82 let mut exports_configured = false;
83 let mut include_generated_code_from_file = false;
84
85 if input.peek(token::Brace) {
86 let content;
87 syn::braced!(content in input);
88 let fields = Punctuated::<Opt, Token![,]>::parse_terminated(&content)?;
89 for field in fields.into_pairs() {
90 match field.into_value() {
91 Opt::Path(p) => {
92 paths.extend(p.into_iter().map(|p| p.value()));
93 }
94 Opt::World(s) => {
95 if world.is_some() {
96 return Err(Error::new(s.span(), "cannot specify second world"));
97 }
98 world = Some(s.value());
99 }
100 Opt::Inline(s) => {
101 if inline.is_some() {
102 return Err(Error::new(s.span(), "cannot specify second source"));
103 }
104 inline = Some(s.value());
105 }
106 Opt::Debug(val) => opts.debug = val,
107 Opt::TrappableErrorType(val) => opts.trappable_error_type = val,
108 Opt::Ownership(val) => opts.ownership = val,
109 Opt::Interfaces(s) => {
110 if inline.is_some() {
111 return Err(Error::new(s.span(), "cannot specify a second source"));
112 }
113 inline = Some(format!(
114 "
115 package wasmtime:component-macro-synthesized;
116
117 world interfaces {{
118 {}
119 }}
120 ",
121 s.value()
122 ));
123
124 if world.is_some() {
125 return Err(Error::new(
126 s.span(),
127 "cannot specify a world with `interfaces`",
128 ));
129 }
130 world = Some("wasmtime:component-macro-synthesized/interfaces".to_string());
131
132 opts.only_interfaces = true;
133 }
134 Opt::With(val) => opts.with.extend(val),
135 Opt::AdditionalDerives(paths) => {
136 opts.additional_derive_attributes = paths
137 .into_iter()
138 .map(|p| p.into_token_stream().to_string())
139 .collect()
140 }
141 Opt::Stringify(val) => opts.stringify = val,
142 Opt::SkipMutForwardingImpls(val) => opts.skip_mut_forwarding_impls = val,
143 Opt::RequireStoreDataSend(val) => opts.require_store_data_send = val,
144 Opt::WasmtimeCrate(f) => {
145 opts.wasmtime_crate = Some(f.into_token_stream().to_string())
146 }
147 Opt::IncludeGeneratedCodeFromFile(i) => include_generated_code_from_file = i,
148 Opt::Imports(config, span) => {
149 if imports_configured {
150 return Err(Error::new(span, "cannot specify imports configuration"));
151 }
152 opts.imports = config;
153 imports_configured = true;
154 }
155 Opt::Exports(config, span) => {
156 if exports_configured {
157 return Err(Error::new(span, "cannot specify exports configuration"));
158 }
159 opts.exports = config;
160 exports_configured = true;
161 }
162 }
163 }
164 } else {
165 world = input.parse::<Option<syn::LitStr>>()?.map(|s| s.value());
166 if input.parse::<Option<syn::token::In>>()?.is_some() {
167 paths.push(input.parse::<syn::LitStr>()?.value());
168 }
169 }
170 let (resolve, pkgs, files) = parse_source(&paths, &inline)
171 .map_err(|err| Error::new(call_site, format!("{err:?}")))?;
172
173 let world = resolve
174 .select_world(&pkgs, world.as_deref())
175 .map_err(|e| Error::new(call_site, format!("{e:?}")))?;
176 Ok(Config {
177 opts,
178 resolve,
179 world,
180 files,
181 include_generated_code_from_file,
182 })
183 }
184}
185
186fn parse_source(
187 paths: &Vec<String>,
188 inline: &Option<String>,
189) -> anyhow::Result<(Resolve, Vec<PackageId>, Vec<PathBuf>)> {
190 let mut resolve = Resolve::default();
191 resolve.all_features = true;
192 let mut files = Vec::new();
193 let mut pkgs = Vec::new();
194 let root = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
195 let default = root.join("wit");
196
197 let parse = |resolve: &mut Resolve,
198 files: &mut Vec<PathBuf>,
199 pkgs: &mut Vec<PackageId>,
200 paths: &[PathBuf]|
201 -> anyhow::Result<_> {
202 for path in paths {
203 let p = root.join(path);
204 let normalized_path = match std::fs::canonicalize(&p) {
208 Ok(p) => p,
209 Err(_) => p.to_path_buf(),
210 };
211 let (pkg, sources) = resolve.push_path(normalized_path)?;
212 pkgs.push(pkg);
213 files.extend(sources.paths().map(|p| p.to_owned()));
214 }
215 Ok(())
216 };
217
218 if paths.is_empty() {
219 if default.exists() {
220 parse(&mut resolve, &mut files, &mut pkgs, &[default])?;
221 }
222 } else {
223 parse(
224 &mut resolve,
225 &mut files,
226 &mut pkgs,
227 &paths.iter().map(|s| s.into()).collect::<Vec<_>>(),
228 )?;
229 }
230
231 if let Some(inline) = inline {
232 pkgs.truncate(0);
233 pkgs.push(resolve.push_group(UnresolvedPackageGroup::parse("macro-input", inline)?)?);
234 }
235
236 Ok((resolve, pkgs, files))
237}
238
239mod kw {
240 syn::custom_keyword!(inline);
241 syn::custom_keyword!(path);
242 syn::custom_keyword!(tracing);
243 syn::custom_keyword!(verbose_tracing);
244 syn::custom_keyword!(trappable_error_type);
245 syn::custom_keyword!(world);
246 syn::custom_keyword!(ownership);
247 syn::custom_keyword!(interfaces);
248 syn::custom_keyword!(with);
249 syn::custom_keyword!(except_imports);
250 syn::custom_keyword!(only_imports);
251 syn::custom_keyword!(additional_derives);
252 syn::custom_keyword!(stringify);
253 syn::custom_keyword!(skip_mut_forwarding_impls);
254 syn::custom_keyword!(require_store_data_send);
255 syn::custom_keyword!(wasmtime_crate);
256 syn::custom_keyword!(include_generated_code_from_file);
257 syn::custom_keyword!(debug);
258 syn::custom_keyword!(imports);
259 syn::custom_keyword!(exports);
260 syn::custom_keyword!(store);
261 syn::custom_keyword!(trappable);
262 syn::custom_keyword!(ignore_wit);
263 syn::custom_keyword!(exact);
264 syn::custom_keyword!(task_exit);
265}
266
267enum Opt {
268 World(syn::LitStr),
269 Path(Vec<syn::LitStr>),
270 Inline(syn::LitStr),
271 TrappableErrorType(Vec<TrappableError>),
272 Ownership(Ownership),
273 Interfaces(syn::LitStr),
274 With(HashMap<String, String>),
275 AdditionalDerives(Vec<syn::Path>),
276 Stringify(bool),
277 SkipMutForwardingImpls(bool),
278 RequireStoreDataSend(bool),
279 WasmtimeCrate(syn::Path),
280 IncludeGeneratedCodeFromFile(bool),
281 Debug(bool),
282 Imports(FunctionConfig, Span),
283 Exports(FunctionConfig, Span),
284}
285
286impl Parse for Opt {
287 fn parse(input: ParseStream<'_>) -> Result<Self> {
288 let l = input.lookahead1();
289 if l.peek(kw::debug) {
290 input.parse::<kw::debug>()?;
291 input.parse::<Token![:]>()?;
292 Ok(Opt::Debug(input.parse::<syn::LitBool>()?.value))
293 } else if l.peek(kw::path) {
294 input.parse::<kw::path>()?;
295 input.parse::<Token![:]>()?;
296
297 let mut paths: Vec<syn::LitStr> = vec![];
298
299 let l = input.lookahead1();
300 if l.peek(syn::LitStr) {
301 paths.push(input.parse()?);
302 } else if l.peek(syn::token::Bracket) {
303 let contents;
304 syn::bracketed!(contents in input);
305 let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?;
306
307 paths.extend(list);
308 } else {
309 return Err(l.error());
310 };
311
312 Ok(Opt::Path(paths))
313 } else if l.peek(kw::inline) {
314 input.parse::<kw::inline>()?;
315 input.parse::<Token![:]>()?;
316 Ok(Opt::Inline(input.parse()?))
317 } else if l.peek(kw::world) {
318 input.parse::<kw::world>()?;
319 input.parse::<Token![:]>()?;
320 Ok(Opt::World(input.parse()?))
321 } else if l.peek(kw::ownership) {
322 input.parse::<kw::ownership>()?;
323 input.parse::<Token![:]>()?;
324 let ownership = input.parse::<syn::Ident>()?;
325 Ok(Opt::Ownership(match ownership.to_string().as_str() {
326 "Owning" => Ownership::Owning,
327 "Borrowing" => Ownership::Borrowing {
328 duplicate_if_necessary: {
329 let contents;
330 braced!(contents in input);
331 let field = contents.parse::<syn::Ident>()?;
332 match field.to_string().as_str() {
333 "duplicate_if_necessary" => {
334 contents.parse::<Token![:]>()?;
335 contents.parse::<syn::LitBool>()?.value
336 }
337 name => {
338 return Err(Error::new(
339 field.span(),
340 format!(
341 "unrecognized `Ownership::Borrowing` field: `{name}`; \
342 expected `duplicate_if_necessary`"
343 ),
344 ));
345 }
346 }
347 },
348 },
349 name => {
350 return Err(Error::new(
351 ownership.span(),
352 format!(
353 "unrecognized ownership: `{name}`; \
354 expected `Owning` or `Borrowing`"
355 ),
356 ));
357 }
358 }))
359 } else if l.peek(kw::trappable_error_type) {
360 input.parse::<kw::trappable_error_type>()?;
361 input.parse::<Token![:]>()?;
362 let contents;
363 let _lbrace = braced!(contents in input);
364 let fields: Punctuated<_, Token![,]> =
365 contents.parse_terminated(trappable_error_field_parse, Token![,])?;
366 Ok(Opt::TrappableErrorType(Vec::from_iter(fields)))
367 } else if l.peek(kw::interfaces) {
368 input.parse::<kw::interfaces>()?;
369 input.parse::<Token![:]>()?;
370 Ok(Opt::Interfaces(input.parse::<syn::LitStr>()?))
371 } else if l.peek(kw::with) {
372 input.parse::<kw::with>()?;
373 input.parse::<Token![:]>()?;
374 let contents;
375 let _lbrace = braced!(contents in input);
376 let fields: Punctuated<(String, String), Token![,]> =
377 contents.parse_terminated(with_field_parse, Token![,])?;
378 Ok(Opt::With(HashMap::from_iter(fields)))
379 } else if l.peek(kw::additional_derives) {
380 input.parse::<kw::additional_derives>()?;
381 input.parse::<Token![:]>()?;
382 let contents;
383 syn::bracketed!(contents in input);
384 let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?;
385 Ok(Opt::AdditionalDerives(list.iter().cloned().collect()))
386 } else if l.peek(kw::stringify) {
387 input.parse::<kw::stringify>()?;
388 input.parse::<Token![:]>()?;
389 Ok(Opt::Stringify(input.parse::<syn::LitBool>()?.value))
390 } else if l.peek(kw::skip_mut_forwarding_impls) {
391 input.parse::<kw::skip_mut_forwarding_impls>()?;
392 input.parse::<Token![:]>()?;
393 Ok(Opt::SkipMutForwardingImpls(
394 input.parse::<syn::LitBool>()?.value,
395 ))
396 } else if l.peek(kw::require_store_data_send) {
397 input.parse::<kw::require_store_data_send>()?;
398 input.parse::<Token![:]>()?;
399 Ok(Opt::RequireStoreDataSend(
400 input.parse::<syn::LitBool>()?.value,
401 ))
402 } else if l.peek(kw::wasmtime_crate) {
403 input.parse::<kw::wasmtime_crate>()?;
404 input.parse::<Token![:]>()?;
405 Ok(Opt::WasmtimeCrate(input.parse()?))
406 } else if l.peek(kw::include_generated_code_from_file) {
407 input.parse::<kw::include_generated_code_from_file>()?;
408 input.parse::<Token![:]>()?;
409 Ok(Opt::IncludeGeneratedCodeFromFile(
410 input.parse::<syn::LitBool>()?.value,
411 ))
412 } else if l.peek(kw::imports) {
413 let span = input.parse::<kw::imports>()?.span;
414 input.parse::<Token![:]>()?;
415 Ok(Opt::Imports(parse_function_config(input)?, span))
416 } else if l.peek(kw::exports) {
417 let span = input.parse::<kw::exports>()?.span;
418 input.parse::<Token![:]>()?;
419 Ok(Opt::Exports(parse_function_config(input)?, span))
420 } else {
421 Err(l.error())
422 }
423 }
424}
425
426fn trappable_error_field_parse(input: ParseStream<'_>) -> Result<TrappableError> {
427 let wit_path = input.parse::<syn::LitStr>()?.value();
428 input.parse::<Token![=>]>()?;
429 let rust_type_name = input.parse::<syn::Path>()?.to_token_stream().to_string();
430 Ok(TrappableError {
431 wit_path,
432 rust_type_name,
433 })
434}
435
436fn with_field_parse(input: ParseStream<'_>) -> Result<(String, String)> {
437 let interface = input.parse::<syn::LitStr>()?.value();
438 input.parse::<Token![:]>()?;
439 let start = input.span();
440 let path = input.parse::<syn::Path>()?;
441
442 let span = start
444 .join(path.segments.last().unwrap().ident.span())
445 .unwrap_or(start);
446
447 let mut buf = String::new();
448 let append = |buf: &mut String, segment: syn::PathSegment| -> Result<()> {
449 if segment.arguments != syn::PathArguments::None {
450 return Err(Error::new(
451 span,
452 "Module path must not contain angles or parens",
453 ));
454 }
455
456 buf.push_str(&segment.ident.to_string());
457
458 Ok(())
459 };
460
461 if path.leading_colon.is_some() {
462 buf.push_str("::");
463 }
464
465 let mut segments = path.segments.into_iter();
466
467 if let Some(segment) = segments.next() {
468 append(&mut buf, segment)?;
469 }
470
471 for segment in segments {
472 buf.push_str("::");
473 append(&mut buf, segment)?;
474 }
475
476 Ok((interface, buf))
477}
478
479fn parse_function_config(input: ParseStream<'_>) -> Result<FunctionConfig> {
480 let content;
481 syn::braced!(content in input);
482 let mut ret = FunctionConfig::new();
483
484 let list = Punctuated::<FunctionConfigSyntax, Token![,]>::parse_terminated(&content)?;
485 for item in list.into_iter() {
486 ret.push(item.filter, item.flags);
487 }
488
489 return Ok(ret);
490
491 struct FunctionConfigSyntax {
492 filter: FunctionFilter,
493 flags: FunctionFlags,
494 }
495
496 impl Parse for FunctionConfigSyntax {
497 fn parse(input: ParseStream<'_>) -> Result<Self> {
498 let l = input.lookahead1();
499 let filter = if l.peek(syn::LitStr) {
500 FunctionFilter::Name(input.parse::<syn::LitStr>()?.value())
501 } else if l.peek(Token![default]) {
502 input.parse::<Token![default]>()?;
503 FunctionFilter::Default
504 } else {
505 return Err(l.error());
506 };
507
508 input.parse::<Token![:]>()?;
509
510 let mut flags = FunctionFlags::empty();
511 while !input.is_empty() {
512 let l = input.lookahead1();
513 if l.peek(Token![async]) {
514 input.parse::<Token![async]>()?;
515 flags |= FunctionFlags::ASYNC;
516 } else if l.peek(kw::tracing) {
517 input.parse::<kw::tracing>()?;
518 flags |= FunctionFlags::TRACING;
519 } else if l.peek(kw::verbose_tracing) {
520 input.parse::<kw::verbose_tracing>()?;
521 flags |= FunctionFlags::VERBOSE_TRACING;
522 } else if l.peek(kw::store) {
523 input.parse::<kw::store>()?;
524 flags |= FunctionFlags::STORE;
525 } else if l.peek(kw::trappable) {
526 input.parse::<kw::trappable>()?;
527 flags |= FunctionFlags::TRAPPABLE;
528 } else if l.peek(kw::ignore_wit) {
529 input.parse::<kw::ignore_wit>()?;
530 flags |= FunctionFlags::IGNORE_WIT;
531 } else if l.peek(kw::exact) {
532 input.parse::<kw::exact>()?;
533 flags |= FunctionFlags::EXACT;
534 } else if l.peek(kw::task_exit) {
535 input.parse::<kw::task_exit>()?;
536 flags |= FunctionFlags::TASK_EXIT;
537 } else {
538 return Err(l.error());
539 }
540
541 if input.peek(Token![|]) {
542 input.parse::<Token![|]>()?;
543 } else {
544 break;
545 }
546 }
547
548 Ok(FunctionConfigSyntax { filter, flags })
549 }
550 }
551}