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<syn::PatType>,
379    /// The return type of the function, if provided
380    pub ret: Option<syn::Type>,
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 Struct being exported
398#[cfg_attr(feature = "extra-traits", derive(Debug))]
399#[derive(Clone)]
400pub struct Struct {
401    /// The name of the struct in Rust code
402    pub rust_name: Ident,
403    /// The name of the struct in JS code
404    pub js_name: String,
405    /// All the fields of this struct to export
406    pub fields: Vec<StructField>,
407    /// The doc comments on this struct, if provided
408    pub comments: Vec<String>,
409    /// Whether this struct is inspectable (provides toJSON/toString properties to JS)
410    pub is_inspectable: bool,
411    /// Whether to generate a typescript definition for this struct
412    pub generate_typescript: bool,
413    /// Path to wasm_bindgen
414    pub wasm_bindgen: Path,
415}
416
417/// The field of a struct
418#[cfg_attr(feature = "extra-traits", derive(Debug))]
419#[derive(Clone)]
420pub struct StructField {
421    /// The name of the field in Rust code
422    pub rust_name: syn::Member,
423    /// The name of the field in JS code
424    pub js_name: String,
425    /// The name of the struct this field is part of
426    pub struct_name: Ident,
427    /// Whether this value is read-only to JS
428    pub readonly: bool,
429    /// The type of this field
430    pub ty: syn::Type,
431    /// The name of the getter shim for this field
432    pub getter: Ident,
433    /// The name of the setter shim for this field
434    pub setter: Ident,
435    /// The doc comments on this field, if any
436    pub comments: Vec<String>,
437    /// Whether to generate a typescript definition for this field
438    pub generate_typescript: bool,
439    /// Whether to generate jsdoc documentation for this field
440    pub generate_jsdoc: bool,
441    /// The span of the `#[wasm_bindgen(getter_with_clone)]` attribute applied
442    /// to this field, if any.
443    ///
444    /// If this is `Some`, the auto-generated getter for this field must clone
445    /// the field instead of copying it.
446    pub getter_with_clone: Option<Span>,
447    /// Path to wasm_bindgen
448    pub wasm_bindgen: Path,
449}
450
451/// The metadata for an Enum
452#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
453#[derive(Clone)]
454pub struct Enum {
455    /// The name of this enum in Rust code
456    pub rust_name: Ident,
457    /// The name of this enum in JS code
458    pub js_name: String,
459    /// Whether the variant values and hole are signed, meaning that they
460    /// represent the bits of a `i32` value.
461    pub signed: bool,
462    /// The variants provided by this enum
463    pub variants: Vec<Variant>,
464    /// The doc comments on this enum, if any
465    pub comments: Vec<String>,
466    /// The value to use for a `none` variant of the enum
467    pub hole: u32,
468    /// Whether to generate a typescript definition for this enum
469    pub generate_typescript: bool,
470    /// Path to wasm_bindgen
471    pub wasm_bindgen: Path,
472}
473
474/// The variant of an enum
475#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
476#[derive(Clone)]
477pub struct Variant {
478    /// The name of this variant
479    pub name: Ident,
480    /// The backing value of this variant
481    pub value: u32,
482    /// The doc comments on this variant, if any
483    pub comments: Vec<String>,
484}
485
486/// Unused, the type of an argument to / return from a function
487#[derive(Copy, Clone, Debug, PartialEq, Eq)]
488pub enum TypeKind {
489    /// A by-reference arg, EG `&T`
490    ByRef,
491    /// A by-mutable-reference arg, EG `&mut T`
492    ByMutRef,
493    /// A by-value arg, EG `T`
494    ByValue,
495}
496
497/// Unused, the location of a type for a function argument (import/export, argument/ret)
498#[derive(Copy, Clone, Debug, PartialEq, Eq)]
499pub enum TypeLocation {
500    /// An imported argument (JS side type)
501    ImportArgument,
502    /// An imported return
503    ImportRet,
504    /// An exported argument (Rust side type)
505    ExportArgument,
506    /// An exported return
507    ExportRet,
508}
509
510/// An enum representing either a literal value (`Lit`) or an expression (`syn::Expr`).
511#[cfg_attr(feature = "extra-traits", derive(Debug))]
512#[derive(Clone)]
513pub enum LitOrExpr {
514    /// Represents an expression that needs to be evaluated before it can be encoded
515    Expr(syn::Expr),
516    /// Represents a literal string that can be directly encoded.
517    Lit(String),
518}
519
520impl Export {
521    /// Mangles a rust -> javascript export, so that the created Ident will be unique over function
522    /// name and class name, if the function belongs to a javascript class.
523    pub(crate) fn rust_symbol(&self) -> Ident {
524        let mut generated_name = String::from("__wasm_bindgen_generated");
525        if let Some(class) = &self.js_class {
526            generated_name.push('_');
527            generated_name.push_str(class);
528        }
529        generated_name.push('_');
530        generated_name.push_str(&self.function.name.to_string());
531        Ident::new(&generated_name, Span::call_site())
532    }
533
534    /// This is the name of the shim function that gets exported and takes the raw
535    /// ABI form of its arguments and converts them back into their normal,
536    /// "high level" form before calling the actual function.
537    pub(crate) fn export_name(&self) -> String {
538        let fn_name = self.function.name.to_string();
539        match &self.js_class {
540            Some(class) => shared::struct_function_export_name(class, &fn_name),
541            None => shared::free_function_export_name(&fn_name),
542        }
543    }
544}
545
546impl ImportKind {
547    /// Whether this type can be inside an `impl` block.
548    pub fn fits_on_impl(&self) -> bool {
549        match *self {
550            ImportKind::Function(_) => true,
551            ImportKind::Static(_) => false,
552            ImportKind::String(_) => false,
553            ImportKind::Type(_) => false,
554            ImportKind::Enum(_) => false,
555        }
556    }
557}
558
559impl Function {
560    /// If the rust object has a `fn xxx(&self) -> MyType` method, get the name for a getter in
561    /// javascript (in this case `xxx`, so you can write `val = obj.xxx`)
562    pub fn infer_getter_property(&self) -> &str {
563        &self.name
564    }
565
566    /// If the rust object has a `fn set_xxx(&mut self, MyType)` style method, get the name
567    /// for a setter in javascript (in this case `xxx`, so you can write `obj.xxx = val`)
568    pub fn infer_setter_property(&self) -> Result<String, Diagnostic> {
569        let name = self.name.to_string();
570
571        // Otherwise we infer names based on the Rust function name.
572        if !name.starts_with("set_") {
573            bail_span!(
574                syn::token::Pub(self.name_span),
575                "setters must start with `set_`, found: {}",
576                name,
577            );
578        }
579        Ok(name[4..].to_string())
580    }
581}