Migrate to the failure crate

Currently errors are reported via Rust panics but there's lots more errors being
added over time so this commit starts the movement towards the `failure` crate
to more idiomatically report errors as well as provide better error messages
over time.
This commit is contained in:
Alex Crichton 2018-04-25 11:42:22 -07:00
parent 2b9c48d5f9
commit 5f59d95130
10 changed files with 359 additions and 256 deletions

View File

@ -19,6 +19,10 @@ matrix:
rust: nightly rust: nightly
before_script: rustup target add $TARGET before_script: rustup target add $TARGET
script: cargo build --manifest-path crates/cli/Cargo.toml --release --target $TARGET script: cargo build --manifest-path crates/cli/Cargo.toml --release --target $TARGET
addons:
apt:
packages:
- musl-tools
# Dist OSX binary # Dist OSX binary
- os: osx - os: osx

View File

@ -12,6 +12,7 @@ Shared support for the wasm-bindgen-cli package, an internal dependency
[dependencies] [dependencies]
base64 = "0.9" base64 = "0.9"
failure = "0.1"
parity-wasm = "0.27" parity-wasm = "0.27"
serde_json = "1.0" serde_json = "1.0"
wasm-bindgen-shared = { path = "../shared", version = '=0.2.5' } wasm-bindgen-shared = { path = "../shared", version = '=0.2.5' }

View File

@ -1,3 +1,5 @@
use failure::Error;
use super::{indent, Context}; use super::{indent, Context};
use descriptor::{Descriptor, Function}; use descriptor::{Descriptor, Function};
@ -59,12 +61,12 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
/// Generates all bindings necessary for the signature in `Function`, /// Generates all bindings necessary for the signature in `Function`,
/// creating necessary argument conversions and return value processing. /// creating necessary argument conversions and return value processing.
pub fn process(&mut self, function: &Function) -> &mut Self { pub fn process(&mut self, function: &Function) -> Result<&mut Self, Error> {
for arg in function.arguments.iter() { for arg in function.arguments.iter() {
self.argument(arg); self.argument(arg)?;
} }
self.ret(&function.ret); self.ret(&function.ret)?;
self Ok(self)
} }
/// Flag this shim as a method call into Rust, so the first Rust argument /// Flag this shim as a method call into Rust, so the first Rust argument
@ -100,7 +102,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
self self
} }
pub fn argument(&mut self, arg: &Descriptor) -> &mut Self { pub fn argument(&mut self, arg: &Descriptor) -> Result<&mut Self, Error> {
let i = self.arg_idx; let i = self.arg_idx;
self.arg_idx += 1; self.arg_idx += 1;
let name = format!("arg{}", i); let name = format!("arg{}", i);
@ -108,8 +110,8 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
if let Some(kind) = arg.vector_kind() { if let Some(kind) = arg.vector_kind() {
self.js_arguments.push((name.clone(), kind.js_ty().to_string())); self.js_arguments.push((name.clone(), kind.js_ty().to_string()));
let func = self.cx.pass_to_wasm_function(kind); let func = self.cx.pass_to_wasm_function(kind)?;
self.cx.expose_set_global_argument(); self.cx.expose_set_global_argument()?;
let global_idx = self.global_idx(); let global_idx = self.global_idx();
self.prelude(&format!("\ self.prelude(&format!("\
const [ptr{i}, len{i}] = {func}({arg});\n\ const [ptr{i}, len{i}] = {func}({arg});\n\
@ -119,10 +121,10 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
self.finally(&format!("\ self.finally(&format!("\
wasm.__wbindgen_free(ptr{i}, len{i} * {size});\n\ wasm.__wbindgen_free(ptr{i}, len{i} * {size});\n\
", i = i, size = kind.size())); ", i = i, size = kind.size()));
self.cx.require_internal_export("__wbindgen_free"); self.cx.require_internal_export("__wbindgen_free")?;
} }
self.rust_arguments.push(format!("ptr{}", i)); self.rust_arguments.push(format!("ptr{}", i));
return self return Ok(self)
} }
if let Some(s) = arg.rust_struct() { if let Some(s) = arg.rust_struct() {
@ -144,7 +146,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
", i = i, arg = name)); ", i = i, arg = name));
self.rust_arguments.push(format!("ptr{}", i)); self.rust_arguments.push(format!("ptr{}", i));
} }
return self return Ok(self)
} }
if arg.is_number() { if arg.is_number() {
@ -156,7 +158,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
} }
self.rust_arguments.push(name); self.rust_arguments.push(name);
return self return Ok(self)
} }
if arg.is_ref_anyref() { if arg.is_ref_anyref() {
@ -164,7 +166,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
self.cx.expose_borrowed_objects(); self.cx.expose_borrowed_objects();
self.finally("stack.pop();"); self.finally("stack.pop();");
self.rust_arguments.push(format!("addBorrowedObject({})", name)); self.rust_arguments.push(format!("addBorrowedObject({})", name));
return self return Ok(self)
} }
match *arg { match *arg {
@ -184,19 +186,19 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
self.rust_arguments.push(format!("addHeapObject({})", name)); self.rust_arguments.push(format!("addHeapObject({})", name));
} }
_ => { _ => {
panic!("unsupported argument to rust function {:?}", arg) bail!("unsupported argument to rust function {:?}", arg)
} }
} }
self Ok(self)
} }
pub fn ret(&mut self, ret: &Option<Descriptor>) -> &mut Self { pub fn ret(&mut self, ret: &Option<Descriptor>) -> Result<&mut Self, Error> {
let ty = match *ret { let ty = match *ret {
Some(ref t) => t, Some(ref t) => t,
None => { None => {
self.ret_ty = "void".to_string(); self.ret_ty = "void".to_string();
self.ret_expr = format!("return RET;"); self.ret_expr = format!("return RET;");
return self return Ok(self)
} }
}; };
@ -204,18 +206,18 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
self.ret_ty = "any".to_string(); self.ret_ty = "any".to_string();
self.cx.expose_get_object(); self.cx.expose_get_object();
self.ret_expr = format!("return getObject(RET);"); self.ret_expr = format!("return getObject(RET);");
return self return Ok(self)
} }
if ty.is_by_ref() { if ty.is_by_ref() {
panic!("cannot return references from Rust to JS yet") bail!("cannot return references from Rust to JS yet")
} }
if let Some(ty) = ty.vector_kind() { if let Some(ty) = ty.vector_kind() {
self.ret_ty = ty.js_ty().to_string(); self.ret_ty = ty.js_ty().to_string();
let f = self.cx.expose_get_vector_from_wasm(ty); let f = self.cx.expose_get_vector_from_wasm(ty);
self.cx.expose_get_global_argument(); self.cx.expose_get_global_argument()?;
self.cx.require_internal_export("__wbindgen_free"); self.cx.require_internal_export("__wbindgen_free")?;
self.ret_expr = format!("\ self.ret_expr = format!("\
const ret = RET;\n\ const ret = RET;\n\
const len = getGlobalArgument(0);\n\ const len = getGlobalArgument(0);\n\
@ -223,19 +225,19 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
wasm.__wbindgen_free(ret, len * {});\n\ wasm.__wbindgen_free(ret, len * {});\n\
return realRet;\n\ return realRet;\n\
", f, ty.size()); ", f, ty.size());
return self return Ok(self)
} }
if let Some(name) = ty.rust_struct() { if let Some(name) = ty.rust_struct() {
self.ret_ty = name.to_string(); self.ret_ty = name.to_string();
self.ret_expr = format!("return {name}.__construct(RET);", name = name); self.ret_expr = format!("return {name}.__construct(RET);", name = name);
return self return Ok(self)
} }
if ty.is_number() { if ty.is_number() {
self.ret_ty = "number".to_string(); self.ret_ty = "number".to_string();
self.ret_expr = format!("return RET;"); self.ret_expr = format!("return RET;");
return self return Ok(self)
} }
match *ty { match *ty {
@ -248,9 +250,9 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
self.cx.expose_take_object(); self.cx.expose_take_object();
self.ret_expr = format!("return takeObject(RET);"); self.ret_expr = format!("return takeObject(RET);");
} }
_ => panic!("unsupported return from Rust to JS {:?}", ty), _ => bail!("unsupported return from Rust to JS {:?}", ty),
} }
self Ok(self)
} }
/// Generate the actual function. /// Generate the actual function.

