Import Upstream version 0.2.83
This commit is contained in:
commit
69d701d869
|
@ -0,0 +1,49 @@
|
|||
# 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 = "2018"
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.83"
|
||||
authors = ["The wasm-bindgen Developers"]
|
||||
description = """
|
||||
Backend code generation of the wasm-bindgen tool
|
||||
"""
|
||||
homepage = "https://rustwasm.github.io/wasm-bindgen/"
|
||||
documentation = "https://docs.rs/wasm-bindgen-backend"
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/backend"
|
||||
|
||||
[dependencies.bumpalo]
|
||||
version = "3.0.0"
|
||||
|
||||
[dependencies.log]
|
||||
version = "0.4"
|
||||
|
||||
[dependencies.once_cell]
|
||||
version = "1.12"
|
||||
|
||||
[dependencies.proc-macro2]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.quote]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "1.0"
|
||||
features = ["full"]
|
||||
|
||||
[dependencies.wasm-bindgen-shared]
|
||||
version = "=0.2.83"
|
||||
|
||||
[features]
|
||||
extra-traits = ["syn/extra-traits"]
|
||||
spans = []
|
|
@ -0,0 +1,25 @@
|
|||
[package]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.83"
|
||||
authors = ["The wasm-bindgen Developers"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/backend"
|
||||
homepage = "https://rustwasm.github.io/wasm-bindgen/"
|
||||
documentation = "https://docs.rs/wasm-bindgen-backend"
|
||||
description = """
|
||||
Backend code generation of the wasm-bindgen tool
|
||||
"""
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
spans = []
|
||||
extra-traits = ["syn/extra-traits"]
|
||||
|
||||
[dependencies]
|
||||
bumpalo = "3.0.0"
|
||||
log = "0.4"
|
||||
once_cell = "1.12"
|
||||
proc-macro2 = "1.0"
|
||||
quote = '1.0'
|
||||
syn = { version = '1.0', features = ['full'] }
|
||||
wasm-bindgen-shared = { path = "../shared", version = "=0.2.83" }
|
|
@ -0,0 +1,201 @@
|
|||
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.
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (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.
|
|
@ -0,0 +1,453 @@
|
|||
//! 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::Diagnostic;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use std::hash::{Hash, Hasher};
|
||||
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(Default, Clone)]
|
||||
pub struct Program {
|
||||
/// rust -> js interfaces
|
||||
pub exports: Vec<Export>,
|
||||
/// js -> rust interfaces
|
||||
pub imports: Vec<Import>,
|
||||
/// rust enums
|
||||
pub enums: Vec<Enum>,
|
||||
/// rust structs
|
||||
pub structs: Vec<Struct>,
|
||||
/// custom typescript sections to be included in the definition file
|
||||
pub typescript_custom_sections: Vec<String>,
|
||||
/// Inline JS snippets
|
||||
pub inline_js: Vec<String>,
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<String>,
|
||||
/// The rust function
|
||||
pub function: Function,
|
||||
/// The class name in JS this is attached to
|
||||
pub js_class: Option<String>,
|
||||
/// The kind (static, named, regular)
|
||||
pub method_kind: MethodKind,
|
||||
/// The type of `self` (either `self`, `&self`, or `&mut self`)
|
||||
pub method_self: Option<MethodSelf>,
|
||||
/// The struct name, in Rust, this is attached to
|
||||
pub rust_class: Option<Ident>,
|
||||
/// 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,
|
||||
}
|
||||
|
||||
/// The 3 types variations of `self`.
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(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<ImportModule>,
|
||||
/// The namespace to access the item through, if any
|
||||
pub js_namespace: Option<Vec<String>>,
|
||||
/// 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<H: Hasher>(&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 type/class
|
||||
Type(ImportType),
|
||||
/// Importing a JS enum
|
||||
Enum(ImportEnum),
|
||||
}
|
||||
|
||||
/// 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<syn::Type>,
|
||||
/// 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,
|
||||
}
|
||||
|
||||
/// 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
|
||||
Getter(Option<Ident>),
|
||||
/// A method for setting the value of the provided Ident
|
||||
Setter(Option<Ident>),
|
||||
/// 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,
|
||||
}
|
||||
|
||||
/// 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<syn::Attribute>,
|
||||
/// The TS definition to generate for this type
|
||||
pub typescript_type: Option<String>,
|
||||
/// The doc comment applied to this type, if one exists
|
||||
pub doc_comment: Option<String>,
|
||||
/// 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<syn::Expr>,
|
||||
/// The list of classes this extends, if any
|
||||
pub extends: Vec<syn::Path>,
|
||||
/// A custom prefix to add and attempt to fall back to, if the type isn't found
|
||||
pub vendor_prefixes: Vec<Ident>,
|
||||
/// If present, don't generate a `Deref` impl
|
||||
pub no_deref: bool,
|
||||
}
|
||||
|
||||
/// The metadata for an Enum being imported
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub struct ImportEnum {
|
||||
/// The Rust enum's visibility
|
||||
pub vis: syn::Visibility,
|
||||
/// The Rust enum's identifiers
|
||||
pub name: Ident,
|
||||
/// The Rust identifiers for the variants
|
||||
pub variants: Vec<Ident>,
|
||||
/// The JS string values of the variants
|
||||
pub variant_values: Vec<String>,
|
||||
/// Attributes to apply to the Rust enum
|
||||
pub rust_attrs: Vec<syn::Attribute>,
|
||||
}
|
||||
|
||||
/// 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<syn::PatType>,
|
||||
/// The return type of the function, if provided
|
||||
pub ret: Option<syn::Type>,
|
||||
/// Any custom attributes being applied to the function
|
||||
pub rust_attrs: Vec<syn::Attribute>,
|
||||
/// The visibility of this function in Rust
|
||||
pub rust_vis: syn::Visibility,
|
||||
/// Whether this is an `async` function
|
||||
pub r#async: bool,
|
||||
/// Whether to generate a typescript definition for this function
|
||||
pub generate_typescript: bool,
|
||||
/// Whether this is a function with a variadict parameter
|
||||
pub variadic: bool,
|
||||
}
|
||||
|
||||
/// Information about a Struct being exported
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[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<StructField>,
|
||||
/// The doc comments on this struct, if provided
|
||||
pub comments: Vec<String>,
|
||||
/// 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,
|
||||
}
|
||||
|
||||
/// The field of a struct
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[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<String>,
|
||||
/// Whether to generate a typescript definition for this field
|
||||
pub generate_typescript: bool,
|
||||
/// Whether to use .clone() in the auto-generated getter for this field
|
||||
pub getter_with_clone: bool,
|
||||
}
|
||||
|
||||
/// Information about an Enum being exported
|
||||
#[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,
|
||||
/// The variants provided by this enum
|
||||
pub variants: Vec<Variant>,
|
||||
/// The doc comments on this enum, if any
|
||||
pub comments: Vec<String>,
|
||||
/// 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,
|
||||
}
|
||||
|
||||
/// 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<String>,
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}
|
||||
|
||||
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_str("_");
|
||||
generated_name.push_str(class);
|
||||
}
|
||||
generated_name.push_str("_");
|
||||
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::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<String, Diagnostic> {
|
||||
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())
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,536 @@
|
|||
use 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 crate::ast;
|
||||
use crate::Diagnostic;
|
||||
|
||||
pub struct EncodeResult {
|
||||
pub custom_section: Vec<u8>,
|
||||
pub included_files: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
pub fn encode(program: &ast::Program) -> Result<EncodeResult, Diagnostic> {
|
||||
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<HashMap<String, LocalFile>>,
|
||||
root: PathBuf,
|
||||
crate_name: String,
|
||||
has_package_json: Cell<bool>,
|
||||
}
|
||||
|
||||
struct LocalFile {
|
||||
path: PathBuf,
|
||||
definition: Span,
|
||||
new_identifier: String,
|
||||
}
|
||||
|
||||
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) -> Result<ImportModule, 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 id.starts_with("/") {
|
||||
self.root.join(&id[1..])
|
||||
} 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,
|
||||
};
|
||||
files.insert(id.to_string(), file);
|
||||
drop(files);
|
||||
self.resolve_import_module(id, span)
|
||||
}
|
||||
|
||||
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<Program<'a>, Diagnostic> {
|
||||
Ok(Program {
|
||||
exports: prog
|
||||
.exports
|
||||
.iter()
|
||||
.map(|a| shared_export(a, intern))
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
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::<Result<Vec<_>, _>>()?,
|
||||
typescript_custom_sections: prog
|
||||
.typescript_custom_sections
|
||||
.iter()
|
||||
.map(|x| -> &'a str { &x })
|
||||
.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),
|
||||
})
|
||||
.map_err(|e| {
|
||||
let msg = format!("failed to read file `{}`: {}", file.path.display(), e);
|
||||
Diagnostic::span_error(file.definition, msg)
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
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<Export<'a>, Diagnostic> {
|
||||
let consumed = match export.method_self {
|
||||
Some(ast::MethodSelf::ByValue) => true,
|
||||
_ => false,
|
||||
};
|
||||
let method_kind = from_ast_method_kind(&export.function, intern, &export.method_kind)?;
|
||||
Ok(Export {
|
||||
class: export.js_class.as_ref().map(|s| &**s),
|
||||
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 arg_names = func
|
||||
.arguments
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, arg)| {
|
||||
if let syn::Pat::Ident(x) = &*arg.pat {
|
||||
return x.ident.to_string();
|
||||
}
|
||||
format!("arg{}", idx)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
Function {
|
||||
arg_names,
|
||||
asyncness: func.r#async,
|
||||
name: &func.name,
|
||||
generate_typescript: func.generate_typescript,
|
||||
variadic: func.variadic,
|
||||
}
|
||||
}
|
||||
|
||||
fn shared_enum<'a>(e: &'a ast::Enum, intern: &'a Interner) -> Enum<'a> {
|
||||
Enum {
|
||||
name: &e.js_name,
|
||||
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<Import<'a>, Diagnostic> {
|
||||
Ok(Import {
|
||||
module: i
|
||||
.module
|
||||
.as_ref()
|
||||
.map(|m| shared_module(m, intern))
|
||||
.transpose()?,
|
||||
js_namespace: i.js_namespace.clone(),
|
||||
kind: shared_import_kind(&i.kind, intern)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn shared_module<'a>(
|
||||
m: &'a ast::ImportModule,
|
||||
intern: &'a Interner,
|
||||
) -> Result<ImportModule<'a>, Diagnostic> {
|
||||
Ok(match m {
|
||||
ast::ImportModule::Named(m, span) => intern.resolve_import_module(m, *span)?,
|
||||
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<ImportKind<'a>, 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::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<ImportFunction<'a>, 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_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::ImportEnum, _intern: &'a Interner) -> ImportEnum {
|
||||
ImportEnum {}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
trait Encode {
|
||||
fn encode(&self, dst: &mut Encoder);
|
||||
}
|
||||
|
||||
struct Encoder {
|
||||
dst: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Encoder {
|
||||
fn new() -> Encoder {
|
||||
Encoder {
|
||||
dst: vec![0, 0, 0, 0],
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(mut self) -> Vec<u8> {
|
||||
let len = (self.dst.len() - 4) as u32;
|
||||
self.dst[..4].copy_from_slice(&len.to_le_bytes()[..]);
|
||||
self.dst
|
||||
}
|
||||
|
||||
fn byte(&mut self, byte: u8) {
|
||||
self.dst.push(byte);
|
||||
}
|
||||
}
|
||||
|
||||
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_value() as usize);
|
||||
(*self as u32).encode(dst);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for &'a [u8] {
|
||||
fn encode(&self, dst: &mut Encoder) {
|
||||
self.len().encode(dst);
|
||||
dst.dst.extend_from_slice(*self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for &'a str {
|
||||
fn encode(&self, dst: &mut Encoder) {
|
||||
self.as_bytes().encode(dst);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encode for String {
|
||||
fn encode(&self, dst: &mut Encoder) {
|
||||
self.as_bytes().encode(dst);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode> Encode for Vec<T> {
|
||||
fn encode(&self, dst: &mut Encoder) {
|
||||
self.len().encode(dst);
|
||||
for item in self {
|
||||
item.encode(dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode> Encode for Option<T> {
|
||||
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<MethodKind<'a>, 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(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(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 })
|
||||
}
|
||||
})
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
use 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<Diagnostic>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Diagnostic {
|
||||
/// Generate a `Diagnostic` from an informational message with no Span
|
||||
pub fn error<T: Into<String>>(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<T: Into<String>>(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<T: Into<String>>(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<Diagnostic>) -> Result<(), Diagnostic> {
|
||||
if diagnostics.len() == 0 {
|
||||
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<Error> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
//! 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;
|
|
@ -0,0 +1,161 @@
|
|||
//! 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
|
||||
fn is_rust_keyword(name: &str) -> bool {
|
||||
match 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" | "_" => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an `Ident`, possibly mangling it if it conflicts with a Rust keyword.
|
||||
pub fn rust_ident(name: &str) -> Ident {
|
||||
if name == "" {
|
||||
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::<proc_macro2::TokenStream>()
|
||||
.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<I>(segments: I) -> syn::Type
|
||||
where
|
||||
I: IntoIterator<Item = Ident>,
|
||||
{
|
||||
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<I>(segments: I) -> syn::Type
|
||||
where
|
||||
I: IntoIterator<Item = Ident>,
|
||||
{
|
||||
path_ty(true, segments)
|
||||
}
|
||||
|
||||
fn path_ty<I>(leading_colon: bool, segments: I) -> syn::Type
|
||||
where
|
||||
I: IntoIterator<Item = Ident>,
|
||||
{
|
||||
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<T>(pub T);
|
||||
|
||||
impl<T: Hash> fmt::Display for ShortHash<T> {
|
||||
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())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue