repl_test: write Rust replacements for JS functions

This commit is contained in:
Brian Carroll 2022-02-19 23:29:06 +00:00
parent ef97ad69ae
commit a75aa52b91
5 changed files with 188 additions and 8 deletions

49
Cargo.lock generated
View File

@ -1193,6 +1193,32 @@ version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
[[package]]
name = "dynasm"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b1801e630bd336d0bbbdbf814de6cc749c9a400c7e3d995e6adfd455d0c83c"
dependencies = [
"bitflags",
"byteorder",
"lazy_static",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "dynasmrt"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d428afc93ad288f6dffc1fa5f4a78201ad2eec33c5a522e51c181009eb09061"
dependencies = [
"byteorder",
"dynasm",
"memmap2 0.5.0",
]
[[package]]
name = "either"
version = "1.6.1"
@ -3192,9 +3218,10 @@ dependencies = [
"indoc",
"roc_cli",
"roc_repl_cli",
"roc_repl_wasm",
"roc_test_utils",
"strip-ansi-escapes",
"wasmer",
"wasmer-wasi",
]
[[package]]
@ -4748,6 +4775,7 @@ dependencies = [
"thiserror",
"wasmer-compiler",
"wasmer-compiler-cranelift",
"wasmer-compiler-singlepass",
"wasmer-derive",
"wasmer-engine",
"wasmer-engine-dylib",
@ -4796,6 +4824,25 @@ dependencies = [
"wasmer-vm",
]
[[package]]
name = "wasmer-compiler-singlepass"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9429b9f7708c582d855b1787f09c7029ff23fb692550d4a1cc351c8ea84c3014"
dependencies = [
"byteorder",
"dynasm",
"dynasmrt",
"lazy_static",
"loupe",
"more-asserts",
"rayon",
"smallvec",
"wasmer-compiler",
"wasmer-types",
"wasmer-vm",
]
[[package]]
name = "wasmer-derive"
version = "2.0.0"

View File

@ -39,7 +39,7 @@ libc = "0.2.106"
inkwell = { path = "../../vendor/inkwell" }
target-lexicon = "0.12.2"
libloading = "0.7.1"
wasmer = { version = "2.0.0", default-features = false, features = ["default-cranelift", "default-universal"] }
wasmer = { version = "2.0.0", default-features = false, features = ["default-singlepass", "default-universal"] }
wasmer-wasi = "2.0.0"
tempfile = "3.2.0"
indoc = "1.0.3"

View File

@ -12,13 +12,12 @@ roc_cli = {path = "../cli"}
[dev-dependencies]
indoc = "1.0.3"
roc_test_utils = {path = "../test_utils"}
strip-ansi-escapes = "0.1.1"
wasmer = {version = "2.0.0", default-features = false, features = ["default-singlepass", "default-universal"]}
wasmer-wasi = "2.0.0"
roc_repl_cli = {path = "../repl_cli"}
# roc_repl_wasm = {path = "../repl_wasm"}
roc_test_utils = {path = "../test_utils"}
[features]
default = ["cli"]
cli = []
wasm = []

View File

@ -1,10 +1,15 @@
use indoc::indoc;
#[cfg(not(feature = "wasm"))]
mod cli;
#[cfg(feature = "cli")]
#[cfg(not(feature = "wasm"))]
use crate::cli::{expect_failure, expect_success};
#[cfg(feature = "wasm")]
mod wasm;
#[cfg(feature = "wasm")]
use crate::wasm::{expect_failure, expect_success};
#[test]
fn literal_0() {
expect_success("0", "0 : Num *");

129
repl_test/src/wasm.rs Normal file
View File

@ -0,0 +1,129 @@
use std::{
cell::RefCell,
ops::{Deref, DerefMut},
thread_local,
};
use wasmer::Instance;
thread_local! {
static REPL_STATE: RefCell<Option<ReplState>> = RefCell::new(None)
}
struct ReplState {
compiler: Instance,
app: Option<Instance>,
result_addr: Option<u32>,
}
fn instantiate_compiler(wasm_module_bytes: &[u8]) {
use wasmer::{Module, Store};
use wasmer_wasi::WasiState;
let store = Store::default();
let wasmer_module = Module::new(&store, &wasm_module_bytes).unwrap();
// First, we create the `WasiEnv`
let mut wasi_env = WasiState::new("hello").finalize().unwrap();
// Then, we get the import object related to our WASI
// and attach it to the Wasm instance.
let import_object = wasi_env
.import_object(&wasmer_module)
.unwrap_or_else(|_| wasmer::imports!());
let instance = wasmer::Instance::new(&wasmer_module, &import_object).unwrap();
REPL_STATE.with(|f| {
if let Some(state) = f.borrow_mut().deref_mut() {
state.app = Some(instance)
} else {
panic!("REPL state not found")
}
})
}
fn wasmer_create_app(wasm_module_bytes: &[u8]) {
use wasmer::{Module, Store};
use wasmer_wasi::WasiState;
let store = Store::default();
let wasmer_module = Module::new(&store, &wasm_module_bytes).unwrap();
// First, we create the `WasiEnv`
let mut wasi_env = WasiState::new("hello").finalize().unwrap();
// Then, we get the import object related to our WASI
// and attach it to the Wasm instance.
let import_object = wasi_env
.import_object(&wasmer_module)
.unwrap_or_else(|_| wasmer::imports!());
let instance = wasmer::Instance::new(&wasmer_module, &import_object).unwrap();
REPL_STATE.with(|f| {
if let Some(state) = f.borrow_mut().deref_mut() {
state.app = Some(instance)
} else {
panic!("REPL state not found")
}
})
}
fn wasmer_run_app() -> u32 {
REPL_STATE.with(|f| {
if let Some(state) = f.borrow_mut().deref_mut() {
if let Some(app) = &state.app {
let wrapper = app.exports.get_function("wrapper").unwrap();
let result_addr: i32 = match wrapper.call(&[]) {
Err(e) => panic!("{:?}", e),
Ok(result) => match result[0] {
wasmer::Value::I32(a) => a,
_ => panic!("Expected an i32 address, got {:?}", result),
},
};
state.result_addr = Some(result_addr as u32);
let memory = app.exports.get_memory("memory").unwrap();
memory.size().bytes().0 as u32
} else {
panic!("App not found")
}
} else {
panic!("REPL state not found")
}
})
}
fn wasmer_get_result_and_memory(buffer_alloc_addr: u32) -> u32 {
REPL_STATE.with(|f| {
if let Some(state) = f.borrow().deref() {
if let Some(app) = &state.app {
let app_memory = app.exports.get_memory("memory").unwrap();
let compiler_memory = state.compiler.exports.get_memory("memory").unwrap();
let result_addr = state.result_addr.unwrap();
let compiler_memory_bytes: &mut [u8] =
unsafe { compiler_memory.data_unchecked_mut() };
let app_memory_bytes: &[u8] = unsafe { app_memory.data_unchecked() };
let buf_addr = buffer_alloc_addr as usize;
let len = app_memory_bytes.len();
compiler_memory_bytes[buf_addr..][..len].copy_from_slice(app_memory_bytes);
result_addr
} else {
panic!("REPL app not found")
}
} else {
panic!("REPL state not found")
}
})
}
pub fn expect_success(input: &str, expected: &str) {
todo!()
}
pub fn expect_failure(input: &str, expected: &str) {
todo!()
}