mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 00:01:16 +00:00
Merge remote-tracking branch 'origin/trunk' into list-str-capacity
This commit is contained in:
commit
07063a8e18
458 changed files with 20371 additions and 8316 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -55,3 +55,10 @@ where
|
|||
{
|
||||
run_test()
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum RefCount {
|
||||
Live(u32),
|
||||
Deallocated,
|
||||
Constant,
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
//--------------------------
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue