Don't use JSON for custom section format

This commit migrates away from using Serde for the custom section in
wasm executables. This is a refactoring of a purely-internal data
structure to `wasm-bindgen` and should have no visible functional change
on users.

The motivation for this commit is two fold:

* First, the compile times using `serde_json` and `serde_derive` for the
  syntax extension isn't the most fun.
* Second, eventually we're going to want to stablize the layout of the
  custom section, and it's highly unlikely to be json!

Primarily, though, the intention of this commit is to improve the
cold-cache compile time of `wasm-bindgen` by ensuring that for new users
this project builds as quickly as possible. By removing some heavyweight
dependencies from the procedural macro, `serde`, `serde_derive`, and
`serde_json`, we're able to get a pretty nice build time improvement for
the `wasm-bindgen` crate itself:

|             | single-core build | parallel build |
|-------------|-------------------|----------------|
| master      |             36.5s |          17.3s |
| this commit |             20.5s |          11.8s |

These are't really end-all-be-all wins but they're much better
especially on the spectrum of weaker CPUs (in theory modeled by the
single-core case showing we have 42% less CPU work in theory).
This commit is contained in:
Alex Crichton 2018-08-26 15:43:33 -07:00
parent aac8696d05
commit f749c7cf95
12 changed files with 752 additions and 385 deletions

View File

@ -19,6 +19,5 @@ lazy_static = "1.0.0"
log = "0.4" log = "0.4"
proc-macro2 = "0.4.8" proc-macro2 = "0.4.8"
quote = '0.6' quote = '0.6'
serde_json = "1.0" syn = { version = '0.15', features = ['full'] }
syn = { version = '0.15', features = ['full', 'visit'] }
wasm-bindgen-shared = { path = "../shared", version = "=0.2.25" } wasm-bindgen-shared = { path = "../shared", version = "=0.2.25" }

View File

@ -1,9 +1,8 @@
use Diagnostic;
use proc_macro2::{Ident, Span}; use proc_macro2::{Ident, Span};
use shared; use shared;
use syn; use syn;
use Diagnostic;
/// An abstract syntax tree representing a rust program. Contains /// An abstract syntax tree representing a rust program. Contains
/// extra information for joining up this rust code with javascript. /// extra information for joining up this rust code with javascript.
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq))] #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq))]
@ -260,36 +259,6 @@ pub struct DictionaryField {
pub ty: syn::Type, pub ty: syn::Type,
} }
impl Program {
pub(crate) fn shared(&self) -> Result<shared::Program, Diagnostic> {
let mut errors = Vec::new();
let mut imports = Vec::new();
for import in self.imports.iter() {
match import.shared() {
Ok(i) => imports.push(i),
Err(e) => errors.push(e),
}
}
Diagnostic::from_vec(errors)?;
Ok(shared::Program {
exports: self.exports.iter().map(|a| a.shared()).collect(),
structs: self.structs.iter().map(|a| a.shared()).collect(),
enums: self.enums.iter().map(|a| a.shared()).collect(),
imports,
version: shared::version(),
schema_version: shared::SCHEMA_VERSION.to_string(),
})
}
}
impl Function {
fn shared(&self) -> shared::Function {
shared::Function {
name: self.name.to_string(),
}
}
}
impl Export { impl Export {
/// Mangles a rust -> javascript export, so that the created Ident will be unique over function /// 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. /// name and class name, if the function belongs to a javascript class.
@ -314,51 +283,6 @@ impl Export {
None => shared::free_function_export_name(&fn_name), None => shared::free_function_export_name(&fn_name),
} }
} }
fn shared(&self) -> shared::Export {
let (method, consumed) = match self.method_self {
Some(MethodSelf::ByValue) => (true, true),
Some(_) => (true, false),
None => (false, false),
};
shared::Export {
class: self.class.as_ref().map(|s| s.to_string()),
method,
consumed,
is_constructor: self.is_constructor,
function: self.function.shared(),
comments: self.comments.clone(),
}
}
}
impl Enum {
fn shared(&self) -> shared::Enum {
shared::Enum {
name: self.name.to_string(),
variants: self.variants.iter().map(|v| v.shared()).collect(),
comments: self.comments.clone(),
}
}
}
impl Variant {
fn shared(&self) -> shared::EnumVariant {
shared::EnumVariant {
name: self.name.to_string(),
value: self.value,
}
}
}
impl Import {
fn shared(&self) -> Result<shared::Import, Diagnostic> {
Ok(shared::Import {
module: self.module.clone(),
js_namespace: self.js_namespace.as_ref().map(|s| s.to_string()),
kind: self.kind.shared()?,
})
}
} }
impl ImportKind { impl ImportKind {
@ -371,27 +295,18 @@ impl ImportKind {
ImportKind::Enum(_) => false, ImportKind::Enum(_) => false,
} }
} }
fn shared(&self) -> Result<shared::ImportKind, Diagnostic> {
Ok(match *self {
ImportKind::Function(ref f) => shared::ImportKind::Function(f.shared()?),
ImportKind::Static(ref f) => shared::ImportKind::Static(f.shared()),
ImportKind::Type(ref f) => shared::ImportKind::Type(f.shared()),
ImportKind::Enum(ref f) => shared::ImportKind::Enum(f.shared()),
})
}
} }
impl ImportFunction { impl ImportFunction {
/// If the rust object has a `fn xxx(&self) -> MyType` method, get the name for a getter in /// 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`) /// javascript (in this case `xxx`, so you can write `val = obj.xxx`)
fn infer_getter_property(&self) -> String { pub fn infer_getter_property(&self) -> &str {
self.function.name.to_string() &self.function.name
} }
/// If the rust object has a `fn set_xxx(&mut self, MyType)` style method, get the 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`) /// for a setter in javascript (in this case `xxx`, so you can write `obj.xxx = val`)
fn infer_setter_property(&self) -> Result<String, Diagnostic> { pub fn infer_setter_property(&self) -> Result<String, Diagnostic> {
let name = self.function.name.to_string(); let name = self.function.name.to_string();
// if `#[wasm_bindgen(js_name = "...")]` is used then that explicitly // if `#[wasm_bindgen(js_name = "...")]` is used then that explicitly
@ -410,102 +325,4 @@ impl ImportFunction {
} }
Ok(name[4..].to_string()) Ok(name[4..].to_string())
} }
fn shared(&self) -> Result<shared::ImportFunction, Diagnostic> {
let shared_operation = |operation: &Operation| -> Result<_, Diagnostic> {
let is_static = operation.is_static;
let kind = match &operation.kind {
OperationKind::Regular => shared::OperationKind::Regular,
OperationKind::Getter(g) => {
let g = g.as_ref().map(|g| g.to_string());
shared::OperationKind::Getter(g.unwrap_or_else(|| self.infer_getter_property()))
}
OperationKind::Setter(s) => {
let s = s.as_ref().map(|s| s.to_string());
shared::OperationKind::Setter(match s {
Some(s) => s,
None => self.infer_setter_property()?,
})
}
OperationKind::IndexingGetter => shared::OperationKind::IndexingGetter,
OperationKind::IndexingSetter => shared::OperationKind::IndexingSetter,
OperationKind::IndexingDeleter => shared::OperationKind::IndexingDeleter,
};
Ok(shared::Operation { is_static, kind })
};
let method = match self.kind {
ImportFunctionKind::Method {
ref class,
ref kind,
..
} => {
let kind = match kind {
MethodKind::Constructor => shared::MethodKind::Constructor,
MethodKind::Operation(op) => {
shared::MethodKind::Operation(shared_operation(op)?)
}
};
Some(shared::MethodData {
class: class.clone(),
kind,
})
}
ImportFunctionKind::Normal => None,
};
Ok(shared::ImportFunction {
shim: self.shim.to_string(),
catch: self.catch,
variadic: self.variadic,
method,
structural: self.structural,
function: self.function.shared(),
})
}
}
impl ImportStatic {
fn shared(&self) -> shared::ImportStatic {
shared::ImportStatic {
name: self.js_name.to_string(),
shim: self.shim.to_string(),
}
}
}
impl ImportType {
fn shared(&self) -> shared::ImportType {
shared::ImportType {
name: self.js_name.clone(),
instanceof_shim: self.instanceof_shim.clone(),
vendor_prefixes: self.vendor_prefixes.iter().map(|s| s.to_string()).collect(),
}
}
}
impl ImportEnum {
fn shared(&self) -> shared::ImportEnum {
shared::ImportEnum {}
}
}
impl Struct {
fn shared(&self) -> shared::Struct {
shared::Struct {
name: self.name.to_string(),
fields: self.fields.iter().map(|s| s.shared()).collect(),
comments: self.comments.clone(),
}
}
}
impl StructField {
fn shared(&self) -> shared::StructField {
shared::StructField {
name: self.name.to_string(),
readonly: self.readonly,
comments: self.comments.clone(),
}
}
} }

