Merge pull request #945 from alexcrichton/remove-json

Don't use JSON for custom section format
This commit is contained in:
Nick Fitzgerald 2018-10-16 05:49:00 -07:00 committed by GitHub
commit 0b59657603
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 752 additions and 385 deletions

View File

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

View File

@ -1,9 +1,8 @@
use Diagnostic;
use proc_macro2::{Ident, Span};
use shared;
use syn;
use Diagnostic;
/// 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, PartialEq))]
@ -260,36 +259,6 @@ pub struct DictionaryField {
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 {
/// 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.
@ -314,51 +283,6 @@ impl Export {
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 {
@ -371,27 +295,18 @@ impl ImportKind {
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 {
/// 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`)
fn infer_getter_property(&self) -> String {
self.function.name.to_string()
pub fn infer_getter_property(&self) -> &str {
&self.function.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`)
fn infer_setter_property(&self) -> Result<String, Diagnostic> {
pub fn infer_setter_property(&self) -> Result<String, Diagnostic> {
let name = self.function.name.to_string();
// if `#[wasm_bindgen(js_name = "...")]` is used then that explicitly
@ -410,102 +325,4 @@ impl ImportFunction {
}
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::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use std::sync::Mutex;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use proc_macro2::{Ident, Literal, Span, TokenStream};
use quote::ToTokens;
use serde_json;
use shared;
use syn;
use ast;
use encode;
use util::ShortHash;
use Diagnostic;
@ -87,20 +87,20 @@ impl TryToTokens for ast::Program {
);
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
// 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_length = bytes.len();
let generated_static_value = syn::LitByteStr::new(&bytes, Span::call_site());
(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")]
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate proc_macro2;
#[macro_use]
extern crate quote;
extern crate serde_json;
extern crate syn;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate wasm_bindgen_shared as shared;
pub use codegen::TryToTokens;
@ -25,5 +25,6 @@ mod error;
pub mod ast;
mod codegen;
mod encode;
pub mod defined;
pub mod util;

View File

@ -14,8 +14,6 @@ Shared support for the wasm-bindgen-cli package, an internal dependency
base64 = "0.9"
failure = "0.1.2"
parity-wasm = "0.34"
serde = "1.0"
serde_json = "1.0"
tempfile = "3.0"
wasm-bindgen-shared = { path = "../shared", 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::mem;
use decode;
use failure::{Error, ResultExt};
use parity_wasm::elements::*;
use shared;
@ -25,8 +26,8 @@ pub struct Context<'a> {
pub typescript: String,
pub exposed_globals: HashSet<&'static str>,
pub required_internal_exports: HashSet<&'static str>,
pub imported_functions: HashSet<String>,
pub imported_statics: HashSet<String>,
pub imported_functions: HashSet<&'a str>,
pub imported_statics: HashSet<&'a str>,
pub config: &'a Bindgen,
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
/// identifiers we've already imported from the module to what they're
/// 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
/// imported, used to generate new identifiers.
@ -59,9 +60,9 @@ pub struct ExportedClass {
}
pub struct SubContext<'a, 'b: 'a> {
pub program: &'a shared::Program,
pub program: &'b decode::Program<'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"];
@ -1690,7 +1691,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
})?;
}
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);
}
}
@ -1707,7 +1708,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
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 {
return self.generate_export_for_class(class, export);
}
@ -1734,8 +1735,8 @@ impl<'a, 'b> SubContext<'a, 'b> {
fn generate_export_for_class(
&mut self,
class_name: &str,
export: &shared::Export,
class_name: &'b str,
export: &decode::Export,
) -> Result<(), Error> {
let wasm_name = shared::struct_function_export_name(class_name, &export.function.name);
@ -1785,9 +1786,9 @@ impl<'a, 'b> SubContext<'a, 'b> {
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 {
shared::ImportKind::Function(ref f) => {
decode::ImportKind::Function(ref f) => {
self.generate_import_function(import, f).with_context(|_| {
format!(
"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(|_| {
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(|_| {
format!("failed to generate bindings for JS import `{}`", ty.name,)
})?;
}
shared::ImportKind::Enum(_) => {}
decode::ImportKind::Enum(_) => {}
}
Ok(())
}
fn generate_import_static(
&mut self,
info: &shared::Import,
import: &shared::ImportStatic,
info: &decode::Import<'b>,
import: &decode::ImportStatic<'b>,
) -> Result<(), Error> {
// The same static can be imported in multiple locations, so only
// generate bindings once for it.
if !self.cx.imported_statics.insert(import.shim.clone()) {
if !self.cx.imported_statics.insert(import.shim) {
return Ok(());
}
@ -1841,8 +1842,8 @@ impl<'a, 'b> SubContext<'a, 'b> {
fn generate_import_function(
&mut self,
info: &shared::Import,
import: &shared::ImportFunction,
info: &decode::Import<'b>,
import: &decode::ImportFunction<'b>,
) -> Result<(), Error> {
if !self.cx.wasm_import_needed(&import.shim) {
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,
// 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(());
}
@ -1872,8 +1873,8 @@ impl<'a, 'b> SubContext<'a, 'b> {
fn generated_import_target(
&mut self,
info: &shared::Import,
import: &shared::ImportFunction,
info: &decode::Import<'b>,
import: &decode::ImportFunction,
descriptor: &Descriptor,
) -> Result<String, Error> {
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 op = match &method_data.kind {
shared::MethodKind::Constructor => return Ok(format!("new {}", class)),
shared::MethodKind::Operation(op) => op,
decode::MethodKind::Constructor => return Ok(format!("new {}", class)),
decode::MethodKind::Operation(op) => op,
};
let target = if import.structural {
let location = if op.is_static { &class } else { "this" };
match &op.kind {
shared::OperationKind::Regular => {
decode::OperationKind::Regular => {
let nargs = descriptor.unwrap_function().arguments.len();
let mut s = format!("function(");
for i in 0..nargs - 1 {
@ -1924,31 +1925,31 @@ impl<'a, 'b> SubContext<'a, 'b> {
s.push_str(");\n}");
s
}
shared::OperationKind::Getter(g) => format!(
decode::OperationKind::Getter(g) => format!(
"function() {{
return {}.{};
}}",
location, g
),
shared::OperationKind::Setter(s) => format!(
decode::OperationKind::Setter(s) => format!(
"function(y) {{
{}.{} = y;
}}",
location, s
),
shared::OperationKind::IndexingGetter => format!(
decode::OperationKind::IndexingGetter => format!(
"function(y) {{
return {}[y];
}}",
location
),
shared::OperationKind::IndexingSetter => format!(
decode::OperationKind::IndexingSetter => format!(
"function(y, z) {{
{}[y] = z;
}}",
location
),
shared::OperationKind::IndexingDeleter => format!(
decode::OperationKind::IndexingDeleter => format!(
"function(y) {{
delete {}[y];
}}",
@ -1960,30 +1961,30 @@ impl<'a, 'b> SubContext<'a, 'b> {
class,
if op.is_static { "" } else { ".prototype" });
let (mut target, name) = match &op.kind {
shared::OperationKind::Regular => {
decode::OperationKind::Regular => {
(format!("{}.{}", target, import.function.name), &import.function.name)
}
shared::OperationKind::Getter(g) => {
decode::OperationKind::Getter(g) => {
self.cx.expose_get_inherited_descriptor();
(format!(
"GetOwnOrInheritedPropertyDescriptor({}, '{}').get",
target, g,
), g)
}
shared::OperationKind::Setter(s) => {
decode::OperationKind::Setter(s) => {
self.cx.expose_get_inherited_descriptor();
(format!(
"GetOwnOrInheritedPropertyDescriptor({}, '{}').set",
target, s,
), s)
}
shared::OperationKind::IndexingGetter => {
decode::OperationKind::IndexingGetter => {
panic!("indexing getter should be structural")
}
shared::OperationKind::IndexingSetter => {
decode::OperationKind::IndexingSetter => {
panic!("indexing setter should be structural")
}
shared::OperationKind::IndexingDeleter => {
decode::OperationKind::IndexingDeleter => {
panic!("indexing deleter should be structural")
}
};
@ -2009,8 +2010,8 @@ impl<'a, 'b> SubContext<'a, 'b> {
fn generate_import_type(
&mut self,
info: &shared::Import,
import: &shared::ImportType,
info: &decode::Import<'b>,
import: &decode::ImportType,
) -> Result<(), Error> {
if !self.cx.wasm_import_needed(&import.instanceof_shim) {
return Ok(());
@ -2029,7 +2030,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
Ok(())
}
fn generate_enum(&mut self, enum_: &shared::Enum) {
fn generate_enum(&mut self, enum_: &decode::Enum) {
let mut variants = String::new();
for variant in enum_.variants.iter() {
@ -2052,7 +2053,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
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 ts_dst = String::new();
for field in struct_.fields.iter() {
@ -2098,7 +2099,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
let class = self
.cx
.exported_classes
.entry(struct_.name.clone())
.entry(struct_.name.to_string())
.or_insert_with(Default::default);
class.comments = format_doc_comments(&struct_.comments, None);
class.contents.push_str(&dst);
@ -2110,18 +2111,18 @@ impl<'a, 'b> SubContext<'a, 'b> {
fn register_vendor_prefix(
&mut self,
info: &shared::ImportType,
info: &decode::ImportType<'b>,
) {
if info.vendor_prefixes.len() == 0 {
return
}
self.vendor_prefixes
.entry(info.name.to_string())
.entry(info.name)
.or_insert(Vec::new())
.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
// not sure how to import them.
if self.cx.config.no_modules {
@ -2180,7 +2181,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
let identifier = self
.cx
.imported_names
.entry(import.module.clone())
.entry(import.module)
.or_insert_with(Default::default)
.entry(name_to_import.to_string())
.or_insert_with(|| {
@ -2207,7 +2208,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
switch(imports_post, &name, "", vendor_prefixes);
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 {
dst.push_str(prefix);
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
.iter()
.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")]
extern crate parity_wasm;
extern crate serde_json;
#[macro_use]
extern crate wasm_bindgen_shared as shared;
extern crate wasm_bindgen_gc;
#[macro_use]
@ -13,10 +13,12 @@ use std::env;
use std::fs;
use std::mem;
use std::path::{Path, PathBuf};
use std::str;
use failure::{Error, ResultExt};
use parity_wasm::elements::*;
mod decode;
mod descriptor;
mod js;
pub mod wasm2es6js;
@ -137,7 +139,8 @@ impl Bindgen {
(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")?;
// 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> {
let version = shared::version();
let mut ret = Vec::new();
fn extract_programs<'a>(
module: &mut Module,
program_storage: &'a mut Vec<Vec<u8>>,
) -> Result<Vec<decode::Program<'a>>, Error> {
let my_version = shared::version();
let mut to_remove = Vec::new();
assert!(program_storage.is_empty());
for (i, s) in module.sections().iter().enumerate() {
let custom = match *s {
Section::Custom(ref s) => s,
for (i, s) in module.sections_mut().iter_mut().enumerate() {
let custom = match s {
Section::Custom(s) => s,
_ => continue,
};
if custom.name() != "__wasm_bindgen_unstable" {
continue;
}
to_remove.push(i);
program_storage.push(mem::replace(custom.payload_mut(), Vec::new()));
}
let mut payload = custom.payload();
while payload.len() > 0 {
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;
for i in to_remove.into_iter().rev() {
module.sections_mut().remove(i);
}
let p: shared::ProgramOnlySchema = match serde_json::from_slice(&a) {
Ok(f) => f,
Err(e) => bail!("failed to decode what looked like wasm-bindgen data: {}", e),
};
if p.schema_version != shared::SCHEMA_VERSION {
let mut ret = Vec::new();
for program in program_storage.iter() {
let mut payload = &program[..];
while let Some(data) = get_remaining(&mut payload) {
// 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!(
"
@ -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
to open an issue at https://github.com/rustwasm/wasm-bindgen/issues!
",
p.version,
version
their_version,
my_version,
);
}
let p: shared::Program = match serde_json::from_slice(&a) {
Ok(f) => f,
Err(e) => bail!("failed to decode what looked like wasm-bindgen data: {}", e),
};
ret.push(p);
let next = get_remaining(&mut payload).unwrap();
ret.push(<decode::Program as decode::Decode>::decode_all(next));
}
}
for i in to_remove.into_iter().rev() {
module.sections_mut().remove(i);
}
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 {
let mut indent: u32 = 0;
let mut dst = String::new();

View File

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