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}