From 73619b5d157a1e2141f22d828e68c9da13120a34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Flemstr=C3=B6m?= Date: Wed, 28 Mar 2018 01:22:31 +0200 Subject: [PATCH] Add support for constructing JsValue instances generically --- crates/backend/src/codegen.rs | 16 ++++++++++++ crates/cli-support/src/js.rs | 10 ++++++++ crates/shared/src/lib.rs | 9 +++++++ src/lib.rs | 8 +++--- tests/classes.rs | 47 +++++++++++++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 4 deletions(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 5c2ae3d7f..8778f1cc2 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -88,6 +88,7 @@ impl ToTokens for ast::Program { impl ToTokens for ast::Struct { fn to_tokens(&self, tokens: &mut Tokens) { let name = &self.name; + let new_fn = syn::Ident::from(shared::new_function(self.name.as_ref())); let free_fn = syn::Ident::from(shared::free_function(self.name.as_ref())); let c = shared::name_to_descriptor(name.as_ref()); let descriptor = Literal::byte_string(format!("{:4}", c).as_bytes()); @@ -153,6 +154,21 @@ impl ToTokens for ast::Struct { } } + impl ::std::convert::From<#name> for ::wasm_bindgen::JsValue { + fn from(value: #name) -> Self { + let ptr = ::wasm_bindgen::convert::WasmBoundary::into_js(value); + + #[wasm_import_module = "__wbindgen_placeholder__"] + extern { + fn #new_fn(ptr: u32) -> u32; + } + + unsafe { + ::wasm_bindgen::JsValue::__from_idx(#new_fn(ptr)) + } + } + } + #[no_mangle] pub unsafe extern fn #free_fn(ptr: u32) { <#name as ::wasm_bindgen::convert::WasmBoundary>::from_abi( diff --git a/crates/cli-support/src/js.rs b/crates/cli-support/src/js.rs index 3afbbd319..a22288f26 100644 --- a/crates/cli-support/src/js.rs +++ b/crates/cli-support/src/js.rs @@ -266,6 +266,11 @@ impl<'a> Context<'a> { }} ")); ts_dst.push_str("constructor(ptr: number, sym: Symbol);\n"); + + self.globals.push_str(&format!(" + export function {new_name}(ptr) {{ + return addHeapObject(new {class}(ptr, token)); + }}", new_name=shared::new_function(&class), class=class)); } else { dst.push_str(&format!(" constructor(ptr) {{ @@ -273,6 +278,11 @@ impl<'a> Context<'a> { }} ")); ts_dst.push_str("constructor(ptr: number);\n"); + + self.globals.push_str(&format!(" + export function {new_name}(ptr) {{ + return addHeapObject(new {class}(ptr)); + }}", new_name=shared::new_function(&class), class=class)); } dst.push_str(&format!(" diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index 9d67f95cd..30126d9f3 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -88,6 +88,15 @@ pub struct CustomTypeName { pub name: String, } +pub fn new_function(struct_name: &str) -> String { + let mut name = format!("__wbg_"); + name.extend(struct_name + .chars() + .flat_map(|s| s.to_lowercase())); + name.push_str("_new"); + return name +} + pub fn free_function(struct_name: &str) -> String { let mut name = format!("__wbg_"); name.extend(struct_name diff --git a/src/lib.rs b/src/lib.rs index e9e3cb313..9b8bc817a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,10 +94,10 @@ impl JsValue { } } - // #[doc(hidden)] - // pub fn __from_idx(idx: u32) -> JsValue { - // JsValue { idx } - // } + #[doc(hidden)] + pub unsafe fn __from_idx(idx: u32) -> JsValue { + JsValue { idx } + } // // #[doc(hidden)] // pub fn __get_idx(&self) -> u32 { diff --git a/tests/classes.rs b/tests/classes.rs index 13e03b267..6d22be72e 100644 --- a/tests/classes.rs +++ b/tests/classes.rs @@ -314,3 +314,50 @@ fn issue_27() { "#) .test(); } + +#[test] +fn pass_into_js_as_js_class() { + test_support::project() + .file("src/lib.rs", r#" + #![feature(proc_macro, wasm_custom_section, wasm_import_module)] + + extern crate wasm_bindgen; + + use wasm_bindgen::prelude::*; + + #[wasm_bindgen] + pub struct Foo(i32); + + #[wasm_bindgen] + impl Foo { + pub fn inner(&self) -> i32 { + self.0 + } + } + + #[wasm_bindgen(module = "./test")] + extern { + fn take_foo(foo: JsValue); + } + + #[wasm_bindgen] + pub fn run() { + take_foo(Foo(13).into()); + } + "#) + .file("test.ts", r#" + import { run, Foo } from "./out"; + import * as assert from "assert"; + + export function take_foo(foo: any) { + assert(foo instanceof Foo); + assert.strictEqual(foo.inner(), 13); + foo.free(); + } + + export function test() { + run(); + } + "#) + .test(); +}