diff --git a/crates/wasm-bindgen-cli-support/src/js.rs b/crates/wasm-bindgen-cli-support/src/js.rs index 7f830523..4fb9b061 100644 --- a/crates/wasm-bindgen-cli-support/src/js.rs +++ b/crates/wasm-bindgen-cli-support/src/js.rs @@ -483,6 +483,7 @@ impl<'a> Js<'a> { self.gen_import_shim(&shared::mangled_import_name(None, &import.name), &imported_name, + false, import) } @@ -493,10 +494,10 @@ impl<'a> Js<'a> { ", import.name, module)); } - for import in import.functions.iter() { - self.generate_import_struct_function(&import.function.name, - import.method, - &import.function); + for f in import.functions.iter() { + self.generate_import_struct_function(&import.name, + f.method, + &f.function); } } @@ -513,6 +514,7 @@ impl<'a> Js<'a> { }; self.gen_import_shim(&shared::mangled_import_name(Some(class), &function.name), &delegate, + is_method, function) } @@ -520,18 +522,25 @@ impl<'a> Js<'a> { &mut self, shim_name: &str, shim_delegate: &str, + is_method: bool, import: &shared::Function, ) { let mut dst = String::new(); dst.push_str(&format!("function {}(", shim_name)); - let mut invocation = String::new(); + + if is_method { + dst.push_str("ptr"); + invocation.push_str("getObject(ptr)"); + self.expose_get_object(); + } + for (i, arg) in import.arguments.iter().enumerate() { if invocation.len() > 0 { invocation.push_str(", "); } - if i > 0 { + if i > 0 || is_method { dst.push_str(", "); } match *arg { diff --git a/crates/wasm-bindgen-macro/src/lib.rs b/crates/wasm-bindgen-macro/src/lib.rs index 9b76789a..7372bc5f 100644 --- a/crates/wasm-bindgen-macro/src/lib.rs +++ b/crates/wasm-bindgen-macro/src/lib.rs @@ -431,11 +431,6 @@ fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) { fn bindgen_imported_struct(import: &ast::ImportStruct, tokens: &mut Tokens) { let name = import.name; - (my_quote! { - pub struct #name { - obj: ::wasm_bindgen::JsObject, - } - }).to_tokens(tokens); let mut methods = Tokens::new(); @@ -448,9 +443,28 @@ fn bindgen_imported_struct(import: &ast::ImportStruct, tokens: &mut Tokens) { } (my_quote! { + pub struct #name { + obj: ::wasm_bindgen::JsObject, + } + impl #name { #methods } + + impl ::wasm_bindgen::convert::WasmBoundary for #name { + type Js = <::wasm_bindgen::JsObject as + ::wasm_bindgen::convert::WasmBoundary>::Js; + const DESCRIPTOR: char = <::wasm_bindgen::JsObject as + ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR; + + fn into_js(self) -> Self::Js { + self.obj.into_js() + } + + unsafe fn from_js(js: Self::Js) -> Self { + #name { obj: ::wasm_bindgen::JsObject::from_js(js) } + } + } }).to_tokens(tokens); } @@ -467,10 +481,26 @@ fn bindgen_import_function(import: &ast::ImportFunction, let mut arg_conversions = Vec::new(); let ret_ident = syn::Ident::from("_ret"); - let names = import.rust_decl.inputs + let inputs = import.rust_decl.inputs.iter().collect::>(); + let (is_method, inputs) = match inputs.get(0) { + Some(&&syn::FnArg::Captured(_)) => (false, &inputs[..]), + Some(_) => (true, &inputs[1..]), + None => (false, &inputs[..]), + }; + + if is_method { + let ptr = syn::Ident::from("ptr"); + abi_argument_names.push(ptr); + abi_arguments.push(my_quote! { #ptr: u32 }); + arg_conversions.push(my_quote! { + let #ptr = ::wasm_bindgen::convert::ToRefWasmBoundary::to_js_ref(&self.obj); + }); + } + + let names = inputs .iter() .map(|arg| { - match *arg { + match **arg { syn::FnArg::Captured(ref c) => c, _ => panic!("arguments cannot be `self` or ignored"), } diff --git a/tests/import-class.rs b/tests/import-class.rs index e4fc86b4..bf0b4dd3 100644 --- a/tests/import-class.rs +++ b/tests/import-class.rs @@ -1,125 +1,130 @@ extern crate test_support; -// #[test] -// fn simple() { -// test_support::project() -// .file("src/lib.rs", r#" -// #![feature(proc_macro)] -// -// extern crate wasm_bindgen; -// -// use wasm_bindgen::prelude::*; -// -// wasm_bindgen! { -// extern struct Math { -// fn random() -> f64; -// fn log(a: f64) -> f64; -// } -// -// pub fn get_random() -> f64 { -// Math::random() -// } -// -// pub fn do_log(a: f64) -> f64 { -// Math::log(a) -// } -// } -// "#) -// .file("test.ts", r#" -// import * as wasm from "./out"; -// import * as assert from "assert"; -// -// export function test() { -// wasm.get_random(); -// assert.strictEqual(wasm.do_log(1.0), Math.log(1.0)); -// } -// "#) -// .test(); -// } -// -// #[test] -// fn import_class() { -// test_support::project() -// .file("src/lib.rs", r#" -// #![feature(proc_macro)] -// -// extern crate wasm_bindgen; -// -// use wasm_bindgen::prelude::*; -// -// wasm_bindgen! { -// #[wasm_module = "./test"] -// extern struct Foo { -// fn bar(); -// } -// -// pub fn bar() { -// Foo::bar(); -// } -// } -// "#) -// .file("test.ts", r#" -// import * as wasm from "./out"; -// import * as assert from "assert"; -// -// let called = false; -// -// export class Foo { -// static bar() { -// called = true; -// } -// } -// -// export function test() { -// wasm.bar(); -// assert.strictEqual(called, true); -// } -// "#) -// .test(); -// } -// -// #[test] -// fn construct() { -// test_support::project() -// .file("src/lib.rs", r#" -// #![feature(proc_macro)] -// -// extern crate wasm_bindgen; -// -// use wasm_bindgen::prelude::*; -// -// wasm_bindgen! { -// #[wasm_module = "./test"] -// extern struct Foo { -// fn create() -> Foo; -// fn doit(&self); -// } -// -// pub fn bar() { -// let foo = Foo::bar(); -// } -// } -// "#) -// .file("test.ts", r#" -// import * as wasm from "./out"; -// import * as assert from "assert"; -// -// let called = false; -// -// export class Foo { -// static create() { -// return new Foo(); -// } -// -// doit() { -// called = true; -// } -// } -// -// export function test() { -// wasm.bar(); -// assert.strictEqual(called, true); -// } -// "#) -// .test(); -// } +#[test] +fn simple() { + test_support::project() + .file("src/lib.rs", r#" + #![feature(proc_macro)] + + extern crate wasm_bindgen; + + use wasm_bindgen::prelude::*; + + wasm_bindgen! { + extern struct Math { + fn random() -> f64; + fn log(a: f64) -> f64; + } + + pub fn get_random() -> f64 { + Math::random() + } + + pub fn do_log(a: f64) -> f64 { + Math::log(a) + } + } + "#) + .file("test.ts", r#" + import * as wasm from "./out"; + import * as assert from "assert"; + + export function test() { + wasm.get_random(); + assert.strictEqual(wasm.do_log(1.0), Math.log(1.0)); + } + "#) + .test(); +} + +#[test] +fn import_class() { + test_support::project() + .file("src/lib.rs", r#" + #![feature(proc_macro)] + + extern crate wasm_bindgen; + + use wasm_bindgen::prelude::*; + + wasm_bindgen! { + #[wasm_module = "./test"] + extern struct Foo { + fn bar(); + } + + pub fn bar() { + Foo::bar(); + } + } + "#) + .file("test.ts", r#" + import * as wasm from "./out"; + import * as assert from "assert"; + + let called = false; + + export class Foo { + static bar() { + called = true; + } + } + + export function test() { + wasm.bar(); + assert.strictEqual(called, true); + } + "#) + .test(); +} + +#[test] +fn construct() { + test_support::project() + .file("src/lib.rs", r#" + #![feature(proc_macro)] + + extern crate wasm_bindgen; + + use wasm_bindgen::prelude::*; + + wasm_bindgen! { + #[wasm_module = "./test"] + extern struct Foo { + fn create() -> Foo; + fn doit(&self); + } + + pub fn bar() { + Foo::create().doit(); + } + } + "#) + .file("test.ts", r#" + import * as wasm from "./out"; + import * as assert from "assert"; + + let called = false; + + export class Foo { + private random_property: string = ''; + + static create() { + const ret = new Foo(); + ret.random_property = 'this'; + return ret; + } + + doit() { + assert.strictEqual(this.random_property, 'this'); + called = true; + } + } + + export function test() { + wasm.bar(); + assert.strictEqual(called, true); + } + "#) + .test(); +}