mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
repl_test: 2 thread_locals to avoid mutable borrow issues. Tests running but very slow.
This commit is contained in:
parent
25cd7f9fab
commit
02889300d0
3 changed files with 81 additions and 52 deletions
|
@ -3,4 +3,4 @@
|
||||||
# We can try to make the build nicer later
|
# We can try to make the build nicer later
|
||||||
|
|
||||||
cargo build --target wasm32-unknown-unknown -p roc_repl_wasm --features wasmer --release
|
cargo build --target wasm32-unknown-unknown -p roc_repl_wasm --features wasmer --release
|
||||||
cargo test -p repl_test --features wasm -- --nocapture literal_42
|
cargo test -p repl_test --features wasm
|
||||||
|
|
|
@ -10,28 +10,31 @@ use wasmer_wasi::WasiState;
|
||||||
|
|
||||||
const WASM_REPL_COMPILER_PATH: &str = "../target/wasm32-unknown-unknown/release/roc_repl_wasm.wasm";
|
const WASM_REPL_COMPILER_PATH: &str = "../target/wasm32-unknown-unknown/release/roc_repl_wasm.wasm";
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static COMPILER: RefCell<Option<Instance>> = RefCell::new(None)
|
||||||
|
}
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static REPL_STATE: RefCell<Option<ReplState>> = RefCell::new(None)
|
static REPL_STATE: RefCell<Option<ReplState>> = RefCell::new(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ReplState {
|
struct ReplState {
|
||||||
src: &'static str,
|
src: &'static str,
|
||||||
compiler: Instance,
|
|
||||||
app: Option<Instance>,
|
app: Option<Instance>,
|
||||||
result_addr: Option<u32>,
|
result_addr: Option<u32>,
|
||||||
output: Option<String>,
|
output: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wasmer_create_app(app_bytes_ptr: u32, app_bytes_len: u32) {
|
fn wasmer_create_app(app_bytes_ptr: u32, app_bytes_len: u32) {
|
||||||
REPL_STATE.with(|f| {
|
let app = COMPILER.with(|f| {
|
||||||
if let Some(state) = f.borrow_mut().deref_mut() {
|
if let Some(compiler) = f.borrow().deref() {
|
||||||
let compiler_memory = state.compiler.exports.get_memory("memory").unwrap();
|
let memory = compiler.exports.get_memory("memory").unwrap();
|
||||||
let compiler_memory_bytes: &mut [u8] = unsafe { compiler_memory.data_unchecked_mut() };
|
let memory_bytes: &[u8] = unsafe { memory.data_unchecked() };
|
||||||
|
|
||||||
// Find the slice of bytes for the compiled Roc app
|
// Find the slice of bytes for the compiled Roc app
|
||||||
let ptr = app_bytes_ptr as usize;
|
let ptr = app_bytes_ptr as usize;
|
||||||
let len = app_bytes_len as usize;
|
let len = app_bytes_len as usize;
|
||||||
let app_module_bytes: &[u8] = &compiler_memory_bytes[ptr..][..len];
|
let app_module_bytes: &[u8] = &memory_bytes[ptr..][..len];
|
||||||
|
|
||||||
// Parse the bytes into a Wasmer module
|
// Parse the bytes into a Wasmer module
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
|
@ -43,13 +46,20 @@ fn wasmer_create_app(app_bytes_ptr: u32, app_bytes_len: u32) {
|
||||||
.import_object(&wasmer_module)
|
.import_object(&wasmer_module)
|
||||||
.unwrap_or_else(|_| imports!());
|
.unwrap_or_else(|_| imports!());
|
||||||
|
|
||||||
// Create an executable instance (give it a stack & heap, etc. For ELF, this would be the OS's job.)
|
// Create an executable instance. (Give it a stack & heap, etc. If this was ELF, it would be the OS's job.)
|
||||||
let instance = Instance::new(&wasmer_module, &import_object).unwrap();
|
Instance::new(&wasmer_module, &import_object).unwrap()
|
||||||
state.app = Some(instance)
|
|
||||||
} else {
|
} else {
|
||||||
panic!("REPL state not found")
|
unreachable!()
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
REPL_STATE.with(|f| {
|
||||||
|
if let Some(state) = f.borrow_mut().deref_mut() {
|
||||||
|
state.app = Some(app)
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wasmer_run_app() -> u32 {
|
fn wasmer_run_app() -> u32 {
|
||||||
|
@ -67,10 +77,10 @@ fn wasmer_run_app() -> u32 {
|
||||||
let memory = app.exports.get_memory("memory").unwrap();
|
let memory = app.exports.get_memory("memory").unwrap();
|
||||||
memory.size().bytes().0 as u32
|
memory.size().bytes().0 as u32
|
||||||
} else {
|
} else {
|
||||||
panic!("App not found")
|
unreachable!()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
panic!("REPL state not found")
|
unreachable!()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -80,17 +90,23 @@ fn wasmer_get_result_and_memory(buffer_alloc_addr: u32) -> u32 {
|
||||||
if let Some(state) = f.borrow().deref() {
|
if let Some(state) = f.borrow().deref() {
|
||||||
if let Some(app) = &state.app {
|
if let Some(app) = &state.app {
|
||||||
let app_memory = app.exports.get_memory("memory").unwrap();
|
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 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 app_memory_bytes: &[u8] = unsafe { app_memory.data_unchecked() };
|
||||||
|
|
||||||
let buf_addr = buffer_alloc_addr as usize;
|
let buf_addr = buffer_alloc_addr as usize;
|
||||||
let len = app_memory_bytes.len();
|
let len = app_memory_bytes.len();
|
||||||
|
|
||||||
|
COMPILER.with(|f| {
|
||||||
|
if let Some(compiler) = f.borrow().deref() {
|
||||||
|
let memory = compiler.exports.get_memory("memory").unwrap();
|
||||||
|
let compiler_memory_bytes: &mut [u8] =
|
||||||
|
unsafe { memory.data_unchecked_mut() };
|
||||||
compiler_memory_bytes[buf_addr..][..len].copy_from_slice(app_memory_bytes);
|
compiler_memory_bytes[buf_addr..][..len].copy_from_slice(app_memory_bytes);
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
result_addr
|
result_addr
|
||||||
} else {
|
} else {
|
||||||
|
@ -103,33 +119,49 @@ fn wasmer_get_result_and_memory(buffer_alloc_addr: u32) -> u32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wasmer_copy_input_string(src_buffer_addr: u32) {
|
fn wasmer_copy_input_string(src_buffer_addr: u32) {
|
||||||
REPL_STATE.with(|f| {
|
let src = REPL_STATE.with(|rs| {
|
||||||
if let Some(state) = f.borrow().deref() {
|
if let Some(state) = rs.borrow_mut().deref_mut() {
|
||||||
let compiler_memory = state.compiler.exports.get_memory("memory").unwrap();
|
std::mem::take(&mut state.src)
|
||||||
let compiler_memory_bytes: &mut [u8] = unsafe { compiler_memory.data_unchecked_mut() };
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
COMPILER.with(|c| {
|
||||||
|
if let Some(compiler) = c.borrow().deref() {
|
||||||
|
let memory = compiler.exports.get_memory("memory").unwrap();
|
||||||
|
let memory_bytes: &mut [u8] = unsafe { memory.data_unchecked_mut() };
|
||||||
|
|
||||||
let buf_addr = src_buffer_addr as usize;
|
let buf_addr = src_buffer_addr as usize;
|
||||||
let len = state.src.len();
|
let len = src.len();
|
||||||
compiler_memory_bytes[buf_addr..][..len].copy_from_slice(state.src.as_bytes());
|
memory_bytes[buf_addr..][..len].copy_from_slice(src.as_bytes());
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wasmer_copy_output_string(output_ptr: u32, output_len: u32) {
|
fn wasmer_copy_output_string(output_ptr: u32, output_len: u32) {
|
||||||
REPL_STATE.with(|f| {
|
let output: String = COMPILER.with(|c| {
|
||||||
if let Some(state) = f.borrow_mut().deref_mut() {
|
if let Some(compiler) = c.borrow().deref() {
|
||||||
let compiler_memory = state.compiler.exports.get_memory("memory").unwrap();
|
let memory = compiler.exports.get_memory("memory").unwrap();
|
||||||
let compiler_memory_bytes: &mut [u8] = unsafe { compiler_memory.data_unchecked_mut() };
|
let memory_bytes: &[u8] = unsafe { memory.data_unchecked() };
|
||||||
|
|
||||||
// Find the slice of bytes for the compiled Roc app
|
// Find the slice of bytes for the output string
|
||||||
let ptr = output_ptr as usize;
|
let ptr = output_ptr as usize;
|
||||||
let len = output_len as usize;
|
let len = output_len as usize;
|
||||||
let output_bytes: &[u8] = &compiler_memory_bytes[ptr..][..len];
|
let output_bytes: &[u8] = &memory_bytes[ptr..][..len];
|
||||||
|
|
||||||
// Copy it out of the Wasm module
|
// Copy it out of the Wasm module
|
||||||
let copied_bytes = output_bytes.to_vec();
|
let copied_bytes = output_bytes.to_vec();
|
||||||
let output = unsafe { String::from_utf8_unchecked(copied_bytes) };
|
unsafe { String::from_utf8_unchecked(copied_bytes) }
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
REPL_STATE.with(|f| {
|
||||||
|
if let Some(state) = f.borrow_mut().deref_mut() {
|
||||||
state.output = Some(output)
|
state.output = Some(output)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -160,39 +192,36 @@ fn init_compiler() -> Instance {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(src: &'static str) -> (bool, String) {
|
fn run(src: &'static str) -> (bool, String) {
|
||||||
REPL_STATE.with(|f| {
|
REPL_STATE.with(|rs| {
|
||||||
let compiler = init_compiler();
|
*rs.borrow_mut().deref_mut() = Some(ReplState {
|
||||||
|
|
||||||
let new_state = ReplState {
|
|
||||||
src,
|
src,
|
||||||
compiler,
|
|
||||||
app: None,
|
app: None,
|
||||||
result_addr: None,
|
result_addr: None,
|
||||||
output: None,
|
output: None,
|
||||||
};
|
});
|
||||||
|
});
|
||||||
|
|
||||||
{
|
let ok = COMPILER.with(|c| {
|
||||||
*f.borrow_mut().deref_mut() = Some(new_state);
|
*c.borrow_mut().deref_mut() = Some(init_compiler());
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(state) = f.borrow().deref() {
|
if let Some(compiler) = c.borrow().deref() {
|
||||||
let entrypoint = state
|
let entrypoint = compiler
|
||||||
.compiler
|
|
||||||
.exports
|
.exports
|
||||||
.get_function("entrypoint_from_test")
|
.get_function("entrypoint_from_test")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let src_len = Value::I32(src.len() as i32);
|
let src_len = Value::I32(src.len() as i32);
|
||||||
let wasm_ok: i32 = entrypoint.call(&[src_len]).unwrap().deref()[0].unwrap_i32();
|
let wasm_ok: i32 = entrypoint.call(&[src_len]).unwrap().deref()[0].unwrap_i32();
|
||||||
let ok = wasm_ok != 0;
|
wasm_ok != 0
|
||||||
|
|
||||||
let final_state = f.take().unwrap();
|
|
||||||
|
|
||||||
(ok, final_state.output.unwrap())
|
|
||||||
} else {
|
} else {
|
||||||
panic!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
let final_state: ReplState = REPL_STATE.with(|rs| rs.take()).unwrap();
|
||||||
|
let output: String = final_state.output.unwrap();
|
||||||
|
|
||||||
|
(ok, output)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_compiler_load_error(e: std::io::Error) -> String {
|
fn format_compiler_load_error(e: std::io::Error) -> String {
|
||||||
|
|
|
@ -237,7 +237,7 @@ pub async fn entrypoint_from_js(src: String) -> Result<String, String> {
|
||||||
// Transform the Expr to a string
|
// Transform the Expr to a string
|
||||||
// `Result::Err` becomes a JS exception that will be caught and displayed
|
// `Result::Err` becomes a JS exception that will be caught and displayed
|
||||||
match format_answer(arena, res_answer, expr_type_str) {
|
match format_answer(arena, res_answer, expr_type_str) {
|
||||||
ReplOutput::NoProblems { expr, expr_type } => Ok(format!("\n{}: {}", expr, expr_type)),
|
ReplOutput::NoProblems { expr, expr_type } => Ok(format!("{} : {}", expr, expr_type)),
|
||||||
ReplOutput::Problems(lines) => Err(format!("\n{}\n", lines.join("\n\n"))),
|
ReplOutput::Problems(lines) => Err(format!("\n{}\n", lines.join("\n\n"))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue