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    /// [`true`] if using the new `thread_local` representation.
279    pub thread_local: bool,
280}
281
282/// The type of a static string being imported
283#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
284#[derive(Clone)]
285pub struct ImportString {
286    /// The visibility of this static string in Rust
287    pub vis: syn::Visibility,
288    /// The type specified by the user, which we only use to show an error if the wrong type is used.
289    pub ty: syn::Type,
290    /// The name of the shim function used to access this static
291    pub shim: Ident,
292    /// The name of this static on the Rust side
293    pub rust_name: Ident,
294    /// Path to wasm_bindgen
295    pub wasm_bindgen: Path,
296    /// Path to js_sys
297    pub js_sys: Path,
298    /// The string to export.
299    pub string: String,
300}
301
302/// The metadata for a type being imported
303#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
304#[derive(Clone)]
305pub struct ImportType {
306    /// The visibility of this type in Rust
307    pub vis: syn::Visibility,
308    /// The name of this type on the Rust side
309    pub rust_name: Ident,
310    /// The name of this type on the JS side
311    pub js_name: String,
312    /// The custom attributes to apply to this type
313    pub attrs: Vec<syn::Attribute>,
314    /// The TS definition to generate for this type
315    pub typescript_type: Option<String>,
316    /// The doc comment applied to this type, if one exists
317    pub doc_comment: Option<String>,
318    /// The name of the shim to check instanceof for this type
319    pub instanceof_shim: String,
320    /// The name of the remote function to use for the generated is_type_of
321    pub is_type_of: Option<syn::Expr>,
322    /// The list of classes this extends, if any
323    pub extends: Vec<syn::Path>,
324    /// A custom prefix to add and attempt to fall back to, if the type isn't found
325    pub vendor_prefixes: Vec<Ident>,
326    /// If present, don't generate a `Deref` impl
327    pub no_deref: bool,
328    /// Path to wasm_bindgen
329    pub wasm_bindgen: Path,
330}
331
332/// The metadata for a String Enum
333#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
334#[derive(Clone)]
335pub struct StringEnum {
336    /// The Rust enum's visibility
337    pub vis: syn::Visibility,
338    /// The Rust enum's identifiers
339    pub name: Ident,
340    /// The Rust identifiers for the variants
341    pub variants: Vec<Ident>,
342    /// The JS string values of the variants
343    pub variant_values: Vec<String>,
344    /// Attributes to apply to the Rust enum
345    pub rust_attrs: Vec<syn::Attribute>,
346    /// Path to wasm_bindgen
347    pub wasm_bindgen: Path,
348}
349
350/// Information about a function being imported or exported
351#[cfg_attr(feature = "extra-traits", derive(Debug))]
352#[derive(Clone)]
353pub struct Function {
354    /// The name of the function
355    pub name: String,
356    /// The span of the function's name in Rust code
357    pub name_span: Span,
358    /// Whether the function has a js_name attribute
359    pub renamed_via_js_name: bool,
360    /// The arguments to the function
361    pub arguments: Vec<syn::PatType>,
362    /// The return type of the function, if provided
363    pub ret: Option<syn::Type>,
364    /// Any custom attributes being applied to the function
365    pub rust_attrs: Vec<syn::Attribute>,
366    /// The visibility of this function in Rust
367    pub rust_vis: syn::Visibility,
368    /// Whether this is an `unsafe` function
369    pub r#unsafe: bool,
370    /// Whether this is an `async` function
371    pub r#async: bool,
372    /// Whether to generate a typescript definition for this function
373    pub generate_typescript: bool,
374    /// Whether to generate jsdoc documentation for this function
375    pub generate_jsdoc: bool,
376    /// Whether this is a function with a variadict parameter
377    pub variadic: bool,
378}
379
380/// Information about a Struct being exported
381#[cfg_attr(feature = "extra-traits", derive(Debug))]
382#[derive(Clone)]
383pub struct Struct {
384    /// The name of the struct in Rust code
385    pub rust_name: Ident,
386    /// The name of the struct in JS code
387    pub js_name: String,
388    /// All the fields of this struct to export
389    pub fields: Vec<StructField>,
390    /// The doc comments on this struct, if provided
391    pub comments: Vec<String>,
392    /// Whether this struct is inspectable (provides toJSON/toString properties to JS)
393    pub is_inspectable: bool,
394    /// Whether to generate a typescript definition for this struct
395    pub generate_typescript: bool,
396    /// Path to wasm_bindgen
397    pub wasm_bindgen: Path,
398}
399
400/// The field of a struct
401#[cfg_attr(feature = "extra-traits", derive(Debug))]
402#[derive(Clone)]
403pub struct StructField {
404    /// The name of the field in Rust code
405    pub rust_name: syn::Member,
406    /// The name of the field in JS code
407    pub js_name: String,
408    /// The name of the struct this field is part of
409    pub struct_name: Ident,
410    /// Whether this value is read-only to JS
411    pub readonly: bool,
412    /// The type of this field
413    pub ty: syn::Type,
414    /// The name of the getter shim for this field
415    pub getter: Ident,
416    /// The name of the setter shim for this field
417    pub setter: Ident,
418    /// The doc comments on this field, if any
419    pub comments: Vec<String>,
420    /// Whether to generate a typescript definition for this field
421    pub generate_typescript: bool,
422    /// Whether to generate jsdoc documentation for this field
423    pub generate_jsdoc: bool,
424    /// The span of the `#[wasm_bindgen(getter_with_clone)]` attribute applied
425    /// to this field, if any.
426    ///
427    /// If this is `Some`, the auto-generated getter for this field must clone
428    /// the field instead of copying it.
429    pub getter_with_clone: Option<Span>,
430    /// Path to wasm_bindgen
431    pub wasm_bindgen: Path,
432}
433
434/// The metadata for an Enum
435#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
436#[derive(Clone)]
437pub struct Enum {
438    /// The name of this enum in Rust code
439    pub rust_name: Ident,
440    /// The name of this enum in JS code
441    pub js_name: String,
442    /// The variants provided by this enum
443    pub variants: Vec<Variant>,
444    /// The doc comments on this enum, if any
445    pub comments: Vec<String>,
446    /// The value to use for a `none` variant of the enum
447    pub hole: u32,
448    /// Whether to generate a typescript definition for this enum
449    pub generate_typescript: bool,
450    /// Path to wasm_bindgen
451    pub wasm_bindgen: Path,
452}
453
454/// The variant of an enum
455#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
456#[derive(Clone)]
457pub struct Variant {
458    /// The name of this variant
459    pub name: Ident,
460    /// The backing value of this variant
461    pub value: u32,
462    /// The doc comments on this variant, if any
463    pub comments: Vec<String>,
464}
465
466/// Unused, the type of an argument to / return from a function
467#[derive(Copy, Clone, Debug, PartialEq, Eq)]
468pub enum TypeKind {
469    /// A by-reference arg, EG `&T`
470    ByRef,
471    /// A by-mutable-reference arg, EG `&mut T`
472    ByMutRef,
473    /// A by-value arg, EG `T`
474    ByValue,
475}
476
477/// Unused, the location of a type for a function argument (import/export, argument/ret)
478#[derive(Copy, Clone, Debug, PartialEq, Eq)]
479pub enum TypeLocation {
480    /// An imported argument (JS side type)
481    ImportArgument,
482    /// An imported return
483    ImportRet,
484    /// An exported argument (Rust side type)
485    ExportArgument,
486    /// An exported return
487    ExportRet,
488}
489
490/// An enum representing either a literal value (`Lit`) or an expression (`syn::Expr`).
491#[cfg_attr(feature = "extra-traits", derive(Debug))]
492#[derive(Clone)]
493pub enum LitOrExpr {
494    /// Represents an expression that needs to be evaluated before it can be encoded
495    Expr(syn::Expr),
496    /// Represents a literal string that can be directly encoded.
497    Lit(String),
498}
499
500impl Export {
501    /// Mangles a rust -> javascript export, so that the created Ident will be unique over function
502    /// name and class name, if the function belongs to a javascript class.
503    pub(crate) fn rust_symbol(&self) -> Ident {
504        let mut generated_name = String::from("__wasm_bindgen_generated");
505        if let Some(class) = &self.js_class {
506            generated_name.push('_');
507            generated_name.push_str(class);
508        }
509        generated_name.push('_');
510        generated_name.push_str(&self.function.name.to_string());
511        Ident::new(&generated_name, Span::call_site())
512    }
513
514    /// This is the name of the shim function that gets exported and takes the raw
515    /// ABI form of its arguments and converts them back into their normal,
516    /// "high level" form before calling the actual function.
517    pub(crate) fn export_name(&self) -> String {
518        let fn_name = self.function.name.to_string();
519        match &self.js_class {
520            Some(class) => shared::struct_function_export_name(class, &fn_name),
521            None => shared::free_function_export_name(&fn_name),
522        }
523    }
524}
525
526impl ImportKind {
527    /// Whether this type can be inside an `impl` block.
528    pub fn fits_on_impl(&self) -> bool {
529        match *self {
530            ImportKind::Function(_) => true,
531            ImportKind::Static(_) => false,
532            ImportKind::String(_) => false,
533            ImportKind::Type(_) => false,
534            ImportKind::Enum(_) => false,
535        }
536    }
537}
538
539impl Function {
540    /// If the rust object has a `fn xxx(&self) -> MyType` method, get the name for a getter in
541    /// javascript (in this case `xxx`, so you can write `val = obj.xxx`)
542    pub fn infer_getter_property(&self) -> &str {
543        &self.name
544    }
545
546    /// If the rust object has a `fn set_xxx(&mut self, MyType)` style method, get the name
547    /// for a setter in javascript (in this case `xxx`, so you can write `obj.xxx = val`)
548    pub fn infer_setter_property(&self) -> Result<String, Diagnostic> {
549        let name = self.name.to_string();
550
551        // Otherwise we infer names based on the Rust function name.
552        if !name.starts_with("set_") {
553            bail_span!(
554                syn::token::Pub(self.name_span),
555                "setters must start with `set_`, found: {}",
556                name,
557            );
558        }
559        Ok(name[4..].to_string())
560    }
561}