1 use crate::Diagnostic;
2 use proc_macro2::{Ident, Span};
3 use std::hash::{Hash, Hasher};
4 use syn;
5 use wasm_bindgen_shared as shared;
6 
7 /// An abstract syntax tree representing a rust program. Contains
8 /// extra information for joining up this rust code with javascript.
9 #[cfg_attr(feature = "extra-traits", derive(Debug))]
10 #[derive(Default, Clone)]
11 pub struct Program {
12     /// rust -> js interfaces
13     pub exports: Vec<Export>,
14     /// js -> rust interfaces
15     pub imports: Vec<Import>,
16     /// rust enums
17     pub enums: Vec<Enum>,
18     /// rust structs
19     pub structs: Vec<Struct>,
20     /// custom typescript sections to be included in the definition file
21     pub typescript_custom_sections: Vec<String>,
22     /// Inline JS snippets
23     pub inline_js: Vec<String>,
24 }
25 
26 impl Program {
27     /// Returns true if the Program is empty
is_empty(&self) -> bool28     pub fn is_empty(&self) -> bool {
29         self.exports.is_empty()
30             && self.imports.is_empty()
31             && self.enums.is_empty()
32             && self.structs.is_empty()
33             && self.typescript_custom_sections.is_empty()
34             && self.inline_js.is_empty()
35     }
36 }
37 
38 /// A rust to js interface. Allows interaction with rust objects/functions
39 /// from javascript.
40 #[cfg_attr(feature = "extra-traits", derive(Debug))]
41 #[derive(Clone)]
42 pub struct Export {
43     /// Comments extracted from the rust source.
44     pub comments: Vec<String>,
45     /// The rust function
46     pub function: Function,
47     /// The class name in JS this is attached to
48     pub js_class: Option<String>,
49     /// The kind (static, named, regular)
50     pub method_kind: MethodKind,
51     /// The type of `self` (either `self`, `&self`, or `&mut self`)
52     pub method_self: Option<MethodSelf>,
53     /// The struct name, in Rust, this is attached to
54     pub rust_class: Option<Ident>,
55     /// The name of the rust function/method on the rust side.
56     pub rust_name: Ident,
57     /// Whether or not this function should be flagged as the wasm start
58     /// function.
59     pub start: bool,
60 }
61 
62 /// The 3 types variations of `self`.
63 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
64 #[derive(Clone)]
65 pub enum MethodSelf {
66     /// `self`
67     ByValue,
68     /// `&mut self`
69     RefMutable,
70     /// `&self`
71     RefShared,
72 }
73 
74 #[cfg_attr(feature = "extra-traits", derive(Debug))]
75 #[derive(Clone)]
76 pub struct Import {
77     pub module: ImportModule,
78     pub js_namespace: Option<Vec<String>>,
79     pub kind: ImportKind,
80 }
81 
82 #[cfg_attr(feature = "extra-traits", derive(Debug))]
83 #[derive(Clone)]
84 pub enum ImportModule {
85     None,
86     Named(String, Span),
87     RawNamed(String, Span),
88     Inline(usize, Span),
89 }
90 
91 impl Hash for ImportModule {
hash<H: Hasher>(&self, h: &mut H)92     fn hash<H: Hasher>(&self, h: &mut H) {
93         match self {
94             ImportModule::None => {
95                 0u8.hash(h);
96             }
97             ImportModule::Named(name, _) => {
98                 1u8.hash(h);
99                 name.hash(h);
100             }
101             ImportModule::Inline(idx, _) => {
102                 2u8.hash(h);
103                 idx.hash(h);
104             }
105             ImportModule::RawNamed(name, _) => {
106                 3u8.hash(h);
107                 name.hash(h);
108             }
109         }
110     }
111 }
112 
113 #[cfg_attr(feature = "extra-traits", derive(Debug))]
114 #[derive(Clone)]
115 pub enum ImportKind {
116     Function(ImportFunction),
117     Static(ImportStatic),
118     Type(ImportType),
119     Enum(ImportEnum),
120 }
121 
122 #[cfg_attr(feature = "extra-traits", derive(Debug))]
123 #[derive(Clone)]
124 pub struct ImportFunction {
125     pub function: Function,
126     pub rust_name: Ident,
127     pub js_ret: Option<syn::Type>,
128     pub catch: bool,
129     pub variadic: bool,
130     pub structural: bool,
131     pub assert_no_shim: bool,
132     pub kind: ImportFunctionKind,
133     pub shim: Ident,
134     pub doc_comment: Option<String>,
135 }
136 
137 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
138 #[derive(Clone)]
139 pub enum ImportFunctionKind {
140     Method {
141         class: String,
142         ty: syn::Type,
143         kind: MethodKind,
144     },
145     Normal,
146 }
147 
148 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
149 #[derive(Clone)]
150 pub enum MethodKind {
151     Constructor,
152     Operation(Operation),
153 }
154 
155 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
156 #[derive(Clone)]
157 pub struct Operation {
158     pub is_static: bool,
159     pub kind: OperationKind,
160 }
161 
162 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
163 #[derive(Clone)]
164 pub enum OperationKind {
165     Regular,
166     Getter(Option<Ident>),
167     Setter(Option<Ident>),
168     IndexingGetter,
169     IndexingSetter,
170     IndexingDeleter,
171 }
172 
173 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
174 #[derive(Clone)]
175 pub struct ImportStatic {
176     pub vis: syn::Visibility,
177     pub ty: syn::Type,
178     pub shim: Ident,
179     pub rust_name: Ident,
180     pub js_name: String,
181 }
182 
183 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
184 #[derive(Clone)]
185 pub struct ImportType {
186     pub vis: syn::Visibility,
187     pub rust_name: Ident,
188     pub js_name: String,
189     pub attrs: Vec<syn::Attribute>,
190     pub typescript_type: Option<String>,
191     pub doc_comment: Option<String>,
192     pub instanceof_shim: String,
193     pub is_type_of: Option<syn::Expr>,
194     pub extends: Vec<syn::Path>,
195     pub vendor_prefixes: Vec<Ident>,
196 }
197 
198 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
199 #[derive(Clone)]
200 pub struct ImportEnum {
201     /// The Rust enum's visibility
202     pub vis: syn::Visibility,
203     /// The Rust enum's identifiers
204     pub name: Ident,
205     /// The Rust identifiers for the variants
206     pub variants: Vec<Ident>,
207     /// The JS string values of the variants
208     pub variant_values: Vec<String>,
209     /// Attributes to apply to the Rust enum
210     pub rust_attrs: Vec<syn::Attribute>,
211 }
212 
213 #[cfg_attr(feature = "extra-traits", derive(Debug))]
214 #[derive(Clone)]
215 pub struct Function {
216     pub name: String,
217     pub name_span: Span,
218     pub renamed_via_js_name: bool,
219     pub arguments: Vec<syn::PatType>,
220     pub ret: Option<syn::Type>,
221     pub rust_attrs: Vec<syn::Attribute>,
222     pub rust_vis: syn::Visibility,
223     pub r#async: bool,
224     pub generate_typescript: bool,
225 }
226 
227 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
228 #[derive(Clone)]
229 pub struct Struct {
230     pub rust_name: Ident,
231     pub js_name: String,
232     pub fields: Vec<StructField>,
233     pub comments: Vec<String>,
234     pub is_inspectable: bool,
235     pub generate_typescript: bool,
236 }
237 
238 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
239 #[derive(Clone)]
240 pub struct StructField {
241     pub name: syn::Member,
242     pub struct_name: Ident,
243     pub readonly: bool,
244     pub ty: syn::Type,
245     pub getter: Ident,
246     pub setter: Ident,
247     pub comments: Vec<String>,
248     pub generate_typescript: bool,
249 }
250 
251 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
252 #[derive(Clone)]
253 pub struct Enum {
254     pub rust_name: Ident,
255     pub js_name: String,
256     pub variants: Vec<Variant>,
257     pub comments: Vec<String>,
258     pub hole: u32,
259     pub generate_typescript: bool,
260 }
261 
262 #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
263 #[derive(Clone)]
264 pub struct Variant {
265     pub name: Ident,
266     pub value: u32,
267     pub comments: Vec<String>,
268 }
269 
270 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
271 pub enum TypeKind {
272     ByRef,
273     ByMutRef,
274     ByValue,
275 }
276 
277 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
278 pub enum TypeLocation {
279     ImportArgument,
280     ImportRet,
281     ExportArgument,
282     ExportRet,
283 }
284 
285 impl Export {
286     /// Mangles a rust -> javascript export, so that the created Ident will be unique over function
287     /// name and class name, if the function belongs to a javascript class.
rust_symbol(&self) -> Ident288     pub(crate) fn rust_symbol(&self) -> Ident {
289         let mut generated_name = String::from("__wasm_bindgen_generated");
290         if let Some(class) = &self.js_class {
291             generated_name.push_str("_");
292             generated_name.push_str(class);
293         }
294         generated_name.push_str("_");
295         generated_name.push_str(&self.function.name.to_string());
296         Ident::new(&generated_name, Span::call_site())
297     }
298 
299     /// This is the name of the shim function that gets exported and takes the raw
300     /// ABI form of its arguments and converts them back into their normal,
301     /// "high level" form before calling the actual function.
export_name(&self) -> String302     pub(crate) fn export_name(&self) -> String {
303         let fn_name = self.function.name.to_string();
304         match &self.js_class {
305             Some(class) => shared::struct_function_export_name(class, &fn_name),
306             None => shared::free_function_export_name(&fn_name),
307         }
308     }
309 }
310 
311 impl ImportKind {
312     /// Whether this type can be inside an `impl` block.
fits_on_impl(&self) -> bool313     pub fn fits_on_impl(&self) -> bool {
314         match *self {
315             ImportKind::Function(_) => true,
316             ImportKind::Static(_) => false,
317             ImportKind::Type(_) => false,
318             ImportKind::Enum(_) => false,
319         }
320     }
321 }
322 
323 impl Function {
324     /// If the rust object has a `fn xxx(&self) -> MyType` method, get the name for a getter in
325     /// javascript (in this case `xxx`, so you can write `val = obj.xxx`)
infer_getter_property(&self) -> &str326     pub fn infer_getter_property(&self) -> &str {
327         &self.name
328     }
329 
330     /// If the rust object has a `fn set_xxx(&mut self, MyType)` style method, get the name
331     /// for a setter in javascript (in this case `xxx`, so you can write `obj.xxx = val`)
infer_setter_property(&self) -> Result<String, Diagnostic>332     pub fn infer_setter_property(&self) -> Result<String, Diagnostic> {
333         let name = self.name.to_string();
334 
335         // Otherwise we infer names based on the Rust function name.
336         if !name.starts_with("set_") {
337             bail_span!(
338                 syn::token::Pub(self.name_span),
339                 "setters must start with `set_`, found: {}",
340                 name,
341             );
342         }
343         Ok(name[4..].to_string())
344     }
345 }
346