mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 05:49:08 +00:00
166 lines
6.5 KiB
Rust
166 lines
6.5 KiB
Rust
use bumpalo::Bump;
|
|
use roc_wasm_interp::{
|
|
wasi, DefaultImportDispatcher, ImportDispatcher, Instance, Value, WasiDispatcher,
|
|
};
|
|
|
|
const COMPILER_BYTES: &[u8] =
|
|
include_bytes!("../../../target/wasm32-wasi/release/roc_repl_wasm.wasm");
|
|
|
|
struct CompilerDispatcher<'a> {
|
|
arena: &'a Bump,
|
|
src: &'a str,
|
|
answer: String,
|
|
wasi: WasiDispatcher<'a>,
|
|
app: Option<Instance<'a, DefaultImportDispatcher<'a>>>,
|
|
result_addr: Option<i32>,
|
|
}
|
|
|
|
impl<'a> ImportDispatcher for CompilerDispatcher<'a> {
|
|
fn dispatch(
|
|
&mut self,
|
|
module_name: &str,
|
|
function_name: &str,
|
|
arguments: &[Value],
|
|
compiler_memory: &mut [u8],
|
|
) -> Option<Value> {
|
|
let unknown = || {
|
|
panic!(
|
|
"I could not find an implementation for import {}.{}",
|
|
module_name, function_name
|
|
)
|
|
};
|
|
|
|
if module_name == wasi::MODULE_NAME {
|
|
self.wasi
|
|
.dispatch(function_name, arguments, compiler_memory)
|
|
} else if module_name == "env" {
|
|
match function_name {
|
|
"test_create_app" => {
|
|
// Get some bytes from the compiler Wasm instance and create the app Wasm instance
|
|
// fn test_create_app(app_bytes_ptr: *const u8, app_bytes_len: usize) -> u32;
|
|
assert_eq!(arguments.len(), 2);
|
|
let app_bytes_ptr = arguments[0].expect_i32().unwrap() as usize;
|
|
let app_bytes_len = arguments[1].expect_i32().unwrap() as usize;
|
|
let app_bytes = &compiler_memory[app_bytes_ptr..][..app_bytes_len];
|
|
|
|
let is_debug_mode = false;
|
|
let instance = Instance::from_bytes(
|
|
self.arena,
|
|
app_bytes,
|
|
DefaultImportDispatcher::default(),
|
|
is_debug_mode,
|
|
)
|
|
.unwrap();
|
|
|
|
self.app = Some(instance);
|
|
let ok = Value::I32(true as i32);
|
|
Some(ok)
|
|
}
|
|
"test_run_app" => {
|
|
// fn test_run_app() -> usize;
|
|
assert_eq!(arguments.len(), 0);
|
|
match &mut self.app {
|
|
Some(instance) => {
|
|
let result_addr = instance
|
|
.call_export("wrapper", [])
|
|
.unwrap()
|
|
.expect("No return address from wrapper")
|
|
.expect_i32()
|
|
.unwrap();
|
|
self.result_addr = Some(result_addr);
|
|
let memory_size = instance.memory.len();
|
|
Some(Value::I32(memory_size as i32))
|
|
}
|
|
None => panic!("Trying to run the app but it hasn't been created"),
|
|
}
|
|
}
|
|
"test_get_result_and_memory" => {
|
|
// Copy the app's entire memory buffer into the compiler's memory,
|
|
// and return the location in that buffer where we can find the app result.
|
|
// fn test_get_result_and_memory(buffer_alloc_addr: *mut u8) -> usize;
|
|
assert_eq!(arguments.len(), 1);
|
|
let buffer_alloc_addr = arguments[0].expect_i32().unwrap() as usize;
|
|
match &self.app {
|
|
Some(instance) => {
|
|
let len = instance.memory.len();
|
|
compiler_memory[buffer_alloc_addr..][..len]
|
|
.copy_from_slice(&instance.memory);
|
|
self.result_addr.map(Value::I32)
|
|
}
|
|
None => panic!("Trying to get result and memory but there is no app"),
|
|
}
|
|
}
|
|
"test_copy_input_string" => {
|
|
// Copy the Roc source code from the test into the compiler Wasm instance
|
|
// fn test_copy_input_string(src_buffer_addr: *mut u8);
|
|
assert_eq!(arguments.len(), 1);
|
|
let src_buffer_addr = arguments[0].expect_i32().unwrap() as usize;
|
|
let len = self.src.len();
|
|
compiler_memory[src_buffer_addr..][..len].copy_from_slice(self.src.as_bytes());
|
|
None
|
|
}
|
|
"test_copy_output_string" => {
|
|
// The REPL now has a string representing the answer. Make it available to the test code.
|
|
// fn test_copy_output_string(output_ptr: *const u8, output_len: usize);
|
|
assert_eq!(arguments.len(), 2);
|
|
let output_ptr = arguments[0].expect_i32().unwrap() as usize;
|
|
let output_len = arguments[1].expect_i32().unwrap() as usize;
|
|
match std::str::from_utf8(&compiler_memory[output_ptr..][..output_len]) {
|
|
Ok(answer) => {
|
|
self.answer = answer.into();
|
|
}
|
|
Err(e) => panic!("{:?}", e),
|
|
}
|
|
None
|
|
}
|
|
"now" => Some(Value::F64(0.0)),
|
|
_ => unknown(),
|
|
}
|
|
} else {
|
|
unknown()
|
|
}
|
|
}
|
|
}
|
|
|
|
fn run(src: &'static str) -> Result<String, String> {
|
|
let arena = Bump::new();
|
|
|
|
let mut instance = {
|
|
let dispatcher = CompilerDispatcher {
|
|
arena: &arena,
|
|
src,
|
|
answer: String::new(),
|
|
wasi: WasiDispatcher::default(),
|
|
app: None,
|
|
result_addr: None,
|
|
};
|
|
|
|
let is_debug_mode = false; // logs every instruction!
|
|
Instance::from_bytes(&arena, COMPILER_BYTES, dispatcher, is_debug_mode).unwrap()
|
|
};
|
|
|
|
let len = Value::I32(src.len() as i32);
|
|
let wasm_ok: i32 = instance
|
|
.call_export("entrypoint_from_test", [len])
|
|
.unwrap()
|
|
.unwrap()
|
|
.expect_i32()
|
|
.unwrap();
|
|
let answer_str = instance.import_dispatcher.answer.to_owned();
|
|
|
|
if wasm_ok == 0 {
|
|
Err(answer_str)
|
|
} else {
|
|
Ok(answer_str)
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn expect_success(input: &'static str, expected: &str) {
|
|
assert_eq!(run(input), Ok(expected.into()));
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn expect_failure(input: &'static str, expected: &str) {
|
|
assert_eq!(run(input), Err(expected.into()));
|
|
}
|