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