wasm_bindgen_backend/ast.rs
1//! A representation of the Abstract Syntax Tree of a Rust program,
2//! with all the added metadata necessary to generate Wasm bindings
3//! for it.
4
5use crate::{util::ShortHash, Diagnostic};
6use proc_macro2::{Ident, Span};
7use std::hash::{Hash, Hasher};
8use syn::Path;
9use wasm_bindgen_shared as shared;
10
11/// An abstract syntax tree representing a rust program. Contains
12/// extra information for joining up this rust code with javascript.
13#[cfg_attr(feature = "extra-traits", derive(Debug))]
14#[derive(Clone)]
15pub struct Program {
16 /// rust -> js interfaces
17 pub exports: Vec<Export>,
18 /// js -> rust interfaces
19 pub imports: Vec<Import>,
20 /// linked-to modules
21 pub linked_modules: Vec<ImportModule>,
22 /// rust enums
23 pub enums: Vec<Enum>,
24 /// rust structs
25 pub structs: Vec<Struct>,
26 /// custom typescript sections to be included in the definition file
27 pub typescript_custom_sections: Vec<LitOrExpr>,
28 /// Inline JS snippets
29 pub inline_js: Vec<String>,
30 /// Path to wasm_bindgen
31 pub wasm_bindgen: Path,
32 /// Path to js_sys
33 pub js_sys: Path,
34 /// Path to wasm_bindgen_futures
35 pub wasm_bindgen_futures: Path,
36}
37
38impl Default for Program {
39 fn default() -> Self {
40 Self {
41 exports: Default::default(),
42 imports: Default::default(),
43 linked_modules: Default::default(),
44 enums: Default::default(),
45 structs: Default::default(),
46 typescript_custom_sections: Default::default(),
47 inline_js: Default::default(),
48 wasm_bindgen: syn::parse_quote! { wasm_bindgen },
49 js_sys: syn::parse_quote! { js_sys },
50 wasm_bindgen_futures: syn::parse_quote! { wasm_bindgen_futures },
51 }
52 }
53}
54
55impl Program {
56 /// Returns true if the Program is empty
57 pub fn is_empty(&self) -> bool {
58 self.exports.is_empty()
59 && self.imports.is_empty()
60 && self.enums.is_empty()
61 && self.structs.is_empty()
62 && self.typescript_custom_sections.is_empty()
63 && self.inline_js.is_empty()
64 }
65
66 /// Name of the link function for a specific linked module
67 pub fn link_function_name(&self, idx: usize) -> String {
68 let hash = match &self.linked_modules[idx] {
69 ImportModule::Inline(idx, _) => ShortHash((1, &self.inline_js[*idx])).to_string(),
70 other => ShortHash((0, other)).to_string(),
71 };
72 format!("__wbindgen_link_{}", hash)
73 }
74}
75
76/// An abstract syntax tree representing a link to a module in Rust.
77/// In contrast to Program, LinkToModule must expand to an expression.
78/// linked_modules of the inner Program must contain exactly one element
79/// whose link is produced by the expression.
80#[cfg_attr(feature = "extra-traits", derive(Debug))]
81#[derive(Clone)]
82pub struct LinkToModule(pub Program);
83
84/// A rust to js interface. Allows interaction with rust objects/functions
85/// from javascript.
86#[cfg_attr(feature = "extra-traits", derive(Debug))]
87#[derive(Clone)]
88pub struct Export {
89 /// Comments extracted from the rust source.
90 pub comments: Vec<String>,
91 /// The rust function
92 pub function: Function,
93 /// The class name in JS this is attached to
94 pub js_class: Option<String>,
95 /// The kind (static, named, regular)
96 pub method_kind: MethodKind,
97 /// The type of `self` (either `self`, `&self`, or `&mut self`)
98 pub method_self: Option<MethodSelf>,
99 /// The struct name, in Rust, this is attached to
100 pub rust_class: Option<Ident>,
101 /// The name of the rust function/method on the rust side.
102 pub rust_name: Ident,
103 /// Whether or not this function should be flagged as the Wasm start
104 /// function.
105 pub start: bool,
106 /// Path to wasm_bindgen
107 pub wasm_bindgen: Path,
108 /// Path to wasm_bindgen_futures
109 pub wasm_bindgen_futures: Path,
110}
111
112/// The 3 types variations of `self`.
113#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
114#[derive(Copy, Clone)]
115pub enum MethodSelf {
116 /// `self`
117 ByValue,
118 /// `&mut self`
119 RefMutable,
120 /// `&self`
121 RefShared,
122}
123
124/// Things imported from a JS module (in an `extern` block)
125#[cfg_attr(feature = "extra-traits", derive(Debug))]
126#[derive(Clone)]
127pub struct Import {
128 /// The type of module being imported from, if any
129 pub module: Option<ImportModule>,
130 /// The namespace to access the item through, if any
131 pub js_namespace: Option<Vec<String>>,
132 /// The type of item being imported
133 pub kind: ImportKind,
134}
135
136/// The possible types of module to import from
137#[cfg_attr(feature = "extra-traits", derive(Debug))]
138#[derive(Clone)]
139pub enum ImportModule {
140 /// Import from the named module, with relative paths interpreted
141 Named(String, Span),
142 /// Import from the named module, without interpreting paths
143 RawNamed(String, Span),
144 /// Import from an inline JS snippet
145 Inline(usize, Span),
146}
147
148impl Hash for ImportModule {
149 fn hash<H: Hasher>(&self, h: &mut H) {
150 match self {
151 ImportModule::Named(name, _) => (1u8, name).hash(h),
152 ImportModule::Inline(idx, _) => (2u8, idx).hash(h),
153 ImportModule::RawNamed(name, _) => (3u8, name).hash(h),
154 }
155 }
156}
157
158/// The type of item being imported
159#[cfg_attr(feature = "extra-traits", derive(Debug))]
160#[derive(Clone)]
161pub enum ImportKind {
162 /// Importing a function
163 Function(ImportFunction),
164 /// Importing a static value
165 Static(ImportStatic),
166 /// Importing a static string
167 String(ImportString),
168 /// Importing a type/class
169 Type(ImportType),
170 /// Importing a JS enum
171 Enum(StringEnum),
172}
173
174/// A function being imported from JS
175#[cfg_attr(feature = "extra-traits", derive(Debug))]
176#[derive(Clone)]
177pub struct ImportFunction {
178 /// The full signature of the function
179 pub function: Function,
180 /// The name rust code will use
181 pub rust_name: Ident,
182 /// The type being returned
183 pub js_ret: Option<syn::Type>,
184 /// Whether to catch JS exceptions
185 pub catch: bool,
186 /// Whether the function is variadic on the JS side
187 pub variadic: bool,
188 /// Whether the function should use structural type checking
189 pub structural: bool,
190 /// Causes the Builder (See cli-support::js::binding::Builder) to error out if
191 /// it finds itself generating code for a function with this signature
192 pub assert_no_shim: bool,
193 /// The kind of function being imported
194 pub kind: ImportFunctionKind,
195 /// The shim name to use in the generated code. The 'shim' is a function that appears in
196 /// the generated JS as a wrapper around the actual function to import, performing any
197 /// necessary conversions (EG adding a try/catch to change a thrown error into a Result)
198 pub shim: Ident,
199 /// The doc comment on this import, if one is provided
200 pub doc_comment: String,
201 /// Path to wasm_bindgen
202 pub wasm_bindgen: Path,
203 /// Path to wasm_bindgen_futures
204 pub wasm_bindgen_futures: Path,
205}
206
207/// The type of a function being imported
208#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
209#[derive(Clone)]
210pub enum ImportFunctionKind {
211 /// A class method
212 Method {
213 /// The name of the class for this method, in JS
214 class: String,
215 /// The type of the class for this method, in Rust
216 ty: syn::Type,
217 /// The kind of method this is
218 kind: MethodKind,
219 },
220 /// A standard function
221 Normal,
222}
223
224/// The type of a method
225#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
226#[derive(Clone)]
227pub enum MethodKind {
228 /// A class constructor
229 Constructor,
230 /// Any other kind of method
231 Operation(Operation),
232}
233
234/// The operation performed by a class method
235#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
236#[derive(Clone)]
237pub struct Operation {
238 /// Whether this method is static
239 pub is_static: bool,
240 /// The internal kind of this Operation
241 pub kind: OperationKind,
242}
243
244/// The kind of operation performed by a method
245#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
246#[derive(Clone)]
247pub enum OperationKind {
248 /// A standard method, nothing special
249 Regular,
250 /// A method for getting the value of the provided Ident or String
251 Getter(Option<String>),
252 /// A method for setting the value of the provided Ident or String
253 Setter(Option<String>),
254 /// A dynamically intercepted getter
255 IndexingGetter,
256 /// A dynamically intercepted setter
257 IndexingSetter,
258 /// A dynamically intercepted deleter
259 IndexingDeleter,
260}
261
262/// The type of a static being imported
263#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
264#[derive(Clone)]
265pub struct ImportStatic {
266 /// The visibility of this static in Rust
267 pub vis: syn::Visibility,
268 /// The type of static being imported
269 pub ty: syn::Type,
270 /// The name of the shim function used to access this static
271 pub shim: Ident,
272 /// The name of this static on the Rust side
273 pub rust_name: Ident,
274 /// The name of this static on the JS side
275 pub js_name: String,
276 /// Path to wasm_bindgen
277 pub wasm_bindgen: Path,
278 /// Version of `thread_local`, if any.
279 pub thread_local: Option<ThreadLocal>,
280}
281
282/// Which version of the `thread_local` attribute is enabled.
283#[derive(Copy, Clone, Debug, PartialEq, Eq)]
284pub enum ThreadLocal {
285 /// V1.
286 V1,
287 /// V2.
288 V2,
289}
290
291/// The type of a static string being imported
292#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
293#[derive(Clone)]
294pub struct ImportString {
295 /// The visibility of this static string in Rust
296 pub vis: syn::Visibility,
297 /// The type specified by the user, which we only use to show an error if the wrong type is used.
298 pub ty: syn::Type,
299 /// The name of the shim function used to access this static
300 pub shim: Ident,
301 /// The name of this static on the Rust side
302 pub rust_name: Ident,
303 /// Path to wasm_bindgen
304 pub wasm_bindgen: Path,
305 /// Path to js_sys
306 pub js_sys: Path,
307 /// The string to export.
308 pub string: String,
309 /// Version of `thread_local`.
310 pub thread_local: ThreadLocal,
311}
312
313/// The metadata for a type being imported
314#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
315#[derive(Clone)]
316pub struct ImportType {
317 /// The visibility of this type in Rust
318 pub vis: syn::Visibility,
319 /// The name of this type on the Rust side
320 pub rust_name: Ident,
321 /// The name of this type on the JS side
322 pub js_name: String,
323 /// The custom attributes to apply to this type
324 pub attrs: Vec<syn::Attribute>,
325 /// The TS definition to generate for this type
326 pub typescript_type: Option<String>,
327 /// The doc comment applied to this type, if one exists
328 pub doc_comment: Option<String>,
329 /// The name of the shim to check instanceof for this type
330 pub instanceof_shim: String,
331 /// The name of the remote function to use for the generated is_type_of
332 pub is_type_of: Option<syn::Expr>,
333 /// The list of classes this extends, if any
334 pub extends: Vec<syn::Path>,
335 /// A custom prefix to add and attempt to fall back to, if the type isn't found
336 pub vendor_prefixes: Vec<Ident>,
337 /// If present, don't generate a `Deref` impl
338 pub no_deref: bool,
339 /// Path to wasm_bindgen
340 pub wasm_bindgen: Path,
341}
342
343/// The metadata for a String Enum
344#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
345#[derive(Clone)]
346pub struct StringEnum {
347 /// The Rust enum's visibility
348 pub vis: syn::Visibility,
349 /// The Rust enum's identifiers
350 pub name: Ident,
351 /// The name of this string enum in JS/TS code
352 pub js_name: String,
353 /// The Rust identifiers for the variants
354 pub variants: Vec<Ident>,
355 /// The JS string values of the variants
356 pub variant_values: Vec<String>,
357 /// The doc comments on this enum, if any
358 pub comments: Vec<String>,
359 /// Attributes to apply to the Rust enum
360 pub rust_attrs: Vec<syn::Attribute>,
361 /// Whether to generate a typescript definition for this enum
362 pub generate_typescript: bool,
363 /// Path to wasm_bindgen
364 pub wasm_bindgen: Path,
365}
366
367/// Information about a function being imported or exported
368#[cfg_attr(feature = "extra-traits", derive(Debug))]
369#[derive(Clone)]
370pub struct Function {
371 /// The name of the function
372 pub name: String,
373 /// The span of the function's name in Rust code
374 pub name_span: Span,
375 /// Whether the function has a js_name attribute
376 pub renamed_via_js_name: bool,
377 /// The arguments to the function
378 pub arguments: Vec<FunctionArgumentData>,
379 /// The data of return type of the function
380 pub ret: Option<FunctionReturnData>,
381 /// Any custom attributes being applied to the function
382 pub rust_attrs: Vec<syn::Attribute>,
383 /// The visibility of this function in Rust
384 pub rust_vis: syn::Visibility,
385 /// Whether this is an `unsafe` function
386 pub r#unsafe: bool,
387 /// Whether this is an `async` function
388 pub r#async: bool,
389 /// Whether to generate a typescript definition for this function
390 pub generate_typescript: bool,
391 /// Whether to generate jsdoc documentation for this function
392 pub generate_jsdoc: bool,
393 /// Whether this is a function with a variadict parameter
394 pub variadic: bool,
395}
396
397/// Information about a function's return
398#[cfg_attr(feature = "extra-traits", derive(Debug))]
399#[derive(Clone)]
400pub struct FunctionReturnData {
401 /// Specifies the type of the function's return
402 pub r#type: syn::Type,
403 /// Specifies the JS return type override
404 pub js_type: Option<String>,
405 /// Specifies the return description
406 pub desc: Option<String>,
407}
408
409/// Information about a function's argument
410#[cfg_attr(feature = "extra-traits", derive(Debug))]
411#[derive(Clone)]
412pub struct FunctionArgumentData {
413 /// Specifies the type of the function's argument
414 pub pat_type: syn::PatType,
415 /// Specifies the JS argument name override
416 pub js_name: Option<String>,
417 /// Specifies the JS function argument type override
418 pub js_type: Option<String>,
419 /// Specifies the argument description
420 pub desc: Option<String>,
421}
422
423/// Information about a Struct being exported
424#[cfg_attr(feature = "extra-traits", derive(Debug))]
425#[derive(Clone)]
426pub struct Struct {
427 /// The name of the struct in Rust code
428 pub rust_name: Ident,
429 /// The name of the struct in JS code
430 pub js_name: String,
431 /// All the fields of this struct to export
432 pub fields: Vec<StructField>,
433 /// The doc comments on this struct, if provided
434 pub comments: Vec<String>,
435 /// Whether this struct is inspectable (provides toJSON/toString properties to JS)
436 pub is_inspectable: bool,
437 /// Whether to generate a typescript definition for this struct
438 pub generate_typescript: bool,
439 /// Path to wasm_bindgen
440 pub wasm_bindgen: Path,
441}
442
443/// The field of a struct
444#[cfg_attr(feature = "extra-traits", derive(Debug))]
445#[derive(Clone)]
446pub struct StructField {
447 /// The name of the field in Rust code
448 pub rust_name: syn::Member,
449 /// The name of the field in JS code
450 pub js_name: String,
451 /// The name of the struct this field is part of
452 pub struct_name: Ident,
453 /// Whether this value is read-only to JS
454 pub readonly: bool,
455 /// The type of this field
456 pub ty: syn::Type,
457 /// The name of the getter shim for this field
458 pub getter: Ident,
459 /// The name of the setter shim for this field
460 pub setter: Ident,
461 /// The doc comments on this field, if any
462 pub comments: Vec<String>,
463 /// Whether to generate a typescript definition for this field
464 pub generate_typescript: bool,
465 /// Whether to generate jsdoc documentation for this field
466 pub generate_jsdoc: bool,
467 /// The span of the `#[wasm_bindgen(getter_with_clone)]` attribute applied
468 /// to this field, if any.
469 ///
470 /// If this is `Some`, the auto-generated getter for this field must clone
471 /// the field instead of copying it.
472 pub getter_with_clone: Option<Span>,
473 /// Path to wasm_bindgen
474 pub wasm_bindgen: Path,
475}
476
477/// The metadata for an Enum
478#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
479#[derive(Clone)]
480pub struct Enum {
481 /// The name of this enum in Rust code
482 pub rust_name: Ident,
483 /// The name of this enum in JS code
484 pub js_name: String,
485 /// Whether the variant values and hole are signed, meaning that they
486 /// represent the bits of a `i32` value.
487 pub signed: bool,
488 /// The variants provided by this enum
489 pub variants: Vec<Variant>,
490 /// The doc comments on this enum, if any
491 pub comments: Vec<String>,
492 /// The value to use for a `none` variant of the enum
493 pub hole: u32,
494 /// Whether to generate a typescript definition for this enum
495 pub generate_typescript: bool,
496 /// Path to wasm_bindgen
497 pub wasm_bindgen: Path,
498}
499
500/// The variant of an enum
501#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
502#[derive(Clone)]
503pub struct Variant {
504 /// The name of this variant
505 pub name: Ident,
506 /// The backing value of this variant
507 pub value: u32,
508 /// The doc comments on this variant, if any
509 pub comments: Vec<String>,
510}
511
512/// Unused, the type of an argument to / return from a function
513#[derive(Copy, Clone, Debug, PartialEq, Eq)]
514pub enum TypeKind {
515 /// A by-reference arg, EG `&T`
516 ByRef,
517 /// A by-mutable-reference arg, EG `&mut T`
518 ByMutRef,
519 /// A by-value arg, EG `T`
520 ByValue,
521}
522
523/// Unused, the location of a type for a function argument (import/export, argument/ret)
524#[derive(Copy, Clone, Debug, PartialEq, Eq)]
525pub enum TypeLocation {
526 /// An imported argument (JS side type)
527 ImportArgument,
528 /// An imported return
529 ImportRet,
530 /// An exported argument (Rust side type)
531 ExportArgument,
532 /// An exported return
533 ExportRet,
534}
535
536/// An enum representing either a literal value (`Lit`) or an expression (`syn::Expr`).
537#[cfg_attr(feature = "extra-traits", derive(Debug))]
538#[derive(Clone)]
539pub enum LitOrExpr {
540 /// Represents an expression that needs to be evaluated before it can be encoded
541 Expr(syn::Expr),
542 /// Represents a literal string that can be directly encoded.
543 Lit(String),
544}
545
546impl Export {
547 /// Mangles a rust -> javascript export, so that the created Ident will be unique over function
548 /// name and class name, if the function belongs to a javascript class.
549 pub(crate) fn rust_symbol(&self) -> Ident {
550 let mut generated_name = String::from("__wasm_bindgen_generated");
551 if let Some(class) = &self.js_class {
552 generated_name.push('_');
553 generated_name.push_str(class);
554 }
555 generated_name.push('_');
556 generated_name.push_str(&self.function.name.to_string());
557 Ident::new(&generated_name, Span::call_site())
558 }
559
560 /// This is the name of the shim function that gets exported and takes the raw
561 /// ABI form of its arguments and converts them back into their normal,
562 /// "high level" form before calling the actual function.
563 pub(crate) fn export_name(&self) -> String {
564 let fn_name = self.function.name.to_string();
565 match &self.js_class {
566 Some(class) => shared::struct_function_export_name(class, &fn_name),
567 None => shared::free_function_export_name(&fn_name),
568 }
569 }
570}
571
572impl ImportKind {
573 /// Whether this type can be inside an `impl` block.
574 pub fn fits_on_impl(&self) -> bool {
575 match *self {
576 ImportKind::Function(_) => true,
577 ImportKind::Static(_) => false,
578 ImportKind::String(_) => false,
579 ImportKind::Type(_) => false,
580 ImportKind::Enum(_) => false,
581 }
582 }
583}
584
585impl Function {
586 /// If the rust object has a `fn xxx(&self) -> MyType` method, get the name for a getter in
587 /// javascript (in this case `xxx`, so you can write `val = obj.xxx`)
588 pub fn infer_getter_property(&self) -> &str {
589 &self.name
590 }
591
592 /// If the rust object has a `fn set_xxx(&mut self, MyType)` style method, get the name
593 /// for a setter in javascript (in this case `xxx`, so you can write `obj.xxx = val`)
594 pub fn infer_setter_property(&self) -> Result<String, Diagnostic> {
595 let name = self.name.to_string();
596
597 // Otherwise we infer names based on the Rust function name.
598 if !name.starts_with("set_") {
599 bail_span!(
600 syn::token::Pub(self.name_span),
601 "setters must start with `set_`, found: {}",
602 name,
603 );
604 }
605 Ok(name[4..].to_string())
606 }
607}