mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-16 18:20:51 +00:00
Merge pull request #945 from alexcrichton/remove-json
Don't use JSON for custom section format
This commit is contained in:
commit
0b59657603
@ -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" }
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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! {
|
||||
|
368
crates/backend/src/encode.rs
Normal file
368
crates/backend/src/encode.rs
Normal 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);
|
@ -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;
|
||||
|
@ -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' }
|
||||
|
147
crates/cli-support/src/decode.rs
Normal file
147
crates/cli-support/src/decode.rs
Normal 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);
|
@ -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('"')))
|
||||
|
@ -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();
|
||||
|
@ -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" }
|
||||
|
@ -10,7 +10,3 @@ description = """
|
||||
Shared support between wasm-bindgen and wasm-bindgen cli, an internal
|
||||
dependency.
|
||||
"""
|
||||
|
||||
[dependencies]
|
||||
serde_derive = "1"
|
||||
serde = "1"
|
||||
|
@ -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_");
|
||||
|
Loading…
x
Reference in New Issue
Block a user