Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JS runtime option infra and redirect-stdout-to-stderr option #739

Merged
merged 3 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions crates/cli/src/codegen/builder.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::codegen::{CodeGen, CodeGenType, DynamicGenerator, StaticGenerator};
use anyhow::{bail, Result};
use javy_config::Config;
use std::path::PathBuf;

/// Options for using WIT in the code generation process.
Expand Down Expand Up @@ -79,18 +80,23 @@ impl CodeGenBuilder {
}

/// Build a [`CodeGenerator`].
pub fn build<T>(self) -> Result<Box<dyn CodeGen>>
pub fn build<T>(self, js_runtime_config: Config) -> Result<Box<dyn CodeGen>>
where
T: CodeGen,
{
match T::classify() {
CodeGenType::Static => self.build_static(),
CodeGenType::Dynamic => self.build_dynamic(),
CodeGenType::Static => self.build_static(js_runtime_config),
CodeGenType::Dynamic => {
if js_runtime_config != Config::all() {
bail!("Cannot set JS runtime options when building a dynamic module")
}
self.build_dynamic()
}
}
}

fn build_static(self) -> Result<Box<dyn CodeGen>> {
let mut static_gen = Box::new(StaticGenerator::new());
fn build_static(self, js_runtime_config: Config) -> Result<Box<dyn CodeGen>> {
let mut static_gen = Box::new(StaticGenerator::new(js_runtime_config));

static_gen.source_compression = self.source_compression;
static_gen.wit_opts = self.wit_opts;
Expand Down
12 changes: 8 additions & 4 deletions crates/cli/src/codegen/static.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,20 @@ pub(crate) struct StaticGenerator {
function_exports: Exports,
/// WIT options for code generation.
pub wit_opts: WitOptions,
/// JS runtime options for code generation.
pub js_runtime_config: Config,
}

impl StaticGenerator {
/// Creates a new [`StaticGenerator`].
pub fn new() -> Self {
pub fn new(js_runtime_config: Config) -> Self {
let engine = include_bytes!(concat!(env!("OUT_DIR"), "/engine.wasm"));
Self {
engine,
source_compression: Default::default(),
function_exports: Default::default(),
wit_opts: Default::default(),
js_runtime_config,
}
}
}
Expand Down Expand Up @@ -71,9 +74,10 @@ impl CodeGen for StaticGenerator {
.unwrap()
.set_stdin(Box::new(ReadPipe::from(js.as_bytes())));

WASI.get_mut()
.unwrap()
.push_env("JS_RUNTIME_CONFIG", &Config::all().bits().to_string())?;
WASI.get_mut().unwrap().push_env(
"JS_RUNTIME_CONFIG",
&self.js_runtime_config.bits().to_string(),
)?;
};

let wasm = Wizer::new()
Expand Down
104 changes: 95 additions & 9 deletions crates/cli/src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use anyhow::{anyhow, bail};
use clap::{Parser, Subcommand};
use javy_config::Config;
use std::{path::PathBuf, str::FromStr};

use crate::codegen::WitOptions;
Expand Down Expand Up @@ -91,6 +92,16 @@ pub struct BuildCommandOpts {
)]
/// Codegen options.
pub codegen: Vec<CodegenOption>,

#[arg(
short = 'J',
long = "js",
long_help = "Available JS runtime options:
-J redirect-stdout-to-stderr[=y|n] -- Redirects console.log to stderr.
"
)]
/// JS runtime options.
pub js_runtime: Vec<JsRuntimeOption>,
}

#[derive(Debug, Parser)]
Expand All @@ -110,6 +121,16 @@ pub struct CodegenOptionGroup {
pub source_compression: bool,
}

impl Default for CodegenOptionGroup {
fn default() -> Self {
Self {
dynamic: false,
wit: WitOptions::default(),
source_compression: true,
}
}
}