View File

@ -1,14 +1,14 @@
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use std::sync::Mutex; use std::sync::Mutex;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use proc_macro2::{Ident, Literal, Span, TokenStream}; use proc_macro2::{Ident, Literal, Span, TokenStream};
use quote::ToTokens; use quote::ToTokens;
use serde_json;
use shared; use shared;
use syn; use syn;
use ast; use ast;
use encode;
use util::ShortHash; use util::ShortHash;
use Diagnostic; use Diagnostic;
@ -87,20 +87,20 @@ impl TryToTokens for ast::Program {
); );
let generated_static_name = Ident::new(&generated_static_name, Span::call_site()); let generated_static_name = Ident::new(&generated_static_name, Span::call_site());
let description = serde_json::to_string(&self.shared()?).unwrap(); // See comments in `crates/cli-support/src/lib.rs` about what this
// `schema_version` is.
let prefix_json = format!(r#"{{"schema_version":"{}","version":"{}"}}"#,
shared::SCHEMA_VERSION,
shared::version());
let mut bytes = Vec::new();
bytes.push((prefix_json.len() >> 0) as u8);
bytes.push((prefix_json.len() >> 8) as u8);
bytes.push((prefix_json.len() >> 16) as u8);
bytes.push((prefix_json.len() >> 24) as u8);
bytes.extend_from_slice(prefix_json.as_bytes());
bytes.extend_from_slice(&encode::encode(self)?);
// Each JSON blob is prepended with the length of the JSON blob so when let generated_static_length = bytes.len();
// all these sections are concatenated in the final wasm file we know
// how to extract all the JSON pieces, so insert the byte length here.
// The value is little-endian.
let generated_static_length = description.len() + 4;
let mut bytes = vec![
(description.len() >> 0) as u8,
(description.len() >> 8) as u8,
(description.len() >> 16) as u8,
(description.len() >> 24) as u8,
];
bytes.extend_from_slice(description.as_bytes());
let generated_static_value = syn::LitByteStr::new(&bytes, Span::call_site()); let generated_static_value = syn::LitByteStr::new(&bytes, Span::call_site());
(quote! { (quote! {

View File

@ -0,0 +1,368 @@
use std::cell::RefCell;
use std::collections::HashMap;
use proc_macro2::{Ident, Span};
use Diagnostic;
use ast;
pub fn encode(program: &ast::Program) -> Result<Vec<u8>, Diagnostic> {
let mut e = Encoder::new();
let i = Interner::new();
shared_program(program, &i)?.encode(&mut e);
Ok(e.finish())
}
struct Interner {
map: RefCell<HashMap<Ident, String>>,
}
impl Interner {
fn new() -> Interner {
Interner { map: RefCell::new(HashMap::new()) }
}
fn intern(&self, s: &Ident) -> &str {
let mut map = self.map.borrow_mut();
if let Some(s) = map.get(s) {
return unsafe { &*(&**s as *const str) }
}
map.insert(s.clone(), s.to_string());
unsafe { &*(&*map[s] as *const str) }
}
fn intern_str(&self, s: &str) -> &str {
self.intern(&Ident::new(s, Span::call_site()))
}
}
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(),
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<_>, _>>()?,
// version: shared::version(),
// schema_version: shared::SCHEMA_VERSION.to_string(),
})
}
fn shared_export<'a>(export: &'a ast::Export, intern: &'a Interner) -> Export<'a> {
let (method, consumed) = match export.method_self {
Some(ast::MethodSelf::ByValue) => (true, true),
Some(_) => (true, false),
None => (false, false),
};
Export {
class: export.class.as_ref().map(|s| intern.intern(s)),
method,
consumed,
is_constructor: export.is_constructor,
function: shared_function(&export.function, intern),
comments: export.comments.iter().map(|s| &**s).collect(),
}
}
fn shared_function<'a>(func: &'a ast::Function, _intern: &'a Interner) -> Function<'a> {
Function {
name: &func.name,
}
}
fn shared_enum<'a>(e: &'a ast::Enum, intern: &'a Interner) -> Enum<'a> {
Enum {
name: intern.intern(&e.name),
variants: e.variants.iter().map(|v| shared_variant(v, intern)).collect(),
comments: e.comments.iter().map(|s| &**s).collect(),
}
}
fn shared_variant<'a>(v: &'a ast::Variant, intern: &'a Interner) -> EnumVariant<'a> {
EnumVariant {
name: intern.intern(&v.name),
value: v.value,
}
}
fn shared_import<'a>(i: &'a ast::Import, intern: &'a Interner)
-> Result<Import<'a>, Diagnostic>
{
Ok(Import {
module: i.module.as_ref().map(|s| &**s),
js_namespace: i.js_namespace.as_ref().map(|s| intern.intern(s)),
kind: shared_import_kind(&i.kind, intern)?,
})
}
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 = match 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::Regular => OperationKind::Regular,
ast::OperationKind::Getter(g) => {
let g = g.as_ref().map(|g| intern.intern(g));
OperationKind::Getter(
g.unwrap_or_else(|| i.infer_getter_property()),
)
}
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(&i.infer_setter_property()?),
})
}
ast::OperationKind::IndexingGetter => OperationKind::IndexingGetter,
ast::OperationKind::IndexingSetter => OperationKind::IndexingSetter,
ast::OperationKind::IndexingDeleter => OperationKind::IndexingDeleter,
};
MethodKind::Operation(Operation { is_static, kind })
}
};
Some(MethodData {
class,
kind,
})
}
ast::ImportFunctionKind::Normal => None,
};
Ok(ImportFunction {
shim: intern.intern(&i.shim),
catch: i.catch,
method,
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: intern.intern(&s.name),
fields: s.fields.iter().map(|s| shared_struct_field(s, intern)).collect(),
comments: s.comments.iter().map(|s| &**s).collect(),
}
}
fn shared_struct_field<'a>(s: &'a ast::StructField, intern: &'a Interner)
-> StructField<'a>
{
StructField {
name: intern.intern(&s.name),
readonly: s.readonly,
comments: s.comments.iter().map(|s| &**s).collect(),
}
}
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;
self.dst[0] = (len >> 0) as u8;
self.dst[1] = (len >> 8) as u8;
self.dst[2] = (len >> 16) as u8;
self.dst[3] = (len >> 24) as u8;
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<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)*);
);
}
shared_api!(encode_api);

