|
1 | 1 | use anyhow::Result;
|
2 |
| -use std::boxed::Box; |
| 2 | +use javy_runner::{Runner, RunnerError}; |
| 3 | +use std::path::{Path, PathBuf}; |
3 | 4 | use std::str;
|
4 |
| -use wasi_common::{pipe::WritePipe, sync::WasiCtxBuilder, WasiCtx, WasiFile}; |
5 |
| -use wasmtime::{AsContextMut, Engine, Instance, Linker, Store}; |
6 | 5 |
|
7 |
| -mod common; |
| 6 | +static ROOT: &str = env!("CARGO_MANIFEST_DIR"); |
8 | 7 |
|
9 | 8 | #[test]
|
10 | 9 | fn test_dylib() -> Result<()> {
|
11 | 10 | let js_src = "console.log(42);";
|
12 |
| - let stderr = WritePipe::new_in_memory(); |
13 |
| - run_js_src(js_src, &stderr)?; |
| 11 | + let mut runner = Runner::with_dylib(provider_module()?)?; |
14 | 12 |
|
15 |
| - let output = stderr.try_into_inner().unwrap().into_inner(); |
16 |
| - assert_eq!("42\n", str::from_utf8(&output)?); |
| 13 | + let (_, logs, _) = runner.exec_through_dylib(js_src, None)?; |
| 14 | + assert_eq!("42\n", str::from_utf8(&logs)?); |
17 | 15 |
|
18 | 16 | Ok(())
|
19 | 17 | }
|
20 | 18 |
|
21 | 19 | #[test]
|
22 | 20 | fn test_dylib_with_error() -> Result<()> {
|
23 | 21 | let js_src = "function foo() { throw new Error('foo error'); } foo();";
|
24 |
| - let stderr = WritePipe::new_in_memory(); |
25 |
| - let result = run_js_src(js_src, &stderr); |
26 | 22 |
|
27 |
| - assert!(result.is_err()); |
28 |
| - let output = stderr.try_into_inner().unwrap().into_inner(); |
| 23 | + let mut runner = Runner::with_dylib(provider_module()?)?; |
29 | 24 |
|
| 25 | + let res = runner.exec_through_dylib(js_src, None); |
| 26 | + |
| 27 | + assert!(res.is_err()); |
| 28 | + |
| 29 | + let e = res.err().unwrap(); |
30 | 30 | let expected_log_output = "Error:1:24 foo error\n at foo (function.mjs:1:24)\n at <anonymous> (function.mjs:1:50)\n\n";
|
31 |
| - assert_eq!(expected_log_output, str::from_utf8(&output)?); |
| 31 | + assert_eq!( |
| 32 | + expected_log_output, |
| 33 | + String::from_utf8(e.downcast_ref::<RunnerError>().unwrap().stderr.clone())? |
| 34 | + ); |
32 | 35 |
|
33 | 36 | Ok(())
|
34 | 37 | }
|
35 | 38 |
|
36 | 39 | #[test]
|
37 | 40 | fn test_dylib_with_exported_func() -> Result<()> {
|
38 | 41 | let js_src = "export function foo() { console.log('In foo'); }; console.log('Toplevel');";
|
39 |
| - let stderr = WritePipe::new_in_memory(); |
40 |
| - run_invoke(js_src, "foo", &stderr)?; |
41 | 42 |
|
42 |
| - let output = stderr.try_into_inner().unwrap().into_inner(); |
43 |
| - assert_eq!("Toplevel\nIn foo\n", str::from_utf8(&output)?); |
| 43 | + let mut runner = Runner::with_dylib(provider_module()?)?; |
44 | 44 |
|
45 |
| - Ok(()) |
46 |
| -} |
| 45 | + let (_, logs, _) = runner.exec_through_dylib(js_src, Some("foo"))?; |
| 46 | + assert_eq!("Toplevel\nIn foo\n", str::from_utf8(&logs)?); |
47 | 47 |
|
48 |
| -fn run_js_src<T: WasiFile + Clone + 'static>(js_src: &str, stderr: &T) -> Result<()> { |
49 |
| - let (instance, mut store) = create_wasm_env(stderr)?; |
50 |
| - |
51 |
| - let eval_bytecode_func = |
52 |
| - instance.get_typed_func::<(u32, u32), ()>(store.as_context_mut(), "eval_bytecode")?; |
53 |
| - let (bytecode_ptr, bytecode_len) = |
54 |
| - compile_src(js_src.as_bytes(), &instance, store.as_context_mut())?; |
55 |
| - eval_bytecode_func.call(store.as_context_mut(), (bytecode_ptr, bytecode_len))?; |
56 | 48 | Ok(())
|
57 | 49 | }
|
58 | 50 |
|
59 |
| -fn run_invoke<T: WasiFile + Clone + 'static>( |
60 |
| - js_src: &str, |
61 |
| - fn_to_invoke: &str, |
62 |
| - stderr: &T, |
63 |
| -) -> Result<()> { |
64 |
| - let (instance, mut store) = create_wasm_env(stderr)?; |
65 |
| - |
66 |
| - let invoke_func = |
67 |
| - instance.get_typed_func::<(u32, u32, u32, u32), ()>(store.as_context_mut(), "invoke")?; |
68 |
| - let (bytecode_ptr, bytecode_len) = |
69 |
| - compile_src(js_src.as_bytes(), &instance, store.as_context_mut())?; |
70 |
| - let (fn_name_ptr, fn_name_len) = |
71 |
| - copy_func_name(fn_to_invoke, &instance, store.as_context_mut())?; |
72 |
| - invoke_func.call( |
73 |
| - store.as_context_mut(), |
74 |
| - (bytecode_ptr, bytecode_len, fn_name_ptr, fn_name_len), |
75 |
| - )?; |
76 |
| - Ok(()) |
77 |
| -} |
78 |
| - |
79 |
| -fn create_wasm_env<T: WasiFile + Clone + 'static>( |
80 |
| - stderr: &T, |
81 |
| -) -> Result<(Instance, Store<WasiCtx>)> { |
82 |
| - let engine = Engine::default(); |
83 |
| - let mut linker = Linker::new(&engine); |
84 |
| - wasi_common::sync::add_to_linker(&mut linker, |s| s)?; |
85 |
| - let wasi = WasiCtxBuilder::new() |
86 |
| - .stderr(Box::new(stderr.clone())) |
87 |
| - .build(); |
88 |
| - let module = common::create_quickjs_provider_module(&engine)?; |
89 |
| - |
90 |
| - let mut store = Store::new(&engine, wasi); |
91 |
| - let instance = linker.instantiate(store.as_context_mut(), &module)?; |
92 |
| - |
93 |
| - Ok((instance, store)) |
94 |
| -} |
95 |
| - |
96 |
| -fn compile_src( |
97 |
| - js_src: &[u8], |
98 |
| - instance: &Instance, |
99 |
| - mut store: impl AsContextMut, |
100 |
| -) -> Result<(u32, u32)> { |
101 |
| - let memory = instance |
102 |
| - .get_memory(store.as_context_mut(), "memory") |
103 |
| - .unwrap(); |
104 |
| - let compile_src_func = |
105 |
| - instance.get_typed_func::<(u32, u32), u32>(store.as_context_mut(), "compile_src")?; |
106 |
| - |
107 |
| - let js_src_ptr = allocate_memory( |
108 |
| - instance, |
109 |
| - store.as_context_mut(), |
110 |
| - 1, |
111 |
| - js_src.len().try_into()?, |
112 |
| - )?; |
113 |
| - memory.write(store.as_context_mut(), js_src_ptr.try_into()?, js_src)?; |
114 |
| - |
115 |
| - let ret_ptr = compile_src_func.call( |
116 |
| - store.as_context_mut(), |
117 |
| - (js_src_ptr, js_src.len().try_into()?), |
118 |
| - )?; |
119 |
| - let mut ret_buffer = [0; 8]; |
120 |
| - memory.read(store.as_context(), ret_ptr.try_into()?, &mut ret_buffer)?; |
121 |
| - let bytecode_ptr = u32::from_le_bytes(ret_buffer[0..4].try_into()?); |
122 |
| - let bytecode_len = u32::from_le_bytes(ret_buffer[4..8].try_into()?); |
123 |
| - |
124 |
| - Ok((bytecode_ptr, bytecode_len)) |
125 |
| -} |
126 |
| - |
127 |
| -fn copy_func_name( |
128 |
| - fn_name: &str, |
129 |
| - instance: &Instance, |
130 |
| - mut store: impl AsContextMut, |
131 |
| -) -> Result<(u32, u32)> { |
132 |
| - let memory = instance |
133 |
| - .get_memory(store.as_context_mut(), "memory") |
134 |
| - .unwrap(); |
135 |
| - let fn_name_bytes = fn_name.as_bytes(); |
136 |
| - let fn_name_ptr = allocate_memory( |
137 |
| - instance, |
138 |
| - store.as_context_mut(), |
139 |
| - 1, |
140 |
| - fn_name_bytes.len().try_into()?, |
141 |
| - )?; |
142 |
| - memory.write( |
143 |
| - store.as_context_mut(), |
144 |
| - fn_name_ptr.try_into()?, |
145 |
| - fn_name_bytes, |
146 |
| - )?; |
147 |
| - |
148 |
| - Ok((fn_name_ptr, fn_name_bytes.len().try_into()?)) |
149 |
| -} |
| 51 | +fn provider_module() -> Result<Vec<u8>> { |
| 52 | + let mut lib_path = PathBuf::from(ROOT); |
| 53 | + lib_path.pop(); |
| 54 | + lib_path.pop(); |
| 55 | + lib_path = lib_path.join( |
| 56 | + Path::new("target") |
| 57 | + .join("wasm32-wasi") |
| 58 | + .join("release") |
| 59 | + .join("javy_quickjs_provider_wizened.wasm"), |
| 60 | + ); |
150 | 61 |
|
151 |
| -fn allocate_memory( |
152 |
| - instance: &Instance, |
153 |
| - mut store: impl AsContextMut, |
154 |
| - alignment: u32, |
155 |
| - new_size: u32, |
156 |
| -) -> Result<u32> { |
157 |
| - let realloc_func = instance.get_typed_func::<(u32, u32, u32, u32), u32>( |
158 |
| - store.as_context_mut(), |
159 |
| - "canonical_abi_realloc", |
160 |
| - )?; |
161 |
| - let orig_ptr = 0; |
162 |
| - let orig_size = 0; |
163 |
| - realloc_func |
164 |
| - .call( |
165 |
| - store.as_context_mut(), |
166 |
| - (orig_ptr, orig_size, alignment, new_size), |
167 |
| - ) |
168 |
| - .map_err(Into::into) |
| 62 | + std::fs::read(lib_path).map_err(Into::into) |
169 | 63 | }
|
0 commit comments