Merge remote-tracking branch 'origin/trunk' into list-str-capacity

This commit is contained in:
Folkert 2022-03-09 14:36:34 +01:00
commit 07063a8e18
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
458 changed files with 20371 additions and 8316 deletions

View file

@ -258,6 +258,7 @@
function fd_write(fd, iovs_ptr, iovs_len, nwritten_mut_ptr) {
let string_buffer = "";
let nwritten = 0;
const STDOUT = 1;
for (let i = 0; i < iovs_len; i++) {
const index32 = iovs_ptr >> 2;
@ -282,16 +283,18 @@
}
wasiLinkObject.memory32[nwritten_mut_ptr >> 2] = nwritten;
if (string_buffer) {
console.log(string_buffer);
if (fd === STDOUT) {
console.log(string_buffer);
} else {
console.error(string_buffer);
}
}
return 0;
}
// proc_exit : (i32) -> nil
function proc_exit(exit_code) {
if (exit_code) {
throw new Error(`Wasm exited with code ${exit_code}`);
}
throw new Error(`Wasm exited with code ${exit_code}`);
}
// Signatures from wasm_test_platform.o

View file

@ -1,5 +1,5 @@
use roc_gen_wasm::wasm32_sized::Wasm32Sized;
use roc_std::{RocDec, RocList, RocOrder, RocStr};
use roc_std::{ReferenceCount, RocDec, RocList, RocOrder, RocStr};
pub trait FromWasmerMemory: Wasm32Sized {
fn decode(memory: &wasmer::Memory, offset: u32) -> Self;
@ -61,19 +61,19 @@ impl FromWasmerMemory for RocStr {
let actual_length = (last_byte ^ 0b1000_0000) as usize;
let slice = &bytes.to_ne_bytes()[..actual_length as usize];
RocStr::from_slice(slice)
unsafe { RocStr::from_slice(slice) }
} else {
// this is a big string
let ptr: wasmer::WasmPtr<u8, wasmer::Array> = wasmer::WasmPtr::new(elements);
let foobar = (ptr.deref(memory, 0, length)).unwrap();
let wasm_slice = unsafe { std::mem::transmute(foobar) };
RocStr::from_slice(wasm_slice)
unsafe { RocStr::from_slice(wasm_slice) }
}
}
}
impl<T: FromWasmerMemory + Clone> FromWasmerMemory for RocList<T> {
impl<T: FromWasmerMemory + Clone + ReferenceCount> FromWasmerMemory for RocList<T> {
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
let bytes = <u64 as FromWasmerMemory>::decode(memory, offset);

View file

@ -489,10 +489,7 @@ where
match test_wrapper.call(&[]) {
Err(e) => Err(format!("call to `test_wrapper`: {:?}", e)),
Ok(result) => {
let address = match result[0] {
wasmer::Value::I32(a) => a,
_ => panic!(),
};
let address = result[0].unwrap_i32();
let output = <T as crate::helpers::llvm::FromWasmerMemory>::decode(
memory,
@ -582,19 +579,31 @@ macro_rules! assert_llvm_evals_to {
#[allow(unused_macros)]
macro_rules! assert_evals_to {
($src:expr, $expected:expr, $ty:ty) => {{
assert_evals_to!($src, $expected, $ty, $crate::helpers::llvm::identity);
assert_evals_to!($src, $expected, $ty, $crate::helpers::llvm::identity, false);
}};
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {{
// same as above, except with an additional transformation argument.
{
#[cfg(feature = "wasm-cli-run")]
$crate::helpers::llvm::assert_wasm_evals_to!(
$src, $expected, $ty, $transform, false, false
);
assert_evals_to!($src, $expected, $ty, $transform, false);
}};
($src:expr, $expected:expr, $ty:ty, $transform:expr, $ignore_problems: expr) => {{
// same as above, except with ignore_problems.
#[cfg(feature = "wasm-cli-run")]
$crate::helpers::llvm::assert_wasm_evals_to!(
$src,
$expected,
$ty,
$transform,
$ignore_problems
);
$crate::helpers::llvm::assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
}
};
$crate::helpers::llvm::assert_llvm_evals_to!(
$src,
$expected,
$ty,
$transform,
$ignore_problems
);
}};
}
#[allow(unused_macros)]

View file

@ -55,3 +55,10 @@ where
{
run_test()
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RefCount {
Live(u32),
Deallocated,
Constant,
}

View file

@ -6,6 +6,7 @@ use std::marker::PhantomData;
use std::path::{Path, PathBuf};
use wasmer::{Memory, WasmPtr};
use super::RefCount;
use crate::helpers::from_wasmer_memory::FromWasmerMemory;
use roc_collections::all::{MutMap, MutSet};
use roc_gen_wasm::wasm32_result::Wasm32Result;
@ -17,6 +18,7 @@ const OUT_DIR_VAR: &str = "TEST_GEN_OUT";
const TEST_WRAPPER_NAME: &str = "test_wrapper";
const INIT_REFCOUNT_NAME: &str = "init_refcount_test";
const PANIC_MSG_NAME: &str = "panic_msg";
fn promote_expr_to_module(src: &str) -> String {
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
@ -176,7 +178,7 @@ fn load_bytes_into_runtime(bytes: Vec<u8>) -> wasmer::Instance {
}
#[allow(dead_code)]
pub fn assert_wasm_evals_to_help<T>(src: &str, phantom: PhantomData<T>) -> Result<T, String>
pub fn assert_evals_to_help<T>(src: &str, phantom: PhantomData<T>) -> Result<T, String>
where
T: FromWasmerMemory + Wasm32Result,
{
@ -192,12 +194,15 @@ where
let test_wrapper = instance.exports.get_function(TEST_WRAPPER_NAME).unwrap();
match test_wrapper.call(&[]) {
Err(e) => Err(format!("{:?}", e)),
Err(e) => {
if let Some(msg) = get_roc_panic_msg(&instance, memory) {
Err(msg)
} else {
Err(e.to_string())
}
}
Ok(result) => {
let address = match result[0] {
wasmer::Value::I32(a) => a,
_ => panic!(),
};
let address = result[0].unwrap_i32();
if false {
println!("test_wrapper returned 0x{:x}", address);
@ -216,12 +221,37 @@ where
}
}
/// Our test roc_panic stores a pointer to its message in a global variable so we can find it.
fn get_roc_panic_msg(instance: &wasmer::Instance, memory: &Memory) -> Option<String> {
let memory_bytes = unsafe { memory.data_unchecked() };
// We need to dereference twice!
// The Wasm Global only points at the memory location of the C global value
let panic_msg_global = instance.exports.get_global(PANIC_MSG_NAME).unwrap();
let global_addr = panic_msg_global.get().unwrap_i32() as usize;
let global_ptr = memory_bytes[global_addr..].as_ptr() as *const u32;
// Dereference again to find the bytes of the message string
let msg_addr = unsafe { *global_ptr };
if msg_addr == 0 {
return None;
}
let msg_index = msg_addr as usize;
let msg_len = memory_bytes[msg_index..]
.iter()
.position(|c| *c == 0)
.unwrap();
let msg_bytes = memory_bytes[msg_index..][..msg_len].to_vec();
let msg = unsafe { String::from_utf8_unchecked(msg_bytes) };
Some(msg)
}
#[allow(dead_code)]
pub fn assert_wasm_refcounts_help<T>(
src: &str,
phantom: PhantomData<T>,
num_refcounts: usize,
) -> Result<Vec<u32>, String>
) -> Result<Vec<RefCount>, String>
where
T: FromWasmerMemory + Wasm32Result,
{
@ -239,10 +269,7 @@ where
let init_result = init_refcount_test.call(&[wasmer::Value::I32(expected_len)]);
let refcount_vector_addr = match init_result {
Err(e) => return Err(format!("{:?}", e)),
Ok(result) => match result[0] {
wasmer::Value::I32(a) => a,
_ => panic!(),
},
Ok(result) => result[0].unwrap_i32(),
};
// Run the test
@ -270,12 +297,15 @@ where
for i in 0..num_refcounts {
let rc_ptr = refcount_ptrs[i].get();
let rc = if rc_ptr.offset() == 0 {
// RC pointer has been set to null, which means the value has been freed.
// In tests, we simply represent this as zero refcount.
0
RefCount::Deallocated
} else {
let rc_encoded = rc_ptr.deref(memory).unwrap().get();
(rc_encoded - i32::MIN + 1) as u32
let rc_encoded: i32 = rc_ptr.deref(memory).unwrap().get();
if rc_encoded == 0 {
RefCount::Constant
} else {
let rc = rc_encoded - i32::MIN + 1;
RefCount::Live(rc as u32)
}
};
refcounts.push(rc);
}
@ -313,42 +343,44 @@ pub fn debug_memory_hex(memory: &Memory, address: i32, size: usize) {
}
#[allow(unused_macros)]
macro_rules! assert_wasm_evals_to {
macro_rules! assert_evals_to {
($src:expr, $expected:expr, $ty:ty) => {
$crate::helpers::wasm::assert_evals_to!(
$src,
$expected,
$ty,
$crate::helpers::wasm::identity,
false
)
};
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
$crate::helpers::wasm::assert_evals_to!($src, $expected, $ty, $transform, false);
};
($src:expr, $expected:expr, $ty:ty, $transform:expr, $ignore_problems: expr) => {{
let phantom = std::marker::PhantomData;
match $crate::helpers::wasm::assert_wasm_evals_to_help::<$ty>($src, phantom) {
Err(msg) => panic!("{:?}", msg),
let _ = $ignore_problems; // Always ignore "problems"! One backend (LLVM) is enough to cover them.
match $crate::helpers::wasm::assert_evals_to_help::<$ty>($src, phantom) {
Err(msg) => panic!("{}", msg),
Ok(actual) => {
assert_eq!($transform(actual), $expected)
}
}
};
($src:expr, $expected:expr, $ty:ty) => {
$crate::helpers::wasm::assert_wasm_evals_to!(
$src,
$expected,
$ty,
$crate::helpers::wasm::identity
);
};
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
$crate::helpers::wasm::assert_wasm_evals_to!($src, $expected, $ty, $transform);
};
}};
}
#[allow(unused_macros)]
macro_rules! assert_evals_to {
($src:expr, $expected:expr, $ty:ty) => {{
assert_evals_to!($src, $expected, $ty, $crate::helpers::wasm::identity);
macro_rules! expect_runtime_error_panic {
($src:expr) => {{
$crate::helpers::wasm::assert_evals_to!(
$src,
false, // fake value/type for eval
bool,
$crate::helpers::wasm::identity,
true // ignore problems
);
}};
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
// Same as above, except with an additional transformation argument.
{
$crate::helpers::wasm::assert_wasm_evals_to!($src, $expected, $ty, $transform);
}
};
}
#[allow(dead_code)]
@ -377,8 +409,9 @@ macro_rules! assert_refcounts {
#[allow(unused_imports)]
pub(crate) use assert_evals_to;
#[allow(unused_imports)]
pub(crate) use assert_wasm_evals_to;
pub(crate) use expect_runtime_error_panic;
#[allow(unused_imports)]
pub(crate) use assert_refcounts;

View file

@ -1,4 +1,5 @@
#include <stdio.h>
#include <stdlib.h>
// Makes test runs take 50% longer, due to linking
#define ENABLE_PRINTF 0
@ -121,14 +122,18 @@ void roc_dealloc(void *ptr, unsigned int alignment)
//--------------------------
void roc_panic(void *ptr, unsigned int alignment)
// Allow the test to probe the panic message
char* panic_msg;
void roc_panic(char *msg, unsigned int tag_id)
{
#if ENABLE_PRINTF
char *msg = (char *)ptr;
fprintf(stderr,
"Application crashed with message\n\n %s\n\nShutting down\n", msg);
#endif
abort();
panic_msg = msg;
// Note: no dynamic string formatting
fputs("Application crashed with message\n\n ", stderr);
fputs(msg, stderr);
fputs("\n\nShutting down\n", stderr);
exit(101);
}
//--------------------------