From 748184ae66c66256b021830e711bd73e1d28260e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Apr 2018 13:08:54 -0700 Subject: [PATCH] Work with `#![no_std]` contexts This commit adds support for both `#![no_std]` in the wasm-bindgen runtime support (disabled by default with an on-by-default `std` feature). This also adds support to work and compile in the context of `#![no_std]` crates. Closes #146 --- Cargo.toml | 4 ++ crates/backend/src/codegen.rs | 88 ++++++++++++++++------------ crates/cli-support/src/js/js2rust.rs | 6 +- crates/cli-support/src/js/mod.rs | 30 +++++++--- crates/cli-support/src/js/rust2js.rs | 4 +- src/closure.rs | 1 + src/convert.rs | 85 +++++++++++++++------------ src/describe.rs | 27 +++++---- src/lib.rs | 86 +++++++++++++++++++-------- tests/all/main.rs | 28 +++++++-- tests/all/simple.rs | 86 +++++++++++++++++++++++++++ 11 files changed, 318 insertions(+), 127 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e25940df1..41a4a54ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,10 @@ Easy support for interacting between JS and Rust. test = false doctest = false +[features] +default = ["std"] +std = [] + [dependencies] wasm-bindgen-macro = { path = "crates/macro", version = "=0.2.4" } diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 99c7aec42..790fb0d95 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -116,13 +116,22 @@ impl ToTokens for ast::Struct { } } + ::wasm_bindgen::__wbindgen_if_not_std! { + compile_error! { + "exporting a class to JS requires the `std` feature to \ + be enabled in the `wasm-bindgen` crate" + } + } + impl ::wasm_bindgen::convert::IntoWasmAbi for #name { type Abi = u32; fn into_abi(self, _extra: &mut ::wasm_bindgen::convert::Stack) -> u32 { - Box::into_raw(Box::new(::wasm_bindgen::__rt::WasmRefCell::new(self))) as u32 + use wasm_bindgen::__rt::std::boxed::Box; + use wasm_bindgen::__rt::WasmRefCell; + Box::into_raw(Box::new(WasmRefCell::new(self))) as u32 } } @@ -132,14 +141,49 @@ impl ToTokens for ast::Struct { unsafe fn from_abi(js: u32, _extra: &mut ::wasm_bindgen::convert::Stack) -> Self { - let ptr = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>; - ::wasm_bindgen::__rt::assert_not_null(ptr); + use wasm_bindgen::__rt::std::boxed::Box; + use wasm_bindgen::__rt::{assert_not_null, WasmRefCell}; + + let ptr = js as *mut WasmRefCell<#name>; + assert_not_null(ptr); let js = Box::from_raw(ptr); js.borrow_mut(); // make sure no one's borrowing js.into_inner() } } + impl ::wasm_bindgen::__rt::core::convert::From<#name> for + ::wasm_bindgen::JsValue + { + fn from(value: #name) -> Self { + let ptr = ::wasm_bindgen::convert::IntoWasmAbi::into_abi( + value, + unsafe { &mut ::wasm_bindgen::convert::GlobalStack::new() }, + ); + + #[wasm_import_module = "__wbindgen_placeholder__"] + extern { + fn #new_fn(ptr: u32) -> u32; + } + + unsafe { + <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::FromWasmAbi> + ::from_abi( + #new_fn(ptr), + &mut ::wasm_bindgen::convert::GlobalStack::new(), + ) + } + } + } + + #[no_mangle] + pub unsafe extern fn #free_fn(ptr: u32) { + <#name as ::wasm_bindgen::convert::FromWasmAbi>::from_abi( + ptr, + &mut ::wasm_bindgen::convert::GlobalStack::new(), + ); + } + impl ::wasm_bindgen::convert::RefFromWasmAbi for #name { type Abi = u32; type Anchor = ::wasm_bindgen::__rt::Ref<'static, #name>; @@ -167,36 +211,6 @@ impl ToTokens for ast::Struct { (*js).borrow_mut() } } - - impl ::std::convert::From<#name> for ::wasm_bindgen::JsValue { - fn from(value: #name) -> Self { - let ptr = ::wasm_bindgen::convert::IntoWasmAbi::into_abi( - value, - unsafe { &mut ::wasm_bindgen::convert::GlobalStack::new() }, - ); - - #[wasm_import_module = "__wbindgen_placeholder__"] - extern { - fn #new_fn(ptr: u32) -> u32; - } - - unsafe { - <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::FromWasmAbi> - ::from_abi( - #new_fn(ptr), - &mut ::wasm_bindgen::convert::GlobalStack::new(), - ) - } - } - } - - #[no_mangle] - pub unsafe extern fn #free_fn(ptr: u32) { - <#name as ::wasm_bindgen::convert::FromWasmAbi>::from_abi( - ptr, - &mut ::wasm_bindgen::convert::GlobalStack::new(), - ); - } }).to_tokens(tokens); } } @@ -419,7 +433,7 @@ impl ToTokens for ast::ImportType { impl ::wasm_bindgen::convert::RefFromWasmAbi for #name { type Abi = <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::RefFromWasmAbi>::Abi; - type Anchor = ::std::mem::ManuallyDrop<#name>; + type Anchor = ::wasm_bindgen::__rt::core::mem::ManuallyDrop<#name>; unsafe fn ref_from_abi( js: Self::Abi, @@ -427,8 +441,8 @@ impl ToTokens for ast::ImportType { ) -> Self::Anchor { let tmp = <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::RefFromWasmAbi> ::ref_from_abi(js, extra); - ::std::mem::ManuallyDrop::new(#name { - obj: ::std::mem::ManuallyDrop::into_inner(tmp), + ::wasm_bindgen::__rt::core::mem::ManuallyDrop::new(#name { + obj: ::wasm_bindgen::__rt::core::mem::ManuallyDrop::into_inner(tmp), }) } } @@ -702,7 +716,7 @@ impl ToTokens for ast::ImportStatic { } } ::wasm_bindgen::JsStatic { - __inner: ::std::cell::UnsafeCell::new(None), + __inner: ::wasm_bindgen::__rt::core::cell::UnsafeCell::new(None), __init: init, } }; diff --git a/crates/cli-support/src/js/js2rust.rs b/crates/cli-support/src/js/js2rust.rs index 1659db1e0..fcd0d5649 100644 --- a/crates/cli-support/src/js/js2rust.rs +++ b/crates/cli-support/src/js/js2rust.rs @@ -119,9 +119,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> { self.finally(&format!("\ wasm.__wbindgen_free(ptr{i}, len{i} * {size});\n\ ", i = i, size = kind.size())); - self.cx.required_internal_exports.insert( - "__wbindgen_free", - ); + self.cx.require_internal_export("__wbindgen_free"); } self.rust_arguments.push(format!("ptr{}", i)); return @@ -216,7 +214,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> { self.ret_ty = ty.js_ty().to_string(); let f = self.cx.expose_get_vector_from_wasm(ty); self.cx.expose_get_global_argument(); - self.cx.required_internal_exports.insert("__wbindgen_free"); + self.cx.require_internal_export("__wbindgen_free"); self.ret_expr = format!("\ const ret = RET;\n\ const len = getGlobalArgument(0);\n\ diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 1717188ea..dd382d619 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -62,6 +62,22 @@ impl<'a> Context<'a> { self.global(&global); } + fn require_internal_export(&mut self, name: &'static str) { + if !self.required_internal_exports.insert(name) { + return + } + if let Some(s) = self.module.export_section() { + if s.entries().iter().any(|e| e.field() == name) { + return + } + } + + panic!("\n\nthe exported function `{}` is required to generate bindings \ + but it was not found in the wasm file, perhaps the `std` feature \ + of the `wasm-bindgen` crate needs to be enabled?\n\n", + name); + } + pub fn finalize(&mut self, module_name: &str) -> (String, String) { self.unexport_unused_internal_exports(); self.gc(); @@ -642,7 +658,7 @@ impl<'a> Context<'a> { if !self.exposed_globals.insert("pass_string_to_wasm") { return; } - self.required_internal_exports.insert("__wbindgen_malloc"); + self.require_internal_export("__wbindgen_malloc"); self.expose_text_encoder(); self.expose_uint8_memory(); let debug = if self.config.debug { @@ -668,7 +684,7 @@ impl<'a> Context<'a> { if !self.exposed_globals.insert("pass_array8_to_wasm") { return; } - self.required_internal_exports.insert("__wbindgen_malloc"); + self.require_internal_export("__wbindgen_malloc"); self.expose_uint8_memory(); self.global(&format!(" function passArray8ToWasm(arg) {{ @@ -683,7 +699,7 @@ impl<'a> Context<'a> { if !self.exposed_globals.insert("pass_array16_to_wasm") { return; } - self.required_internal_exports.insert("__wbindgen_malloc"); + self.require_internal_export("__wbindgen_malloc"); self.expose_uint16_memory(); self.global(&format!(" function passArray16ToWasm(arg) {{ @@ -698,7 +714,7 @@ impl<'a> Context<'a> { if !self.exposed_globals.insert("pass_array32_to_wasm") { return; } - self.required_internal_exports.insert("__wbindgen_malloc"); + self.require_internal_export("__wbindgen_malloc"); self.expose_uint32_memory(); self.global(&format!(" function passArray32ToWasm(arg) {{ @@ -713,7 +729,7 @@ impl<'a> Context<'a> { if !self.exposed_globals.insert("pass_array_f32_to_wasm") { return; } - self.required_internal_exports.insert("__wbindgen_malloc"); + self.require_internal_export("__wbindgen_malloc"); self.global(&format!(" function passArrayF32ToWasm(arg) {{ const ptr = wasm.__wbindgen_malloc(arg.length * 4); @@ -727,7 +743,7 @@ impl<'a> Context<'a> { if !self.exposed_globals.insert("pass_array_f64_to_wasm") { return; } - self.required_internal_exports.insert("__wbindgen_malloc"); + self.require_internal_export("__wbindgen_malloc"); self.global(&format!(" function passArrayF64ToWasm(arg) {{ const ptr = wasm.__wbindgen_malloc(arg.length * 8); @@ -1172,7 +1188,7 @@ impl<'a> Context<'a> { if !self.exposed_globals.insert("global_argument_ptr") { return; } - self.required_internal_exports.insert("__wbindgen_global_argument_ptr"); + self.require_internal_export("__wbindgen_global_argument_ptr"); self.global(" let cachedGlobalArgumentPtr = null; function globalArgumentPtr() { diff --git a/crates/cli-support/src/js/rust2js.rs b/crates/cli-support/src/js/rust2js.rs index 111aca481..b3831a222 100644 --- a/crates/cli-support/src/js/rust2js.rs +++ b/crates/cli-support/src/js/rust2js.rs @@ -91,9 +91,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> { self.prelude(&format!("\ wasm.__wbindgen_free(arg{0}, len{0} * {size});\ ", i, size = ty.size())); - self.cx.required_internal_exports.insert( - "__wbindgen_free" - ); + self.cx.require_internal_export("__wbindgen_free"); } self.js_arguments.push(format!("v{}", i)); return diff --git a/src/closure.rs b/src/closure.rs index 821448bd2..d7227d83d 100644 --- a/src/closure.rs +++ b/src/closure.rs @@ -7,6 +7,7 @@ use std::cell::UnsafeCell; use std::marker::Unsize; use std::mem::{self, ManuallyDrop}; +use std::prelude::v1::*; use JsValue; use convert::*; diff --git a/src/convert.rs b/src/convert.rs index 8d194f453..80d49f249 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -1,14 +1,17 @@ //! This is mostly an internal module, no stability guarantees are provied. Use //! at your own risk. -use std::mem::{self, ManuallyDrop}; -use std::ops::{Deref, DerefMut}; -use std::slice; -use std::str; +use core::mem::{self, ManuallyDrop}; +use core::ops::{Deref, DerefMut}; +use core::slice; +use core::str; use {JsValue, throw}; use describe::*; +#[cfg(feature = "std")] +use std::prelude::v1::*; + #[derive(PartialEq, Eq, Copy, Clone)] pub struct Descriptor { #[doc(hidden)] @@ -132,6 +135,7 @@ impl FromWasmAbi for *mut T { macro_rules! vectors { ($($t:ident)*) => ($( + #[cfg(feature = "std")] impl IntoWasmAbi for Box<[$t]> { type Abi = u32; @@ -144,6 +148,7 @@ macro_rules! vectors { } } + #[cfg(feature = "std")] impl FromWasmAbi for Box<[$t]> { type Abi = u32; @@ -183,34 +188,36 @@ vectors! { u8 i8 u16 i16 u32 i32 f32 f64 } -impl IntoWasmAbi for Vec where Box<[T]>: IntoWasmAbi { - type Abi = as IntoWasmAbi>::Abi; - fn into_abi(self, extra: &mut Stack) -> Self::Abi { - self.into_boxed_slice().into_abi(extra) +if_std! { + impl IntoWasmAbi for Vec where Box<[T]>: IntoWasmAbi { + type Abi = as IntoWasmAbi>::Abi; + fn into_abi(self, extra: &mut Stack) -> Self::Abi { + self.into_boxed_slice().into_abi(extra) + } } -} -impl FromWasmAbi for Vec where Box<[T]>: FromWasmAbi { - type Abi = as FromWasmAbi>::Abi; + impl FromWasmAbi for Vec where Box<[T]>: FromWasmAbi { + type Abi = as FromWasmAbi>::Abi; - unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self { - >::from_abi(js, extra).into() + unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self { + >::from_abi(js, extra).into() + } } -} -impl IntoWasmAbi for String { - type Abi = u32; + impl IntoWasmAbi for String { + type Abi = u32; - fn into_abi(self, extra: &mut Stack) -> u32 { - self.into_bytes().into_abi(extra) + fn into_abi(self, extra: &mut Stack) -> u32 { + self.into_bytes().into_abi(extra) + } } -} -impl FromWasmAbi for String { - type Abi = u32; + impl FromWasmAbi for String { + type Abi = u32; - unsafe fn from_abi(js: u32, extra: &mut Stack) -> Self { - String::from_utf8_unchecked(>::from_abi(js, extra)) + unsafe fn from_abi(js: u32, extra: &mut Stack) -> Self { + String::from_utf8_unchecked(>::from_abi(js, extra)) + } } } @@ -265,25 +272,27 @@ impl RefFromWasmAbi for JsValue { } } -impl IntoWasmAbi for Box<[JsValue]> { - type Abi = u32; +if_std! { + impl IntoWasmAbi for Box<[JsValue]> { + type Abi = u32; - fn into_abi(self, extra: &mut Stack) -> u32 { - let ptr = self.as_ptr(); - let len = self.len(); - mem::forget(self); - extra.push(len as u32); - ptr.into_abi(extra) + fn into_abi(self, extra: &mut Stack) -> u32 { + let ptr = self.as_ptr(); + let len = self.len(); + mem::forget(self); + extra.push(len as u32); + ptr.into_abi(extra) + } } -} -impl FromWasmAbi for Box<[JsValue]> { - type Abi = u32; + impl FromWasmAbi for Box<[JsValue]> { + type Abi = u32; - unsafe fn from_abi(js: u32, extra: &mut Stack) -> Self { - let ptr = <*mut JsValue>::from_abi(js, extra); - let len = extra.pop() as usize; - Vec::from_raw_parts(ptr, len, len).into_boxed_slice() + unsafe fn from_abi(js: u32, extra: &mut Stack) -> Self { + let ptr = <*mut JsValue>::from_abi(js, extra); + let len = extra.pop() as usize; + Vec::from_raw_parts(ptr, len, len).into_boxed_slice() + } } } diff --git a/src/describe.rs b/src/describe.rs index 293e480f3..39446542c 100644 --- a/src/describe.rs +++ b/src/describe.rs @@ -72,7 +72,6 @@ simple! { f64 => F64 bool => BOOLEAN str => STRING - String => STRING JsValue => ANYREF } @@ -105,16 +104,24 @@ impl<'a, T: WasmDescribe + ?Sized> WasmDescribe for &'a mut T { } } -impl WasmDescribe for Box<[T]> { - fn describe() { - inform(VECTOR); - T::describe(); - } -} +if_std! { + use std::prelude::v1::*; -impl WasmDescribe for Vec where Box<[T]>: WasmDescribe { - fn describe() { - >::describe(); + impl WasmDescribe for String { + fn describe() { inform(STRING) } + } + + impl WasmDescribe for Box<[T]> { + fn describe() { + inform(VECTOR); + T::describe(); + } + } + + impl WasmDescribe for Vec where Box<[T]>: WasmDescribe { + fn describe() { + >::describe(); + } } } diff --git a/src/lib.rs b/src/lib.rs index c63a99016..85667269e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,15 +6,22 @@ //! interface. #![feature(use_extern_macros, wasm_import_module, try_reserve, unsize)] +#![no_std] extern crate wasm_bindgen_macro; -use std::cell::UnsafeCell; -use std::ops::Deref; -use std::ptr; +use core::cell::UnsafeCell; +use core::ops::Deref; +use core::ptr; use convert::FromWasmAbi; +macro_rules! if_std { + ($($i:item)*) => ($( + #[cfg(feature = "std")] $i + )*) +} + /// A module which is typically glob imported from: /// /// ``` @@ -23,13 +30,21 @@ use convert::FromWasmAbi; pub mod prelude { pub use wasm_bindgen_macro::wasm_bindgen; pub use JsValue; - pub use closure::Closure; + + if_std! { + pub use closure::Closure; + } } pub mod convert; -pub mod closure; pub mod describe; +if_std! { + extern crate std; + use std::prelude::v1::*; + pub mod closure; +} + /// Representation of an object owned by JS. /// /// A `JsValue` doesn't actually live in Rust right now but actually in a table @@ -136,6 +151,7 @@ impl JsValue { /// /// If this JS value is not an instance of a string or if it's not valid /// utf-8 then this returns `None`. + #[cfg(feature = "std")] pub fn as_string(&self) -> Option { unsafe { let mut len = 0; @@ -192,9 +208,11 @@ impl<'a> From<&'a str> for JsValue { } } -impl<'a> From<&'a String> for JsValue { - fn from(s: &'a String) -> JsValue { - JsValue::from_str(s) +if_std! { + impl<'a> From<&'a String> for JsValue { + fn from(s: &'a String) -> JsValue { + JsValue::from_str(s) + } } } @@ -321,9 +339,23 @@ pub fn throw(s: &str) -> ! { #[doc(hidden)] pub mod __rt { - use std::cell::{Cell, UnsafeCell}; - use std::mem; - use std::ops::{Deref, DerefMut}; + use core::cell::{Cell, UnsafeCell}; + use core::ops::{Deref, DerefMut}; + pub extern crate core; + #[cfg(feature = "std")] + pub extern crate std; + + #[macro_export] + #[cfg(feature = "std")] + macro_rules! __wbindgen_if_not_std { + ($($i:item)*) => () + } + + #[macro_export] + #[cfg(not(feature = "std"))] + macro_rules! __wbindgen_if_not_std { + ($($i:item)*) => ($($i)*) + } #[inline] pub fn assert_not_null(s: *mut T) { @@ -457,20 +489,26 @@ pub mod __rt { unsafe aliasing in rust"); } - #[no_mangle] - pub extern fn __wbindgen_malloc(size: usize) -> *mut u8 { - let mut ret = Vec::new(); - if ret.try_reserve_exact(size).is_err() { - super::throw("invalid malloc request"); - } - let ptr = ret.as_mut_ptr(); - mem::forget(ret); - return ptr - } + if_std! { + use std::prelude::v1::*; - #[no_mangle] - pub unsafe extern fn __wbindgen_free(ptr: *mut u8, size: usize) { - drop(Vec::::from_raw_parts(ptr, 0, size)); + #[no_mangle] + pub extern fn __wbindgen_malloc(size: usize) -> *mut u8 { + use core::mem; + + let mut ret = Vec::new(); + if ret.try_reserve_exact(size).is_err() { + super::throw("invalid malloc request"); + } + let ptr = ret.as_mut_ptr(); + mem::forget(ret); + return ptr + } + + #[no_mangle] + pub unsafe extern fn __wbindgen_free(ptr: *mut u8, size: usize) { + drop(Vec::::from_raw_parts(ptr, 0, size)); + } } pub fn link_this_library() {} diff --git a/tests/all/main.rs b/tests/all/main.rs index 8c7aac10a..6f239aff9 100644 --- a/tests/all/main.rs +++ b/tests/all/main.rs @@ -15,6 +15,7 @@ struct Project { files: Vec<(String, String)>, debug: bool, node: bool, + no_std: bool, } fn project() -> Project { @@ -25,6 +26,7 @@ fn project() -> Project { Project { debug: true, node: false, + no_std: false, files: vec![ ("Cargo.toml".to_string(), format!(r#" [package] @@ -40,8 +42,7 @@ fn project() -> Project { # XXX: It is important that `[dependencies]` is the last section # here, so that `add_local_dependency` functions correctly! [dependencies] - wasm-bindgen = {{ path = '{}' }} - "#, IDX.with(|x| *x), dir.display())), + "#, IDX.with(|x| *x))), ("Cargo.lock".to_string(), lockfile), @@ -138,6 +139,11 @@ impl Project { self } + fn no_std(&mut self, no_std: bool) -> &mut Project { + self.no_std = no_std; + self + } + fn add_local_dependency(&mut self, name: &str, path: &str) -> &mut Project { { let cargo_toml = self.files @@ -145,14 +151,28 @@ impl Project { .find(|f| f.0 == "Cargo.toml") .expect("should have Cargo.toml file!"); cargo_toml.1.push_str(name); - cargo_toml.1.push_str(" = { path = \""); + cargo_toml.1.push_str(" = { path = '"); cargo_toml.1.push_str(path); - cargo_toml.1.push_str("\" }"); + cargo_toml.1.push_str("' }\n"); } self } fn test(&mut self) { + { + let cargo_toml = self.files + .iter_mut() + .find(|f| f.0 == "Cargo.toml") + .expect("should have Cargo.toml file!"); + cargo_toml.1.push_str("wasm-bindgen = { path = '"); + cargo_toml.1.push_str(env!("CARGO_MANIFEST_DIR")); + if self.no_std { + cargo_toml.1.push_str("', default-features = false"); + } else { + cargo_toml.1.push_str("'"); + } + cargo_toml.1.push_str(" }\n"); + } let root = root(); drop(fs::remove_dir_all(&root)); for &(ref file, ref contents) in self.files.iter() { diff --git a/tests/all/simple.rs b/tests/all/simple.rs index 2c61243ef..a86b718d7 100644 --- a/tests/all/simple.rs +++ b/tests/all/simple.rs @@ -201,3 +201,89 @@ fn other_exports() { "#) .test(); } + +#[test] +fn no_std() { + project() + .no_std(true) + .file("src/lib.rs", r#" + #![feature(proc_macro, wasm_custom_section, wasm_import_module)] + #![no_std] + #![allow(dead_code)] + + extern crate wasm_bindgen; + extern crate std as _some_other_name; + + use wasm_bindgen::prelude::*; + + #[wasm_bindgen] + extern { + fn test(a: &str); + + type Js; + #[wasm_bindgen(constructor)] + fn new() -> Js; + #[wasm_bindgen(method)] + fn init(this: &Js); + } + + #[wasm_bindgen] + pub fn foo(_a: u32) {} + "#) + .file("test.ts", r#" + import * as wasm from "./out_bg"; + + export function test() { + // mostly just testing the project compiles here + wasm.foo(1); + } + "#) + .test(); +} + +#[test] +fn no_std_class() { + project() + .file("src/lib.rs", r#" + #![feature(proc_macro, wasm_custom_section, wasm_import_module)] + #![no_std] + #![allow(dead_code)] + + extern crate wasm_bindgen; + extern crate std as _some_other_name; + + use wasm_bindgen::prelude::*; + + #[wasm_bindgen] + extern { + fn test(a: &str); + + type Js; + #[wasm_bindgen(constructor)] + fn new() -> Js; + #[wasm_bindgen(method, structural)] + fn init(this: &Js); + } + + #[wasm_bindgen] + pub fn foo(_a: u32) {} + + #[wasm_bindgen] + pub struct A {} + + #[wasm_bindgen] + impl A { + pub fn foo(&self) {} + pub fn bar(&mut self) {} + } + "#) + .file("test.ts", r#" + import * as wasm from "./out_bg"; + + export function test() { + // mostly just testing the project compiles here + wasm.foo(1); + } + "#) + .test(); +}