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 
5 use crate::Diagnostic;
6 use proc_macro2::{Ident, Span};
7 use std::hash::{Hash, Hasher};
8 use syn;
9 use 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(Default, Clone)]
15 pub struct Program {
16     /// rust -> js interfaces
17     pub exports: Vec<Export>,
18     /// js -> rust interfaces
19     pub imports: Vec<Import>,
20     /// rust enums
21     pub enums: Vec<Enum>,
22     /// rust structs
23     pub structs: Vec<Struct>,
24     /// custom typescript sections to be included in the definition file
25     pub typescript_custom_sections: Vec<String>,
26     /// Inline JS snippets
27     pub inline_js: Vec<String>,
28 }
29 
30 impl Program {
31     /// Returns true if the Program is empty
is_empty(&self) -> bool32     pub fn is_empty(&self) -> bool {
33         self.exports.is_empty()
34             && self.imports.is_empty()
35             && self.enums.is_empty()
36             && self.structs.is_empty()
37             && self.typescript_custom_sections.is_empty()
38             && self.inline_js.is_empty()
39     }
40 }
41 
42 /// A rust to js interface. Allows interaction with rust objects/functions
43 /// from javascript.
44 #[cfg_attr(feature = "extra-traits", derive(Debug))]
45 #[derive(Clone)]
46 pub struct Export {
47     /// Comments extracted from the rust source.
48     pub comments: Vec<String>,
49     /// The rust function
50     pub function: Function,
51     /// The class name in JS this is attached to
52     pub js_class: Option<String>,
53     /// The kind (static, named, regular)
54     pub method_kind: MethodKind,
55     /// The type of `self` (either `self`, `&self`, or `&mut self`)
56     pub method_self: Option<MethodSelf>,
57     /// The struct name, in Rust, this is attached to
58     pub rust_class: Option<Ident>,
59     /// The name of the rust function/method on the rust side.
60     pub rust_name: Ident,
61     /// Whether or not this function should be flagged as the wasm start
62     /// function.
63     pub start: bool,
64 }
65 
66 /// The 3 types variations of `self`.
67 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
68 #[derive(Clone)]
69 pub enum MethodSelf {
70     /// `self`
71     ByValue,
72     /// `&mut self`
73     RefMutable,
74     /// `&self`
75     RefShared,
76 }
77 
78 /// Things imported from a JS module (in an `extern` block)
79 #[cfg_attr(feature = "extra-traits", derive(Debug))]
80 #[derive(Clone)]
81 pub struct Import {
82     /// The type of module being imported from
83     pub module: ImportModule,
84     /// The namespace to access the item through, if any
85     pub js_namespace: Option<Vec<String>>,
86     /// The type of item being imported
87     pub kind: ImportKind,
88 }
89 
90 /// The possible types of module to import from
91 #[cfg_attr(feature = "extra-traits", derive(Debug))]
92 #[derive(Clone)]
93 pub enum ImportModule {
94     /// No module / import from global scope
95     None,
96     /// Import from the named module, with relative paths interpreted
97     Named(String, Span),
98     /// Import from the named module, without interpreting paths
99     RawNamed(String, Span),
100     /// Import from an inline JS snippet
101     Inline(usize, Span),
102 }
103 
104 impl Hash for ImportModule {
hash<H: Hasher>(&self, h: &mut H)105     fn hash<H: Hasher>(&self, h: &mut H) {
106         match self {
107             ImportModule::None => {
108                 0u8.hash(h);
109             }
110             ImportModule::Named(name, _) => {
111                 1u8.hash(h);
112                 name.hash(h);
113             }
114             ImportModule::Inline(idx, _) => {
115                 2u8.hash(h);
116                 idx.hash(h);
117             }
118             ImportModule::RawNamed(name, _) => {
119                 3u8.hash(h);
120                 name.hash(h);
121             }
122         }
123     }
124 }
125 
126 /// The type of item being imported
127 #[cfg_attr(feature = "extra-traits", derive(Debug))]
128 #[derive(Clone)]
129 pub enum ImportKind {
130     /// Importing a function
131     Function(ImportFunction),
132     /// Importing a static value
133     Static(ImportStatic),
134     /// Importing a type/class
135     Type(ImportType),
136     /// Importing a JS enum
137     Enum(ImportEnum),
138 }
139 
140 /// A function being imported from JS
141 #[cfg_attr(feature = "extra-traits", derive(Debug))]
142 #[derive(Clone)]
143 pub struct ImportFunction {
144     /// The full signature of the function
145     pub function: Function,
146     /// The name rust code will use
147     pub rust_name: Ident,
148     /// The type being returned
149     pub js_ret: Option<syn::Type>,
150     /// Whether to catch JS exceptions
151     pub catch: bool,
152     /// Whether the function is variadic on the JS side
153     pub variadic: bool,
154     /// Whether the function should use structural type checking
155     pub structural: bool,
156     /// Causes the Builder (See cli-support::js::binding::Builder) to error out if
157     /// it finds itself generating code for a function with this signature
158     pub assert_no_shim: bool,
159     /// The kind of function being imported
160     pub kind: ImportFunctionKind,
161     /// The shim name to use in the generated code. The 'shim' is a function that appears in
162     /// the generated JS as a wrapper around the actual function to import, performing any
163     /// necessary conversions (EG adding a try/catch to change a thrown error into a Result)
164     pub shim: Ident,
165     /// The doc comment on this import, if one is provided
166     pub doc_comment: Option<String>,
167 }
168 
169 /// The type of a function being imported
170 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
171 #[derive(Clone)]
172 pub enum ImportFunctionKind {
173     /// A class method
174     Method {
175         /// The name of the class for this method, in JS
176         class: String,
177         /// The type of the class for this method, in Rust
178         ty: syn::Type,
179         /// The kind of method this is
180         kind: MethodKind,
181     },
182     /// A standard function
183     Normal,
184 }
185 
186 /// The type of a method
187 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
188 #[derive(Clone)]
189 pub enum MethodKind {
190     /// A class constructor
191     Constructor,
192     /// Any other kind of method
193     Operation(Operation),
194 }
195 
196 /// The operation performed by a class method
197 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
198 #[derive(Clone)]
199 pub struct Operation {
200     /// Whether this method is static
201     pub is_static: bool,
202     /// The internal kind of this Operation
203     pub kind: OperationKind,
204 }
205 
206 /// The kind of operation performed by a method
207 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
208 #[derive(Clone)]
209 pub enum OperationKind {
210     /// A standard method, nothing special
211     Regular,
212     /// A method for getting the value of the provided Ident
213     Getter(Option<Ident>),
214     /// A method for setting the value of the provided Ident
215     Setter(Option<Ident>),
216     /// A dynamically intercepted getter
217     IndexingGetter,
218     /// A dynamically intercepted setter
219     IndexingSetter,
220     /// A dynamically intercepted deleter
221     IndexingDeleter,
222 }
223 
224 /// The type of a static being imported
225 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
226 #[derive(Clone)]
227 pub struct ImportStatic {
228     /// The visibility of this static in Rust
229     pub vis: syn::Visibility,
230     /// The type of static being imported
231     pub ty: syn::Type,
232     /// The name of the shim function used to access this static
233     pub shim: Ident,
234     /// The name of this static on the Rust side
235     pub rust_name: Ident,
236     /// The name of this static on the JS side
237     pub js_name: String,
238 }
239 
240 /// The metadata for a type being imported
241 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
242 #[derive(Clone)]
243 pub struct ImportType {
244     /// The visibility of this type in Rust
245     pub vis: syn::Visibility,
246     /// The name of this type on the Rust side
247     pub rust_name: Ident,
248     /// The name of this type on the JS side
249     pub js_name: String,
250     /// The custom attributes to apply to this type
251     pub attrs: Vec<syn::Attribute>,
252     /// The TS definition to generate for this type
253     pub typescript_type: Option<String>,
254     /// The doc comment applied to this type, if one exists
255     pub doc_comment: Option<String>,
256     /// The name of the shim to check instanceof for this type
257     pub instanceof_shim: String,
258     /// The name of the remote function to use for the generated is_type_of
259     pub is_type_of: Option<syn::Expr>,
260     /// The list of classes this extends, if any
261     pub extends: Vec<syn::Path>,
262     /// A custom prefix to add and attempt to fall back to, if the type isn't found
263     pub vendor_prefixes: Vec<Ident>,
264     /// If present, don't generate a `Deref` impl
265     pub no_deref: bool,
266 }
267 
268 /// The metadata for an Enum being imported
269 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
270 #[derive(Clone)]
271 pub struct ImportEnum {
272     /// The Rust enum's visibility
273     pub vis: syn::Visibility,
274     /// The Rust enum's identifiers
275     pub name: Ident,
276     /// The Rust identifiers for the variants
277     pub variants: Vec<Ident>,
278     /// The JS string values of the variants
279     pub variant_values: Vec<String>,
280     /// Attributes to apply to the Rust enum
281     pub rust_attrs: Vec<syn::Attribute>,
282 }
283 
284 /// Information about a function being imported or exported
285 #[cfg_attr(feature = "extra-traits", derive(Debug))]
286 #[derive(Clone)]
287 pub struct Function {
288     /// The name of the function
289     pub name: String,
290     /// The span of the function's name in Rust code
291     pub name_span: Span,
292     /// Whether the function has a js_name attribute
293     pub renamed_via_js_name: bool,
294     /// The arguments to the function
295     pub arguments: Vec<syn::PatType>,
296     /// The return type of the function, if provided
297     pub ret: Option<syn::Type>,
298     /// Any custom attributes being applied to the function
299     pub rust_attrs: Vec<syn::Attribute>,
300     /// The visibility of this function in Rust
301     pub rust_vis: syn::Visibility,
302     /// Whether this is an `async` function
303     pub r#async: bool,
304     /// Whether to generate a typescript definition for this function
305     pub generate_typescript: bool,
306 }
307 
308 /// Information about a Struct being exported
309 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
310 #[derive(Clone)]
311 pub struct Struct {
312     /// The name of the struct in Rust code
313     pub rust_name: Ident,
314     /// The name of the struct in JS code
315     pub js_name: String,
316     /// All the fields of this struct to export
317     pub fields: Vec<StructField>,
318     /// The doc comments on this struct, if provided
319     pub comments: Vec<String>,
320     /// Whether this struct is inspectable (provides toJSON/toString properties to JS)
321     pub is_inspectable: bool,
322     /// Whether to generate a typescript definition for this struct
323     pub generate_typescript: bool,
324 }
325 
326 /// The field of a struct
327 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
328 #[derive(Clone)]
329 pub struct StructField {
330     /// The name of the field in Rust code
331     pub rust_name: syn::Member,
332     /// The name of the field in JS code
333     pub js_name: String,
334     /// The name of the struct this field is part of
335     pub struct_name: Ident,
336     /// Whether this value is read-only to JS
337     pub readonly: bool,
338     /// The type of this field
339     pub ty: syn::Type,
340     /// The name of the getter shim for this field
341     pub getter: Ident,
342     /// The name of the setter shim for this field
343     pub setter: Ident,
344     /// The doc comments on this field, if any
345     pub comments: Vec<String>,
346     /// Whether to generate a typescript definition for this field
347     pub generate_typescript: bool,
348     /// Whether to use .clone() in the auto-generated getter for this field
349     pub getter_with_clone: bool,
350 }
351 
352 /// Information about an Enum being exported
353 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
354 #[derive(Clone)]
355 pub struct Enum {
356     /// The name of this enum in Rust code
357     pub rust_name: Ident,
358     /// The name of this enum in JS code
359     pub js_name: String,
360     /// The variants provided by this enum
361     pub variants: Vec<Variant>,
362     /// The doc comments on this enum, if any
363     pub comments: Vec<String>,
364     /// The value to use for a `none` variant of the enum
365     pub hole: u32,
366     /// Whether to generate a typescript definition for this enum
367     pub generate_typescript: bool,
368 }
369 
370 /// The variant of an enum
371 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
372 #[derive(Clone)]
373 pub struct Variant {
374     /// The name of this variant
375     pub name: Ident,
376     /// The backing value of this variant
377     pub value: u32,
378     /// The doc comments on this variant, if any
379     pub comments: Vec<String>,
380 }
381 
382 /// Unused, the type of an argument to / return from a function
383 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
384 pub enum TypeKind {
385     /// A by-reference arg, EG `&T`
386     ByRef,
387     /// A by-mutable-reference arg, EG `&mut T`
388     ByMutRef,
389     /// A by-value arg, EG `T`
390     ByValue,
391 }
392 
393 /// Unused, the location of a type for a function argument (import/export, argument/ret)
394 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
395 pub enum TypeLocation {
396     /// An imported argument (JS side type)
397     ImportArgument,
398     /// An imported return
399     ImportRet,
400     /// An exported argument (Rust side type)
401     ExportArgument,
402     /// An exported return
403     ExportRet,
404 }
405 
406 impl Export {
407     /// Mangles a rust -> javascript export, so that the created Ident will be unique over function
408     /// name and class name, if the function belongs to a javascript class.
rust_symbol(&self) -> Ident409     pub(crate) fn rust_symbol(&self) -> Ident {
410         let mut generated_name = String::from("__wasm_bindgen_generated");
411         if let Some(class) = &self.js_class {
412             generated_name.push_str("_");
413             generated_name.push_str(class);
414         }
415         generated_name.push_str("_");
416         generated_name.push_str(&self.function.name.to_string());
417         Ident::new(&generated_name, Span::call_site())
418     }
419 
420     /// This is the name of the shim function that gets exported and takes the raw
421     /// ABI form of its arguments and converts them back into their normal,
422     /// "high level" form before calling the actual function.
export_name(&self) -> String423     pub(crate) fn export_name(&self) -> String {
424         let fn_name = self.function.name.to_string();
425         match &self.js_class {
426             Some(class) => shared::struct_function_export_name(class, &fn_name),
427             None => shared::free_function_export_name(&fn_name),
428         }
429     }
430 }
431 
432 impl ImportKind {
433     /// Whether this type can be inside an `impl` block.
fits_on_impl(&self) -> bool434     pub fn fits_on_impl(&self) -> bool {
435         match *self {
436             ImportKind::Function(_) => true,
437             ImportKind::Static(_) => false,
438             ImportKind::Type(_) => false,
439             ImportKind::Enum(_) => false,
440         }
441     }
442 }
443 
444 impl Function {
445     /// If the rust object has a `fn xxx(&self) -> MyType` method, get the name for a getter in
446     /// javascript (in this case `xxx`, so you can write `val = obj.xxx`)
infer_getter_property(&self) -> &str447     pub fn infer_getter_property(&self) -> &str {
448         &self.name
449     }
450 
451     /// If the rust object has a `fn set_xxx(&mut self, MyType)` style method, get the name
452     /// for a setter in javascript (in this case `xxx`, so you can write `obj.xxx = val`)
infer_setter_property(&self) -> Result<String, Diagnostic>453     pub fn infer_setter_property(&self) -> Result<String, Diagnostic> {
454         let name = self.name.to_string();
455 
456         // Otherwise we infer names based on the Rust function name.
457         if !name.starts_with("set_") {
458             bail_span!(
459                 syn::token::Pub(self.name_span),
460                 "setters must start with `set_`, found: {}",
461                 name,
462             );
463         }
464         Ok(name[4..].to_string())
465     }
466 }
467