blob: c6eee6670662d4a35b1096d4e556a58351f6ab2a [file] [log] [blame] [edit]
use std::env;
use std::fs;
use std::io::{Read, Write};
use std::process::{Command, Stdio};
fn compile(args: &[&str], src: &str) -> Vec<u8> {
compile_with_files(args, src, &[])
}
fn compile_with_files(args: &[&str], src: &str, files: &[(&str, &str)]) -> Vec<u8> {
let tempdir = tempfile::TempDir::new().unwrap();
for (name, content) in files {
fs::write(tempdir.path().join(name), content.as_bytes()).unwrap();
}
let mut myself = env::current_exe().unwrap();
myself.pop(); // exe name
myself.pop(); // 'deps'
myself.push("wasm-component-ld");
let mut rustc = Command::new("rustc")
.arg("--target")
.arg("wasm32-wasip1")
.arg("-")
.arg("-o")
.arg("-")
.arg("-C")
.arg(&format!("linker={}", myself.to_str().unwrap()))
.args(args)
.current_dir(tempdir.path())
.stdout(Stdio::piped())
.stdin(Stdio::piped())
.spawn()
.expect("failed to spawn rustc");
rustc
.stdin
.take()
.unwrap()
.write_all(src.as_bytes())
.unwrap();
let mut ret = Vec::new();
rustc.stdout.take().unwrap().read_to_end(&mut ret).unwrap();
assert!(rustc.wait().unwrap().success());
ret
}
fn assert_component(bytes: &[u8]) {
assert!(wasmparser::Parser::is_component(&bytes));
wasmparser::Validator::new().validate_all(&bytes).unwrap();
}
fn assert_module(bytes: &[u8]) {
assert!(wasmparser::Parser::is_core_wasm(&bytes));
wasmparser::Validator::new().validate_all(&bytes).unwrap();
}
#[test]
fn empty() {
let output = compile(&["--crate-type", "cdylib"], "");
assert_component(&output);
}
#[test]
fn empty_main() {
let output = compile(&[], "fn main() {}");
assert_component(&output);
}
#[test]
fn hello_world() {
let output = compile(
&[],
r#"
fn main() {
println!("hello!");
}
"#,
);
assert_component(&output);
}
#[test]
fn cdylib_arbitrary_export() {
let output = compile(
&["--crate-type", "cdylib"],
r#"
#[no_mangle]
pub extern "C" fn foo() {
println!("x");
}
"#,
);
assert_component(&output);
}
#[test]
fn can_access_badfd() {
let output = compile(
&[],
r#"
#[link(wasm_import_module = "wasi_snapshot_preview1")]
extern "C" {
fn adapter_open_badfd(fd: &mut u32) -> u32;
}
fn main() {
let mut fd = 0;
let rc = unsafe {
adapter_open_badfd(&mut fd)
};
assert_eq!(rc, 0);
assert_eq!(fd, 3);
}
"#,
);
assert_component(&output);
}
#[test]
fn linker_flags() {
let output = compile(
&[
"-Clink-arg=--max-memory=65536",
"-Clink-arg=-zstack-size=32",
"-Clink-arg=--global-base=2048",
],
r#"
fn main() {
}
"#,
);
assert_component(&output);
}
#[test]
fn component_type_wit_file() {
let output = compile_with_files(
&[
"-Clink-arg=--component-type",
"-Clink-arg=foo.wit",
"-Clink-arg=--string-encoding",
"-Clink-arg=utf16",
"--crate-type",
"cdylib",
],
r#"
#[no_mangle]
pub extern "C" fn cabi_realloc(ptr: *mut u8, old_size: i32, align: i32, new_size: i32) -> *mut u8 {
_ = (ptr, old_size, align, new_size);
unreachable!()
}
#[link(wasm_import_module = "foo:bar/foo")]
extern "C" {
#[link_name = "bar"]
fn import(ptr: *mut u8, len: i32, return_ptr: *mut *mut u8);
}
#[export_name = "foo:bar/foo#bar"]
pub unsafe extern "C" fn export(ptr: *mut u8, len: i32) -> *mut u8 {
let mut result = std::ptr::null_mut();
import(ptr, len, &mut result);
result
}
"#,
&[(
"foo.wit",
r#"
package foo:bar;
interface foo {
bar: func(s: string) -> string;
}
world root {
import foo;
export foo;
}
"#,
)],
);
assert_component(&output);
}
#[test]
fn skip_component() {
let output = compile(
&["-Clink-arg=--skip-wit-component"],
r#"
fn main() {
}
"#,
);
assert_module(&output);
}