View File

@ -5,16 +5,16 @@
)] )]
#![doc(html_root_url = "https://docs.rs/wasm-bindgen-backend/0.2")] #![doc(html_root_url = "https://docs.rs/wasm-bindgen-backend/0.2")]
#[macro_use]
extern crate lazy_static;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate proc_macro2; extern crate proc_macro2;
#[macro_use] #[macro_use]
extern crate quote; extern crate quote;
extern crate serde_json;
extern crate syn; extern crate syn;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate wasm_bindgen_shared as shared; extern crate wasm_bindgen_shared as shared;
pub use codegen::TryToTokens; pub use codegen::TryToTokens;
@ -25,5 +25,6 @@ mod error;
pub mod ast; pub mod ast;
mod codegen; mod codegen;
mod encode;
pub mod defined; pub mod defined;
pub mod util; pub mod util;

View File

@ -14,8 +14,6 @@ Shared support for the wasm-bindgen-cli package, an internal dependency
base64 = "0.9" base64 = "0.9"
failure = "0.1.2" failure = "0.1.2"
parity-wasm = "0.34" parity-wasm = "0.34"
serde = "1.0"
serde_json = "1.0"
tempfile = "3.0" tempfile = "3.0"
wasm-bindgen-shared = { path = "../shared", version = '=0.2.25' } wasm-bindgen-shared = { path = "../shared", version = '=0.2.25' }
wasm-bindgen-wasm-interpreter = { path = "../wasm-interpreter", version = '=0.2.25' } wasm-bindgen-wasm-interpreter = { path = "../wasm-interpreter", version = '=0.2.25' }

View File