#[derive(Clone, Debug)]
pub enum CodegenOption {
/// Creates a smaller module that requires a dynamically linked QuickJS provider Wasm
Expand Down Expand Up @@ -163,24 +184,89 @@ impl TryFrom<Vec<CodegenOption>> for CodegenOptionGroup {
type Error = anyhow::Error;

fn try_from(value: Vec<CodegenOption>) -> Result<Self, Self::Error> {
let mut dynamic = false;
let mut options = Self::default();
let mut wit = None;
let mut wit_world = None;
let mut source_compression = true;

for option in value {
match option {
CodegenOption::Dynamic(enabled) => dynamic = enabled,
CodegenOption::Dynamic(enabled) => options.dynamic = enabled,
CodegenOption::Wit(path) => wit = Some(path),
CodegenOption::WitWorld(world) => wit_world = Some(world),
CodegenOption::SourceCompression(enabled) => source_compression = enabled,
CodegenOption::SourceCompression(enabled) => options.source_compression = enabled,
}
}

Ok(Self {
dynamic,
wit: WitOptions::from_tuple((wit, wit_world))?,
source_compression,
})
options.wit = WitOptions::from_tuple((wit, wit_world))?;
Ok(options)
}
}

#[derive(Debug, PartialEq)]
pub struct JsRuntimeOptionGroup {
/// Whether to redirect console.log to stderr.
pub redirect_stdout_to_stderr: bool,
}

impl Default for JsRuntimeOptionGroup {
fn default() -> Self {
Self {
redirect_stdout_to_stderr: true,
}
}
}

#[derive(Clone, Debug)]
pub enum JsRuntimeOption {
/// Whether to redirect console.log to stderr.
RedirectStdoutToStderr(bool),
}

impl FromStr for JsRuntimeOption {
type Err = anyhow::Error;

fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let mut parts = s.splitn(2, '=');
let key = parts
.next()
.ok_or_else(|| anyhow!("Invalid JS runtime key"))?;
let value = parts.next();
let option = match key {
"redirect-stdout-to-stderr" => Self::RedirectStdoutToStderr(match value {
None => true,
Some("y") => true,
Some("n") => false,
_ => bail!("Invalid value for redirect-stdout-to-stderr"),
}),
_ => bail!("Invalid JS runtime key"),
};
Ok(option)
}
}

impl From<Vec<JsRuntimeOption>> for JsRuntimeOptionGroup {
fn from(value: Vec<JsRuntimeOption>) -> Self {
let mut group = Self::default();

for option in value {
match option {
JsRuntimeOption::RedirectStdoutToStderr(enabled) => {
group.redirect_stdout_to_stderr = enabled;
}
}
}

group
}
}

impl From<JsRuntimeOptionGroup> for Config {
fn from(value: JsRuntimeOptionGroup) -> Self {
let mut config = Self::all();
config.set(
Config::REDIRECT_STDOUT_TO_STDERR,
value.redirect_stdout_to_stderr,
);
config
}
}
17 changes: 10 additions & 7 deletions crates/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ mod commands;
mod js;
mod wit;

use crate::codegen::{DynamicGenerator, StaticGenerator, WitOptions};
use crate::codegen::WitOptions;
use crate::commands::{Cli, Command, EmitProviderCommandOpts};
use anyhow::Result;
use clap::Parser;
use codegen::CodeGenBuilder;
use commands::CodegenOptionGroup;
use codegen::{CodeGenBuilder, DynamicGenerator, StaticGenerator};
use commands::{CodegenOptionGroup, JsRuntimeOptionGroup};
use javy_config::Config;
use js::JS;
use std::fs;
use std::fs::File;
Expand Down Expand Up @@ -43,10 +44,11 @@ fn main() -> Result<()> {
.source_compression(!opts.no_source_compression)
.provider_version("2");

let config = Config::all();
let mut gen = if opts.dynamic {
builder.build::<DynamicGenerator>()?
builder.build::<DynamicGenerator>(config)?
} else {
builder.build::<StaticGenerator>()?
builder.build::<StaticGenerator>(config)?
};

let wasm = gen.generate(&js)?;
Expand All @@ -63,10 +65,11 @@ fn main() -> Result<()> {
.source_compression(codegen.source_compression)
.provider_version("2");

let js_runtime_options: JsRuntimeOptionGroup = opts.js_runtime.clone().into();
let mut gen = if codegen.dynamic {
builder.build::<DynamicGenerator>()?
builder.build::<DynamicGenerator>(js_runtime_options.into())?
} else {
builder.build::<StaticGenerator>()?
builder.build::<StaticGenerator>(js_runtime_options.into())?
};

let wasm = gen.generate(&js)?;
Expand Down
18 changes: 17 additions & 1 deletion crates/cli/tests/dynamic_linking_test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anyhow::Result;
use javy_runner::Builder;
use javy_runner::{Builder, JavyCommand};
use std::path::{Path, PathBuf};
use std::str;

Expand Down Expand Up @@ -152,6 +152,22 @@ fn test_producers_section_present() -> Result<()> {
})
}

