wasm-bindgen-backend-0.2.104/.cargo_vcs_info.json0000644000000001540000000000100151400ustar { "git": { "sha1": "4ea9d98ce6448028743534ecc5584af28446fd2e" }, "path_in_vcs": "crates/backend" }wasm-bindgen-backend-0.2.104/Cargo.lock0000644000000034020000000000100131120ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "bumpalo" version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "log" version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "proc-macro2" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "syn" version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "wasm-bindgen-backend" version = "0.2.104" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" dependencies = [ "unicode-ident", ] wasm-bindgen-backend-0.2.104/Cargo.toml0000644000000033730000000000100131440ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.57" name = "wasm-bindgen-backend" version = "0.2.104" authors = ["The wasm-bindgen Developers"] build = false include = [ "/LICENSE-*", "/src", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = """ Backend code generation of the wasm-bindgen tool """ homepage = "https://wasm-bindgen.github.io/wasm-bindgen/" documentation = "https://docs.rs/wasm-bindgen-backend" readme = false license = "MIT OR Apache-2.0" repository = "https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/backend" [features] extra-traits = ["syn/extra-traits"] [lib] name = "wasm_bindgen_backend" path = "src/lib.rs" [dependencies.bumpalo] version = "3.0.0" [dependencies.log] version = "0.4" [dependencies.proc-macro2] version = "1.0" [dependencies.quote] version = "1.0" [dependencies.syn] version = "2.0" features = ["full"] [dependencies.wasm-bindgen-shared] version = "=0.2.104" [lints.clippy] large_enum_variant = "allow" new_without_default = "allow" overly_complex_bool_expr = "allow" too_many_arguments = "allow" type_complexity = "allow" [lints.rust.unexpected_cfgs] level = "warn" priority = 0 check-cfg = [ "cfg(wasm_bindgen_unstable_test_coverage)", "cfg(xxx_debug_only_print_generated_code)", ] wasm-bindgen-backend-0.2.104/Cargo.toml.orig000064400000000000000000000013471046102023000166240ustar 00000000000000[package] authors = ["The wasm-bindgen Developers"] description = """ Backend code generation of the wasm-bindgen tool """ documentation = "https://docs.rs/wasm-bindgen-backend" edition = "2021" homepage = "https://wasm-bindgen.github.io/wasm-bindgen/" include = ["/LICENSE-*", "/src"] license = "MIT OR Apache-2.0" name = "wasm-bindgen-backend" repository = "https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/backend" rust-version = "1.57" version = "0.2.104" [features] extra-traits = ["syn/extra-traits"] [dependencies] bumpalo = "3.0.0" log = "0.4" proc-macro2 = "1.0" quote = '1.0' syn = { version = '2.0', features = ['full'] } wasm-bindgen-shared = { path = "../shared", version = "=0.2.104" } [lints] workspace = true wasm-bindgen-backend-0.2.104/LICENSE-APACHE000064400000000000000000000251371046102023000156640ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. wasm-bindgen-backend-0.2.104/LICENSE-MIT000064400000000000000000000020411046102023000153610ustar 00000000000000Copyright (c) 2014 Alex Crichton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. wasm-bindgen-backend-0.2.104/src/ast.rs000064400000000000000000000504301046102023000156560ustar 00000000000000//! A representation of the Abstract Syntax Tree of a Rust program, //! with all the added metadata necessary to generate Wasm bindings //! for it. use crate::{util::ShortHash, Diagnostic}; use proc_macro2::{Ident, Span}; use std::hash::{Hash, Hasher}; use syn::Path; use wasm_bindgen_shared as shared; /// An abstract syntax tree representing a rust program. Contains /// extra information for joining up this rust code with javascript. #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub struct Program { /// rust -> js interfaces pub exports: Vec, /// js -> rust interfaces pub imports: Vec, /// linked-to modules pub linked_modules: Vec, /// rust enums pub enums: Vec, /// rust structs pub structs: Vec, /// custom typescript sections to be included in the definition file pub typescript_custom_sections: Vec, /// Inline JS snippets pub inline_js: Vec, /// Path to wasm_bindgen pub wasm_bindgen: Path, /// Path to js_sys pub js_sys: Path, /// Path to wasm_bindgen_futures pub wasm_bindgen_futures: Path, } impl Default for Program { fn default() -> Self { Self { exports: Default::default(), imports: Default::default(), linked_modules: Default::default(), enums: Default::default(), structs: Default::default(), typescript_custom_sections: Default::default(), inline_js: Default::default(), wasm_bindgen: syn::parse_quote! { wasm_bindgen }, js_sys: syn::parse_quote! { js_sys }, wasm_bindgen_futures: syn::parse_quote! { wasm_bindgen_futures }, } } } impl Program { /// Returns true if the Program is empty pub fn is_empty(&self) -> bool { self.exports.is_empty() && self.imports.is_empty() && self.enums.is_empty() && self.structs.is_empty() && self.typescript_custom_sections.is_empty() && self.inline_js.is_empty() } /// Name of the link function for a specific linked module pub fn link_function_name(&self, idx: usize) -> String { let hash = match &self.linked_modules[idx] { ImportModule::Inline(idx, _) => ShortHash((1, &self.inline_js[*idx])).to_string(), other => ShortHash((0, other)).to_string(), }; format!("__wbindgen_link_{}", hash) } } /// An abstract syntax tree representing a link to a module in Rust. /// In contrast to Program, LinkToModule must expand to an expression. /// linked_modules of the inner Program must contain exactly one element /// whose link is produced by the expression. #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub struct LinkToModule(pub Program); /// A rust to js interface. Allows interaction with rust objects/functions /// from javascript. #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub struct Export { /// Comments extracted from the rust source. pub comments: Vec, /// The rust function pub function: Function, /// The class name in JS this is attached to pub js_class: Option, /// The kind (static, named, regular) pub method_kind: MethodKind, /// The type of `self` (either `self`, `&self`, or `&mut self`) pub method_self: Option, /// The struct name, in Rust, this is attached to pub rust_class: Option, /// The name of the rust function/method on the rust side. pub rust_name: Ident, /// Whether or not this function should be flagged as the Wasm start /// function. pub start: bool, /// Path to wasm_bindgen pub wasm_bindgen: Path, /// Path to wasm_bindgen_futures pub wasm_bindgen_futures: Path, } /// The 3 types variations of `self`. #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Copy, Clone)] pub enum MethodSelf { /// `self` ByValue, /// `&mut self` RefMutable, /// `&self` RefShared, } /// Things imported from a JS module (in an `extern` block) #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub struct Import { /// The type of module being imported from, if any pub module: Option, /// The namespace to access the item through, if any pub js_namespace: Option>, /// The type of item being imported pub kind: ImportKind, } /// The possible types of module to import from #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub enum ImportModule { /// Import from the named module, with relative paths interpreted Named(String, Span), /// Import from the named module, without interpreting paths RawNamed(String, Span), /// Import from an inline JS snippet Inline(usize, Span), } impl Hash for ImportModule { fn hash(&self, h: &mut H) { match self { ImportModule::Named(name, _) => (1u8, name).hash(h), ImportModule::Inline(idx, _) => (2u8, idx).hash(h), ImportModule::RawNamed(name, _) => (3u8, name).hash(h), } } } /// The type of item being imported #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub enum ImportKind { /// Importing a function Function(ImportFunction), /// Importing a static value Static(ImportStatic), /// Importing a static string String(ImportString), /// Importing a type/class Type(ImportType), /// Importing a JS enum Enum(StringEnum), } /// A function being imported from JS #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub struct ImportFunction { /// The full signature of the function pub function: Function, /// The name rust code will use pub rust_name: Ident, /// The type being returned pub js_ret: Option, /// Whether to catch JS exceptions pub catch: bool, /// Whether the function is variadic on the JS side pub variadic: bool, /// Whether the function should use structural type checking pub structural: bool, /// Causes the Builder (See cli-support::js::binding::Builder) to error out if /// it finds itself generating code for a function with this signature pub assert_no_shim: bool, /// The kind of function being imported pub kind: ImportFunctionKind, /// The shim name to use in the generated code. The 'shim' is a function that appears in /// the generated JS as a wrapper around the actual function to import, performing any /// necessary conversions (EG adding a try/catch to change a thrown error into a Result) pub shim: Ident, /// The doc comment on this import, if one is provided pub doc_comment: String, /// Path to wasm_bindgen pub wasm_bindgen: Path, /// Path to wasm_bindgen_futures pub wasm_bindgen_futures: Path, } /// The type of a function being imported #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub enum ImportFunctionKind { /// A class method Method { /// The name of the class for this method, in JS class: String, /// The type of the class for this method, in Rust ty: syn::Type, /// The kind of method this is kind: MethodKind, }, /// A standard function Normal, } /// The type of a method #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub enum MethodKind { /// A class constructor Constructor, /// Any other kind of method Operation(Operation), } /// The operation performed by a class method #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct Operation { /// Whether this method is static pub is_static: bool, /// The internal kind of this Operation pub kind: OperationKind, } /// The kind of operation performed by a method #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub enum OperationKind { /// A standard method, nothing special Regular, /// A method for getting the value of the provided Ident or String Getter(Option), /// A method for setting the value of the provided Ident or String Setter(Option), /// A dynamically intercepted getter IndexingGetter, /// A dynamically intercepted setter IndexingSetter, /// A dynamically intercepted deleter IndexingDeleter, } /// The type of a static being imported #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct ImportStatic { /// The visibility of this static in Rust pub vis: syn::Visibility, /// The type of static being imported pub ty: syn::Type, /// The name of the shim function used to access this static pub shim: Ident, /// The name of this static on the Rust side pub rust_name: Ident, /// The name of this static on the JS side pub js_name: String, /// Path to wasm_bindgen pub wasm_bindgen: Path, /// Version of `thread_local`, if any. pub thread_local: Option, } /// Which version of the `thread_local` attribute is enabled. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ThreadLocal { /// V1. V1, /// V2. V2, } /// The type of a static string being imported #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct ImportString { /// The visibility of this static string in Rust pub vis: syn::Visibility, /// The type specified by the user, which we only use to show an error if the wrong type is used. pub ty: syn::Type, /// The name of the shim function used to access this static pub shim: Ident, /// The name of this static on the Rust side pub rust_name: Ident, /// Path to wasm_bindgen pub wasm_bindgen: Path, /// Path to js_sys pub js_sys: Path, /// The string to export. pub string: String, /// Version of `thread_local`. pub thread_local: ThreadLocal, } /// The metadata for a type being imported #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct ImportType { /// The visibility of this type in Rust pub vis: syn::Visibility, /// The name of this type on the Rust side pub rust_name: Ident, /// The name of this type on the JS side pub js_name: String, /// The custom attributes to apply to this type pub attrs: Vec, /// The TS definition to generate for this type pub typescript_type: Option, /// The doc comment applied to this type, if one exists pub doc_comment: Option, /// The name of the shim to check instanceof for this type pub instanceof_shim: String, /// The name of the remote function to use for the generated is_type_of pub is_type_of: Option, /// The list of classes this extends, if any pub extends: Vec, /// A custom prefix to add and attempt to fall back to, if the type isn't found pub vendor_prefixes: Vec, /// If present, don't generate a `Deref` impl pub no_deref: bool, /// Path to wasm_bindgen pub wasm_bindgen: Path, } /// The metadata for a String Enum #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct StringEnum { /// The Rust enum's visibility pub vis: syn::Visibility, /// The Rust enum's identifiers pub name: Ident, /// The name of this string enum in JS/TS code pub js_name: String, /// The Rust identifiers for the variants pub variants: Vec, /// The JS string values of the variants pub variant_values: Vec, /// The doc comments on this enum, if any pub comments: Vec, /// Attributes to apply to the Rust enum pub rust_attrs: Vec, /// Whether to generate a typescript definition for this enum pub generate_typescript: bool, /// Path to wasm_bindgen pub wasm_bindgen: Path, } /// Information about a function being imported or exported #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub struct Function { /// The name of the function pub name: String, /// The span of the function's name in Rust code pub name_span: Span, /// Whether the function has a js_name attribute pub renamed_via_js_name: bool, /// The arguments to the function pub arguments: Vec, /// The data of return type of the function pub ret: Option, /// Any custom attributes being applied to the function pub rust_attrs: Vec, /// The visibility of this function in Rust pub rust_vis: syn::Visibility, /// Whether this is an `unsafe` function pub r#unsafe: bool, /// Whether this is an `async` function pub r#async: bool, /// Whether to generate a typescript definition for this function pub generate_typescript: bool, /// Whether to generate jsdoc documentation for this function pub generate_jsdoc: bool, /// Whether this is a function with a variadict parameter pub variadic: bool, } /// Information about a function's return #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub struct FunctionReturnData { /// Specifies the type of the function's return pub r#type: syn::Type, /// Specifies the JS return type override pub js_type: Option, /// Specifies the return description pub desc: Option, } /// Information about a function's argument #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub struct FunctionArgumentData { /// Specifies the type of the function's argument pub pat_type: syn::PatType, /// Specifies the JS argument name override pub js_name: Option, /// Specifies the JS function argument type override pub js_type: Option, /// Specifies the argument description pub desc: Option, } /// Information about a Struct being exported #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub struct Struct { /// The name of the struct in Rust code pub rust_name: Ident, /// The name of the struct in JS code pub js_name: String, /// All the fields of this struct to export pub fields: Vec, /// The doc comments on this struct, if provided pub comments: Vec, /// Whether this struct is inspectable (provides toJSON/toString properties to JS) pub is_inspectable: bool, /// Whether to generate a typescript definition for this struct pub generate_typescript: bool, /// Path to wasm_bindgen pub wasm_bindgen: Path, } /// The field of a struct #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub struct StructField { /// The name of the field in Rust code pub rust_name: syn::Member, /// The name of the field in JS code pub js_name: String, /// The name of the struct this field is part of pub struct_name: Ident, /// Whether this value is read-only to JS pub readonly: bool, /// The type of this field pub ty: syn::Type, /// The name of the getter shim for this field pub getter: Ident, /// The name of the setter shim for this field pub setter: Ident, /// The doc comments on this field, if any pub comments: Vec, /// Whether to generate a typescript definition for this field pub generate_typescript: bool, /// Whether to generate jsdoc documentation for this field pub generate_jsdoc: bool, /// The span of the `#[wasm_bindgen(getter_with_clone)]` attribute applied /// to this field, if any. /// /// If this is `Some`, the auto-generated getter for this field must clone /// the field instead of copying it. pub getter_with_clone: Option, /// Path to wasm_bindgen pub wasm_bindgen: Path, } /// The metadata for an Enum #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct Enum { /// The name of this enum in Rust code pub rust_name: Ident, /// The name of this enum in JS code pub js_name: String, /// Whether the variant values and hole are signed, meaning that they /// represent the bits of a `i32` value. pub signed: bool, /// The variants provided by this enum pub variants: Vec, /// The doc comments on this enum, if any pub comments: Vec, /// The value to use for a `none` variant of the enum pub hole: u32, /// Whether to generate a typescript definition for this enum pub generate_typescript: bool, /// Path to wasm_bindgen pub wasm_bindgen: Path, } /// The variant of an enum #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct Variant { /// The name of this variant pub name: Ident, /// The backing value of this variant pub value: u32, /// The doc comments on this variant, if any pub comments: Vec, } /// Unused, the type of an argument to / return from a function #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum TypeKind { /// A by-reference arg, EG `&T` ByRef, /// A by-mutable-reference arg, EG `&mut T` ByMutRef, /// A by-value arg, EG `T` ByValue, } /// Unused, the location of a type for a function argument (import/export, argument/ret) #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum TypeLocation { /// An imported argument (JS side type) ImportArgument, /// An imported return ImportRet, /// An exported argument (Rust side type) ExportArgument, /// An exported return ExportRet, } /// An enum representing either a literal value (`Lit`) or an expression (`syn::Expr`). #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub enum LitOrExpr { /// Represents an expression that needs to be evaluated before it can be encoded Expr(syn::Expr), /// Represents a literal string that can be directly encoded. Lit(String), } impl Export { /// Mangles a rust -> javascript export, so that the created Ident will be unique over function /// name and class name, if the function belongs to a javascript class. pub(crate) fn rust_symbol(&self) -> Ident { let mut generated_name = String::from("__wasm_bindgen_generated"); if let Some(class) = &self.js_class { generated_name.push('_'); generated_name.push_str(class); } generated_name.push('_'); generated_name.push_str(&self.function.name.to_string()); Ident::new(&generated_name, Span::call_site()) } /// This is the name of the shim function that gets exported and takes the raw /// ABI form of its arguments and converts them back into their normal, /// "high level" form before calling the actual function. pub(crate) fn export_name(&self) -> String { let fn_name = self.function.name.to_string(); match &self.js_class { Some(class) => shared::struct_function_export_name(class, &fn_name), None => shared::free_function_export_name(&fn_name), } } } impl ImportKind { /// Whether this type can be inside an `impl` block. pub fn fits_on_impl(&self) -> bool { match *self { ImportKind::Function(_) => true, ImportKind::Static(_) => false, ImportKind::String(_) => false, ImportKind::Type(_) => false, ImportKind::Enum(_) => false, } } } impl Function { /// If the rust object has a `fn xxx(&self) -> MyType` method, get the name for a getter in /// javascript (in this case `xxx`, so you can write `val = obj.xxx`) pub fn infer_getter_property(&self) -> &str { &self.name } /// If the rust object has a `fn set_xxx(&mut self, MyType)` style method, get the name /// for a setter in javascript (in this case `xxx`, so you can write `obj.xxx = val`) pub fn infer_setter_property(&self) -> Result { let name = self.name.to_string(); // Otherwise we infer names based on the Rust function name. if !name.starts_with("set_") { bail_span!( syn::token::Pub(self.name_span), "setters must start with `set_`, found: {}", name, ); } Ok(name[4..].to_string()) } } wasm-bindgen-backend-0.2.104/src/codegen.rs000064400000000000000000002233531046102023000165010ustar 00000000000000use crate::ast; use crate::encode; use crate::encode::EncodeChunk; use crate::Diagnostic; use proc_macro2::{Ident, Span, TokenStream}; use quote::format_ident; use quote::quote_spanned; use quote::{quote, ToTokens}; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use syn::parse_quote; use syn::spanned::Spanned; use wasm_bindgen_shared as shared; /// A trait for converting AST structs into Tokens and adding them to a TokenStream, /// or providing a diagnostic if conversion fails. pub trait TryToTokens { /// Attempt to convert a `Self` into tokens and add it to the `TokenStream` fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic>; /// Attempt to convert a `Self` into a new `TokenStream` fn try_to_token_stream(&self) -> Result { let mut tokens = TokenStream::new(); self.try_to_tokens(&mut tokens)?; Ok(tokens) } } impl TryToTokens for ast::Program { // Generate wrappers for all the items that we've found fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> { let mut errors = Vec::new(); for export in self.exports.iter() { if let Err(e) = export.try_to_tokens(tokens) { errors.push(e); } } for s in self.structs.iter() { s.to_tokens(tokens); } let mut types = HashMap::new(); for i in self.imports.iter() { if let ast::ImportKind::Type(t) = &i.kind { types.insert(t.rust_name.to_string(), t.rust_name.clone()); } } for i in self.imports.iter() { DescribeImport { kind: &i.kind, wasm_bindgen: &self.wasm_bindgen, } .to_tokens(tokens); // If there is a js namespace, check that name isn't a type. If it is, // this import might be a method on that type. if let Some(nss) = &i.js_namespace { // When the namespace is `A.B`, the type name should be `B`. if let Some(ns) = nss.last().and_then(|t| types.get(t)) { if i.kind.fits_on_impl() { let kind = match i.kind.try_to_token_stream() { Ok(kind) => kind, Err(e) => { errors.push(e); continue; } }; (quote! { #[automatically_derived] impl #ns { #kind } }) .to_tokens(tokens); continue; } } } if let Err(e) = i.kind.try_to_tokens(tokens) { errors.push(e); } } for e in self.enums.iter() { e.to_tokens(tokens); } Diagnostic::from_vec(errors)?; // Generate a static which will eventually be what lives in a custom section // of the Wasm executable. For now it's just a plain old static, but we'll // eventually have it actually in its own section. // See comments in `crates/cli-support/src/lib.rs` about what this // `schema_version` is. let prefix_json = format!( r#"{{"schema_version":"{}","version":"{}"}}"#, shared::SCHEMA_VERSION, shared::version() ); let wasm_bindgen = &self.wasm_bindgen; let encoded = encode::encode(self)?; let encoded_chunks: Vec<_> = encoded .custom_section .iter() .map(|chunk| match chunk { EncodeChunk::EncodedBuf(buf) => { let buf = syn::LitByteStr::new(buf.as_slice(), Span::call_site()); quote!(#buf) } EncodeChunk::StrExpr(expr) => { // encode expr as str quote!({ use #wasm_bindgen::__rt::{encode_u32_to_fixed_len_bytes}; const _STR_EXPR: &str = #expr; const _STR_EXPR_BYTES: &[u8] = _STR_EXPR.as_bytes(); const _STR_EXPR_BYTES_LEN: usize = _STR_EXPR_BYTES.len() + 5; const _ENCODED_BYTES: [u8; _STR_EXPR_BYTES_LEN] = flat_byte_slices([ &encode_u32_to_fixed_len_bytes(_STR_EXPR_BYTES.len() as u32), _STR_EXPR_BYTES, ]); &_ENCODED_BYTES }) } }) .collect(); let chunk_len = encoded_chunks.len(); // concatenate all encoded chunks and write the length in front of the chunk; let encode_bytes = quote!({ const _CHUNK_SLICES: [&[u8]; #chunk_len] = [ #(#encoded_chunks,)* ]; #[allow(long_running_const_eval)] const _CHUNK_LEN: usize = flat_len(_CHUNK_SLICES); #[allow(long_running_const_eval)] const _CHUNKS: [u8; _CHUNK_LEN] = flat_byte_slices(_CHUNK_SLICES); const _LEN_BYTES: [u8; 4] = (_CHUNK_LEN as u32).to_le_bytes(); const _ENCODED_BYTES_LEN: usize = _CHUNK_LEN + 4; #[allow(long_running_const_eval)] const _ENCODED_BYTES: [u8; _ENCODED_BYTES_LEN] = flat_byte_slices([&_LEN_BYTES, &_CHUNKS]); &_ENCODED_BYTES }); // We already consumed the contents of included files when generating // the custom section, but we want to make sure that updates to the // generated files will cause this macro to rerun incrementally. To do // that we use `include_str!` to force rustc to think it has a // dependency on these files. That way when the file changes Cargo will // automatically rerun rustc which will rerun this macro. Other than // this we don't actually need the results of the `include_str!`, so // it's just shoved into an anonymous static. let file_dependencies = encoded.included_files.iter().map(|file| { let file = file.to_str().unwrap(); quote! { include_str!(#file) } }); let len = prefix_json.len() as u32; let prefix_json_bytes = [&len.to_le_bytes()[..], prefix_json.as_bytes()].concat(); let prefix_json_bytes = syn::LitByteStr::new(&prefix_json_bytes, Span::call_site()); (quote! { #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] #[automatically_derived] const _: () = { use #wasm_bindgen::__rt::{flat_len, flat_byte_slices}; static _INCLUDED_FILES: &[&str] = &[#(#file_dependencies),*]; const _ENCODED_BYTES: &[u8] = #encode_bytes; const _PREFIX_JSON_BYTES: &[u8] = #prefix_json_bytes; const _ENCODED_BYTES_LEN: usize = _ENCODED_BYTES.len(); const _PREFIX_JSON_BYTES_LEN: usize = _PREFIX_JSON_BYTES.len(); const _LEN: usize = _PREFIX_JSON_BYTES_LEN + _ENCODED_BYTES_LEN; #[link_section = "__wasm_bindgen_unstable"] #[allow(long_running_const_eval)] static _GENERATED: [u8; _LEN] = flat_byte_slices([_PREFIX_JSON_BYTES, _ENCODED_BYTES]); }; }) .to_tokens(tokens); Ok(()) } } impl TryToTokens for ast::LinkToModule { fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> { let mut program = TokenStream::new(); self.0.try_to_tokens(&mut program)?; let link_function_name = self.0.link_function_name(0); let name = Ident::new(&link_function_name, Span::call_site()); let wasm_bindgen = &self.0.wasm_bindgen; let abi_ret = quote! { #wasm_bindgen::convert::WasmRet<<#wasm_bindgen::__rt::alloc::string::String as #wasm_bindgen::convert::FromWasmAbi>::Abi> }; let extern_fn = extern_fn(&name, &[], &[], &[], abi_ret); (quote! { { #program #extern_fn static __VAL: #wasm_bindgen::__rt::LazyLock<#wasm_bindgen::__rt::alloc::string::String> = #wasm_bindgen::__rt::LazyLock::new(|| unsafe { <#wasm_bindgen::__rt::alloc::string::String as #wasm_bindgen::convert::FromWasmAbi>::from_abi(#name().join()) }); #wasm_bindgen::__rt::alloc::string::String::clone(&__VAL) } }) .to_tokens(tokens); Ok(()) } } impl ToTokens for ast::Struct { fn to_tokens(&self, tokens: &mut TokenStream) { let name = &self.rust_name; let name_str = self.js_name.to_string(); let name_len = name_str.len() as u32; let name_chars: Vec = name_str.chars().map(|c| c as u32).collect(); let new_fn = Ident::new(&shared::new_function(&name_str), Span::call_site()); let free_fn = Ident::new(&shared::free_function(&name_str), Span::call_site()); let unwrap_fn = Ident::new(&shared::unwrap_function(&name_str), Span::call_site()); let wasm_bindgen = &self.wasm_bindgen; (quote! { #[automatically_derived] impl #wasm_bindgen::__rt::marker::SupportsConstructor for #name {} #[automatically_derived] impl #wasm_bindgen::__rt::marker::SupportsInstanceProperty for #name {} #[automatically_derived] impl #wasm_bindgen::__rt::marker::SupportsStaticProperty for #name {} #[automatically_derived] impl #wasm_bindgen::describe::WasmDescribe for #name { fn describe() { use #wasm_bindgen::describe::*; inform(RUST_STRUCT); inform(#name_len); #(inform(#name_chars);)* } } #[automatically_derived] impl #wasm_bindgen::convert::IntoWasmAbi for #name { type Abi = u32; fn into_abi(self) -> u32 { use #wasm_bindgen::__rt::alloc::rc::Rc; use #wasm_bindgen::__rt::WasmRefCell; Rc::into_raw(Rc::new(WasmRefCell::new(self))) as u32 } } #[automatically_derived] impl #wasm_bindgen::convert::FromWasmAbi for #name { type Abi = u32; unsafe fn from_abi(js: u32) -> Self { use #wasm_bindgen::__rt::alloc::rc::Rc; use #wasm_bindgen::__rt::core::result::Result::{Ok, Err}; use #wasm_bindgen::__rt::{assert_not_null, WasmRefCell}; let ptr = js as *mut WasmRefCell<#name>; assert_not_null(ptr); let rc = Rc::from_raw(ptr); match Rc::try_unwrap(rc) { Ok(cell) => cell.into_inner(), Err(_) => #wasm_bindgen::throw_str( "attempted to take ownership of Rust value while it was borrowed" ), } } } #[automatically_derived] impl #wasm_bindgen::__rt::core::convert::From<#name> for #wasm_bindgen::JsValue { fn from(value: #name) -> Self { let ptr = #wasm_bindgen::convert::IntoWasmAbi::into_abi(value); #[link(wasm_import_module = "__wbindgen_placeholder__")] #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] extern "C" { fn #new_fn(ptr: u32) -> u32; } #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))] unsafe fn #new_fn(_: u32) -> u32 { panic!("cannot convert to JsValue outside of the Wasm target") } unsafe { <#wasm_bindgen::JsValue as #wasm_bindgen::convert::FromWasmAbi> ::from_abi(#new_fn(ptr)) } } } #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] #[automatically_derived] const _: () = { #wasm_bindgen::__wbindgen_coverage! { #[no_mangle] #[doc(hidden)] // `allow_delayed` is whether it's ok to not actually free the `ptr` immediately // if it's still borrowed. pub unsafe extern "C" fn #free_fn(ptr: u32, allow_delayed: u32) { use #wasm_bindgen::__rt::alloc::rc::Rc; if allow_delayed != 0 { // Just drop the implicit `Rc` owned by JS, and then if the value is still // referenced it'll be kept alive by its other `Rc`s. let ptr = ptr as *mut #wasm_bindgen::__rt::WasmRefCell<#name>; #wasm_bindgen::__rt::assert_not_null(ptr); drop(Rc::from_raw(ptr)); } else { // Claim ownership of the value, which will panic if it's borrowed. let _ = <#name as #wasm_bindgen::convert::FromWasmAbi>::from_abi(ptr); } } } }; #[automatically_derived] impl #wasm_bindgen::convert::RefFromWasmAbi for #name { type Abi = u32; type Anchor = #wasm_bindgen::__rt::RcRef<#name>; unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor { use #wasm_bindgen::__rt::alloc::rc::Rc; let js = js as *mut #wasm_bindgen::__rt::WasmRefCell<#name>; #wasm_bindgen::__rt::assert_not_null(js); Rc::increment_strong_count(js); let rc = Rc::from_raw(js); #wasm_bindgen::__rt::RcRef::new(rc) } } #[automatically_derived] impl #wasm_bindgen::convert::RefMutFromWasmAbi for #name { type Abi = u32; type Anchor = #wasm_bindgen::__rt::RcRefMut<#name>; unsafe fn ref_mut_from_abi(js: Self::Abi) -> Self::Anchor { use #wasm_bindgen::__rt::alloc::rc::Rc; let js = js as *mut #wasm_bindgen::__rt::WasmRefCell<#name>; #wasm_bindgen::__rt::assert_not_null(js); Rc::increment_strong_count(js); let rc = Rc::from_raw(js); #wasm_bindgen::__rt::RcRefMut::new(rc) } } #[automatically_derived] impl #wasm_bindgen::convert::LongRefFromWasmAbi for #name { type Abi = u32; type Anchor = #wasm_bindgen::__rt::RcRef<#name>; unsafe fn long_ref_from_abi(js: Self::Abi) -> Self::Anchor { ::ref_from_abi(js) } } #[automatically_derived] impl #wasm_bindgen::convert::OptionIntoWasmAbi for #name { #[inline] fn none() -> Self::Abi { 0 } } #[automatically_derived] impl #wasm_bindgen::convert::OptionFromWasmAbi for #name { #[inline] fn is_none(abi: &Self::Abi) -> bool { *abi == 0 } } #[automatically_derived] impl #wasm_bindgen::convert::TryFromJsValue for #name { type Error = #wasm_bindgen::JsValue; fn try_from_js_value(value: #wasm_bindgen::JsValue) -> #wasm_bindgen::__rt::core::result::Result { let idx = #wasm_bindgen::convert::IntoWasmAbi::into_abi(&value); #[link(wasm_import_module = "__wbindgen_placeholder__")] #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] extern "C" { fn #unwrap_fn(ptr: u32) -> u32; } #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))] unsafe fn #unwrap_fn(_: u32) -> u32 { panic!("cannot convert from JsValue outside of the Wasm target") } let ptr = unsafe { #unwrap_fn(idx) }; if ptr == 0 { #wasm_bindgen::__rt::core::result::Result::Err(value) } else { // Don't run `JsValue`'s destructor, `unwrap_fn` already did that for us. #[allow(clippy::mem_forget)] #wasm_bindgen::__rt::core::mem::forget(value); unsafe { #wasm_bindgen::__rt::core::result::Result::Ok( ::from_abi(ptr) ) } } } } #[automatically_derived] impl #wasm_bindgen::describe::WasmDescribeVector for #name { fn describe_vector() { use #wasm_bindgen::describe::*; inform(VECTOR); inform(NAMED_EXTERNREF); inform(#name_len); #(inform(#name_chars);)* } } #[automatically_derived] impl #wasm_bindgen::convert::VectorIntoWasmAbi for #name { type Abi = < #wasm_bindgen::__rt::alloc::boxed::Box<[#wasm_bindgen::JsValue]> as #wasm_bindgen::convert::IntoWasmAbi >::Abi; fn vector_into_abi( vector: #wasm_bindgen::__rt::alloc::boxed::Box<[#name]> ) -> Self::Abi { #wasm_bindgen::convert::js_value_vector_into_abi(vector) } } #[automatically_derived] impl #wasm_bindgen::convert::VectorFromWasmAbi for #name { type Abi = < #wasm_bindgen::__rt::alloc::boxed::Box<[#wasm_bindgen::JsValue]> as #wasm_bindgen::convert::FromWasmAbi >::Abi; unsafe fn vector_from_abi( js: Self::Abi ) -> #wasm_bindgen::__rt::alloc::boxed::Box<[#name]> { #wasm_bindgen::convert::js_value_vector_from_abi(js) } } }) .to_tokens(tokens); for field in self.fields.iter() { field.to_tokens(tokens); } } } impl ToTokens for ast::StructField { fn to_tokens(&self, tokens: &mut TokenStream) { let rust_name = &self.rust_name; let struct_name = &self.struct_name; let ty = &self.ty; let getter = &self.getter; let setter = &self.setter; let maybe_assert_copy = if self.getter_with_clone.is_some() { quote! {} } else { quote! { assert_copy::<#ty>() } }; let maybe_assert_copy = respan(maybe_assert_copy, ty); // Split this out so that it isn't affected by `quote_spanned!`. // // If we don't do this, it might end up being unable to reference `js` // properly because it doesn't have the same span. // // See https://github.com/wasm-bindgen/wasm-bindgen/pull/3725. let js_token = quote! { js }; let mut val = quote_spanned!(self.rust_name.span()=> (*#js_token).borrow().#rust_name); if let Some(span) = self.getter_with_clone { val = quote_spanned!(span=> <#ty as Clone>::clone(&#val) ); } let wasm_bindgen = &self.wasm_bindgen; (quote! { #[automatically_derived] const _: () = { #wasm_bindgen::__wbindgen_coverage! { #[cfg_attr(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")), no_mangle)] #[doc(hidden)] pub unsafe extern "C" fn #getter(js: u32) -> #wasm_bindgen::convert::WasmRet<<#ty as #wasm_bindgen::convert::IntoWasmAbi>::Abi> { use #wasm_bindgen::__rt::{WasmRefCell, assert_not_null}; use #wasm_bindgen::convert::IntoWasmAbi; fn assert_copy(){} #maybe_assert_copy; let js = js as *mut WasmRefCell<#struct_name>; assert_not_null(js); let val = #val; <#ty as IntoWasmAbi>::into_abi(val).into() } } }; }) .to_tokens(tokens); Descriptor { ident: getter, inner: quote! { <#ty as WasmDescribe>::describe(); }, attrs: vec![], wasm_bindgen: &self.wasm_bindgen, } .to_tokens(tokens); if self.readonly { return; } let abi = quote! { <#ty as #wasm_bindgen::convert::FromWasmAbi>::Abi }; let (args, names) = splat(wasm_bindgen, &Ident::new("val", rust_name.span()), &abi); (quote! { #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] #[automatically_derived] const _: () = { #wasm_bindgen::__wbindgen_coverage! { #[no_mangle] #[doc(hidden)] pub unsafe extern "C" fn #setter( js: u32, #(#args,)* ) { use #wasm_bindgen::__rt::{WasmRefCell, assert_not_null}; use #wasm_bindgen::convert::FromWasmAbi; let js = js as *mut WasmRefCell<#struct_name>; assert_not_null(js); let val = <#abi as #wasm_bindgen::convert::WasmAbi>::join(#(#names),*); let val = <#ty as FromWasmAbi>::from_abi(val); (*js).borrow_mut().#rust_name = val; } } }; }) .to_tokens(tokens); } } impl TryToTokens for ast::Export { fn try_to_tokens(self: &ast::Export, into: &mut TokenStream) -> Result<(), Diagnostic> { let generated_name = self.rust_symbol(); let export_name = self.export_name(); let mut args = vec![]; let mut arg_conversions = vec![]; let mut converted_arguments = vec![]; let ret = Ident::new("_ret", Span::call_site()); let offset = if self.method_self.is_some() { args.push(quote! { me: u32 }); 1 } else { 0 }; let name = &self.rust_name; let wasm_bindgen = &self.wasm_bindgen; let wasm_bindgen_futures = &self.wasm_bindgen_futures; let receiver = match self.method_self { Some(ast::MethodSelf::ByValue) => { let class = self.rust_class.as_ref().unwrap(); arg_conversions.push(quote! { let me = unsafe { <#class as #wasm_bindgen::convert::FromWasmAbi>::from_abi(me) }; }); quote! { me.#name } } Some(ast::MethodSelf::RefMutable) => { let class = self.rust_class.as_ref().unwrap(); arg_conversions.push(quote! { let mut me = unsafe { <#class as #wasm_bindgen::convert::RefMutFromWasmAbi> ::ref_mut_from_abi(me) }; let me = &mut *me; }); quote! { me.#name } } Some(ast::MethodSelf::RefShared) => { let class = self.rust_class.as_ref().unwrap(); let (trait_, func, borrow) = if self.function.r#async { ( quote!(LongRefFromWasmAbi), quote!(long_ref_from_abi), quote!( <<#class as #wasm_bindgen::convert::LongRefFromWasmAbi> ::Anchor as #wasm_bindgen::__rt::core::borrow::Borrow<#class>> ::borrow(&me) ), ) } else { (quote!(RefFromWasmAbi), quote!(ref_from_abi), quote!(&*me)) }; arg_conversions.push(quote! { let me = unsafe { <#class as #wasm_bindgen::convert::#trait_>::#func(me) }; let me = #borrow; }); quote! { me.#name } } None => match &self.rust_class { Some(class) => quote! { #class::#name }, None => quote! { #name }, }, }; let mut argtys = Vec::new(); for (i, arg) in self.function.arguments.iter().enumerate() { argtys.push(&*arg.pat_type.ty); let i = i + offset; let ident = Ident::new(&format!("arg{}", i), Span::call_site()); fn unwrap_nested_types(ty: &syn::Type) -> &syn::Type { match &ty { syn::Type::Group(syn::TypeGroup { ref elem, .. }) => unwrap_nested_types(elem), syn::Type::Paren(syn::TypeParen { ref elem, .. }) => unwrap_nested_types(elem), _ => ty, } } let ty = unwrap_nested_types(&arg.pat_type.ty); match &ty { syn::Type::Reference(syn::TypeReference { mutability: Some(_), elem, .. }) => { let abi = quote! { <#elem as #wasm_bindgen::convert::RefMutFromWasmAbi>::Abi }; let (prim_args, prim_names) = splat(wasm_bindgen, &ident, &abi); args.extend(prim_args); arg_conversions.push(quote! { let mut #ident = unsafe { <#elem as #wasm_bindgen::convert::RefMutFromWasmAbi> ::ref_mut_from_abi( <#abi as #wasm_bindgen::convert::WasmAbi>::join(#(#prim_names),*) ) }; let #ident = &mut *#ident; }); } syn::Type::Reference(syn::TypeReference { elem, .. }) => { if self.function.r#async { let abi = quote! { <#elem as #wasm_bindgen::convert::LongRefFromWasmAbi>::Abi }; let (prim_args, prim_names) = splat(wasm_bindgen, &ident, &abi); args.extend(prim_args); arg_conversions.push(quote! { let #ident = unsafe { <#elem as #wasm_bindgen::convert::LongRefFromWasmAbi> ::long_ref_from_abi( <#abi as #wasm_bindgen::convert::WasmAbi>::join(#(#prim_names),*) ) }; let #ident = <<#elem as #wasm_bindgen::convert::LongRefFromWasmAbi> ::Anchor as core::borrow::Borrow<#elem>> ::borrow(&#ident); }); } else { let abi = quote! { <#elem as #wasm_bindgen::convert::RefFromWasmAbi>::Abi }; let (prim_args, prim_names) = splat(wasm_bindgen, &ident, &abi); args.extend(prim_args); arg_conversions.push(quote! { let #ident = unsafe { <#elem as #wasm_bindgen::convert::RefFromWasmAbi> ::ref_from_abi( <#abi as #wasm_bindgen::convert::WasmAbi>::join(#(#prim_names),*) ) }; let #ident = &*#ident; }); } } _ => { let abi = quote! { <#ty as #wasm_bindgen::convert::FromWasmAbi>::Abi }; let (prim_args, prim_names) = splat(wasm_bindgen, &ident, &abi); args.extend(prim_args); arg_conversions.push(quote! { let #ident = unsafe { <#ty as #wasm_bindgen::convert::FromWasmAbi> ::from_abi( <#abi as #wasm_bindgen::convert::WasmAbi>::join(#(#prim_names),*) ) }; }); } } converted_arguments.push(quote! { #ident }); } let syn_unit = syn::Type::Tuple(syn::TypeTuple { elems: Default::default(), paren_token: Default::default(), }); let syn_ret = self .function .ret .as_ref() .map(|ret| &ret.r#type) .unwrap_or(&syn_unit); if let syn::Type::Reference(_) = syn_ret { bail_span!(syn_ret, "cannot return a borrowed ref with #[wasm_bindgen]",) } // For an `async` function we always run it through `future_to_promise` // since we're returning a promise to JS, and this will implicitly // require that the function returns a `Future>` let (ret_ty, inner_ret_ty, ret_expr) = if self.function.r#async { if self.start { ( quote! { () }, quote! { () }, quote! { <#syn_ret as #wasm_bindgen::__rt::Start>::start(#ret.await) }, ) } else { ( quote! { #wasm_bindgen::JsValue }, quote! { #syn_ret }, quote! { <#syn_ret as #wasm_bindgen::__rt::IntoJsResult>::into_js_result(#ret.await) }, ) } } else if self.start { ( quote! { () }, quote! { () }, quote! { <#syn_ret as #wasm_bindgen::__rt::Start>::start(#ret) }, ) } else { (quote! { #syn_ret }, quote! { #syn_ret }, quote! { #ret }) }; let mut call = quote! { { #(#arg_conversions)* let #ret = #receiver(#(#converted_arguments),*); #ret_expr } }; if self.function.r#async { if self.start { call = quote! { #wasm_bindgen_futures::spawn_local(async move { #call }) } } else { call = quote! { #wasm_bindgen_futures::future_to_promise(async move { #call }).into() } } } let projection = quote! { <#ret_ty as #wasm_bindgen::convert::ReturnWasmAbi> }; let convert_ret = quote! { #projection::return_abi(#ret).into() }; let describe_ret = quote! { <#ret_ty as WasmDescribe>::describe(); <#inner_ret_ty as WasmDescribe>::describe(); }; let nargs = self.function.arguments.len() as u32; let attrs = &self.function.rust_attrs; let mut checks = Vec::new(); if self.start { checks.push(quote! { const _ASSERT: fn() = || -> #projection::Abi { loop {} }; }); }; if let Some(class) = self.rust_class.as_ref() { // little helper function to make sure the check points to the // location of the function causing the assert to fail let mut add_check = |token_stream| { checks.push(respan(token_stream, &self.rust_name)); }; match &self.method_kind { ast::MethodKind::Constructor => { add_check(quote! { let _: #wasm_bindgen::__rt::marker::CheckSupportsConstructor<#class>; }); if self.function.r#async { (quote_spanned! { self.function.name_span => const _: () = { #[deprecated(note = "async constructors produce invalid TS code and support will be removed in the future")] const fn constructor() {} constructor(); }; }) .to_tokens(into); } } ast::MethodKind::Operation(operation) => match operation.kind { ast::OperationKind::Getter(_) | ast::OperationKind::Setter(_) => { if operation.is_static { add_check(quote! { let _: #wasm_bindgen::__rt::marker::CheckSupportsStaticProperty<#class>; }); } else { add_check(quote! { let _: #wasm_bindgen::__rt::marker::CheckSupportsInstanceProperty<#class>; }); } } _ => {} }, } } (quote! { #[automatically_derived] const _: () = { #wasm_bindgen::__wbindgen_coverage! { #(#attrs)* #[cfg_attr( all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")), export_name = #export_name, )] pub unsafe extern "C" fn #generated_name(#(#args),*) -> #wasm_bindgen::convert::WasmRet<#projection::Abi> { const _: () = { #(#checks)* }; let #ret = #call; #convert_ret } } }; }) .to_tokens(into); let describe_args: TokenStream = argtys .iter() .map(|ty| match ty { syn::Type::Reference(reference) if self.function.r#async && reference.mutability.is_none() => { let inner = &reference.elem; quote! { inform(LONGREF); <#inner as WasmDescribe>::describe(); } } _ => quote! { <#ty as WasmDescribe>::describe(); }, }) .collect(); // In addition to generating the shim function above which is what // our generated JS will invoke, we *also* generate a "descriptor" // shim. This descriptor shim uses the `WasmDescribe` trait to // programmatically describe the type signature of the generated // shim above. This in turn is then used to inform the // `wasm-bindgen` CLI tool exactly what types and such it should be // using in JS. // // Note that this descriptor function is a purely an internal detail // of `#[wasm_bindgen]` and isn't intended to be exported to anyone // or actually part of the final was binary. Additionally, this is // literally executed when the `wasm-bindgen` tool executes. // // In any case, there's complications in `wasm-bindgen` to handle // this, but the tl;dr; is that this is stripped from the final wasm // binary along with anything it references. let export = Ident::new(&export_name, Span::call_site()); Descriptor { ident: &export, inner: quote! { inform(FUNCTION); inform(0); inform(#nargs); #describe_args #describe_ret }, attrs: attrs.clone(), wasm_bindgen: &self.wasm_bindgen, } .to_tokens(into); Ok(()) } } impl TryToTokens for ast::ImportKind { fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> { match *self { ast::ImportKind::Function(ref f) => f.try_to_tokens(tokens)?, ast::ImportKind::Static(ref s) => s.to_tokens(tokens), ast::ImportKind::String(ref s) => s.to_tokens(tokens), ast::ImportKind::Type(ref t) => t.to_tokens(tokens), ast::ImportKind::Enum(ref e) => e.to_tokens(tokens), } Ok(()) } } impl ToTokens for ast::ImportType { fn to_tokens(&self, tokens: &mut TokenStream) { let vis = &self.vis; let rust_name = &self.rust_name; let attrs = &self.attrs; let doc_comment = match &self.doc_comment { None => "", Some(comment) => comment, }; let instanceof_shim = Ident::new(&self.instanceof_shim, Span::call_site()); let wasm_bindgen = &self.wasm_bindgen; let internal_obj = match self.extends.first() { Some(target) => { quote! { #target } } None => { quote! { #wasm_bindgen::JsValue } } }; let description = if let Some(typescript_type) = &self.typescript_type { let typescript_type_len = typescript_type.len() as u32; let typescript_type_chars = typescript_type.chars().map(|c| c as u32); quote! { use #wasm_bindgen::describe::*; inform(NAMED_EXTERNREF); inform(#typescript_type_len); #(inform(#typescript_type_chars);)* } } else { quote! { JsValue::describe() } }; let is_type_of = self.is_type_of.as_ref().map(|is_type_of| { quote! { #[inline] fn is_type_of(val: &JsValue) -> bool { let is_type_of: fn(&JsValue) -> bool = #is_type_of; is_type_of(val) } } }); let no_deref = self.no_deref; let doc = if doc_comment.is_empty() { quote! {} } else { quote! { #[doc = #doc_comment] } }; (quote! { #[automatically_derived] #(#attrs)* #doc #[repr(transparent)] #vis struct #rust_name { obj: #internal_obj } #[automatically_derived] const _: () = { use #wasm_bindgen::convert::TryFromJsValue; use #wasm_bindgen::convert::{IntoWasmAbi, FromWasmAbi}; use #wasm_bindgen::convert::{OptionIntoWasmAbi, OptionFromWasmAbi}; use #wasm_bindgen::convert::{RefFromWasmAbi, LongRefFromWasmAbi}; use #wasm_bindgen::describe::WasmDescribe; use #wasm_bindgen::{JsValue, JsCast}; use #wasm_bindgen::__rt::core; #[automatically_derived] impl WasmDescribe for #rust_name { fn describe() { #description } } #[automatically_derived] impl IntoWasmAbi for #rust_name { type Abi = ::Abi; #[inline] fn into_abi(self) -> Self::Abi { self.obj.into_abi() } } #[automatically_derived] impl OptionIntoWasmAbi for #rust_name { #[inline] fn none() -> Self::Abi { 0 } } #[automatically_derived] impl<'a> OptionIntoWasmAbi for &'a #rust_name { #[inline] fn none() -> Self::Abi { 0 } } #[automatically_derived] impl FromWasmAbi for #rust_name { type Abi = ::Abi; #[inline] unsafe fn from_abi(js: Self::Abi) -> Self { #rust_name { obj: JsValue::from_abi(js).into(), } } } #[automatically_derived] impl OptionFromWasmAbi for #rust_name { #[inline] fn is_none(abi: &Self::Abi) -> bool { *abi == 0 } } #[automatically_derived] impl<'a> IntoWasmAbi for &'a #rust_name { type Abi = <&'a JsValue as IntoWasmAbi>::Abi; #[inline] fn into_abi(self) -> Self::Abi { (&self.obj).into_abi() } } #[automatically_derived] impl RefFromWasmAbi for #rust_name { type Abi = ::Abi; type Anchor = core::mem::ManuallyDrop<#rust_name>; #[inline] unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor { let tmp = ::ref_from_abi(js); core::mem::ManuallyDrop::new(#rust_name { obj: core::mem::ManuallyDrop::into_inner(tmp).into(), }) } } #[automatically_derived] impl LongRefFromWasmAbi for #rust_name { type Abi = ::Abi; type Anchor = #rust_name; #[inline] unsafe fn long_ref_from_abi(js: Self::Abi) -> Self::Anchor { let tmp = ::long_ref_from_abi(js); #rust_name { obj: tmp.into() } } } // TODO: remove this on the next major version #[automatically_derived] impl From for #rust_name { #[inline] fn from(obj: JsValue) -> #rust_name { #rust_name { obj: obj.into() } } } #[automatically_derived] impl AsRef for #rust_name { #[inline] fn as_ref(&self) -> &JsValue { self.obj.as_ref() } } #[automatically_derived] impl AsRef<#rust_name> for #rust_name { #[inline] fn as_ref(&self) -> &#rust_name { self } } #[automatically_derived] impl From<#rust_name> for JsValue { #[inline] fn from(obj: #rust_name) -> JsValue { obj.obj.into() } } #[automatically_derived] impl JsCast for #rust_name { fn instanceof(val: &JsValue) -> bool { #[link(wasm_import_module = "__wbindgen_placeholder__")] #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] extern "C" { fn #instanceof_shim(val: u32) -> u32; } #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))] unsafe fn #instanceof_shim(_: u32) -> u32 { panic!("cannot check instanceof on non-wasm targets"); } unsafe { let idx = val.into_abi(); #instanceof_shim(idx) != 0 } } #is_type_of #[inline] fn unchecked_from_js(val: JsValue) -> Self { #rust_name { obj: val.into() } } #[inline] fn unchecked_from_js_ref(val: &JsValue) -> &Self { // Should be safe because `#rust_name` is a transparent // wrapper around `val` unsafe { &*(val as *const JsValue as *const #rust_name) } } } }; }) .to_tokens(tokens); if !no_deref { (quote! { #[automatically_derived] impl #wasm_bindgen::__rt::core::ops::Deref for #rust_name { type Target = #internal_obj; #[inline] fn deref(&self) -> &#internal_obj { &self.obj } } }) .to_tokens(tokens); } for superclass in self.extends.iter() { (quote! { #[automatically_derived] impl From<#rust_name> for #superclass { #[inline] fn from(obj: #rust_name) -> #superclass { use #wasm_bindgen::JsCast; #superclass::unchecked_from_js(obj.into()) } } #[automatically_derived] impl AsRef<#superclass> for #rust_name { #[inline] fn as_ref(&self) -> &#superclass { use #wasm_bindgen::JsCast; #superclass::unchecked_from_js_ref(self.as_ref()) } } }) .to_tokens(tokens); } } } impl ToTokens for ast::StringEnum { fn to_tokens(&self, tokens: &mut TokenStream) { let vis = &self.vis; let enum_name = &self.name; let name_str = &self.js_name; let name_len = name_str.len() as u32; let name_chars = name_str.chars().map(u32::from); let variants = &self.variants; let variant_count = self.variant_values.len() as u32; let variant_values = &self.variant_values; let variant_indices = (0..variant_count).collect::>(); let invalid = variant_count; let hole = variant_count + 1; let attrs = &self.rust_attrs; let invalid_to_str_msg = format!( "Converting an invalid string enum ({}) back to a string is currently not supported", enum_name ); // A vector of EnumName::VariantName tokens for this enum let variant_paths: Vec = self .variants .iter() .map(|v| quote!(#enum_name::#v).into_token_stream()) .collect(); // Borrow variant_paths because we need to use it multiple times inside the quote! macro let variant_paths_ref = &variant_paths; let wasm_bindgen = &self.wasm_bindgen; (quote! { #(#attrs)* #[non_exhaustive] #[repr(u32)] #vis enum #enum_name { #(#variants = #variant_indices,)* #[automatically_derived] #[doc(hidden)] __Invalid } #[automatically_derived] impl #enum_name { fn from_str(s: &str) -> Option<#enum_name> { match s { #(#variant_values => Some(#variant_paths_ref),)* _ => None, } } fn to_str(&self) -> &'static str { match self { #(#variant_paths_ref => #variant_values,)* #enum_name::__Invalid => panic!(#invalid_to_str_msg), } } #vis fn from_js_value(obj: &#wasm_bindgen::JsValue) -> Option<#enum_name> { obj.as_string().and_then(|obj_str| Self::from_str(obj_str.as_str())) } } #[automatically_derived] impl #wasm_bindgen::convert::IntoWasmAbi for #enum_name { type Abi = u32; #[inline] fn into_abi(self) -> u32 { self as u32 } } #[automatically_derived] impl #wasm_bindgen::convert::FromWasmAbi for #enum_name { type Abi = u32; unsafe fn from_abi(val: u32) -> Self { match val { #(#variant_indices => #variant_paths_ref,)* #invalid => #enum_name::__Invalid, _ => unreachable!("The JS binding should only ever produce a valid value or the specific 'invalid' value"), } } } #[automatically_derived] impl #wasm_bindgen::convert::OptionFromWasmAbi for #enum_name { #[inline] fn is_none(val: &u32) -> bool { *val == #hole } } #[automatically_derived] impl #wasm_bindgen::convert::OptionIntoWasmAbi for #enum_name { #[inline] fn none() -> Self::Abi { #hole } } #[automatically_derived] impl #wasm_bindgen::describe::WasmDescribe for #enum_name { fn describe() { use #wasm_bindgen::describe::*; inform(STRING_ENUM); inform(#name_len); #(inform(#name_chars);)* inform(#variant_count); } } #[automatically_derived] impl #wasm_bindgen::__rt::core::convert::From<#enum_name> for #wasm_bindgen::JsValue { fn from(val: #enum_name) -> Self { #wasm_bindgen::JsValue::from_str(val.to_str()) } } }) .to_tokens(tokens); } } impl TryToTokens for ast::ImportFunction { fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> { let mut class_ty = None; let mut is_method = false; match self.kind { ast::ImportFunctionKind::Method { ref ty, ref kind, .. } => { if let ast::MethodKind::Operation(ast::Operation { is_static: false, .. }) = kind { is_method = true; } class_ty = Some(ty); } ast::ImportFunctionKind::Normal => {} } let vis = &self.function.rust_vis; let ret = match self.function.ret.as_ref().map(|ret| &ret.r#type) { Some(ty) => quote! { -> #ty }, None => quote!(), }; let mut abi_argument_names = Vec::new(); let mut abi_arguments = Vec::new(); let mut arg_conversions = Vec::new(); let mut arguments = Vec::new(); let ret_ident = Ident::new("_ret", Span::call_site()); let wasm_bindgen = &self.wasm_bindgen; let wasm_bindgen_futures = &self.wasm_bindgen_futures; for (i, arg) in self.function.arguments.iter().enumerate() { let ty = &arg.pat_type.ty; let name = match &*arg.pat_type.pat { syn::Pat::Ident(syn::PatIdent { by_ref: None, ident, subpat: None, .. }) => ident.clone(), syn::Pat::Wild(_) => syn::Ident::new(&format!("__genarg_{}", i), Span::call_site()), _ => bail_span!( arg.pat_type.pat, "unsupported pattern in #[wasm_bindgen] imported function", ), }; let abi = quote! { <#ty as #wasm_bindgen::convert::IntoWasmAbi>::Abi }; let (prim_args, prim_names) = splat(wasm_bindgen, &name, &abi); abi_arguments.extend(prim_args); abi_argument_names.extend(prim_names.iter().cloned()); let var = if i == 0 && is_method { quote! { self } } else { arguments.push(quote! { #name: #ty }); quote! { #name } }; arg_conversions.push(quote! { let #name = <#ty as #wasm_bindgen::convert::IntoWasmAbi> ::into_abi(#var); let (#(#prim_names),*) = <#abi as #wasm_bindgen::convert::WasmAbi>::split(#name); }); } let abi_ret; let mut convert_ret; match &self.js_ret { Some(syn::Type::Reference(_)) => { bail_span!( self.js_ret, "cannot return references in #[wasm_bindgen] imports yet" ); } Some(ref ty) => { if self.function.r#async { abi_ret = quote! { #wasm_bindgen::convert::WasmRet<<#wasm_bindgen_futures::js_sys::Promise as #wasm_bindgen::convert::FromWasmAbi>::Abi> }; let future = quote! { #wasm_bindgen_futures::JsFuture::from( <#wasm_bindgen_futures::js_sys::Promise as #wasm_bindgen::convert::FromWasmAbi> ::from_abi(#ret_ident.join()) ).await }; convert_ret = if self.catch { quote! { Ok(#wasm_bindgen::JsCast::unchecked_from_js(#future?)) } } else { quote! { #wasm_bindgen::JsCast::unchecked_from_js(#future.expect("unexpected exception")) } }; } else { abi_ret = quote! { #wasm_bindgen::convert::WasmRet<<#ty as #wasm_bindgen::convert::FromWasmAbi>::Abi> }; convert_ret = quote! { <#ty as #wasm_bindgen::convert::FromWasmAbi> ::from_abi(#ret_ident.join()) }; } } None => { if self.function.r#async { abi_ret = quote! { #wasm_bindgen::convert::WasmRet<<#wasm_bindgen_futures::js_sys::Promise as #wasm_bindgen::convert::FromWasmAbi>::Abi> }; let future = quote! { #wasm_bindgen_futures::JsFuture::from( <#wasm_bindgen_futures::js_sys::Promise as #wasm_bindgen::convert::FromWasmAbi> ::from_abi(#ret_ident.join()) ).await }; convert_ret = if self.catch { quote! { #future?; Ok(()) } } else { quote! { #future.expect("uncaught exception"); } }; } else { abi_ret = quote! { () }; convert_ret = quote! { () }; } } } let mut exceptional_ret = quote!(); if self.catch && !self.function.r#async { convert_ret = quote! { Ok(#convert_ret) }; exceptional_ret = quote! { #wasm_bindgen::__rt::take_last_exception()?; }; } let rust_name = &self.rust_name; let import_name = &self.shim; let attrs = &self.function.rust_attrs; let arguments = &arguments; let abi_arguments = &abi_arguments[..]; let abi_argument_names = &abi_argument_names[..]; let doc = if self.doc_comment.is_empty() { quote! {} } else { let doc_comment = &self.doc_comment; quote! { #[doc = #doc_comment] } }; let me = if is_method { quote! { &self, } } else { quote!() }; // Route any errors pointing to this imported function to the identifier // of the function we're imported from so we at least know what function // is causing issues. // // Note that this is where type errors like "doesn't implement // FromWasmAbi" or "doesn't implement IntoWasmAbi" currently get routed. // I suspect that's because they show up in the signature via trait // projections as types of arguments, and all that needs to typecheck // before the body can be typechecked. Due to rust-lang/rust#60980 (and // probably related issues) we can't really get a precise span. // // Ideally what we want is to point errors for particular types back to // the specific argument/type that generated the error, but it looks // like rustc itself doesn't do great in that regard so let's just do // the best we can in the meantime. let extern_fn = respan( extern_fn( import_name, attrs, abi_arguments, abi_argument_names, abi_ret, ), &self.rust_name, ); let maybe_unsafe = if self.function.r#unsafe { Some(quote! {unsafe}) } else { None }; let maybe_async = if self.function.r#async { Some(quote! {async}) } else { None }; let invocation = quote! { // This is due to `#[automatically_derived]` attribute cannot be // placed onto bare functions. #[allow(nonstandard_style)] #[allow(clippy::all, clippy::nursery, clippy::pedantic, clippy::restriction)] #(#attrs)* #doc #vis #maybe_async #maybe_unsafe fn #rust_name(#me #(#arguments),*) #ret { #extern_fn unsafe { let #ret_ident = { #(#arg_conversions)* #import_name(#(#abi_argument_names),*) }; #exceptional_ret #convert_ret } } }; if let Some(class) = class_ty { (quote! { #[automatically_derived] impl #class { #invocation } }) .to_tokens(tokens); } else { invocation.to_tokens(tokens); } Ok(()) } } // See comment above in ast::Export for what's going on here. struct DescribeImport<'a> { kind: &'a ast::ImportKind, wasm_bindgen: &'a syn::Path, } impl ToTokens for DescribeImport<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { let f = match *self.kind { ast::ImportKind::Function(ref f) => f, ast::ImportKind::Static(_) => return, ast::ImportKind::String(_) => return, ast::ImportKind::Type(_) => return, ast::ImportKind::Enum(_) => return, }; let argtys = f.function.arguments.iter().map(|arg| &arg.pat_type.ty); let nargs = f.function.arguments.len() as u32; let inform_ret = match &f.js_ret { Some(ref t) => quote! { <#t as WasmDescribe>::describe(); }, // async functions always return a JsValue, even if they say to return () None if f.function.r#async => quote! { ::describe(); }, None => quote! { <() as WasmDescribe>::describe(); }, }; Descriptor { ident: &f.shim, inner: quote! { inform(FUNCTION); inform(0); inform(#nargs); #(<#argtys as WasmDescribe>::describe();)* #inform_ret #inform_ret }, attrs: f.function.rust_attrs.clone(), wasm_bindgen: self.wasm_bindgen, } .to_tokens(tokens); } } impl ToTokens for ast::Enum { fn to_tokens(&self, into: &mut TokenStream) { let enum_name = &self.rust_name; let name_str = self.js_name.to_string(); let name_len = name_str.len() as u32; let name_chars = name_str.chars().map(|c| c as u32); let hole = &self.hole; let underlying = if self.signed { quote! { i32 } } else { quote! { u32 } }; let cast_clauses = self.variants.iter().map(|variant| { let variant_name = &variant.name; quote! { if js == #enum_name::#variant_name as #underlying { #enum_name::#variant_name } } }); let try_from_cast_clauses = cast_clauses.clone(); let wasm_bindgen = &self.wasm_bindgen; (quote! { #[automatically_derived] impl #wasm_bindgen::convert::IntoWasmAbi for #enum_name { type Abi = #underlying; #[inline] fn into_abi(self) -> #underlying { self as #underlying } } #[automatically_derived] impl #wasm_bindgen::convert::FromWasmAbi for #enum_name { type Abi = #underlying; #[inline] unsafe fn from_abi(js: #underlying) -> Self { #(#cast_clauses else)* { #wasm_bindgen::throw_str("invalid enum value passed") } } } #[automatically_derived] impl #wasm_bindgen::convert::OptionFromWasmAbi for #enum_name { #[inline] fn is_none(val: &Self::Abi) -> bool { *val == #hole as #underlying } } #[automatically_derived] impl #wasm_bindgen::convert::OptionIntoWasmAbi for #enum_name { #[inline] fn none() -> Self::Abi { #hole as #underlying } } #[automatically_derived] impl #wasm_bindgen::describe::WasmDescribe for #enum_name { fn describe() { use #wasm_bindgen::describe::*; inform(ENUM); inform(#name_len); #(inform(#name_chars);)* inform(#hole); } } #[automatically_derived] impl #wasm_bindgen::__rt::core::convert::From<#enum_name> for #wasm_bindgen::JsValue { fn from(value: #enum_name) -> Self { #wasm_bindgen::JsValue::from_f64((value as #underlying).into()) } } #[automatically_derived] impl #wasm_bindgen::convert::TryFromJsValue for #enum_name { type Error = #wasm_bindgen::JsValue; fn try_from_js_value(value: #wasm_bindgen::JsValue) -> #wasm_bindgen::__rt::core::result::Result::Error> { use #wasm_bindgen::__rt::core::convert::TryFrom; let js = f64::try_from(&value)? as #underlying; #wasm_bindgen::__rt::core::result::Result::Ok( #(#try_from_cast_clauses else)* { return #wasm_bindgen::__rt::core::result::Result::Err(value) } ) } } #[automatically_derived] impl #wasm_bindgen::describe::WasmDescribeVector for #enum_name { fn describe_vector() { use #wasm_bindgen::describe::*; inform(VECTOR); <#wasm_bindgen::JsValue as #wasm_bindgen::describe::WasmDescribe>::describe(); } } #[automatically_derived] impl #wasm_bindgen::convert::VectorIntoWasmAbi for #enum_name { type Abi = < #wasm_bindgen::__rt::alloc::boxed::Box<[#wasm_bindgen::JsValue]> as #wasm_bindgen::convert::IntoWasmAbi >::Abi; fn vector_into_abi( vector: #wasm_bindgen::__rt::alloc::boxed::Box<[#enum_name]> ) -> Self::Abi { #wasm_bindgen::convert::js_value_vector_into_abi(vector) } } #[automatically_derived] impl #wasm_bindgen::convert::VectorFromWasmAbi for #enum_name { type Abi = < #wasm_bindgen::__rt::alloc::boxed::Box<[#wasm_bindgen::JsValue]> as #wasm_bindgen::convert::FromWasmAbi >::Abi; unsafe fn vector_from_abi( js: Self::Abi ) -> #wasm_bindgen::__rt::alloc::boxed::Box<[#enum_name]> { #wasm_bindgen::convert::js_value_vector_from_abi(js) } } }) .to_tokens(into); } } impl ToTokens for ast::ImportStatic { fn to_tokens(&self, into: &mut TokenStream) { let ty = &self.ty; if let Some(thread_local) = self.thread_local { thread_local_import( &self.vis, &self.rust_name, &self.wasm_bindgen, ty, ty, &self.shim, thread_local, ) .to_tokens(into) } else { let vis = &self.vis; let name = &self.rust_name; let wasm_bindgen = &self.wasm_bindgen; let ty = &self.ty; let shim_name = &self.shim; let init = static_init(wasm_bindgen, ty, shim_name); into.extend(quote! { #[automatically_derived] #[deprecated = "use with `#[wasm_bindgen(thread_local_v2)]` instead"] }); into.extend( quote_spanned! { name.span() => #vis static #name: #wasm_bindgen::JsStatic<#ty> = { fn init() -> #ty { #init } #wasm_bindgen::__rt::std::thread_local!(static _VAL: #ty = init();); #wasm_bindgen::JsStatic { __inner: &_VAL, } }; }, ); } Descriptor { ident: &self.shim, inner: quote! { <#ty as WasmDescribe>::describe(); }, attrs: vec![], wasm_bindgen: &self.wasm_bindgen, } .to_tokens(into); } } impl ToTokens for ast::ImportString { fn to_tokens(&self, into: &mut TokenStream) { let js_sys = &self.js_sys; let actual_ty: syn::Type = parse_quote!(#js_sys::JsString); thread_local_import( &self.vis, &self.rust_name, &self.wasm_bindgen, &actual_ty, &self.ty, &self.shim, self.thread_local, ) .to_tokens(into); } } fn thread_local_import( vis: &syn::Visibility, name: &Ident, wasm_bindgen: &syn::Path, actual_ty: &syn::Type, ty: &syn::Type, shim_name: &Ident, thread_local: ast::ThreadLocal, ) -> TokenStream { let init = static_init(wasm_bindgen, ty, shim_name); match thread_local { ast::ThreadLocal::V1 => quote! { #wasm_bindgen::__rt::std::thread_local! { #[automatically_derived] #[deprecated = "use with `#[wasm_bindgen(thread_local_v2)]` instead"] #vis static #name: #actual_ty = { #init }; } }, ast::ThreadLocal::V2 => { quote! { #vis static #name: #wasm_bindgen::JsThreadLocal<#actual_ty> = { fn init() -> #actual_ty { #init } #wasm_bindgen::__wbindgen_thread_local!(#wasm_bindgen, #actual_ty) }; } } } } fn static_init(wasm_bindgen: &syn::Path, ty: &syn::Type, shim_name: &Ident) -> TokenStream { let abi_ret = quote! { #wasm_bindgen::convert::WasmRet<<#ty as #wasm_bindgen::convert::FromWasmAbi>::Abi> }; quote! { #[link(wasm_import_module = "__wbindgen_placeholder__")] #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] extern "C" { fn #shim_name() -> #abi_ret; } #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))] unsafe fn #shim_name() -> #abi_ret { panic!("cannot access imported statics on non-wasm targets") } unsafe { <#ty as #wasm_bindgen::convert::FromWasmAbi>::from_abi(#shim_name().join()) } } } /// Emits the necessary glue tokens for "descriptor", generating an appropriate /// symbol name as well as attributes around the descriptor function itself. struct Descriptor<'a, T> { ident: &'a Ident, inner: T, attrs: Vec, wasm_bindgen: &'a syn::Path, } impl ToTokens for Descriptor<'_, T> { fn to_tokens(&self, tokens: &mut TokenStream) { // It's possible for the same descriptor to be emitted in two different // modules (aka a value imported twice in a crate, each in a separate // module). In this case no need to emit duplicate descriptors (which // leads to duplicate symbol errors), instead just emit one. // // It's up to the descriptors themselves to ensure they have unique // names for unique items imported, currently done via `ShortHash` and // hashing appropriate data into the symbol name. thread_local! { static DESCRIPTORS_EMITTED: RefCell> = RefCell::default(); } let ident = self.ident; if !DESCRIPTORS_EMITTED.with(|list| list.borrow_mut().insert(ident.to_string())) { return; } let name = Ident::new(&format!("__wbindgen_describe_{}", ident), ident.span()); let inner = &self.inner; let attrs = &self.attrs; let wasm_bindgen = &self.wasm_bindgen; (quote! { #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] #[automatically_derived] const _: () = { #wasm_bindgen::__wbindgen_coverage! { #(#attrs)* #[no_mangle] #[doc(hidden)] pub extern "C" fn #name() { use #wasm_bindgen::describe::*; // See definition of `link_mem_intrinsics` for what this is doing #wasm_bindgen::__rt::link_mem_intrinsics(); #inner } } }; }) .to_tokens(tokens); } } fn extern_fn( import_name: &Ident, attrs: &[syn::Attribute], abi_arguments: &[TokenStream], abi_argument_names: &[Ident], abi_ret: TokenStream, ) -> TokenStream { quote! { #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] #(#attrs)* #[link(wasm_import_module = "__wbindgen_placeholder__")] extern "C" { fn #import_name(#(#abi_arguments),*) -> #abi_ret; } #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))] unsafe fn #import_name(#(#abi_arguments),*) -> #abi_ret { #( drop(#abi_argument_names); )* panic!("cannot call wasm-bindgen imported functions on \ non-wasm targets"); } } } /// Splats an argument with the given name and ABI type into 4 arguments, one /// for each primitive that the ABI type splits into. /// /// Returns an `(args, names)` pair, where `args` is the list of arguments to /// be inserted into the function signature, and `names` is a list of the names /// of those arguments. fn splat( wasm_bindgen: &syn::Path, name: &Ident, abi: &TokenStream, ) -> (Vec, Vec) { let mut args = Vec::new(); let mut names = Vec::new(); for n in 1_u32..=4 { let arg_name = format_ident!("{}_{}", name, n); let prim_name = format_ident!("Prim{}", n); args.push(quote! { #arg_name: <#abi as #wasm_bindgen::convert::WasmAbi>::#prim_name }); names.push(arg_name); } (args, names) } /// Converts `span` into a stream of tokens, and attempts to ensure that `input` /// has all the appropriate span information so errors in it point to `span`. fn respan(input: TokenStream, span: &dyn ToTokens) -> TokenStream { let mut first_span = Span::call_site(); let mut last_span = Span::call_site(); let mut spans = TokenStream::new(); span.to_tokens(&mut spans); for (i, token) in spans.into_iter().enumerate() { if i == 0 { first_span = Span::call_site().located_at(token.span()); } last_span = Span::call_site().located_at(token.span()); } let mut new_tokens = Vec::new(); for (i, mut token) in input.into_iter().enumerate() { if i == 0 { token.set_span(first_span); } else { token.set_span(last_span); } new_tokens.push(token); } new_tokens.into_iter().collect() } wasm-bindgen-backend-0.2.104/src/encode.rs000064400000000000000000000450211046102023000163240ustar 00000000000000use crate::util::ShortHash; use proc_macro2::{Ident, Span}; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::env; use std::fs; use std::path::PathBuf; use syn::ext::IdentExt; use crate::ast; use crate::Diagnostic; #[derive(Clone)] pub enum EncodeChunk { EncodedBuf(Vec), StrExpr(syn::Expr), // TODO: support more expr type; } pub struct EncodeResult { pub custom_section: Vec, pub included_files: Vec, } pub fn encode(program: &ast::Program) -> Result { let mut e = Encoder::new(); let i = Interner::new(); shared_program(program, &i)?.encode(&mut e); let custom_section = e.finish(); let included_files = i .files .borrow() .values() .map(|p| &p.path) .cloned() .collect(); Ok(EncodeResult { custom_section, included_files, }) } struct Interner { bump: bumpalo::Bump, files: RefCell>, root: PathBuf, crate_name: String, has_package_json: Cell, } struct LocalFile { path: PathBuf, definition: Span, new_identifier: String, linked_module: bool, } impl Interner { fn new() -> Interner { let root = env::var_os("CARGO_MANIFEST_DIR") .expect("should have CARGO_MANIFEST_DIR env var") .into(); let crate_name = env::var("CARGO_PKG_NAME").expect("should have CARGO_PKG_NAME env var"); Interner { bump: bumpalo::Bump::new(), files: RefCell::new(HashMap::new()), root, crate_name, has_package_json: Cell::new(false), } } fn intern(&self, s: &Ident) -> &str { self.intern_str(&s.to_string()) } fn intern_str(&self, s: &str) -> &str { // NB: eventually this could be used to intern `s` to only allocate one // copy, but for now let's just "transmute" `s` to have the same // lifetime as this struct itself (which is our main goal here) self.bump.alloc_str(s) } /// Given an import to a local module `id` this generates a unique module id /// to assign to the contents of `id`. /// /// Note that repeated invocations of this function will be memoized, so the /// same `id` will always return the same resulting unique `id`. fn resolve_import_module( &self, id: &str, span: Span, linked_module: bool, ) -> Result, Diagnostic> { let mut files = self.files.borrow_mut(); if let Some(file) = files.get(id) { return Ok(ImportModule::Named(self.intern_str(&file.new_identifier))); } self.check_for_package_json(); let path = if let Some(id) = id.strip_prefix('/') { self.root.join(id) } else if id.starts_with("./") || id.starts_with("../") { let msg = "relative module paths aren't supported yet"; return Err(Diagnostic::span_error(span, msg)); } else { return Ok(ImportModule::RawNamed(self.intern_str(id))); }; // Generate a unique ID which is somewhat readable as well, so mix in // the crate name, hash to make it unique, and then the original path. let new_identifier = format!("{}{}", self.unique_crate_identifier(), id); let file = LocalFile { path, definition: span, new_identifier, linked_module, }; files.insert(id.to_string(), file); drop(files); self.resolve_import_module(id, span, linked_module) } fn unique_crate_identifier(&self) -> String { format!("{}-{}", self.crate_name, ShortHash(0)) } fn check_for_package_json(&self) { if self.has_package_json.get() { return; } let path = self.root.join("package.json"); if path.exists() { self.has_package_json.set(true); } } } fn shared_program<'a>( prog: &'a ast::Program, intern: &'a Interner, ) -> Result, Diagnostic> { Ok(Program { exports: prog .exports .iter() .map(|a| shared_export(a, intern)) .collect::, _>>()?, structs: prog .structs .iter() .map(|a| shared_struct(a, intern)) .collect(), enums: prog.enums.iter().map(|a| shared_enum(a, intern)).collect(), imports: prog .imports .iter() .map(|a| shared_import(a, intern)) .collect::, _>>()?, typescript_custom_sections: prog .typescript_custom_sections .iter() .map(|x| shared_lit_or_expr(x, intern)) .collect(), linked_modules: prog .linked_modules .iter() .enumerate() .map(|(i, a)| shared_linked_module(&prog.link_function_name(i), a, intern)) .collect::, _>>()?, local_modules: intern .files .borrow() .values() .map(|file| { fs::read_to_string(&file.path) .map(|s| LocalModule { identifier: intern.intern_str(&file.new_identifier), contents: intern.intern_str(&s), linked_module: file.linked_module, }) .map_err(|e| { let msg = format!("failed to read file `{}`: {}", file.path.display(), e); Diagnostic::span_error(file.definition, msg) }) }) .collect::, _>>()?, inline_js: prog .inline_js .iter() .map(|js| intern.intern_str(js)) .collect(), unique_crate_identifier: intern.intern_str(&intern.unique_crate_identifier()), package_json: if intern.has_package_json.get() { Some(intern.intern_str(intern.root.join("package.json").to_str().unwrap())) } else { None }, }) } fn shared_export<'a>( export: &'a ast::Export, intern: &'a Interner, ) -> Result, Diagnostic> { let consumed = matches!(export.method_self, Some(ast::MethodSelf::ByValue)); let method_kind = from_ast_method_kind(&export.function, intern, &export.method_kind)?; Ok(Export { class: export.js_class.as_deref(), comments: export.comments.iter().map(|s| &**s).collect(), consumed, function: shared_function(&export.function, intern), method_kind, start: export.start, }) } fn shared_function<'a>(func: &'a ast::Function, _intern: &'a Interner) -> Function<'a> { let args = func.arguments .iter() .enumerate() .map(|(idx, arg)| FunctionArgumentData { // use argument's "js_name" if it was provided via attributes // if not use the original Rust argument ident name: arg.js_name.clone().unwrap_or( if let syn::Pat::Ident(x) = &*arg.pat_type.pat { x.ident.unraw().to_string() } else { format!("arg{}", idx) }, ), ty_override: arg.js_type.as_deref(), desc: arg.desc.as_deref(), }) .collect::>(); Function { args, asyncness: func.r#async, name: &func.name, generate_typescript: func.generate_typescript, generate_jsdoc: func.generate_jsdoc, variadic: func.variadic, ret_ty_override: func.ret.as_ref().and_then(|v| v.js_type.as_deref()), ret_desc: func.ret.as_ref().and_then(|v| v.desc.as_deref()), } } fn shared_enum<'a>(e: &'a ast::Enum, intern: &'a Interner) -> Enum<'a> { Enum { name: &e.js_name, signed: e.signed, variants: e .variants .iter() .map(|v| shared_variant(v, intern)) .collect(), comments: e.comments.iter().map(|s| &**s).collect(), generate_typescript: e.generate_typescript, } } fn shared_variant<'a>(v: &'a ast::Variant, intern: &'a Interner) -> EnumVariant<'a> { EnumVariant { name: intern.intern(&v.name), value: v.value, comments: v.comments.iter().map(|s| &**s).collect(), } } fn shared_import<'a>(i: &'a ast::Import, intern: &'a Interner) -> Result, Diagnostic> { Ok(Import { module: i .module .as_ref() .map(|m| shared_module(m, intern, false)) .transpose()?, js_namespace: i.js_namespace.clone(), kind: shared_import_kind(&i.kind, intern)?, }) } fn shared_lit_or_expr<'a>(i: &'a ast::LitOrExpr, _intern: &'a Interner) -> LitOrExpr<'a> { match i { ast::LitOrExpr::Lit(lit) => LitOrExpr::Lit(lit), ast::LitOrExpr::Expr(expr) => LitOrExpr::Expr(expr), } } fn shared_linked_module<'a>( name: &str, i: &'a ast::ImportModule, intern: &'a Interner, ) -> Result, Diagnostic> { Ok(LinkedModule { module: shared_module(i, intern, true)?, link_function_name: intern.intern_str(name), }) } fn shared_module<'a>( m: &'a ast::ImportModule, intern: &'a Interner, linked_module: bool, ) -> Result, Diagnostic> { Ok(match m { ast::ImportModule::Named(m, span) => { intern.resolve_import_module(m, *span, linked_module)? } ast::ImportModule::RawNamed(m, _span) => ImportModule::RawNamed(intern.intern_str(m)), ast::ImportModule::Inline(idx, _) => ImportModule::Inline(*idx as u32), }) } fn shared_import_kind<'a>( i: &'a ast::ImportKind, intern: &'a Interner, ) -> Result, Diagnostic> { Ok(match i { ast::ImportKind::Function(f) => ImportKind::Function(shared_import_function(f, intern)?), ast::ImportKind::Static(f) => ImportKind::Static(shared_import_static(f, intern)), ast::ImportKind::String(f) => ImportKind::String(shared_import_string(f, intern)), ast::ImportKind::Type(f) => ImportKind::Type(shared_import_type(f, intern)), ast::ImportKind::Enum(f) => ImportKind::Enum(shared_import_enum(f, intern)), }) } fn shared_import_function<'a>( i: &'a ast::ImportFunction, intern: &'a Interner, ) -> Result, Diagnostic> { let method = match &i.kind { ast::ImportFunctionKind::Method { class, kind, .. } => { let kind = from_ast_method_kind(&i.function, intern, kind)?; Some(MethodData { class, kind }) } ast::ImportFunctionKind::Normal => None, }; Ok(ImportFunction { shim: intern.intern(&i.shim), catch: i.catch, method, assert_no_shim: i.assert_no_shim, structural: i.structural, function: shared_function(&i.function, intern), variadic: i.variadic, }) } fn shared_import_static<'a>(i: &'a ast::ImportStatic, intern: &'a Interner) -> ImportStatic<'a> { ImportStatic { name: &i.js_name, shim: intern.intern(&i.shim), } } fn shared_import_string<'a>(i: &'a ast::ImportString, intern: &'a Interner) -> ImportString<'a> { ImportString { shim: intern.intern(&i.shim), string: &i.string, } } fn shared_import_type<'a>(i: &'a ast::ImportType, intern: &'a Interner) -> ImportType<'a> { ImportType { name: &i.js_name, instanceof_shim: &i.instanceof_shim, vendor_prefixes: i.vendor_prefixes.iter().map(|x| intern.intern(x)).collect(), } } fn shared_import_enum<'a>(i: &'a ast::StringEnum, _intern: &'a Interner) -> StringEnum<'a> { StringEnum { name: &i.js_name, generate_typescript: i.generate_typescript, variant_values: i.variant_values.iter().map(|x| &**x).collect(), comments: i.comments.iter().map(|s| &**s).collect(), } } fn shared_struct<'a>(s: &'a ast::Struct, intern: &'a Interner) -> Struct<'a> { Struct { name: &s.js_name, fields: s .fields .iter() .map(|s| shared_struct_field(s, intern)) .collect(), comments: s.comments.iter().map(|s| &**s).collect(), is_inspectable: s.is_inspectable, generate_typescript: s.generate_typescript, } } fn shared_struct_field<'a>(s: &'a ast::StructField, _intern: &'a Interner) -> StructField<'a> { StructField { name: &s.js_name, readonly: s.readonly, comments: s.comments.iter().map(|s| &**s).collect(), generate_typescript: s.generate_typescript, generate_jsdoc: s.generate_jsdoc, } } trait Encode { fn encode(&self, dst: &mut Encoder); } struct Encoder { dst: Vec, } enum LitOrExpr<'a> { Expr(&'a syn::Expr), Lit(&'a str), } impl Encode for LitOrExpr<'_> { fn encode(&self, dst: &mut Encoder) { match self { LitOrExpr::Expr(expr) => { dst.dst.push(EncodeChunk::StrExpr((*expr).clone())); } LitOrExpr::Lit(s) => s.encode(dst), } } } impl Encoder { fn new() -> Encoder { Encoder { dst: vec![] } } fn finish(self) -> Vec { self.dst } fn byte(&mut self, byte: u8) { if let Some(EncodeChunk::EncodedBuf(buf)) = self.dst.last_mut() { buf.push(byte); } else { self.dst.push(EncodeChunk::EncodedBuf(vec![byte])); } } fn extend_from_slice(&mut self, slice: &[u8]) { if let Some(EncodeChunk::EncodedBuf(buf)) = self.dst.last_mut() { buf.extend_from_slice(slice); } else { self.dst.push(EncodeChunk::EncodedBuf(slice.to_owned())); } } } impl Encode for bool { fn encode(&self, dst: &mut Encoder) { dst.byte(*self as u8); } } impl Encode for u32 { fn encode(&self, dst: &mut Encoder) { let mut val = *self; while (val >> 7) != 0 { dst.byte((val as u8) | 0x80); val >>= 7; } assert_eq!(val >> 7, 0); dst.byte(val as u8); } } impl Encode for usize { fn encode(&self, dst: &mut Encoder) { assert!(*self <= u32::MAX as usize); (*self as u32).encode(dst); } } impl Encode for &[u8] { fn encode(&self, dst: &mut Encoder) { self.len().encode(dst); dst.extend_from_slice(self); } } impl Encode for &str { fn encode(&self, dst: &mut Encoder) { self.as_bytes().encode(dst); } } impl Encode for String { fn encode(&self, dst: &mut Encoder) { self.as_bytes().encode(dst); } } impl Encode for Vec { fn encode(&self, dst: &mut Encoder) { self.len().encode(dst); for item in self { item.encode(dst); } } } impl Encode for Option { fn encode(&self, dst: &mut Encoder) { match self { None => dst.byte(0), Some(val) => { dst.byte(1); val.encode(dst) } } } } macro_rules! encode_struct { ($name:ident ($($lt:tt)*) $($field:ident: $ty:ty,)*) => { struct $name $($lt)* { $($field: $ty,)* } impl $($lt)* Encode for $name $($lt)* { fn encode(&self, _dst: &mut Encoder) { $(self.$field.encode(_dst);)* } } } } macro_rules! encode_enum { ($name:ident ($($lt:tt)*) $($fields:tt)*) => ( enum $name $($lt)* { $($fields)* } impl$($lt)* Encode for $name $($lt)* { fn encode(&self, dst: &mut Encoder) { use self::$name::*; encode_enum!(@arms self dst (0) () $($fields)*) } } ); (@arms $me:ident $dst:ident ($cnt:expr) ($($arms:tt)*)) => ( encode_enum!(@expr match $me { $($arms)* }) ); (@arms $me:ident $dst:ident ($cnt:expr) ($($arms:tt)*) $name:ident, $($rest:tt)*) => ( encode_enum!( @arms $me $dst ($cnt+1) ($($arms)* $name => $dst.byte($cnt),) $($rest)* ) ); (@arms $me:ident $dst:ident ($cnt:expr) ($($arms:tt)*) $name:ident($t:ty), $($rest:tt)*) => ( encode_enum!( @arms $me $dst ($cnt+1) ($($arms)* $name(val) => { $dst.byte($cnt); val.encode($dst) }) $($rest)* ) ); (@expr $e:expr) => ($e); } macro_rules! encode_api { () => (); (struct $name:ident<'a> { $($fields:tt)* } $($rest:tt)*) => ( encode_struct!($name (<'a>) $($fields)*); encode_api!($($rest)*); ); (struct $name:ident { $($fields:tt)* } $($rest:tt)*) => ( encode_struct!($name () $($fields)*); encode_api!($($rest)*); ); (enum $name:ident<'a> { $($variants:tt)* } $($rest:tt)*) => ( encode_enum!($name (<'a>) $($variants)*); encode_api!($($rest)*); ); (enum $name:ident { $($variants:tt)* } $($rest:tt)*) => ( encode_enum!($name () $($variants)*); encode_api!($($rest)*); ); } wasm_bindgen_shared::shared_api!(encode_api); fn from_ast_method_kind<'a>( function: &'a ast::Function, intern: &'a Interner, method_kind: &'a ast::MethodKind, ) -> Result, Diagnostic> { Ok(match method_kind { ast::MethodKind::Constructor => MethodKind::Constructor, ast::MethodKind::Operation(ast::Operation { is_static, kind }) => { let is_static = *is_static; let kind = match kind { ast::OperationKind::Getter(g) => { let g = g.as_ref().map(|g| intern.intern_str(g)); OperationKind::Getter(g.unwrap_or_else(|| function.infer_getter_property())) } ast::OperationKind::Regular => OperationKind::Regular, ast::OperationKind::Setter(s) => { let s = s.as_ref().map(|s| intern.intern_str(s)); OperationKind::Setter(match s { Some(s) => s, None => intern.intern_str(&function.infer_setter_property()?), }) } ast::OperationKind::IndexingGetter => OperationKind::IndexingGetter, ast::OperationKind::IndexingSetter => OperationKind::IndexingSetter, ast::OperationKind::IndexingDeleter => OperationKind::IndexingDeleter, }; MethodKind::Operation(Operation { is_static, kind }) } }) } wasm-bindgen-backend-0.2.104/src/error.rs000064400000000000000000000076411046102023000162260ustar 00000000000000use proc_macro2::*; use quote::{ToTokens, TokenStreamExt}; use syn::parse::Error; /// Provide a Diagnostic with the given span and message #[macro_export] macro_rules! err_span { ($span:expr, $($msg:tt)*) => ( $crate::Diagnostic::spanned_error(&$span, format!($($msg)*)) ) } /// Immediately fail and return an Err, with the arguments passed to err_span! #[macro_export] macro_rules! bail_span { ($($t:tt)*) => ( return Err(err_span!($($t)*).into()) ) } /// A struct representing a diagnostic to emit to the end-user as an error. #[derive(Debug)] pub struct Diagnostic { inner: Repr, } #[derive(Debug)] enum Repr { Single { text: String, span: Option<(Span, Span)>, }, SynError(Error), Multi { diagnostics: Vec, }, } impl Diagnostic { /// Generate a `Diagnostic` from an informational message with no Span pub fn error>(text: T) -> Diagnostic { Diagnostic { inner: Repr::Single { text: text.into(), span: None, }, } } /// Generate a `Diagnostic` from a Span and an informational message pub fn span_error>(span: Span, text: T) -> Diagnostic { Diagnostic { inner: Repr::Single { text: text.into(), span: Some((span, span)), }, } } /// Generate a `Diagnostic` from the span of any tokenizable object and a message pub fn spanned_error>(node: &dyn ToTokens, text: T) -> Diagnostic { Diagnostic { inner: Repr::Single { text: text.into(), span: extract_spans(node), }, } } /// Attempt to generate a `Diagnostic` from a vector of other `Diagnostic` instances. /// If the `Vec` is empty, returns `Ok(())`, otherwise returns the new `Diagnostic` pub fn from_vec(diagnostics: Vec) -> Result<(), Diagnostic> { if diagnostics.is_empty() { Ok(()) } else { Err(Diagnostic { inner: Repr::Multi { diagnostics }, }) } } /// Immediately trigger a panic from this `Diagnostic` #[allow(unconditional_recursion)] pub fn panic(&self) -> ! { match &self.inner { Repr::Single { text, .. } => panic!("{}", text), Repr::SynError(error) => panic!("{}", error), Repr::Multi { diagnostics } => diagnostics[0].panic(), } } } impl From for Diagnostic { fn from(err: Error) -> Diagnostic { Diagnostic { inner: Repr::SynError(err), } } } fn extract_spans(node: &dyn ToTokens) -> Option<(Span, Span)> { let mut t = TokenStream::new(); node.to_tokens(&mut t); let mut tokens = t.into_iter(); let start = tokens.next().map(|t| t.span()); let end = tokens.last().map(|t| t.span()); start.map(|start| (start, end.unwrap_or(start))) } impl ToTokens for Diagnostic { fn to_tokens(&self, dst: &mut TokenStream) { match &self.inner { Repr::Single { text, span } => { let cs2 = (Span::call_site(), Span::call_site()); let (start, end) = span.unwrap_or(cs2); dst.append(Ident::new("compile_error", start)); dst.append(Punct::new('!', Spacing::Alone)); let mut message = TokenStream::new(); message.append(Literal::string(text)); let mut group = Group::new(Delimiter::Brace, message); group.set_span(end); dst.append(group); } Repr::Multi { diagnostics } => { for diagnostic in diagnostics { diagnostic.to_tokens(dst); } } Repr::SynError(err) => { err.to_compile_error().to_tokens(dst); } } } } wasm-bindgen-backend-0.2.104/src/lib.rs000064400000000000000000000021641046102023000156360ustar 00000000000000//! A common backend for bindgen crates. //! //! This (internal) crate provides functionality common to multiple bindgen //! dependency crates. There are 4 main things exported from this crate: //! //! 1. [**`TryToTokens`**](./trait.TryToTokens.html) //! //! Provides the ability to attempt conversion from an AST struct //! into a TokenStream //! //! 2. [**`Diagnostic`**](./struct.Diagnostic.html) //! //! A struct used to provide diagnostic responses for failures of said //! tokenization //! //! 3. [**`ast`**](./ast/index.html) //! //! Abstract Syntax Tree types used to represent a Rust program, with //! the necessary metadata to generate bindings for it //! //! 4. [**`util`**](./util/index.html) //! //! Common utilities for manipulating parsed types from syn //! #![recursion_limit = "256"] #![cfg_attr(feature = "extra-traits", deny(missing_debug_implementations))] #![deny(missing_docs)] #![doc(html_root_url = "https://docs.rs/wasm-bindgen-backend/0.2")] pub use crate::codegen::TryToTokens; pub use crate::error::Diagnostic; #[macro_use] mod error; pub mod ast; mod codegen; mod encode; pub mod util; wasm-bindgen-backend-0.2.104/src/util.rs000064400000000000000000000126511046102023000160470ustar 00000000000000//! Common utility function for manipulating syn types and //! handling parsed values use std::collections::hash_map::DefaultHasher; use std::env; use std::fmt; use std::hash::{Hash, Hasher}; use std::iter::FromIterator; use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::SeqCst; use crate::ast; use proc_macro2::{self, Ident}; /// Check whether a given `&str` is a Rust keyword #[rustfmt::skip] fn is_rust_keyword(name: &str) -> bool { matches!(name, "abstract" | "alignof" | "as" | "become" | "box" | "break" | "const" | "continue" | "crate" | "do" | "else" | "enum" | "extern" | "false" | "final" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut" | "offsetof" | "override" | "priv" | "proc" | "pub" | "pure" | "ref" | "return" | "Self" | "self" | "sizeof" | "static" | "struct" | "super" | "trait" | "true" | "type" | "typeof" | "unsafe" | "unsized" | "use" | "virtual" | "where" | "while" | "yield" | "bool" | "_" ) } /// Create an `Ident`, possibly mangling it if it conflicts with a Rust keyword. pub fn rust_ident(name: &str) -> Ident { if name.is_empty() { panic!("tried to create empty Ident (from \"\")"); } else if is_rust_keyword(name) { Ident::new(&format!("{}_", name), proc_macro2::Span::call_site()) // we didn't historically have `async` in the `is_rust_keyword` list above, // so for backwards compatibility reasons we need to generate an `async` // identifier as well, but we'll be sure to use a raw identifier to ease // compatibility with the 2018 edition. // // Note, though, that `proc-macro` doesn't support a normal way to create a // raw identifier. To get around that we do some wonky parsing to // roundaboutly create one. } else if name == "async" { let ident = "r#async" .parse::() .unwrap() .into_iter() .next() .unwrap(); match ident { proc_macro2::TokenTree::Ident(i) => i, _ => unreachable!(), } } else if name.chars().next().unwrap().is_ascii_digit() { Ident::new(&format!("N{}", name), proc_macro2::Span::call_site()) } else { raw_ident(name) } } /// Create an `Ident` without checking to see if it conflicts with a Rust /// keyword. pub fn raw_ident(name: &str) -> Ident { Ident::new(name, proc_macro2::Span::call_site()) } /// Create a path type from the given segments. For example an iterator yielding /// the idents `[foo, bar, baz]` will result in the path type `foo::bar::baz`. pub fn simple_path_ty(segments: I) -> syn::Type where I: IntoIterator, { path_ty(false, segments) } /// Create a global path type from the given segments. For example an iterator /// yielding the idents `[foo, bar, baz]` will result in the path type /// `::foo::bar::baz`. pub fn leading_colon_path_ty(segments: I) -> syn::Type where I: IntoIterator, { path_ty(true, segments) } fn path_ty(leading_colon: bool, segments: I) -> syn::Type where I: IntoIterator, { let segments: Vec<_> = segments .into_iter() .map(|i| syn::PathSegment { ident: i, arguments: syn::PathArguments::None, }) .collect(); syn::TypePath { qself: None, path: syn::Path { leading_colon: if leading_colon { Some(Default::default()) } else { None }, segments: syn::punctuated::Punctuated::from_iter(segments), }, } .into() } /// Create a path type with a single segment from a given Identifier pub fn ident_ty(ident: Ident) -> syn::Type { simple_path_ty(Some(ident)) } /// Convert an ImportFunction into the more generic Import type, wrapping the provided function pub fn wrap_import_function(function: ast::ImportFunction) -> ast::Import { ast::Import { module: None, js_namespace: None, kind: ast::ImportKind::Function(function), } } /// Small utility used when generating symbol names. /// /// Hashes the public field here along with a few cargo-set env vars to /// distinguish between runs of the procedural macro. #[derive(Debug)] pub struct ShortHash(pub T); impl fmt::Display for ShortHash { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { static HASHED: AtomicBool = AtomicBool::new(false); static HASH: AtomicUsize = AtomicUsize::new(0); // Try to amortize the cost of loading env vars a lot as we're gonna be // hashing for a lot of symbols. if !HASHED.load(SeqCst) { let mut h = DefaultHasher::new(); env::var("CARGO_PKG_NAME") .expect("should have CARGO_PKG_NAME env var") .hash(&mut h); env::var("CARGO_PKG_VERSION") .expect("should have CARGO_PKG_VERSION env var") .hash(&mut h); // This may chop off 32 bits on 32-bit platforms, but that's ok, we // just want something to mix in below anyway. HASH.store(h.finish() as usize, SeqCst); HASHED.store(true, SeqCst); } let mut h = DefaultHasher::new(); HASH.load(SeqCst).hash(&mut h); self.0.hash(&mut h); write!(f, "{:016x}", h.finish()) } }