Skip to content

Commit 65a5067

Browse files
committed
Enable cargo test where possible
Currently `#[wasm_bindgen]` generates a bunch of references to symbols that don't actually exist on non-wasm targets, making it more difficult to get a crate working across multiple platforms. This commit updates the symbol references to be dummy ones that panic on non-wasm targets to allow simple testing/benchmarking to work on native targets. While this isn't a perfect solution for #114 it's probably as good as we can do for now pending upstream Cargo features, so I'm gonna say that it... Closes #114
1 parent 98f3b96 commit 65a5067

File tree

4 files changed

+182
-57
lines changed

4 files changed

+182
-57
lines changed

crates/backend/src/codegen.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ impl ToTokens for ast::Struct {
176176
}
177177
}
178178

179+
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
179180
#[no_mangle]
180181
pub unsafe extern fn #free_fn(ptr: u32) {
181182
<#name as ::wasm_bindgen::convert::FromWasmAbi>::from_abi(
@@ -229,6 +230,7 @@ impl ToTokens for ast::StructField {
229230
let desc = syn::Ident::from(format!("__wbindgen_describe_{}", getter));
230231
(quote! {
231232
#[no_mangle]
233+
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
232234
pub unsafe extern fn #getter(js: u32)
233235
-> <#ty as ::wasm_bindgen::convert::IntoWasmAbi>::Abi
234236
{
@@ -260,6 +262,7 @@ impl ToTokens for ast::StructField {
260262

261263
(quote! {
262264
#[no_mangle]
265+
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
263266
pub unsafe extern fn #setter(
264267
js: u32,
265268
val: <#ty as ::wasm_bindgen::convert::FromWasmAbi>::Abi,
@@ -395,6 +398,7 @@ impl ToTokens for ast::Export {
395398
let tokens = quote! {
396399
#[export_name = #export_name]
397400
#[allow(non_snake_case)]
401+
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
398402
pub extern fn #generated_name(#(#args),*) #ret_ty {
399403
::wasm_bindgen::__rt::link_this_library();
400404
let #ret = {
@@ -424,6 +428,7 @@ impl ToTokens for ast::Export {
424428
// this, but the tl;dr; is that this is stripped from the final wasm
425429
// binary along with anything it references.
426430
#[no_mangle]
431+
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
427432
pub extern fn #descriptor_name() {
428433
use wasm_bindgen::describe::*;
429434
inform(FUNCTION);
@@ -640,6 +645,7 @@ impl ToTokens for ast::ImportFunction {
640645
.iter()
641646
.skip(if is_method { 1 } else { 0 })
642647
.collect::<Vec<_>>();
648+
let arguments = &arguments[..];
643649

644650
let me = if is_method {
645651
quote! { &self, }
@@ -650,6 +656,7 @@ impl ToTokens for ast::ImportFunction {
650656
let invocation = quote! {
651657
#(#attrs)*
652658
#[allow(bad_style)]
659+
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
653660
#vis extern #fn_token #rust_name(#me #(#arguments),*) #ret {
654661
::wasm_bindgen::__rt::link_this_library();
655662
#[wasm_import_module = "__wbindgen_placeholder__"]
@@ -668,6 +675,14 @@ impl ToTokens for ast::ImportFunction {
668675
}
669676
}
670677

678+
#(#attrs)*
679+
#[allow(bad_style, unused_variables)]
680+
#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
681+
#vis extern #fn_token #rust_name(#me #(#arguments),*) #ret {
682+
panic!("cannot call wasm-bindgen imported functions on \
683+
non-wasm targets");
684+
}
685+
671686
};
672687

673688
if let Some(class) = class_ty {
@@ -766,6 +781,7 @@ impl ToTokens for ast::ImportStatic {
766781
(quote! {
767782
#[allow(bad_style)]
768783
#vis static #name: ::wasm_bindgen::JsStatic<#ty> = {
784+
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
769785
fn init() -> #ty {
770786
#[wasm_import_module = "__wbindgen_placeholder__"]
771787
extern {
@@ -779,6 +795,10 @@ impl ToTokens for ast::ImportStatic {
779795

780796
}
781797
}
798+
#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
799+
fn init() -> #ty {
800+
panic!("cannot access imported statics on non-wasm targets")
801+
}
782802
static mut _VAL: ::wasm_bindgen::__rt::core::cell::UnsafeCell<Option<#ty>> =
783803
::wasm_bindgen::__rt::core::cell::UnsafeCell::new(None);
784804
::wasm_bindgen::JsStatic {

src/lib.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -275,10 +275,27 @@ macro_rules! numbers {
275275

276276
numbers! { i8 u8 i16 u16 i32 u32 f32 f64 }
277277

278-
#[wasm_import_module = "__wbindgen_placeholder__"]
279-
extern {
278+
macro_rules! externs {
279+
($(fn $name:ident($($args:tt)*) -> $ret:ty;)*) => (
280+
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
281+
#[wasm_import_module = "__wbindgen_placeholder__"]
282+
extern {
283+
$(fn $name($($args)*) -> $ret;)*
284+
}
285+
286+
$(
287+
#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
288+
#[allow(unused_variables)]
289+
unsafe extern fn $name($($args)*) -> $ret {
290+
panic!("function not implemented on non-wasm32 targets")
291+
}
292+
)*
293+
)
294+
}
295+
296+
externs! {
280297
fn __wbindgen_object_clone_ref(idx: u32) -> u32;
281-
fn __wbindgen_object_drop_ref(idx: u32);
298+
fn __wbindgen_object_drop_ref(idx: u32) -> ();
282299
fn __wbindgen_string_new(ptr: *const u8, len: usize) -> u32;
283300
fn __wbindgen_number_new(f: f64) -> u32;
284301
fn __wbindgen_number_get(idx: u32, invalid: *mut u8) -> f64;
@@ -293,10 +310,10 @@ extern {
293310
fn __wbindgen_string_get(idx: u32, len: *mut usize) -> *mut u8;
294311
fn __wbindgen_throw(a: *const u8, b: usize) -> !;
295312

296-
fn __wbindgen_cb_drop(idx: u32);
297-
fn __wbindgen_cb_forget(idx: u32);
313+
fn __wbindgen_cb_drop(idx: u32) -> ();
314+
fn __wbindgen_cb_forget(idx: u32) -> ();
298315

299-
fn __wbindgen_describe(v: u32);
316+
fn __wbindgen_describe(v: u32) -> ();
300317

301318
fn __wbindgen_json_parse(ptr: *const u8, len: usize) -> u32;
302319
fn __wbindgen_json_serialize(idx: u32, ptr: *mut *mut u8) -> usize;

tests/all/main.rs

Lines changed: 53 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ struct Project {
1717
node: bool,
1818
no_std: bool,
1919
serde: bool,
20+
rlib: bool,
21+
deps: Vec<String>,
2022
}
2123

2224
fn project() -> Project {
@@ -29,23 +31,9 @@ fn project() -> Project {
2931
node: false,
3032
no_std: false,
3133
serde: false,
34+
rlib: false,
35+
deps: Vec::new(),
3236
files: vec![
33-
("Cargo.toml".to_string(), format!(r#"
34-
[package]
35-
name = "test{}"
36-
version = "0.0.1"
37-
authors = []
38-
39-
[workspace]
40-
41-
[lib]
42-
crate-type = ["cdylib"]
43-
44-
# XXX: It is important that `[dependencies]` is the last section
45-
# here, so that `add_local_dependency` functions correctly!
46-
[dependencies]
47-
"#, IDX.with(|x| *x))),
48-
4937
("Cargo.lock".to_string(), lockfile),
5038

5139
("run.js".to_string(), r#"
@@ -158,59 +146,72 @@ impl Project {
158146
self
159147
}
160148

149+
fn rlib(&mut self, rlib: bool) -> &mut Project {
150+
self.rlib = rlib;
151+
self
152+
}
153+
161154
fn depend(&mut self, dep: &str) -> &mut Project {
162-
{
163-
let cargo_toml = self.files
164-
.iter_mut()
165-
.find(|f| f.0 == "Cargo.toml")
166-
.expect("should have Cargo.toml file!");
167-
cargo_toml.1.push_str(dep);
168-
cargo_toml.1.push_str("\n");
169-
}
155+
self.deps.push(dep.to_string());
170156
self
171157
}
172158

173159
fn add_local_dependency(&mut self, name: &str, path: &str) -> &mut Project {
174-
{
175-
let cargo_toml = self.files
176-
.iter_mut()
177-
.find(|f| f.0 == "Cargo.toml")
178-
.expect("should have Cargo.toml file!");
179-
cargo_toml.1.push_str(name);
180-
cargo_toml.1.push_str(" = { path = '");
181-
cargo_toml.1.push_str(path);
182-
cargo_toml.1.push_str("' }\n");
183-
}
160+
self.deps.push(format!("{} = {{ path = '{}' }}", name, path));
184161
self
185162
}
186163

187-
fn test(&mut self) {
188-
{
189-
let cargo_toml = self.files
190-
.iter_mut()
191-
.find(|f| f.0 == "Cargo.toml")
192-
.expect("should have Cargo.toml file!");
193-
cargo_toml.1.push_str("wasm-bindgen = { path = '");
194-
cargo_toml.1.push_str(env!("CARGO_MANIFEST_DIR"));
195-
cargo_toml.1.push_str("'");
196-
if self.no_std {
197-
cargo_toml.1.push_str(", default-features = false");
198-
}
199-
if self.serde {
200-
cargo_toml.1.push_str(", features = ['serde-serialize']");
201-
}
202-
cargo_toml.1.push_str(" }\n");
164+
fn crate_name(&self) -> String {
165+
format!("test{}", IDX.with(|x| *x))
166+
}
167+
168+
fn build(&mut self) -> (PathBuf, PathBuf) {
169+
let mut manifest = format!(r#"
170+
[package]
171+
name = "test{}"
172+
version = "0.0.1"
173+
authors = []
174+
175+
[workspace]
176+
177+
[lib]
178+
"#, IDX.with(|x| *x));
179+
180+
if !self.rlib {
181+
manifest.push_str("crate-type = [\"cdylib\"]\n");
182+
}
183+
184+
manifest.push_str("[dependencies]\n");
185+
for dep in self.deps.iter() {
186+
manifest.push_str(dep);
187+
manifest.push_str("\n");
188+
}
189+
manifest.push_str("wasm-bindgen = { path = '");
190+
manifest.push_str(env!("CARGO_MANIFEST_DIR"));
191+
manifest.push_str("'");
192+
if self.no_std {
193+
manifest.push_str(", default-features = false");
203194
}
195+
if self.serde {
196+
manifest.push_str(", features = ['serde-serialize']");
197+
}
198+
manifest.push_str(" }\n");
199+
self.files.push(("Cargo.toml".to_string(), manifest));
200+
204201
let root = root();
205202
drop(fs::remove_dir_all(&root));
206203
for &(ref file, ref contents) in self.files.iter() {
207204
let dst = root.join(file);
208205
fs::create_dir_all(dst.parent().unwrap()).unwrap();
209206
fs::File::create(&dst).unwrap().write_all(contents.as_ref()).unwrap();
210207
}
211-
212208
let target_dir = root.parent().unwrap() // chop off test name
213209
.parent().unwrap(); // chop off `generated-tests`
210+
(root.clone(), target_dir.to_path_buf())
211+
}
212+
213+
fn test(&mut self) {
214+
let (root, target_dir) = self.build();
214215

215216
let mut cmd = Command::new("cargo");
216217
cmd.arg("build")
@@ -333,3 +334,4 @@ mod non_debug;
333334
mod simple;
334335
mod slice;
335336
mod structural;
337+
mod non_wasm;

tests/all/non_wasm.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use std::process::Command;
2+
use super::{run, project};
3+
4+
#[test]
5+
fn works() {
6+
let mut p = project();
7+
let name = p.crate_name();
8+
p
9+
.rlib(true)
10+
.file("src/lib.rs", r#"
11+
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
12+
13+
extern crate wasm_bindgen;
14+
15+
use wasm_bindgen::prelude::*;
16+
17+
#[wasm_bindgen]
18+
pub struct A {
19+
x: u32,
20+
}
21+
22+
#[wasm_bindgen]
23+
impl A {
24+
pub fn new() -> A {
25+
A { x: 3 }
26+
}
27+
28+
pub fn foo(&self) {
29+
}
30+
}
31+
32+
#[wasm_bindgen]
33+
pub fn foo(x: bool) {
34+
A::new().foo();
35+
36+
if x {
37+
bar("test");
38+
baz(JsValue::from(3));
39+
}
40+
}
41+
42+
#[wasm_bindgen]
43+
extern {
44+
fn some_import();
45+
static A: JsValue;
46+
}
47+
48+
#[wasm_bindgen]
49+
pub fn bar(_: &str) -> JsValue {
50+
some_import();
51+
A.clone()
52+
}
53+
54+
#[wasm_bindgen]
55+
pub fn baz(_: JsValue) {
56+
}
57+
"#)
58+
.file("tests/foo.rs", &format!("
59+
extern crate {} as mytest;
60+
61+
#[test]
62+
fn foo() {{
63+
mytest::foo(false);
64+
mytest::A::new().foo();
65+
}}
66+
", name))
67+
.file("benches/foo.rs", &format!("
68+
#![feature(test)]
69+
extern crate test;
70+
extern crate {} as mytest;
71+
72+
#[bench]
73+
fn foo(b: &mut test::Bencher) {{
74+
b.iter(|| mytest::foo(false));
75+
}}
76+
", name));
77+
let (root, target_dir) = p.build();
78+
let mut cmd = Command::new("cargo");
79+
cmd.arg("test")
80+
.arg("--test").arg("foo")
81+
.arg("--bench").arg("foo")
82+
.current_dir(&root)
83+
.env("CARGO_TARGET_DIR", &target_dir);
84+
run(&mut cmd, "cargo");
85+
}
86+

0 commit comments

Comments
 (0)