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