Import Upstream version 0.2.83

This commit is contained in:
su-fang 2023-03-08 10:18:12 +08:00
commit 69d701d869
10 changed files with 3013 additions and 0 deletions

49
Cargo.toml Normal file
View File

@ -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 = []

25
Cargo.toml.orig Normal file
View File

@ -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" }

201
LICENSE-APACHE Normal file
View File

@ -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.

25
LICENSE-MIT Normal file
View File

@ -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.

453
src/ast.rs Normal file
View File

@ -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())
}
}

1389
src/codegen.rs Normal file

File diff suppressed because it is too large Load Diff

536
src/encode.rs Normal file
View File

@ -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 })
}
})
}

134
src/error.rs Normal file
View File

@ -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);
}
}
}
}

40
src/lib.rs Normal file
View File

@ -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;

161
src/util.rs Normal file
View File

@ -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())
}
}