#[test]
fn test_using_runtime_flag_with_dynamic_triggers_error() -> Result<()> {
let build_result = Builder::default()
.root(root())
.bin(BIN)
.input("console.js")
.preload("javy_quickjs_provider_v2".into(), provider_module_path())
.command(JavyCommand::Build)
.redirect_stdout_to_stderr(false)
.build();
assert!(build_result.is_err_and(|e| e
.to_string()
.contains("Error: Cannot set JS runtime options when building a dynamic module")));
Ok(())
}

#[test]
// Temporarily ignore given that Javy.JSON is disabled by default.
#[ignore]
Expand Down
69 changes: 53 additions & 16 deletions crates/cli/tests/integration_test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anyhow::Result;
use javy_runner::{Runner, RunnerError};
use javy_runner::{Builder, Runner, RunnerError};
use std::path::PathBuf;
use std::str;

Expand Down Expand Up @@ -95,22 +95,59 @@ fn test_encoding() -> Result<()> {
}

#[test]
fn test_logging() -> Result<()> {
run_with_compile_and_build(|builder| {
let mut runner = builder
.root(sample_scripts())
.bin(BIN)
.input("logging.js")
.build()?;
fn test_logging_with_compile() -> Result<()> {
let mut runner = Builder::default()
.root(sample_scripts())
.bin(BIN)
.input("logging.js")
.command(javy_runner::JavyCommand::Compile)
.build()?;

let (output, logs, fuel_consumed) = run(&mut runner, &[]);
assert!(output.is_empty());
assert_eq!(
"hello world from console.log\nhello world from console.error\n",
logs.as_str(),
);
assert_fuel_consumed_within_threshold(37309, fuel_consumed);
Ok(())
}

let (_output, logs, fuel_consumed) = run(&mut runner, &[]);
assert_eq!(
"hello world from console.log\nhello world from console.error\n",
logs.as_str(),
);
assert_fuel_consumed_within_threshold(34169, fuel_consumed);
Ok(())
})
#[test]
fn test_logging_without_redirect() -> Result<()> {
let mut runner = Builder::default()
.root(sample_scripts())
.bin(BIN)
.input("logging.js")
.command(javy_runner::JavyCommand::Build)
.redirect_stdout_to_stderr(false)
.build()?;

let (output, logs, fuel_consumed) = run(&mut runner, &[]);
assert_eq!(b"hello world from console.log\n".to_vec(), output);
assert_eq!("hello world from console.error\n", logs.as_str());
assert_fuel_consumed_within_threshold(37485, fuel_consumed);
Ok(())
}

#[test]
fn test_logging_with_redirect() -> Result<()> {
let mut runner = Builder::default()
.root(sample_scripts())
.bin(BIN)
.input("logging.js")
.command(javy_runner::JavyCommand::Build)
.redirect_stdout_to_stderr(true)
.build()?;

let (output, logs, fuel_consumed) = run(&mut runner, &[]);
assert!(output.is_empty());
assert_eq!(
"hello world from console.log\nhello world from console.error\n",
logs.as_str(),
);
assert_fuel_consumed_within_threshold(37309, fuel_consumed);
Ok(())
}

#[test]
Expand Down
Loading
Loading