View File

@ -2,6 +2,7 @@ use std::collections::{HashSet, HashMap};
use std::fmt::Write; use std::fmt::Write;
use std::mem; use std::mem;
use failure::{Error, ResultExt};
use parity_wasm::elements::*; use parity_wasm::elements::*;
use parity_wasm; use parity_wasm;
use shared; use shared;
@ -68,33 +69,38 @@ impl<'a> Context<'a> {
self.global(&global); self.global(&global);
} }
fn require_internal_export(&mut self, name: &'static str) { fn require_internal_export(&mut self, name: &'static str)
-> Result<(), Error>
{
if !self.required_internal_exports.insert(name) { if !self.required_internal_exports.insert(name) {
return return Ok(())
} }
if let Some(s) = self.module.export_section() { if let Some(s) = self.module.export_section() {
if s.entries().iter().any(|e| e.field() == name) { if s.entries().iter().any(|e| e.field() == name) {
return return Ok(())
} }
} }
panic!("\n\nthe exported function `{}` is required to generate bindings \ bail!("\n\nthe exported function `{}` is required to generate bindings \
but it was not found in the wasm file, perhaps the `std` feature \ but it was not found in the wasm file, perhaps the `std` feature \
of the `wasm-bindgen` crate needs to be enabled?\n\n", of the `wasm-bindgen` crate needs to be enabled?\n\n",
name); name);
} }
pub fn finalize(&mut self, module_name: &str) -> (String, String) { pub fn finalize(&mut self, module_name: &str) -> Result<(String, String), Error> {
self.unexport_unused_internal_exports(); self.unexport_unused_internal_exports();
self.gc(); self.gc()?;
self.write_classes(); self.write_classes()?;
{ {
let mut bind = |name: &str, f: &Fn(&mut Self) -> String| { let mut bind = |name: &str, f: &Fn(&mut Self) -> Result<String, Error>|
-> Result<(), Error>
{
if !self.wasm_import_needed(name) { if !self.wasm_import_needed(name) {
return; return Ok(());
} }
let contents = f(self); let contents = f(self)?;
self.export(name, &contents); self.export(name, &contents);
Ok(())
}; };
bind("__wbindgen_object_clone_ref", &|me| { bind("__wbindgen_object_clone_ref", &|me| {
@ -109,7 +115,7 @@ impl<'a> Context<'a> {
} else { } else {
String::from("val.cnt += 1;") String::from("val.cnt += 1;")
}; };
format!(" Ok(format!("
function(idx) {{ function(idx) {{
// If this object is on the stack promote it to the heap. // If this object is on the stack promote it to the heap.
if ((idx & 1) === 1) if ((idx & 1) === 1)
@ -121,33 +127,33 @@ impl<'a> Context<'a> {
{} {}
return idx; return idx;
}} }}
", bump_cnt) ", bump_cnt))
}); })?;
bind("__wbindgen_object_drop_ref", &|me| { bind("__wbindgen_object_drop_ref", &|me| {
me.expose_drop_ref(); me.expose_drop_ref();
"function(i) { dropRef(i); }".to_string() Ok("function(i) { dropRef(i); }".to_string())
}); })?;
bind("__wbindgen_string_new", &|me| { bind("__wbindgen_string_new", &|me| {
me.expose_add_heap_object(); me.expose_add_heap_object();
me.expose_get_string_from_wasm(); me.expose_get_string_from_wasm();
String::from(" Ok(String::from("
function(p, l) { function(p, l) {
return addHeapObject(getStringFromWasm(p, l)); return addHeapObject(getStringFromWasm(p, l));
} }
") "))
}); })?;
bind("__wbindgen_number_new", &|me| { bind("__wbindgen_number_new", &|me| {
me.expose_add_heap_object(); me.expose_add_heap_object();
String::from("function(i) { return addHeapObject(i); }") Ok(String::from("function(i) { return addHeapObject(i); }"))
}); })?;
bind("__wbindgen_number_get", &|me| { bind("__wbindgen_number_get", &|me| {
me.expose_get_object(); me.expose_get_object();
me.expose_uint8_memory(); me.expose_uint8_memory();
format!(" Ok(format!("
function(n, invalid) {{ function(n, invalid) {{
let obj = getObject(n); let obj = getObject(n);
if (typeof(obj) === 'number') if (typeof(obj) === 'number')
@ -155,53 +161,53 @@ impl<'a> Context<'a> {
getUint8Memory()[invalid] = 1; getUint8Memory()[invalid] = 1;
return 0; return 0;
}} }}
") "))
}); })?;
bind("__wbindgen_undefined_new", &|me| { bind("__wbindgen_undefined_new", &|me| {
me.expose_add_heap_object(); me.expose_add_heap_object();
String::from("function() { return addHeapObject(undefined); }") Ok(String::from("function() { return addHeapObject(undefined); }"))
}); })?;
bind("__wbindgen_null_new", &|me| { bind("__wbindgen_null_new", &|me| {
me.expose_add_heap_object(); me.expose_add_heap_object();
String::from(" Ok(String::from("
function() { function() {
return addHeapObject(null); return addHeapObject(null);
} }
") "))
}); })?;
bind("__wbindgen_is_null", &|me| { bind("__wbindgen_is_null", &|me| {
me.expose_get_object(); me.expose_get_object();
String::from(" Ok(String::from("
function(idx) { function(idx) {
return getObject(idx) === null ? 1 : 0; return getObject(idx) === null ? 1 : 0;
} }
") "))
}); })?;
bind("__wbindgen_is_undefined", &|me| { bind("__wbindgen_is_undefined", &|me| {
me.expose_get_object(); me.expose_get_object();
String::from(" Ok(String::from("
function(idx) { function(idx) {
return getObject(idx) === undefined ? 1 : 0; return getObject(idx) === undefined ? 1 : 0;
} }
") "))
}); })?;
bind("__wbindgen_boolean_new", &|me| { bind("__wbindgen_boolean_new", &|me| {
me.expose_add_heap_object(); me.expose_add_heap_object();
String::from(" Ok(String::from("
function(v) { function(v) {
return addHeapObject(v === 1); return addHeapObject(v === 1);
} }
") "))
}); })?;
bind("__wbindgen_boolean_get", &|me| { bind("__wbindgen_boolean_get", &|me| {
me.expose_get_object(); me.expose_get_object();
String::from(" Ok(String::from("
function(i) { function(i) {
let v = getObject(i); let v = getObject(i);
if (typeof(v) === 'boolean') { if (typeof(v) === 'boolean') {
@ -210,13 +216,13 @@ impl<'a> Context<'a> {
return 2; return 2;
} }
} }
") "))
}); })?;
bind("__wbindgen_symbol_new", &|me| { bind("__wbindgen_symbol_new", &|me| {
me.expose_get_string_from_wasm(); me.expose_get_string_from_wasm();
me.expose_add_heap_object(); me.expose_add_heap_object();
format!(" Ok(format!("
function(ptr, len) {{ function(ptr, len) {{
let a; let a;
console.log(ptr, len); console.log(ptr, len);
@ -227,32 +233,32 @@ impl<'a> Context<'a> {
}} }}
return addHeapObject(a); return addHeapObject(a);
}} }}
") "))
}); })?;
bind("__wbindgen_is_symbol", &|me| { bind("__wbindgen_is_symbol", &|me| {
me.expose_get_object(); me.expose_get_object();
String::from(" Ok(String::from("
function(i) { function(i) {
return typeof(getObject(i)) === 'symbol' ? 1 : 0; return typeof(getObject(i)) === 'symbol' ? 1 : 0;
} }
") "))
}); })?;
bind("__wbindgen_throw", &|me| { bind("__wbindgen_throw", &|me| {
me.expose_get_string_from_wasm(); me.expose_get_string_from_wasm();
format!(" Ok(format!("
function(ptr, len) {{ function(ptr, len) {{
throw new Error(getStringFromWasm(ptr, len)); throw new Error(getStringFromWasm(ptr, len));
}} }}
") "))
}); })?;
bind("__wbindgen_string_get", &|me| { bind("__wbindgen_string_get", &|me| {
me.expose_pass_string_to_wasm(); me.expose_pass_string_to_wasm()?;
me.expose_get_object(); me.expose_get_object();
me.expose_uint32_memory(); me.expose_uint32_memory();
String::from(" Ok(String::from("
function(i, len_ptr) { function(i, len_ptr) {
let obj = getObject(i); let obj = getObject(i);
if (typeof(obj) !== 'string') if (typeof(obj) !== 'string')
@ -261,27 +267,27 @@ impl<'a> Context<'a> {
getUint32Memory()[len_ptr / 4] = len; getUint32Memory()[len_ptr / 4] = len;
return ptr; return ptr;
} }
") "))
}); })?;
bind("__wbindgen_cb_drop", &|me| { bind("__wbindgen_cb_drop", &|me| {
me.expose_drop_ref(); me.expose_drop_ref();
String::from(" Ok(String::from("
function(i) { function(i) {
let obj = getObject(i).original; let obj = getObject(i).original;
obj.a = obj.b = 0; obj.a = obj.b = 0;
dropRef(i); dropRef(i);
} }
") "))
}); })?;
bind("__wbindgen_cb_forget", &|me| { bind("__wbindgen_cb_forget", &|me| {
me.expose_drop_ref(); me.expose_drop_ref();
String::from(" Ok(String::from("
function(i) { function(i) {
dropRef(i); dropRef(i);
} }
") "))
}); })?;
} }
self.rewrite_imports(module_name); self.rewrite_imports(module_name);
@ -335,23 +341,24 @@ impl<'a> Context<'a> {
}; };
self.export_table(); self.export_table();
self.gc(); self.gc()?;
while js.contains("\n\n\n") { while js.contains("\n\n\n") {
js = js.replace("\n\n\n", "\n\n"); js = js.replace("\n\n\n", "\n\n");
} }
(js, self.typescript.clone()) Ok((js, self.typescript.clone()))
} }
fn write_classes(&mut self) { fn write_classes(&mut self) -> Result<(), Error> {
let classes = mem::replace(&mut self.exported_classes, Default::default()); let classes = mem::replace(&mut self.exported_classes, Default::default());
for (class, exports) in classes { for (class, exports) in classes {
self.write_class(&class, &exports); self.write_class(&class, &exports)?;
} }
Ok(())
} }
fn write_class(&mut self, name: &str, class: &ExportedClass) { fn write_class(&mut self, name: &str, class: &ExportedClass) -> Result<(), Error> {
let mut dst = format!("class {} {{\n", name); let mut dst = format!("class {} {{\n", name);
let mut ts_dst = format!("export {}", dst); let mut ts_dst = format!("export {}", dst);
@ -415,8 +422,8 @@ impl<'a> Context<'a> {
let set = { let set = {
let mut cx = Js2Rust::new(&field.name, self); let mut cx = Js2Rust::new(&field.name, self);
cx.method(true) cx.method(true)
.argument(&descriptor) .argument(&descriptor)?
.ret(&None); .ret(&None)?;
ts_dst.push_str(&format!("{}{}: {}\n", ts_dst.push_str(&format!("{}{}: {}\n",
if field.readonly { "readonly " } else { "" }, if field.readonly { "readonly " } else { "" },
field.name, field.name,
@ -425,7 +432,7 @@ impl<'a> Context<'a> {
}; };
let (get, _ts) = Js2Rust::new(&field.name, self) let (get, _ts) = Js2Rust::new(&field.name, self)
.method(true) .method(true)
.ret(&Some(descriptor)) .ret(&Some(descriptor))?
.finish("", &format!("wasm.{}", wasm_getter)); .finish("", &format!("wasm.{}", wasm_getter));
dst.push_str("get "); dst.push_str("get ");
@ -455,6 +462,8 @@ impl<'a> Context<'a> {
self.export(&name, &dst); self.export(&name, &dst);
self.typescript.push_str(&ts_dst); self.typescript.push_str(&ts_dst);
Ok(())
} }
fn export_table(&mut self) { fn export_table(&mut self) {
@ -697,11 +706,11 @@ impl<'a> Context<'a> {
")); "));
} }
fn expose_pass_string_to_wasm(&mut self) { fn expose_pass_string_to_wasm(&mut self) -> Result<(), Error> {
if !self.exposed_globals.insert("pass_string_to_wasm") { if !self.exposed_globals.insert("pass_string_to_wasm") {
return; return Ok(());
} }
self.require_internal_export("__wbindgen_malloc"); self.require_internal_export("__wbindgen_malloc")?;
self.expose_text_encoder(); self.expose_text_encoder();
self.expose_uint8_memory(); self.expose_uint8_memory();
let debug = if self.config.debug { let debug = if self.config.debug {
@ -721,13 +730,14 @@ impl<'a> Context<'a> {
return [ptr, buf.length]; return [ptr, buf.length];
}} }}
", debug)); ", debug));
Ok(())
} }
fn expose_pass_array8_to_wasm(&mut self) { fn expose_pass_array8_to_wasm(&mut self) -> Result<(), Error> {
if !self.exposed_globals.insert("pass_array8_to_wasm") { if !self.exposed_globals.insert("pass_array8_to_wasm") {
return; return Ok(());
} }
self.require_internal_export("__wbindgen_malloc"); self.require_internal_export("__wbindgen_malloc")?;
self.expose_uint8_memory(); self.expose_uint8_memory();
self.global(&format!(" self.global(&format!("
function passArray8ToWasm(arg) {{ function passArray8ToWasm(arg) {{
@ -736,13 +746,14 @@ impl<'a> Context<'a> {
return [ptr, arg.length]; return [ptr, arg.length];
}} }}
")); "));
Ok(())
} }
fn expose_pass_array16_to_wasm(&mut self) { fn expose_pass_array16_to_wasm(&mut self) -> Result<(), Error> {
if !self.exposed_globals.insert("pass_array16_to_wasm") { if !self.exposed_globals.insert("pass_array16_to_wasm") {
return; return Ok(());
} }
self.require_internal_export("__wbindgen_malloc"); self.require_internal_export("__wbindgen_malloc")?;
self.expose_uint16_memory(); self.expose_uint16_memory();
self.global(&format!(" self.global(&format!("
function passArray16ToWasm(arg) {{ function passArray16ToWasm(arg) {{
@ -751,13 +762,14 @@ impl<'a> Context<'a> {
return [ptr, arg.length]; return [ptr, arg.length];
}} }}
")); "));
Ok(())
} }
fn expose_pass_array32_to_wasm(&mut self) { fn expose_pass_array32_to_wasm(&mut self) -> Result<(), Error> {
if !self.exposed_globals.insert("pass_array32_to_wasm") { if !self.exposed_globals.insert("pass_array32_to_wasm") {
return; return Ok(())
} }
self.require_internal_export("__wbindgen_malloc"); self.require_internal_export("__wbindgen_malloc")?;
self.expose_uint32_memory(); self.expose_uint32_memory();
self.global(&format!(" self.global(&format!("
function passArray32ToWasm(arg) {{ function passArray32ToWasm(arg) {{
@ -766,13 +778,14 @@ impl<'a> Context<'a> {
return [ptr, arg.length]; return [ptr, arg.length];
}} }}
")); "));
Ok(())
} }
fn expose_pass_array_f32_to_wasm(&mut self) { fn expose_pass_array_f32_to_wasm(&mut self) -> Result<(), Error> {
if !self.exposed_globals.insert("pass_array_f32_to_wasm") { if !self.exposed_globals.insert("pass_array_f32_to_wasm") {
return; return Ok(())
} }
self.require_internal_export("__wbindgen_malloc"); self.require_internal_export("__wbindgen_malloc")?;
self.global(&format!(" self.global(&format!("
function passArrayF32ToWasm(arg) {{ function passArrayF32ToWasm(arg) {{
const ptr = wasm.__wbindgen_malloc(arg.length * 4); const ptr = wasm.__wbindgen_malloc(arg.length * 4);
@ -780,13 +793,14 @@ impl<'a> Context<'a> {
return [ptr, arg.length]; return [ptr, arg.length];
}} }}
")); "));
Ok(())
} }
fn expose_pass_array_f64_to_wasm(&mut self) { fn expose_pass_array_f64_to_wasm(&mut self) -> Result<(), Error> {
if !self.exposed_globals.insert("pass_array_f64_to_wasm") { if !self.exposed_globals.insert("pass_array_f64_to_wasm") {
return; return Ok(())
} }
self.require_internal_export("__wbindgen_malloc"); self.require_internal_export("__wbindgen_malloc")?;
self.global(&format!(" self.global(&format!("
function passArrayF64ToWasm(arg) {{ function passArrayF64ToWasm(arg) {{
const ptr = wasm.__wbindgen_malloc(arg.length * 8); const ptr = wasm.__wbindgen_malloc(arg.length * 8);
@ -794,6 +808,7 @@ impl<'a> Context<'a> {
return [ptr, arg.length]; return [ptr, arg.length];
}} }}
")); "));
Ok(())
} }
fn expose_text_encoder(&mut self) { fn expose_text_encoder(&mut self) {
@ -1119,39 +1134,40 @@ impl<'a> Context<'a> {
}) })
} }
fn pass_to_wasm_function(&mut self, t: VectorKind) -> &'static str { fn pass_to_wasm_function(&mut self, t: VectorKind) -> Result<&'static str, Error> {
match t { let s = match t {
VectorKind::String => { VectorKind::String => {
self.expose_pass_string_to_wasm(); self.expose_pass_string_to_wasm()?;
"passStringToWasm" "passStringToWasm"
} }
VectorKind::I8 | VectorKind::I8 |
VectorKind::U8 => { VectorKind::U8 => {
self.expose_pass_array8_to_wasm(); self.expose_pass_array8_to_wasm()?;
"passArray8ToWasm" "passArray8ToWasm"
} }
VectorKind::U16 | VectorKind::U16 |
VectorKind::I16 => { VectorKind::I16 => {
self.expose_pass_array16_to_wasm(); self.expose_pass_array16_to_wasm()?;
"passArray16ToWasm" "passArray16ToWasm"
} }
VectorKind::I32 | VectorKind::I32 |
VectorKind::U32 => { VectorKind::U32 => {
self.expose_pass_array32_to_wasm(); self.expose_pass_array32_to_wasm()?;
"passArray32ToWasm" "passArray32ToWasm"
} }
VectorKind::F32 => { VectorKind::F32 => {
self.expose_pass_array_f32_to_wasm(); self.expose_pass_array_f32_to_wasm()?;
"passArrayF32ToWasm" "passArrayF32ToWasm"
} }
VectorKind::F64 => { VectorKind::F64 => {
self.expose_pass_array_f64_to_wasm(); self.expose_pass_array_f64_to_wasm()?;
"passArrayF64ToWasm" "passArrayF64ToWasm"
} }
VectorKind::Anyref => { VectorKind::Anyref => {
panic!("cannot pass list of JsValue to wasm yet") bail!("cannot pass list of JsValue to wasm yet")
} }
} };
Ok(s)
} }
fn expose_get_vector_from_wasm(&mut self, ty: VectorKind) -> &'static str { fn expose_get_vector_from_wasm(&mut self, ty: VectorKind) -> &'static str {
@ -1199,39 +1215,41 @@ impl<'a> Context<'a> {
} }
} }
fn expose_set_global_argument(&mut self) { fn expose_set_global_argument(&mut self) -> Result<(), Error> {
if !self.exposed_globals.insert("set_global_argument") { if !self.exposed_globals.insert("set_global_argument") {
return; return Ok(());
} }
self.expose_uint32_memory(); self.expose_uint32_memory();
self.expose_global_argument_ptr(); self.expose_global_argument_ptr()?;
self.global(" self.global("
function setGlobalArgument(arg, i) { function setGlobalArgument(arg, i) {
const idx = globalArgumentPtr() / 4 + i; const idx = globalArgumentPtr() / 4 + i;
getUint32Memory()[idx] = arg; getUint32Memory()[idx] = arg;
} }
"); ");
Ok(())
} }
fn expose_get_global_argument(&mut self) { fn expose_get_global_argument(&mut self) -> Result<(), Error> {
if !self.exposed_globals.insert("get_global_argument") { if !self.exposed_globals.insert("get_global_argument") {
return; return Ok(());
} }
self.expose_uint32_memory(); self.expose_uint32_memory();
self.expose_global_argument_ptr(); self.expose_global_argument_ptr()?;
self.global(" self.global("
function getGlobalArgument(arg) { function getGlobalArgument(arg) {
const idx = globalArgumentPtr() / 4 + arg; const idx = globalArgumentPtr() / 4 + arg;
return getUint32Memory()[idx]; return getUint32Memory()[idx];
} }
"); ");
Ok(())
} }
fn expose_global_argument_ptr(&mut self) { fn expose_global_argument_ptr(&mut self) -> Result<(), Error> {
if !self.exposed_globals.insert("global_argument_ptr") { if !self.exposed_globals.insert("global_argument_ptr") {
return; return Ok(());
} }
self.require_internal_export("__wbindgen_global_argument_ptr"); self.require_internal_export("__wbindgen_global_argument_ptr")?;
self.global(" self.global("
let cachedGlobalArgumentPtr = null; let cachedGlobalArgumentPtr = null;
function globalArgumentPtr() { function globalArgumentPtr() {
@ -1240,6 +1258,7 @@ impl<'a> Context<'a> {
return cachedGlobalArgumentPtr; return cachedGlobalArgumentPtr;
} }
"); ");
Ok(())
} }
fn expose_get_inherited_descriptor(&mut self) { fn expose_get_inherited_descriptor(&mut self) {
@ -1267,14 +1286,14 @@ impl<'a> Context<'a> {
"); ");
} }
fn gc(&mut self) { fn gc(&mut self) -> Result<(), Error> {
let module = mem::replace(self.module, Module::default()); let module = mem::replace(self.module, Module::default());
let wasm_bytes = parity_wasm::serialize(module).unwrap(); let wasm_bytes = parity_wasm::serialize(module)?;
let bytes = wasm_gc::Config::new() let bytes = wasm_gc::Config::new()
.demangle(self.config.demangle) .demangle(self.config.demangle)
.gc(&wasm_bytes) .gc(&wasm_bytes)?;
.unwrap(); *self.module = deserialize_buffer(&bytes)?;
*self.module = deserialize_buffer(&bytes).unwrap(); Ok(())
} }
fn describe(&self, name: &str) -> Descriptor { fn describe(&self, name: &str) -> Descriptor {
@ -1298,12 +1317,16 @@ impl<'a> Context<'a> {
} }
impl<'a, 'b> SubContext<'a, 'b> { impl<'a, 'b> SubContext<'a, 'b> {
pub fn generate(&mut self) { pub fn generate(&mut self) -> Result<(), Error> {
for f in self.program.exports.iter() { for f in self.program.exports.iter() {
self.generate_export(f); self.generate_export(f)
.with_context(|_| {
format!("failed to generate bindings for Rust export `{}`",
f.function.name)
})?;
} }
for f in self.program.imports.iter() { for f in self.program.imports.iter() {
self.generate_import(f); self.generate_import(f)?;
} }
for e in self.program.enums.iter() { for e in self.program.enums.iter() {
self.generate_enum(e); self.generate_enum(e);
@ -1320,29 +1343,38 @@ impl<'a, 'b> SubContext<'a, 'b> {
} }
})); }));
} }
Ok(())
} }
pub fn generate_export(&mut self, export: &shared::Export) { fn generate_export(&mut self, export: &shared::Export)
-> 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);
} }
let descriptor = self.cx.describe(&export.function.name); let descriptor = self.cx.describe(&export.function.name);
let (js, ts) = Js2Rust::new(&export.function.name, self.cx) let (js, ts) = Js2Rust::new(&export.function.name, self.cx)
.process(descriptor.unwrap_function()) .process(descriptor.unwrap_function())?
.finish("function", &format!("wasm.{}", export.function.name)); .finish("function", &format!("wasm.{}", export.function.name));
self.cx.export(&export.function.name, &js); self.cx.export(&export.function.name, &js);
self.cx.globals.push_str("\n"); self.cx.globals.push_str("\n");
self.cx.typescript.push_str("export "); self.cx.typescript.push_str("export ");
self.cx.typescript.push_str(&ts); self.cx.typescript.push_str(&ts);
self.cx.typescript.push_str("\n"); self.cx.typescript.push_str("\n");
Ok(())
} }
pub fn generate_export_for_class(&mut self, class_name: &str, export: &shared::Export) { fn generate_export_for_class(
&mut self,
class_name: &str,
export: &shared::Export,
) -> 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);
let descriptor = self.cx.describe(&wasm_name); let descriptor = self.cx.describe(&wasm_name);
let (js, ts) = Js2Rust::new(&export.function.name, self.cx) let (js, ts) = Js2Rust::new(&export.function.name, self.cx)
.method(export.method) .method(export.method)
.process(descriptor.unwrap_function()) .process(descriptor.unwrap_function())?
.finish("", &format!("wasm.{}", wasm_name)); .finish("", &format!("wasm.{}", wasm_name));
let class = self.cx.exported_classes.entry(class_name.to_string()) let class = self.cx.exported_classes.entry(class_name.to_string())
.or_insert(ExportedClass::default()); .or_insert(ExportedClass::default());
@ -1360,7 +1392,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
class.constructor = match constructors.len() { class.constructor = match constructors.len() {
0 => None, 0 => None,
1 => Some(constructors[0].clone()), 1 => Some(constructors[0].clone()),
x @ _ => panic!("There must be only one constructor, not {}", x), x @ _ => bail!("there must be only one constructor, not {}", x),
}; };
class.contents.push_str(&export.function.name); class.contents.push_str(&export.function.name);
@ -1368,44 +1400,61 @@ impl<'a, 'b> SubContext<'a, 'b> {
class.contents.push_str("\n"); class.contents.push_str("\n");
class.typescript.push_str(&ts); class.typescript.push_str(&ts);
class.typescript.push_str("\n"); class.typescript.push_str("\n");
Ok(())
} }
pub fn generate_import(&mut self, import: &shared::Import) { fn generate_import(&mut self, import: &shared::Import) -> Result<(), Error> {
match import.kind { match import.kind {
shared::ImportKind::Function(ref f) => { shared::ImportKind::Function(ref f) => {
self.generate_import_function(import, f) self.generate_import_function(import, f)
.with_context(|_| {
format!("failed to generate bindings for JS import `{}`",
f.function.name)
})?;
} }
shared::ImportKind::Static(ref s) => { shared::ImportKind::Static(ref s) => {
self.generate_import_static(import, s) self.generate_import_static(import, s)
.with_context(|_| {
format!("failed to generate bindings for JS import `{}`",
s.name)
})?;
} }
shared::ImportKind::Type(_) => {} shared::ImportKind::Type(_) => {}
} }
Ok(())
} }
pub fn generate_import_static(&mut self, fn generate_import_static(
info: &shared::Import, &mut self,
import: &shared::ImportStatic) { info: &shared::Import,
import: &shared::ImportStatic,
)
-> Result<(), Error>
{
// TODO: should support more types to import here // TODO: should support more types to import here
let obj = self.import_name(info, &import.name); let obj = self.import_name(info, &import.name)?;
self.cx.expose_add_heap_object(); self.cx.expose_add_heap_object();
self.cx.export(&import.shim, &format!(" self.cx.export(&import.shim, &format!("
function() {{ function() {{
return addHeapObject({}); return addHeapObject({});
}} }}
", obj)); ", obj));
Ok(())
} }
pub fn generate_import_function(&mut self, fn generate_import_function(&mut self,
info: &shared::Import, info: &shared::Import,
import: &shared::ImportFunction) { import: &shared::ImportFunction)
-> Result<(), Error>
{
let descriptor = self.cx.describe(&import.shim); let descriptor = self.cx.describe(&import.shim);
let target = match import.class { let target = match import.class {
Some(ref class) if import.js_new => { Some(ref class) if import.js_new => {
format!("new {}", self.import_name(info, class)) format!("new {}", self.import_name(info, class)?)
} }
Some(ref class) if import.method => { Some(ref class) if import.method => {
let class = self.import_name(info, class); let class = self.import_name(info, class)?;
let target = if let Some(ref g) = import.getter { let target = if let Some(ref g) = import.getter {
if import.structural { if import.structural {
format!("function() {{ return this.{}; }}", g) format!("function() {{ return this.{}; }}", g)
@ -1461,14 +1510,14 @@ impl<'a, 'b> SubContext<'a, 'b> {
format!("{}_target.call", import.shim) format!("{}_target.call", import.shim)
} }
Some(ref class) => { Some(ref class) => {
let class = self.import_name(info, class); let class = self.import_name(info, class)?;
self.cx.global(&format!(" self.cx.global(&format!("
const {}_target = {}.{}; const {}_target = {}.{};
", import.shim, class, import.function.name)); ", import.shim, class, import.function.name));
format!("{}_target", import.shim) format!("{}_target", import.shim)
} }
None => { None => {
let name = self.import_name(info, &import.function.name); let name = self.import_name(info, &import.function.name)?;
if name.contains(".") { if name.contains(".") {
self.cx.global(&format!(" self.cx.global(&format!("
const {}_target = {}; const {}_target = {};
@ -1482,12 +1531,13 @@ impl<'a, 'b> SubContext<'a, 'b> {
let js = Rust2Js::new(self.cx) let js = Rust2Js::new(self.cx)
.catch(import.catch) .catch(import.catch)
.process(descriptor.unwrap_function()) .process(descriptor.unwrap_function())?
.finish(&target); .finish(&target);
self.cx.export(&import.shim, &js); self.cx.export(&import.shim, &js);
Ok(())
} }
pub fn generate_enum(&mut self, enum_: &shared::Enum) { fn generate_enum(&mut self, enum_: &shared::Enum) {
let mut variants = String::new(); let mut variants = String::new();
for variant in enum_.variants.iter() { for variant in enum_.variants.iter() {
@ -1504,10 +1554,13 @@ impl<'a, 'b> SubContext<'a, 'b> {
self.cx.typescript.push_str("}\n"); self.cx.typescript.push_str("}\n");
} }
fn import_name(&mut self, import: &shared::Import, item: &str) -> String { fn import_name(&mut self, import: &shared::Import, item: &str)
-> Result<String, Error>
{
if let Some(ref module) = import.module { if let Some(ref module) = import.module {
if self.cx.config.no_modules { if self.cx.config.no_modules {
panic!("import from `{}` module not allowed in `--no-modules`. use `--nodejs` or `--browser` instead", module); bail!("import from `{}` module not allowed with `--no-modules`; \
use `--nodejs` or `--browser` instead", module);
} }
let name = import.js_namespace.as_ref().map(|s| &**s).unwrap_or(item); let name = import.js_namespace.as_ref().map(|s| &**s).unwrap_or(item);
@ -1524,10 +1577,10 @@ impl<'a, 'b> SubContext<'a, 'b> {
} }
} }
} }
match import.js_namespace { Ok(match import.js_namespace {
Some(ref s) => format!("{}.{}", s, item), Some(ref s) => format!("{}.{}", s, item),
None => item.to_string(), None => item.to_string(),
} })
} }
} }

View File

@ -1,7 +1,7 @@
use super::Context; use failure::Error;
use descriptor::{Descriptor, Function};
use super::{indent, Js2Rust}; use descriptor::{Descriptor, Function};
use super::{indent, Context, Js2Rust};
/// Helper struct for manfuacturing a shim in JS used to translate Rust types to /// Helper struct for manfuacturing a shim in JS used to translate Rust types to
/// JS, then invoking an imported JS function. /// JS, then invoking an imported JS function.
@ -64,15 +64,15 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
/// Generates all bindings necessary for the signature in `Function`, /// Generates all bindings necessary for the signature in `Function`,
/// creating necessary argument conversions and return value processing. /// creating necessary argument conversions and return value processing.
pub fn process(&mut self, function: &Function) -> &mut Self { pub fn process(&mut self, function: &Function) -> Result<&mut Self, Error> {
for arg in function.arguments.iter() { for arg in function.arguments.iter() {
self.argument(arg); self.argument(arg)?;
} }
self.ret(&function.ret); self.ret(&function.ret)?;
self Ok(self)
} }
fn argument(&mut self, arg: &Descriptor) { fn argument(&mut self, arg: &Descriptor) -> Result<(), Error> {
let i = self.arg_idx; let i = self.arg_idx;
self.arg_idx += 1; self.arg_idx += 1;
@ -80,7 +80,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
if let Some(ty) = arg.vector_kind() { if let Some(ty) = arg.vector_kind() {
let f = self.cx.expose_get_vector_from_wasm(ty); let f = self.cx.expose_get_vector_from_wasm(ty);
self.cx.expose_get_global_argument(); self.cx.expose_get_global_argument()?;
let next_global = self.global_idx(); let next_global = self.global_idx();
self.prelude(&format!("\ self.prelude(&format!("\
let len{0} = getGlobalArgument({next_global});\n\ let len{0} = getGlobalArgument({next_global});\n\
@ -91,20 +91,20 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
self.prelude(&format!("\ self.prelude(&format!("\
wasm.__wbindgen_free(arg{0}, len{0} * {size});\ wasm.__wbindgen_free(arg{0}, len{0} * {size});\
", i, size = ty.size())); ", i, size = ty.size()));
self.cx.require_internal_export("__wbindgen_free"); self.cx.require_internal_export("__wbindgen_free")?;
} }
self.js_arguments.push(format!("v{}", i)); self.js_arguments.push(format!("v{}", i));
return return Ok(())
} }
if let Some(class) = arg.rust_struct() { if let Some(class) = arg.rust_struct() {
if arg.is_by_ref() { if arg.is_by_ref() {
panic!("cannot invoke JS functions with custom ref types yet") bail!("cannot invoke JS functions with custom ref types yet")
} }
let assign = format!("let c{0} = {1}.__construct(arg{0});", i, class); let assign = format!("let c{0} = {1}.__construct(arg{0});", i, class);
self.prelude(&assign); self.prelude(&assign);
self.js_arguments.push(format!("c{}", i)); self.js_arguments.push(format!("c{}", i));
return return Ok(())
} }
if let Some((f, mutable)) = arg.stack_closure() { if let Some((f, mutable)) = arg.stack_closure() {
@ -120,10 +120,10 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
} }
builder builder
.rust_argument("this.b") .rust_argument("this.b")
.process(f) .process(f)?
.finish("function", "this.f") .finish("function", "this.f")
}; };
self.cx.expose_get_global_argument(); self.cx.expose_get_global_argument()?;
self.cx.function_table_needed = true; self.cx.function_table_needed = true;
let next_global = self.global_idx(); let next_global = self.global_idx();
self.global_idx(); self.global_idx();
@ -135,7 +135,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
", i, js = js, next_global = next_global)); ", i, js = js, next_global = next_global));
self.finally(&format!("cb{0}.a = cb{0}.b = 0;", i)); self.finally(&format!("cb{0}.a = cb{0}.b = 0;", i));
self.js_arguments.push(format!("cb{0}.bind(cb{0})", i)); self.js_arguments.push(format!("cb{0}.bind(cb{0})", i));
return return Ok(())
} }
if let Some(closure) = arg.ref_closure() { if let Some(closure) = arg.ref_closure() {
@ -151,10 +151,10 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
} }
builder builder
.rust_argument("this.b") .rust_argument("this.b")
.process(&closure.function) .process(&closure.function)?
.finish("function", "this.f") .finish("function", "this.f")
}; };
self.cx.expose_get_global_argument(); self.cx.expose_get_global_argument()?;
self.cx.expose_uint32_memory(); self.cx.expose_uint32_memory();
self.cx.expose_add_heap_object(); self.cx.expose_add_heap_object();
self.cx.function_table_needed = true; self.cx.function_table_needed = true;
@ -181,7 +181,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
", i, indent(&reset_idx))); ", i, indent(&reset_idx)));
self.cx.expose_get_object(); self.cx.expose_get_object();
self.js_arguments.push(format!("getObject(idx{})", i)); self.js_arguments.push(format!("getObject(idx{})", i));
return return Ok(())
} }
let invoc_arg = match *arg { let invoc_arg = match *arg {
@ -195,36 +195,37 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
self.cx.expose_get_object(); self.cx.expose_get_object();
format!("getObject(arg{})", i) format!("getObject(arg{})", i)
} }
_ => panic!("unimplemented argument type in imported function: {:?}", arg), _ => bail!("unimplemented argument type in imported function: {:?}", arg),
}; };
self.js_arguments.push(invoc_arg); self.js_arguments.push(invoc_arg);
Ok(())
} }
fn ret(&mut self, ret: &Option<Descriptor>) { fn ret(&mut self, ret: &Option<Descriptor>) -> Result<(), Error> {
let ty = match *ret { let ty = match *ret {
Some(ref t) => t, Some(ref t) => t,
None => { None => {
self.ret_expr = "JS;".to_string(); self.ret_expr = "JS;".to_string();
return return Ok(())
} }
}; };
if ty.is_by_ref() { if ty.is_by_ref() {
panic!("cannot return a reference from JS to Rust") bail!("cannot return a reference from JS to Rust")
} }
if let Some(ty) = ty.vector_kind() { if let Some(ty) = ty.vector_kind() {
let f = self.cx.pass_to_wasm_function(ty); let f = self.cx.pass_to_wasm_function(ty)?;
self.cx.expose_uint32_memory(); self.cx.expose_uint32_memory();
self.cx.expose_set_global_argument(); self.cx.expose_set_global_argument()?;
self.ret_expr = format!("\ self.ret_expr = format!("\
const [retptr, retlen] = {}(JS);\n\ const [retptr, retlen] = {}(JS);\n\
setGlobalArgument(retlen, 0);\n\ setGlobalArgument(retlen, 0);\n\
return retptr;\n\ return retptr;\n\
", f); ", f);
return return Ok(())
} }
if ty.is_number() { if ty.is_number() {
self.ret_expr = "return JS;".to_string(); self.ret_expr = "return JS;".to_string();
return return Ok(())
} }
self.ret_expr = match *ty { self.ret_expr = match *ty {
Descriptor::Boolean => "return JS ? 1 : 0;".to_string(), Descriptor::Boolean => "return JS ? 1 : 0;".to_string(),
@ -232,8 +233,9 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
self.cx.expose_add_heap_object(); self.cx.expose_add_heap_object();
"return addHeapObject(JS);".to_string() "return addHeapObject(JS);".to_string()
} }
_ => panic!("unimplemented return from JS to Rust: {:?}", ty), _ => bail!("unimplemented return from JS to Rust: {:?}", ty),
} };
Ok(())
} }
pub fn finish(&self, invoc: &str) -> String { pub fn finish(&self, invoc: &str) -> String {

View File

@ -3,6 +3,8 @@ extern crate wasm_bindgen_shared as shared;
extern crate serde_json; extern crate serde_json;
extern crate wasm_gc; extern crate wasm_gc;
extern crate wasmi; extern crate wasmi;
#[macro_use]
extern crate failure;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::fmt; use std::fmt;
@ -10,6 +12,7 @@ use std::fs::File;
use std::io::Write; use std::io::Write;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use failure::{Error, ResultExt};
use parity_wasm::elements::*; use parity_wasm::elements::*;
mod js; mod js;
@ -27,15 +30,6 @@ pub struct Bindgen {
demangle: bool, demangle: bool,
} }
#[derive(Debug)]
pub struct Error(String);
impl<E: std::error::Error> From<E> for Error {
fn from(e: E) -> Error {
Error(e.to_string())
}
}
impl Bindgen { impl Bindgen {
pub fn new() -> Bindgen { pub fn new() -> Bindgen {
Bindgen { Bindgen {
@ -97,11 +91,13 @@ impl Bindgen {
fn _generate(&mut self, out_dir: &Path) -> Result<(), Error> { fn _generate(&mut self, out_dir: &Path) -> Result<(), Error> {
let input = match self.path { let input = match self.path {
Some(ref path) => path, Some(ref path) => path,
None => panic!("must have a path input for now"), None => bail!("must have a path input for now"),
}; };
let stem = input.file_stem().unwrap().to_str().unwrap(); let stem = input.file_stem().unwrap().to_str().unwrap();
let mut module = parity_wasm::deserialize_file(input)?; let mut module = parity_wasm::deserialize_file(input)
let programs = extract_programs(&mut module); .with_context(|_| "failed to parse input file as wasm")?;
let programs = extract_programs(&mut module)
.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
// execution. Why, you might be asking, are we executing wasm code? A // execution. Why, you might be asking, are we executing wasm code? A
@ -116,8 +112,10 @@ impl Bindgen {
// This means that whenever we encounter an import or export we'll // This means that whenever we encounter an import or export we'll
// execute a shim function which informs us about its type so we can // execute a shim function which informs us about its type so we can
// then generate the appropriate bindings. // then generate the appropriate bindings.
let instance = wasmi::Module::from_parity_wasm_module(module.clone())?; let instance = wasmi::Module::from_parity_wasm_module(module.clone())
let instance = wasmi::ModuleInstance::new(&instance, &MyResolver)?; .with_context(|_| "failed to create wasmi module")?;
let instance = wasmi::ModuleInstance::new(&instance, &MyResolver)
.with_context(|_| "failed to instantiate wasm module")?;
let instance = instance.not_started_instance(); let instance = instance.not_started_instance();
let (js, ts) = { let (js, ts) = {
@ -146,19 +144,21 @@ impl Bindgen {
js::SubContext { js::SubContext {
program, program,
cx: &mut cx, cx: &mut cx,
}.generate(); }.generate()?;
} }
cx.finalize(stem) cx.finalize(stem)?
}; };
let js_path = out_dir.join(stem).with_extension("js"); let js_path = out_dir.join(stem).with_extension("js");
File::create(&js_path).unwrap() File::create(&js_path)
.write_all(js.as_bytes()).unwrap(); .and_then(|mut f| f.write_all(js.as_bytes()))
.with_context(|_| format!("failed to write `{}`", js_path.display()))?;
if self.typescript { if self.typescript {
let ts_path = out_dir.join(stem).with_extension("d.ts"); let ts_path = out_dir.join(stem).with_extension("d.ts");
File::create(&ts_path).unwrap() File::create(&ts_path)
.write_all(ts.as_bytes()).unwrap(); .and_then(|mut f| f.write_all(ts.as_bytes()))
.with_context(|_| format!("failed to write `{}`", ts_path.display()))?;
} }
let wasm_path = out_dir.join(format!("{}_bg", stem)).with_extension("wasm"); let wasm_path = out_dir.join(format!("{}_bg", stem)).with_extension("wasm");
@ -166,13 +166,15 @@ impl Bindgen {
if self.nodejs { if self.nodejs {
let js_path = wasm_path.with_extension("js"); let js_path = wasm_path.with_extension("js");
let shim = self.generate_node_wasm_import(&module, &wasm_path); let shim = self.generate_node_wasm_import(&module, &wasm_path);
File::create(&js_path)?.write_all(shim.as_bytes())?; File::create(&js_path)
.and_then(|mut f| f.write_all(shim.as_bytes()))
.with_context(|_| format!("failed to write `{}`", js_path.display()))?;
} }
let wasm_bytes = parity_wasm::serialize(module).map_err(|e| { let wasm_bytes = parity_wasm::serialize(module)?;
Error(format!("{:?}", e)) File::create(&wasm_path)
})?; .and_then(|mut f| f.write_all(&wasm_bytes))
File::create(&wasm_path)?.write_all(&wasm_bytes)?; .with_context(|_| format!("failed to write `{}`", wasm_path.display()))?;
Ok(()) Ok(())
} }
@ -202,18 +204,20 @@ impl Bindgen {
} }
} }
fn extract_programs(module: &mut Module) -> Vec<shared::Program> { fn extract_programs(module: &mut Module) -> Result<Vec<shared::Program>, Error> {
let version = shared::version(); let version = shared::version();
let mut ret = Vec::new(); let mut ret = Vec::new();
let mut to_remove = Vec::new();
module.sections_mut().retain(|s| { for (i, s) in module.sections().iter().enumerate() {
let custom = match *s { let custom = match *s {
Section::Custom(ref s) => s, Section::Custom(ref s) => s,
_ => return true, _ => continue,
}; };
if custom.name() != "__wasm_bindgen_unstable" { if custom.name() != "__wasm_bindgen_unstable" {
return true continue
} }
to_remove.push(i);
let mut payload = custom.payload(); let mut payload = custom.payload();
while payload.len() > 0 { while payload.len() > 0 {
@ -227,11 +231,11 @@ fn extract_programs(module: &mut Module) -> Vec<shared::Program> {
let p: shared::ProgramOnlySchema = match serde_json::from_slice(&a) { let p: shared::ProgramOnlySchema = match serde_json::from_slice(&a) {
Ok(f) => f, Ok(f) => f,
Err(e) => { Err(e) => {
panic!("failed to decode what looked like wasm-bindgen data: {}", e) bail!("failed to decode what looked like wasm-bindgen data: {}", e)
} }
}; };
if p.schema_version != shared::SCHEMA_VERSION { if p.schema_version != shared::SCHEMA_VERSION {
panic!(" bail!("
it looks like the Rust project used to create this wasm file was linked against it looks like the Rust project used to create this wasm file was linked against
a different version of wasm-bindgen than this binary: a different version of wasm-bindgen than this binary:
@ -258,15 +262,17 @@ to open an issue at https://github.com/alexcrichton/wasm-bindgen/issues!
let p: shared::Program = match serde_json::from_slice(&a) { let p: shared::Program = match serde_json::from_slice(&a) {
Ok(f) => f, Ok(f) => f,
Err(e) => { Err(e) => {
panic!("failed to decode what looked like wasm-bindgen data: {}", e) bail!("failed to decode what looked like wasm-bindgen data: {}", e)
} }
}; };
ret.push(p); ret.push(p);
} }
}
false for i in to_remove.into_iter().rev() {
}); module.sections_mut().remove(i);
return ret }
Ok(ret)
} }
struct MyResolver; struct MyResolver;

View File

@ -3,8 +3,7 @@ extern crate base64;
use std::collections::HashSet; use std::collections::HashSet;
use parity_wasm::elements::*; use parity_wasm::elements::*;
use failure::Error;
use super::Error;
pub struct Config { pub struct Config {
base64: bool, base64: bool,
@ -37,11 +36,9 @@ impl Config {
pub fn generate(&mut self, wasm: &[u8]) -> Result<Output, Error> { pub fn generate(&mut self, wasm: &[u8]) -> Result<Output, Error> {
if !self.base64 && !self.fetch_path.is_some() { if !self.base64 && !self.fetch_path.is_some() {
panic!("the option --base64 or --fetch is required"); bail!("the option --base64 or --fetch is required");
} }
let module = deserialize_buffer(wasm).map_err(|e| { let module = deserialize_buffer(wasm)?;
::Error(format!("{:?}", e))
})?;
Ok(Output { Ok(Output {
module, module,
base64: self.base64, base64: self.base64,
@ -110,7 +107,7 @@ impl Output {
return exports return exports
} }
pub fn js(self) -> String { pub fn js(self) -> Result<String, Error> {
let mut js_imports = String::new(); let mut js_imports = String::new();
let mut exports = String::new(); let mut exports = String::new();
let mut imports = String::new(); let mut imports = String::new();
@ -122,13 +119,13 @@ impl Output {
match *entry.external() { match *entry.external() {
External::Function(_) => {} External::Function(_) => {}
External::Table(_) => { External::Table(_) => {
panic!("wasm imports a table which isn't supported yet"); bail!("wasm imports a table which isn't supported yet");
} }
External::Memory(_) => { External::Memory(_) => {
panic!("wasm imports memory which isn't supported yet"); bail!("wasm imports memory which isn't supported yet");
} }
External::Global(_) => { External::Global(_) => {
panic!("wasm imports globals which aren't supported yet"); bail!("wasm imports globals which aren't supported yet");
} }
} }
@ -221,9 +218,9 @@ impl Output {
.then(bytes => {inst})", path = path, inst = inst) .then(bytes => {inst})", path = path, inst = inst)
) )
} else { } else {
panic!("the option --base64 or --fetch is required"); bail!("the option --base64 or --fetch is required");
}; };
format!(" Ok(format!("
{js_imports} {js_imports}
let wasm; let wasm;
{bytes} {bytes}
@ -236,6 +233,6 @@ impl Output {
js_imports = js_imports, js_imports = js_imports,
exports = exports, exports = exports,
mem_export = if export_mem { "export let memory;" } else { "" }, mem_export = if export_mem { "export let memory;" } else { "" },
) ))
} }
} }

View File

@ -14,6 +14,7 @@ information see https://github.com/alexcrichton/wasm-bindgen.
[dependencies] [dependencies]
docopt = "0.8" docopt = "0.8"
failure = "0.1"
parity-wasm = "0.27" parity-wasm = "0.27"
serde = "1.0" serde = "1.0"
serde_derive = "1.0" serde_derive = "1.0"

View File

@ -3,11 +3,15 @@ extern crate wasm_bindgen_cli_support;
extern crate serde_derive; extern crate serde_derive;
extern crate docopt; extern crate docopt;
extern crate wasm_bindgen_shared; extern crate wasm_bindgen_shared;
#[macro_use]
extern crate failure;
use std::path::PathBuf; use std::path::PathBuf;
use std::process;
use docopt::Docopt; use docopt::Docopt;
use wasm_bindgen_cli_support::Bindgen; use wasm_bindgen_cli_support::Bindgen;
use failure::Error;
const USAGE: &'static str = " const USAGE: &'static str = "
Generating JS bindings for a wasm file Generating JS bindings for a wasm file
@ -53,14 +57,25 @@ fn main() {
println!("wasm-bindgen {}", wasm_bindgen_shared::version()); println!("wasm-bindgen {}", wasm_bindgen_shared::version());
return; return;
} }
let err = match rmain(&args) {
Ok(()) => return,
Err(e) => e,
};
println!("error: {}", err);
for cause in err.causes().skip(1) {
println!("\tcaused by: {}", cause);
}
process::exit(1);
}
fn rmain(args: &Args) -> Result<(), Error> {
let input = match args.arg_input { let input = match args.arg_input {
Some(s) => s, Some(ref s) => s,
None => panic!("input file expected"), None => bail!("input file expected"),
}; };
let mut b = Bindgen::new(); let mut b = Bindgen::new();
b.input_path(&input) b.input_path(input)
.nodejs(args.flag_nodejs) .nodejs(args.flag_nodejs)
.browser(args.flag_browser) .browser(args.flag_browser)
.no_modules(args.flag_no_modules) .no_modules(args.flag_no_modules)
@ -73,8 +88,8 @@ fn main() {
let out_dir = match args.flag_out_dir { let out_dir = match args.flag_out_dir {
Some(ref p) => p, Some(ref p) => p,
None => panic!("the `--out-dir` argument is now required"), None => bail!("the `--out-dir` argument is now required"),
}; };
b.generate(out_dir).expect("failed to generate bindings"); b.generate(out_dir)
} }

View File

@ -3,12 +3,16 @@ extern crate serde_derive;
extern crate docopt; extern crate docopt;
extern crate parity_wasm; extern crate parity_wasm;
extern crate wasm_bindgen_cli_support; extern crate wasm_bindgen_cli_support;
#[macro_use]
extern crate failure;
use std::fs::File; use std::fs::File;
use std::io::{Write, Read}; use std::io::{Write, Read};
use std::path::PathBuf; use std::path::PathBuf;
use std::process;
use docopt::Docopt; use docopt::Docopt;
use failure::{Error, ResultExt};
const USAGE: &'static str = " const USAGE: &'static str = "
Converts a wasm file to an ES6 JS module Converts a wasm file to an ES6 JS module
@ -42,38 +46,56 @@ fn main() {
let args: Args = Docopt::new(USAGE) let args: Args = Docopt::new(USAGE)
.and_then(|d| d.deserialize()) .and_then(|d| d.deserialize())
.unwrap_or_else(|e| e.exit()); .unwrap_or_else(|e| e.exit());
let err = match rmain(&args) {
Ok(()) => return,
Err(e) => e,
};
println!("error: {}", err);
for cause in err.causes().skip(1) {
println!("\tcaused by: {}", cause);
}
process::exit(1);
}
fn rmain(args: &Args) -> Result<(), Error> {
if !args.flag_base64 && !args.flag_fetch.is_some() { if !args.flag_base64 && !args.flag_fetch.is_some() {
panic!("unfortunately only works right now with base64 or fetch"); bail!("unfortunately only works right now with base64 or fetch");
} }
let mut wasm = Vec::new(); let mut wasm = Vec::new();
File::open(&args.arg_input).expect("failed to open input") File::open(&args.arg_input)
.read_to_end(&mut wasm).expect("failed to read input"); .and_then(|mut f| f.read_to_end(&mut wasm))
.with_context(|_| format!("failed to read `{}`", args.arg_input.display()))?;
let object = wasm_bindgen_cli_support::wasm2es6js::Config::new() let object = wasm_bindgen_cli_support::wasm2es6js::Config::new()
.base64(args.flag_base64) .base64(args.flag_base64)
.fetch(args.flag_fetch) .fetch(args.flag_fetch.clone())
.generate(&wasm) .generate(&wasm)?;
.expect("failed to parse wasm");
if args.flag_typescript { if args.flag_typescript {
if let Some(ref p) = args.flag_output { if let Some(ref p) = args.flag_output {
let dst = p.with_extension("d.ts"); let dst = p.with_extension("d.ts");
File::create(dst).expect("failed to create output") let ts = object.typescript();
.write_all(object.typescript().as_bytes()).expect("failed to write output"); File::create(&dst)
.and_then(|mut f| f.write_all(ts.as_bytes()))
.with_context(|_| {
format!("failed to write `{}`", dst.display())
})?;
} }
} }
let js = object.js(); let js = object.js()?;
match args.flag_output { match args.flag_output {
Some(ref p) => { Some(ref p) => {
File::create(p).expect("failed to create output") File::create(p)
.write_all(js.as_bytes()).expect("failed to write output"); .and_then(|mut f| f.write_all(js.as_bytes()))
.with_context(|_| format!("failed to write `{}`", p.display()))?;
} }
None => { None => {
println!("{}", js); println!("{}", js);
} }
} }
Ok(())
} }