Skip to content

Commit

Permalink
Flush stdout unconditionally (#668)
Browse files Browse the repository at this point in the history
This commit ensures that `stdout` is always correctly flushed after
reading from it. This is critical for dynamic use-cases of Javy
  • Loading branch information
saulecabrera authored Jun 12, 2024
1 parent 090203f commit 7aa1abe
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 10 deletions.
56 changes: 47 additions & 9 deletions crates/cli/tests/dynamic_linking_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::io::{Cursor, Read, Write};
use std::process::Command;
use std::str;
use uuid::Uuid;
use wasi_common::pipe::WritePipe;
use wasi_common::pipe::{ReadPipe, WritePipe};
use wasi_common::sync::WasiCtxBuilder;
use wasi_common::WasiCtx;
use wasmtime::{Config, Engine, ExternType, Linker, Module, Store};
Expand All @@ -14,7 +14,7 @@ mod common;
#[test]
pub fn test_dynamic_linking() -> Result<()> {
let js_src = "console.log(42);";
let log_output = invoke_fn_on_generated_module(js_src, "_start", None)?;
let log_output = invoke_fn_on_generated_module(js_src, "_start", None, None, None)?;
assert_eq!("42\n", &log_output);
Ok(())
}
Expand All @@ -29,15 +29,16 @@ pub fn test_dynamic_linking_with_func() -> Result<()> {
export foo-bar: func()
}
";
let log_output = invoke_fn_on_generated_module(js_src, "foo-bar", Some((wit, "foo-test")))?;
let log_output =
invoke_fn_on_generated_module(js_src, "foo-bar", Some((wit, "foo-test")), None, None)?;
assert_eq!("Toplevel\nIn foo\n", &log_output);
Ok(())
}

#[test]
pub fn test_dynamic_linking_with_func_without_flag() -> Result<()> {
let js_src = "export function foo() { console.log('In foo'); }; console.log('Toplevel');";
let res = invoke_fn_on_generated_module(js_src, "foo", None);
let res = invoke_fn_on_generated_module(js_src, "foo", None, None, None);
assert_eq!(
"failed to find function export `foo`",
res.err().unwrap().to_string()
Expand All @@ -50,7 +51,7 @@ pub fn check_for_new_imports() -> Result<()> {
// If you need to change this test, then you've likely made a breaking change.
let js_src = "console.log(42);";
let wasm = create_dynamically_linked_wasm_module(js_src, None)?;
let (engine, _linker, _store) = create_wasm_env(WritePipe::new_in_memory())?;
let (engine, _linker, _store) = create_wasm_env(WritePipe::new_in_memory(), None, None)?;
let module = Module::from_binary(&engine, &wasm)?;
for import in module.imports() {
match (import.module(), import.name(), import.ty()) {
Expand Down Expand Up @@ -78,7 +79,7 @@ pub fn check_for_new_imports_for_exports() -> Result<()> {
}
";
let wasm = create_dynamically_linked_wasm_module(js_src, Some((wit, "foo-test")))?;
let (engine, _linker, _store) = create_wasm_env(WritePipe::new_in_memory())?;
let (engine, _linker, _store) = create_wasm_env(WritePipe::new_in_memory(), None, None)?;
let module = Module::from_binary(&engine, &wasm)?;
for import in module.imports() {
match (import.module(), import.name(), import.ty()) {
Expand Down Expand Up @@ -107,8 +108,13 @@ pub fn test_dynamic_linking_with_arrow_fn() -> Result<()> {
export default: func()
}
";
let log_output =
invoke_fn_on_generated_module(js_src, "default", Some((wit, "exported-arrow")))?;
let log_output = invoke_fn_on_generated_module(
js_src,
"default",
Some((wit, "exported-arrow")),
None,
None,
)?;
assert_eq!("42\n", log_output);
Ok(())
}
Expand All @@ -120,6 +126,28 @@ fn test_producers_section_present() -> Result<()> {
Ok(())
}

#[test]
fn javy_json_identity() -> Result<()> {
let src = r#"
console.log(Javy.JSON.toStdout(Javy.JSON.fromStdin()));
"#;

let input = "{\"x\":5}";

let bytes = String::from(input).into_bytes();
let stdin = Some(ReadPipe::from(bytes));
let stdout = WritePipe::new_in_memory();
let out = invoke_fn_on_generated_module(src, "_start", None, stdin, Some(stdout.clone()))?;

assert_eq!(out, "undefined\n");
assert_eq!(
String::from(input),
String::from_utf8(stdout.try_into_inner().unwrap().into_inner()).unwrap(),
);

Ok(())
}

fn create_dynamically_linked_wasm_module(
js_src: &str,
wit: Option<(&str, &str)>,
Expand Down Expand Up @@ -164,11 +192,13 @@ fn invoke_fn_on_generated_module(
js_src: &str,
func: &str,
wit: Option<(&str, &str)>,
stdin: Option<ReadPipe<Cursor<Vec<u8>>>>,
stdout: Option<WritePipe<Cursor<Vec<u8>>>>,
) -> Result<String> {
let js_wasm = create_dynamically_linked_wasm_module(js_src, wit)?;

let stderr = WritePipe::new_in_memory();
let (engine, mut linker, mut store) = create_wasm_env(stderr.clone())?;
let (engine, mut linker, mut store) = create_wasm_env(stderr.clone(), stdin, stdout)?;
let quickjs_provider_module = common::create_quickjs_provider_module(&engine)?;
let js_module = Module::from_binary(&engine, &js_wasm)?;

Expand All @@ -190,11 +220,19 @@ fn invoke_fn_on_generated_module(

fn create_wasm_env(
stderr: WritePipe<Cursor<Vec<u8>>>,
stdin: Option<ReadPipe<Cursor<Vec<u8>>>>,
stdout: Option<WritePipe<Cursor<Vec<u8>>>>,
) -> Result<(Engine, Linker<WasiCtx>, Store<WasiCtx>)> {
let engine = Engine::new(Config::new().wasm_multi_memory(true))?;
let mut linker = Linker::new(&engine);
wasi_common::sync::add_to_linker(&mut linker, |s| s)?;
let wasi = WasiCtxBuilder::new().stderr(Box::new(stderr)).build();
if let Some(stdout) = stdout {
wasi.set_stdout(Box::new(stdout));
}
if let Some(stdin) = stdin {
wasi.set_stdin(Box::new(stdin));
}
let store = Store::new(&engine, wasi);
Ok((engine, linker, store))
}
3 changes: 2 additions & 1 deletion crates/javy/src/apis/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,5 +230,6 @@ fn to_stdout(args: Args<'_>) -> Result<()> {
let (_, args) = args.release();
let mut fd = std::io::stdout();
let buffer = json::stringify(args[0].clone())?;
fd.write_all(&buffer).map_err(Into::into)
fd.write_all(&buffer)?;
fd.flush().map_err(Into::into)
}

0 comments on commit 7aa1abe

Please sign in to comment.