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