@ -0,0 +1,147 @@
use std::str;
pub trait Decode<'src>: Sized {
fn decode(data: &mut &'src [u8]) -> Self;
fn decode_all(mut data: &'src [u8]) -> Self {
let ret = Self::decode(&mut data);
assert!(data.len() == 0);
return ret
}
}
fn get<'a>(b: &mut &'a [u8]) -> u8 {
let r = b[0];
*b = &b[1..];
return r
}
impl<'src> Decode<'src> for bool {
fn decode(data: &mut &'src [u8]) -> Self {
get(data) != 0
}
}
impl<'src> Decode<'src> for u32 {
fn decode(data: &mut &'src [u8]) -> Self {
let mut cur = 0;
let mut offset = 0;
loop {
let byte = get(data);
cur |= ((byte & 0x7f) as u32) << offset;
if byte & 0x80 == 0 {
break cur
}
offset += 7;
}
}
}
impl<'src> Decode<'src> for &'src str {
fn decode(data: &mut &'src [u8]) -> &'src str {
let n = u32::decode(data);
let (a, b) = data.split_at(n as usize);
*data = b;
str::from_utf8(a).unwrap()
}
}
impl<'src, T: Decode<'src>> Decode<'src> for Vec<T> {
fn decode(data: &mut &'src [u8]) -> Self {
let n = u32::decode(data);
let mut v = Vec::with_capacity(n as usize);
for _ in 0..n {
v.push(Decode::decode(data));
}
v
}
}
impl<'src, T: Decode<'src>> Decode<'src> for Option<T> {
fn decode(data: &mut &'src [u8]) -> Self {
match get(data) {
0 => None,
1 => Some(Decode::decode(data)),
_ => unreachable!(),
}
}
}
macro_rules! decode_struct {
($name:ident ($($lt:tt)*) $($field:ident: $ty:ty,)*) => {
pub struct $name <$($lt)*> {
$(pub $field: $ty,)*
}
impl <'a> Decode<'a> for $name <$($lt)*> {
fn decode(_data: &mut &'a [u8]) -> Self {
$name {
$($field: Decode::decode(_data),)*
}
}
}
}
}
macro_rules! decode_enum {
($name:ident ($($lt:tt)*) $($fields:tt)*) => (
pub enum $name <$($lt)*> { $($fields)* }
impl <'a> Decode<'a> for $name <$($lt)*> {
fn decode(data: &mut &'a [u8]) -> Self {
use self::$name::*;
decode_enum!(@arms data dst (0) () $($fields)*)
}
}
);
(@arms $data:ident $dst:ident ($cnt:expr) ($($arms:tt)*)) => (
decode_enum!(@expr match get($data) { $($arms)* _ => unreachable!() })
);
(@arms $data:ident $dst:ident ($cnt:expr) ($($arms:tt)*) $name:ident, $($rest:tt)*) => (
decode_enum!(
@arms
$data
$dst
($cnt+1)
($($arms)* n if n == $cnt => $name, )
$($rest)*
)
);
(@arms $data:ident $dst:ident ($cnt:expr) ($($arms:tt)*) $name:ident($t:ty), $($rest:tt)*) => (
decode_enum!(
@arms
$data
$dst
($cnt+1)
($($arms)* n if n == $cnt => $name(Decode::decode($data)), )
$($rest)*
)
);
(@expr $e:expr) => ($e);
}
macro_rules! decode_api {
() => ();
(struct $name:ident<'a> { $($fields:tt)* } $($rest:tt)*) => (
decode_struct!($name ('a) $($fields)*);
decode_api!($($rest)*);
);
(struct $name:ident { $($fields:tt)* } $($rest:tt)*) => (
decode_struct!($name () $($fields)*);
decode_api!($($rest)*);
);
(enum $name:ident<'a> { $($variants:tt)* } $($rest:tt)*) => (
decode_enum!($name ('a) $($variants)*);
decode_api!($($rest)*);
);
(enum $name:ident { $($variants:tt)* } $($rest:tt)*) => (
decode_enum!($name () $($variants)*);
decode_api!($($rest)*);
);
}
shared_api!(decode_api);

View File

@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet};
use std::fmt::Write; use std::fmt::Write;
use std::mem; use std::mem;
use decode;
use failure::{Error, ResultExt}; use failure::{Error, ResultExt};
use parity_wasm::elements::*; use parity_wasm::elements::*;
use shared; use shared;
@ -25,8 +26,8 @@ pub struct Context<'a> {
pub typescript: String, pub typescript: String,
pub exposed_globals: HashSet<&'static str>, pub exposed_globals: HashSet<&'static str>,
pub required_internal_exports: HashSet<&'static str>, pub required_internal_exports: HashSet<&'static str>,
pub imported_functions: HashSet<String>, pub imported_functions: HashSet<&'a str>,
pub imported_statics: HashSet<String>, pub imported_statics: HashSet<&'a str>,
pub config: &'a Bindgen, pub config: &'a Bindgen,
pub module: &'a mut Module, pub module: &'a mut Module,
@ -37,7 +38,7 @@ pub struct Context<'a> {
/// from, `None` being the global module. The second key is a map of /// from, `None` being the global module. The second key is a map of
/// identifiers we've already imported from the module to what they're /// identifiers we've already imported from the module to what they're
/// called locally. /// called locally.
pub imported_names: HashMap<Option<String>, HashMap<String, String>>, pub imported_names: HashMap<Option<&'a str>, HashMap<String, String>>,
/// A set of all imported identifiers to the number of times they've been /// A set of all imported identifiers to the number of times they've been
/// imported, used to generate new identifiers. /// imported, used to generate new identifiers.
@ -59,9 +60,9 @@ pub struct ExportedClass {
} }
pub struct SubContext<'a, 'b: 'a> { pub struct SubContext<'a, 'b: 'a> {
pub program: &'a shared::Program, pub program: &'b decode::Program<'b>,
pub cx: &'a mut Context<'b>, pub cx: &'a mut Context<'b>,
pub vendor_prefixes: HashMap<String, Vec<String>>, pub vendor_prefixes: HashMap<&'b str, Vec<&'b str>>,
} }
const INITIAL_SLAB_VALUES: &[&str] = &["undefined", "null", "true", "false"]; const INITIAL_SLAB_VALUES: &[&str] = &["undefined", "null", "true", "false"];
@ -1690,7 +1691,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
})?; })?;
} }
for f in self.program.imports.iter() { for f in self.program.imports.iter() {
if let shared::ImportKind::Type(ty) = &f.kind { if let decode::ImportKind::Type(ty) = &f.kind {
self.register_vendor_prefix(ty); self.register_vendor_prefix(ty);
} }
} }
@ -1707,7 +1708,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
Ok(()) Ok(())
} }
fn generate_export(&mut self, export: &shared::Export) -> Result<(), Error> { fn generate_export(&mut self, export: &decode::Export<'b>) -> Result<(), Error> {
if let Some(ref class) = export.class { if let Some(ref class) = export.class {
return self.generate_export_for_class(class, export); return self.generate_export_for_class(class, export);
} }
@ -1734,8 +1735,8 @@ impl<'a, 'b> SubContext<'a, 'b> {
fn generate_export_for_class( fn generate_export_for_class(
&mut self, &mut self,
class_name: &str, class_name: &'b str,
export: &shared::Export, export: &decode::Export,
) -> Result<(), Error> { ) -> Result<(), Error> {
let wasm_name = shared::struct_function_export_name(class_name, &export.function.name); let wasm_name = shared::struct_function_export_name(class_name, &export.function.name);
@ -1785,9 +1786,9 @@ impl<'a, 'b> SubContext<'a, 'b> {
Ok(()) Ok(())
} }
fn generate_import(&mut self, import: &shared::Import) -> Result<(), Error> { fn generate_import(&mut self, import: &decode::Import<'b>) -> Result<(), Error> {
match import.kind { match import.kind {
shared::ImportKind::Function(ref f) => { decode::ImportKind::Function(ref f) => {
self.generate_import_function(import, f).with_context(|_| { self.generate_import_function(import, f).with_context(|_| {
format!( format!(
"failed to generate bindings for JS import `{}`", "failed to generate bindings for JS import `{}`",
@ -1795,29 +1796,29 @@ impl<'a, 'b> SubContext<'a, 'b> {
) )
})?; })?;
} }
shared::ImportKind::Static(ref s) => { decode::ImportKind::Static(ref s) => {
self.generate_import_static(import, s).with_context(|_| { self.generate_import_static(import, s).with_context(|_| {
format!("failed to generate bindings for JS import `{}`", s.name) format!("failed to generate bindings for JS import `{}`", s.name)
})?; })?;
} }
shared::ImportKind::Type(ref ty) => { decode::ImportKind::Type(ref ty) => {
self.generate_import_type(import, ty).with_context(|_| { self.generate_import_type(import, ty).with_context(|_| {
format!("failed to generate bindings for JS import `{}`", ty.name,) format!("failed to generate bindings for JS import `{}`", ty.name,)
})?; })?;
} }
shared::ImportKind::Enum(_) => {} decode::ImportKind::Enum(_) => {}
} }
Ok(()) Ok(())
} }
fn generate_import_static( fn generate_import_static(
&mut self, &mut self,
info: &shared::Import, info: &decode::Import<'b>,
import: &shared::ImportStatic, import: &decode::ImportStatic<'b>,
) -> Result<(), Error> { ) -> Result<(), Error> {
// The same static can be imported in multiple locations, so only // The same static can be imported in multiple locations, so only
// generate bindings once for it. // generate bindings once for it.
if !self.cx.imported_statics.insert(import.shim.clone()) { if !self.cx.imported_statics.insert(import.shim) {
return Ok(()); return Ok(());
} }
@ -1841,8 +1842,8 @@ impl<'a, 'b> SubContext<'a, 'b> {
fn generate_import_function( fn generate_import_function(
&mut self, &mut self,
info: &shared::Import, info: &decode::Import<'b>,
import: &shared::ImportFunction, import: &decode::ImportFunction<'b>,
) -> Result<(), Error> { ) -> Result<(), Error> {
if !self.cx.wasm_import_needed(&import.shim) { if !self.cx.wasm_import_needed(&import.shim) {
return Ok(()); return Ok(());
@ -1850,7 +1851,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
// It's possible for the same function to be imported in two locations, // It's possible for the same function to be imported in two locations,
// but we only want to generate one. // but we only want to generate one.
if !self.cx.imported_functions.insert(import.shim.clone()) { if !self.cx.imported_functions.insert(import.shim) {
return Ok(()); return Ok(());
} }
@ -1872,8 +1873,8 @@ impl<'a, 'b> SubContext<'a, 'b> {
fn generated_import_target( fn generated_import_target(
&mut self, &mut self,
info: &shared::Import, info: &decode::Import<'b>,
import: &shared::ImportFunction, import: &decode::ImportFunction,
descriptor: &Descriptor, descriptor: &Descriptor,
) -> Result<String, Error> { ) -> Result<String, Error> {
let method_data = match &import.method { let method_data = match &import.method {
@ -1896,14 +1897,14 @@ impl<'a, 'b> SubContext<'a, 'b> {
let class = self.import_name(info, &method_data.class)?; let class = self.import_name(info, &method_data.class)?;
let op = match &method_data.kind { let op = match &method_data.kind {
shared::MethodKind::Constructor => return Ok(format!("new {}", class)), decode::MethodKind::Constructor => return Ok(format!("new {}", class)),
shared::MethodKind::Operation(op) => op, decode::MethodKind::Operation(op) => op,
}; };
let target = if import.structural { let target = if import.structural {
let location = if op.is_static { &class } else { "this" }; let location = if op.is_static { &class } else { "this" };
match &op.kind { match &op.kind {
shared::OperationKind::Regular => { decode::OperationKind::Regular => {
let nargs = descriptor.unwrap_function().arguments.len(); let nargs = descriptor.unwrap_function().arguments.len();
let mut s = format!("function("); let mut s = format!("function(");
for i in 0..nargs - 1 { for i in 0..nargs - 1 {
@ -1924,31 +1925,31 @@ impl<'a, 'b> SubContext<'a, 'b> {
s.push_str(");\n}"); s.push_str(");\n}");
s s
} }
shared::OperationKind::Getter(g) => format!( decode::OperationKind::Getter(g) => format!(
"function() {{ "function() {{
return {}.{}; return {}.{};
}}", }}",
location, g location, g
), ),
shared::OperationKind::Setter(s) => format!( decode::OperationKind::Setter(s) => format!(
"function(y) {{ "function(y) {{
{}.{} = y; {}.{} = y;
}}", }}",
location, s location, s
), ),
shared::OperationKind::IndexingGetter => format!( decode::OperationKind::IndexingGetter => format!(
"function(y) {{ "function(y) {{
return {}[y]; return {}[y];
}}", }}",
location location
), ),
shared::OperationKind::IndexingSetter => format!( decode::OperationKind::IndexingSetter => format!(
"function(y, z) {{ "function(y, z) {{
{}[y] = z; {}[y] = z;
}}", }}",
location location
), ),
shared::OperationKind::IndexingDeleter => format!( decode::OperationKind::IndexingDeleter => format!(
"function(y) {{ "function(y) {{
delete {}[y]; delete {}[y];
}}", }}",
@ -1960,30 +1961,30 @@ impl<'a, 'b> SubContext<'a, 'b> {
class, class,
if op.is_static { "" } else { ".prototype" }); if op.is_static { "" } else { ".prototype" });
let (mut target, name) = match &op.kind { let (mut target, name) = match &op.kind {
shared::OperationKind::Regular => { decode::OperationKind::Regular => {
(format!("{}.{}", target, import.function.name), &import.function.name) (format!("{}.{}", target, import.function.name), &import.function.name)
} }
shared::OperationKind::Getter(g) => { decode::OperationKind::Getter(g) => {
self.cx.expose_get_inherited_descriptor(); self.cx.expose_get_inherited_descriptor();
(format!( (format!(
"GetOwnOrInheritedPropertyDescriptor({}, '{}').get", "GetOwnOrInheritedPropertyDescriptor({}, '{}').get",
target, g, target, g,
), g) ), g)
} }
shared::OperationKind::Setter(s) => { decode::OperationKind::Setter(s) => {
self.cx.expose_get_inherited_descriptor(); self.cx.expose_get_inherited_descriptor();
(format!( (format!(
"GetOwnOrInheritedPropertyDescriptor({}, '{}').set", "GetOwnOrInheritedPropertyDescriptor({}, '{}').set",
target, s, target, s,
), s) ), s)
} }
shared::OperationKind::IndexingGetter => { decode::OperationKind::IndexingGetter => {
panic!("indexing getter should be structural") panic!("indexing getter should be structural")
} }
shared::OperationKind::IndexingSetter => { decode::OperationKind::IndexingSetter => {
panic!("indexing setter should be structural") panic!("indexing setter should be structural")
} }
shared::OperationKind::IndexingDeleter => { decode::OperationKind::IndexingDeleter => {
panic!("indexing deleter should be structural") panic!("indexing deleter should be structural")
} }
}; };
@ -2009,8 +2010,8 @@ impl<'a, 'b> SubContext<'a, 'b> {
fn generate_import_type( fn generate_import_type(
&mut self, &mut self,
info: &shared::Import, info: &decode::Import<'b>,
import: &shared::ImportType, import: &decode::ImportType,
) -> Result<(), Error> { ) -> Result<(), Error> {
if !self.cx.wasm_import_needed(&import.instanceof_shim) { if !self.cx.wasm_import_needed(&import.instanceof_shim) {
return Ok(()); return Ok(());
@ -2029,7 +2030,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
Ok(()) Ok(())
} }
fn generate_enum(&mut self, enum_: &shared::Enum) { fn generate_enum(&mut self, enum_: &decode::Enum) {
let mut variants = String::new(); let mut variants = String::new();
for variant in enum_.variants.iter() { for variant in enum_.variants.iter() {
@ -2052,7 +2053,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
self.cx.typescript.push_str("}\n"); self.cx.typescript.push_str("}\n");
} }
fn generate_struct(&mut self, struct_: &shared::Struct) -> Result<(), Error> { fn generate_struct(&mut self, struct_: &decode::Struct) -> Result<(), Error> {
let mut dst = String::new(); let mut dst = String::new();
let mut ts_dst = String::new(); let mut ts_dst = String::new();
for field in struct_.fields.iter() { for field in struct_.fields.iter() {
@ -2098,7 +2099,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
let class = self let class = self
.cx .cx
.exported_classes .exported_classes
.entry(struct_.name.clone()) .entry(struct_.name.to_string())
.or_insert_with(Default::default); .or_insert_with(Default::default);
class.comments = format_doc_comments(&struct_.comments, None); class.comments = format_doc_comments(&struct_.comments, None);
class.contents.push_str(&dst); class.contents.push_str(&dst);
@ -2110,18 +2111,18 @@ impl<'a, 'b> SubContext<'a, 'b> {
fn register_vendor_prefix( fn register_vendor_prefix(
&mut self, &mut self,
info: &shared::ImportType, info: &decode::ImportType<'b>,
) { ) {
if info.vendor_prefixes.len() == 0 { if info.vendor_prefixes.len() == 0 {
return return
} }
self.vendor_prefixes self.vendor_prefixes
.entry(info.name.to_string()) .entry(info.name)
.or_insert(Vec::new()) .or_insert(Vec::new())
.extend(info.vendor_prefixes.iter().cloned()); .extend(info.vendor_prefixes.iter().cloned());
} }
fn import_name(&mut self, import: &shared::Import, item: &str) -> Result<String, Error> { fn import_name(&mut self, import: &decode::Import<'b>, item: &str) -> Result<String, Error> {
// First up, imports don't work at all in `--no-modules` mode as we're // First up, imports don't work at all in `--no-modules` mode as we're
// not sure how to import them. // not sure how to import them.
if self.cx.config.no_modules { if self.cx.config.no_modules {
@ -2180,7 +2181,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
let identifier = self let identifier = self
.cx .cx
.imported_names .imported_names
.entry(import.module.clone()) .entry(import.module)
.or_insert_with(Default::default) .or_insert_with(Default::default)
.entry(name_to_import.to_string()) .entry(name_to_import.to_string())
.or_insert_with(|| { .or_insert_with(|| {
@ -2207,7 +2208,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
switch(imports_post, &name, "", vendor_prefixes); switch(imports_post, &name, "", vendor_prefixes);
imports_post.push_str(";\n"); imports_post.push_str(";\n");
fn switch(dst: &mut String, name: &str, prefix: &str, left: &[String]) { fn switch(dst: &mut String, name: &str, prefix: &str, left: &[&str]) {
if left.len() == 0 { if left.len() == 0 {
dst.push_str(prefix); dst.push_str(prefix);
return dst.push_str(name); return dst.push_str(name);
@ -2254,7 +2255,7 @@ fn generate_identifier(name: &str, used_names: &mut HashMap<String, usize>) -> S
} }
} }
fn format_doc_comments(comments: &Vec<String>, js_doc_comments: Option<String>) -> String { fn format_doc_comments(comments: &[&str], js_doc_comments: Option<String>) -> String {
let body: String = comments let body: String = comments
.iter() .iter()
.map(|c| format!("*{}\n", c.trim_matches('"'))) .map(|c| format!("*{}\n", c.trim_matches('"')))

View File

@ -1,7 +1,7 @@
#![doc(html_root_url = "https://docs.rs/wasm-bindgen-cli-support/0.2")] #![doc(html_root_url = "https://docs.rs/wasm-bindgen-cli-support/0.2")]
extern crate parity_wasm; extern crate parity_wasm;
extern crate serde_json; #[macro_use]
extern crate wasm_bindgen_shared as shared; extern crate wasm_bindgen_shared as shared;
extern crate wasm_bindgen_gc; extern crate wasm_bindgen_gc;
#[macro_use] #[macro_use]
@ -13,10 +13,12 @@ use std::env;
use std::fs; use std::fs;
use std::mem; use std::mem;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str;
use failure::{Error, ResultExt}; use failure::{Error, ResultExt};
use parity_wasm::elements::*; use parity_wasm::elements::*;
mod decode;
mod descriptor; mod descriptor;
mod js; mod js;
pub mod wasm2es6js; pub mod wasm2es6js;
@ -137,7 +139,8 @@ impl Bindgen {
(module, stem) (module, stem)
} }
}; };
let programs = extract_programs(&mut module) let mut program_storage = Vec::new();
let programs = extract_programs(&mut module, &mut program_storage)
.with_context(|_| "failed to extract wasm-bindgen custom sections")?; .with_context(|_| "failed to extract wasm-bindgen custom sections")?;
// Here we're actually instantiating the module we've parsed above for // Here we're actually instantiating the module we've parsed above for
@ -289,35 +292,50 @@ impl Bindgen {
} }
} }
fn extract_programs(module: &mut Module) -> Result<Vec<shared::Program>, Error> { fn extract_programs<'a>(
let version = shared::version(); module: &mut Module,
let mut ret = Vec::new(); program_storage: &'a mut Vec<Vec<u8>>,
) -> Result<Vec<decode::Program<'a>>, Error> {
let my_version = shared::version();
let mut to_remove = Vec::new(); let mut to_remove = Vec::new();
assert!(program_storage.is_empty());
for (i, s) in module.sections().iter().enumerate() { for (i, s) in module.sections_mut().iter_mut().enumerate() {
let custom = match *s { let custom = match s {
Section::Custom(ref s) => s, Section::Custom(s) => s,
_ => continue, _ => continue,
}; };
if custom.name() != "__wasm_bindgen_unstable" { if custom.name() != "__wasm_bindgen_unstable" {
continue; continue;
} }
to_remove.push(i); to_remove.push(i);
program_storage.push(mem::replace(custom.payload_mut(), Vec::new()));
}
let mut payload = custom.payload(); for i in to_remove.into_iter().rev() {
while payload.len() > 0 { module.sections_mut().remove(i);
let len = ((payload[0] as usize) << 0) }
| ((payload[1] as usize) << 8)
| ((payload[2] as usize) << 16)
| ((payload[3] as usize) << 24);
let (a, b) = payload[4..].split_at(len as usize);
payload = b;
let p: shared::ProgramOnlySchema = match serde_json::from_slice(&a) { let mut ret = Vec::new();
Ok(f) => f, for program in program_storage.iter() {
Err(e) => bail!("failed to decode what looked like wasm-bindgen data: {}", e), let mut payload = &program[..];
}; while let Some(data) = get_remaining(&mut payload) {
if p.schema_version != shared::SCHEMA_VERSION { // Historical versions of wasm-bindgen have used JSON as the custom
// data section format. Newer versions, however, are using a custom
// serialization protocol that looks much more like the wasm spec.
//
// We, however, want a sanity check to ensure that if we're running
// against the wrong wasm-bindgen we get a nicer error than an
// internal decode error. To that end we continue to verify a tiny
// bit of json at the beginning of each blob before moving to the
// next blob. This should keep us compatible with older wasm-bindgen
// instances as well as forward-compatible for now.
//
// Note, though, that as `wasm-pack` picks up steam it's hoped we
// can just delete this entirely. The `wasm-pack` project already
// manages versions for us, so we in theory should need this check
// less and less over time.
if let Some(their_version) = verify_schema_matches(data)? {
bail!( bail!(
" "
@ -341,24 +359,67 @@ or you can update the binary with
if this warning fails to go away though and you're not sure what to do feel free if this warning fails to go away though and you're not sure what to do feel free
to open an issue at https://github.com/rustwasm/wasm-bindgen/issues! to open an issue at https://github.com/rustwasm/wasm-bindgen/issues!
", ",
p.version, their_version,
version my_version,
); );
} }
let p: shared::Program = match serde_json::from_slice(&a) { let next = get_remaining(&mut payload).unwrap();
Ok(f) => f, ret.push(<decode::Program as decode::Decode>::decode_all(next));
Err(e) => bail!("failed to decode what looked like wasm-bindgen data: {}", e),
};
ret.push(p);
} }
} }
for i in to_remove.into_iter().rev() {
module.sections_mut().remove(i);
}
Ok(ret) Ok(ret)
} }
fn get_remaining<'a>(data: &mut &'a [u8]) -> Option<&'a [u8]> {
if data.len() == 0 {
return None
}
let len = ((data[0] as usize) << 0)
| ((data[1] as usize) << 8)
| ((data[2] as usize) << 16)
| ((data[3] as usize) << 24);
let (a, b) = data[4..].split_at(len);
*data = b;
Some(a)
}
fn verify_schema_matches<'a>(data: &'a [u8])
-> Result<Option<&'a str>, Error>
{
macro_rules! bad {
() => (bail!("failed to decode what looked like wasm-bindgen data"))
}
let data = match str::from_utf8(data) {
Ok(s) => s,
Err(_) => bad!(),
};
if !data.starts_with("{") || !data.ends_with("}") {
bad!()
}
let needle = "\"schema_version\":\"";
let rest = match data.find(needle) {
Some(i) => &data[i + needle.len()..],
None => bad!(),
};
let their_schema_version = match rest.find("\"") {
Some(i) => &rest[..i],
None => bad!(),
};
if their_schema_version == shared::SCHEMA_VERSION {
return Ok(None)
}
let needle = "\"version\":\"";
let rest = match data.find(needle) {
Some(i) => &data[i + needle.len()..],
None => bad!(),
};
let their_version = match rest.find("\"") {
Some(i) => &rest[..i],
None => bad!(),
};
Ok(Some(their_version))
}
fn reset_indentation(s: &str) -> String { fn reset_indentation(s: &str) -> String {
let mut indent: u32 = 0; let mut indent: u32 = 0;
let mut dst = String::new(); let mut dst = String::new();

View File

@ -15,7 +15,7 @@ spans = ["wasm-bindgen-backend/spans"]
extra-traits = ["syn/extra-traits"] extra-traits = ["syn/extra-traits"]
[dependencies] [dependencies]
syn = { version = '0.15.0', features = ['full'] } syn = { version = '0.15.0', features = ['visit'] }
quote = '0.6' quote = '0.6'
proc-macro2 = "0.4.9" proc-macro2 = "0.4.9"
wasm-bindgen-backend = { path = "../backend", version = "=0.2.25" } wasm-bindgen-backend = { path = "../backend", version = "=0.2.25" }

View File

@ -10,7 +10,3 @@ description = """
Shared support between wasm-bindgen and wasm-bindgen cli, an internal Shared support between wasm-bindgen and wasm-bindgen cli, an internal
dependency. dependency.
""" """
[dependencies]
serde_derive = "1"
serde = "1"

View File

@ -1,139 +1,118 @@
#![doc(html_root_url = "https://docs.rs/wasm-bindgen-shared/0.2")] #![doc(html_root_url = "https://docs.rs/wasm-bindgen-shared/0.2")]
#[macro_use]
extern crate serde_derive;
// The schema is so unstable right now we just force it to change whenever this // The schema is so unstable right now we just force it to change whenever this
// package's version changes, which happens on all publishes. // package's version changes, which happens on all publishes.
pub const SCHEMA_VERSION: &str = env!("CARGO_PKG_VERSION"); pub const SCHEMA_VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Deserialize)] #[macro_export]
pub struct ProgramOnlySchema { macro_rules! shared_api {
pub schema_version: String, ($mac:ident) => ($mac! {
pub version: String, struct Program<'a> {
exports: Vec<Export<'a>>,
enums: Vec<Enum<'a>>,
imports: Vec<Import<'a>>,
structs: Vec<Struct<'a>>,
// version: &'a str,
// schema_version: &'a str,
} }
#[derive(Deserialize, Serialize)] struct Import<'a> {
pub struct Program { module: Option<&'a str>,
pub exports: Vec<Export>, js_namespace: Option<&'a str>,
pub enums: Vec<Enum>, kind: ImportKind<'a>,
pub imports: Vec<Import>,
pub structs: Vec<Struct>,
pub version: String,
pub schema_version: String,
} }
#[derive(Deserialize, Serialize)] enum ImportKind<'a> {
pub struct Import { Function(ImportFunction<'a>),
pub module: Option<String>, Static(ImportStatic<'a>),
pub js_namespace: Option<String>, Type(ImportType<'a>),
pub kind: ImportKind,
}
#[derive(Deserialize, Serialize)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum ImportKind {
Function(ImportFunction),
Static(ImportStatic),
Type(ImportType),
Enum(ImportEnum), Enum(ImportEnum),
} }
#[derive(Deserialize, Serialize)] struct ImportFunction<'a> {
pub struct ImportFunction { shim: &'a str,
pub shim: String, catch: bool,
pub catch: bool, variadic: bool,
pub variadic: bool, method: Option<MethodData<'a>>,
pub method: Option<MethodData>, structural: bool,
pub structural: bool, function: Function<'a>,
pub function: Function,
} }
#[derive(Deserialize, Serialize)] struct MethodData<'a> {
pub struct MethodData { class: &'a str,
pub class: String, kind: MethodKind<'a>,
pub kind: MethodKind,
} }
#[derive(Deserialize, Serialize)] enum MethodKind<'a> {
pub enum MethodKind {
Constructor, Constructor,
Operation(Operation), Operation(Operation<'a>),
} }
#[derive(Deserialize, Serialize)] struct Operation<'a> {
pub struct Operation { is_static: bool,
pub is_static: bool, kind: OperationKind<'a>,
pub kind: OperationKind,
} }
#[derive(Deserialize, Serialize)] enum OperationKind<'a> {
pub enum OperationKind {
Regular, Regular,
Getter(String), Getter(&'a str),
Setter(String), Setter(&'a str),
IndexingGetter, IndexingGetter,
IndexingSetter, IndexingSetter,
IndexingDeleter, IndexingDeleter,
} }
#[derive(Deserialize, Serialize)] struct ImportStatic<'a> {
pub struct ImportStatic { name: &'a str,
pub name: String, shim: &'a str,
pub shim: String,
} }
#[derive(Deserialize, Serialize)] struct ImportType<'a> {
pub struct ImportType { name: &'a str,
pub name: String, instanceof_shim: &'a str,
pub instanceof_shim: String, vendor_prefixes: Vec<&'a str>,
pub vendor_prefixes: Vec<String>,
} }
#[derive(Deserialize, Serialize)] struct ImportEnum {}
pub struct ImportEnum {}
#[derive(Deserialize, Serialize)] struct Export<'a> {
pub struct Export { class: Option<&'a str>,
pub class: Option<String>, method: bool,
pub method: bool, consumed: bool,
pub consumed: bool, is_constructor: bool,
pub is_constructor: bool, function: Function<'a>,
pub function: Function, comments: Vec<&'a str>,
pub comments: Vec<String>,
} }
#[derive(Deserialize, Serialize)] struct Enum<'a> {
pub struct Enum { name: &'a str,
pub name: String, variants: Vec<EnumVariant<'a>>,
pub variants: Vec<EnumVariant>, comments: Vec<&'a str>,
pub comments: Vec<String>,
} }
#[derive(Deserialize, Serialize)] struct EnumVariant<'a> {
pub struct EnumVariant { name: &'a str,
pub name: String, value: u32,
pub value: u32,
} }
#[derive(Deserialize, Serialize)] struct Function<'a> {
pub struct Function { name: &'a str,
pub name: String,
} }
#[derive(Deserialize, Serialize)] struct Struct<'a> {
pub struct Struct { name: &'a str,
pub name: String, fields: Vec<StructField<'a>>,
pub fields: Vec<StructField>, comments: Vec<&'a str>,
pub comments: Vec<String>,
} }
#[derive(Deserialize, Serialize)] struct StructField<'a> {
pub struct StructField { name: &'a str,
pub name: String, readonly: bool,
pub readonly: bool, comments: Vec<&'a str>,
pub comments: Vec<String>,
} }
}) // end of mac case
} // end of mac definition
pub fn new_function(struct_name: &str) -> String { pub fn new_function(struct_name: &str) -> String {
let mut name = format!("__wbg_"); let mut name = format!("__wbg_");