Skip to content

Commit

Permalink
Refactor to use a Provider abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffcharles committed Oct 18, 2024
1 parent 6eef581 commit 447fab5
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 59 deletions.
12 changes: 4 additions & 8 deletions crates/cli/src/bytecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@ use anyhow::{anyhow, Result};
use wasi_common::{sync::WasiCtxBuilder, WasiCtx};
use wasmtime::{AsContextMut, Engine, Instance, Linker, Memory, Module, Store};

pub const QUICKJS_PROVIDER_MODULE: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/provider.wasm"));
use crate::providers::Provider;

/// Use the legacy provider when using the `compile -d` command.
pub const QUICKJS_PROVIDER_V2_MODULE: &[u8] = include_bytes!("./javy_quickjs_provider_v2.wasm");

pub fn compile_source(provider: &[u8], js_source_code: &[u8]) -> Result<Vec<u8>> {
pub fn compile_source(provider: &Provider, js_source_code: &[u8]) -> Result<Vec<u8>> {
let (mut store, instance, memory) = create_wasm_env(provider)?;
let (js_src_ptr, js_src_len) =
copy_source_code_into_instance(js_source_code, store.as_context_mut(), &instance, &memory)?;
Expand All @@ -17,9 +13,9 @@ pub fn compile_source(provider: &[u8], js_source_code: &[u8]) -> Result<Vec<u8>>
Ok(bytecode)
}

fn create_wasm_env(provider_bytes: &[u8]) -> Result<(Store<WasiCtx>, Instance, Memory)> {
fn create_wasm_env(provider: &Provider) -> Result<(Store<WasiCtx>, Instance, Memory)> {
let engine = Engine::default();
let module = Module::new(&engine, provider_bytes)?;
let module = Module::new(&engine, provider.as_bytes())?;
let mut linker = Linker::new(&engine);
wasi_common::sync::snapshots::preview_1::add_wasi_snapshot_preview1_to_linker(
&mut linker,
Expand Down
20 changes: 11 additions & 9 deletions crates/cli/src/codegen/builder.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::codegen::{CodeGen, CodeGenType, DynamicGenerator, StaticGenerator};
use crate::{
codegen::{CodeGen, CodeGenType, DynamicGenerator, StaticGenerator},
providers::Provider,
};
use anyhow::{bail, Result};
use javy_config::Config;
use std::path::PathBuf;
Expand Down Expand Up @@ -47,8 +50,8 @@ impl WitOptions {
/// A code generation builder.
#[derive(Default)]
pub(crate) struct CodeGenBuilder {
/// The QuickJS provider module version.
provider_version: Option<&'static str>,
/// The provider to use.
provider: Option<Provider>,
/// WIT options for code generation.
wit_opts: WitOptions,
/// Whether to compress the original JS source.
Expand All @@ -61,9 +64,9 @@ impl CodeGenBuilder {
Self::default()
}

/// Set the provider version.
pub fn provider_version(&mut self, v: &'static str) -> &mut Self {
self.provider_version = Some(v);
/// Set the provider.
pub fn provider(&mut self, provider: Provider) -> &mut Self {
self.provider = Some(provider);
self
}

Expand Down Expand Up @@ -108,9 +111,8 @@ impl CodeGenBuilder {
let mut dynamic_gen = Box::new(DynamicGenerator::new());
dynamic_gen.source_compression = self.source_compression;

if let Some(v) = self.provider_version {
dynamic_gen.import_namespace = String::from("javy_quickjs_provider_v");
dynamic_gen.import_namespace.push_str(v);
if let Some(p) = self.provider {
dynamic_gen.provider = p
} else {
bail!("Provider version not specified")
}
Expand Down
35 changes: 14 additions & 21 deletions crates/cli/src/codegen/dynamic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use super::transform::{self, SourceCodeSection};
use crate::{
codegen::{exports, CodeGen, CodeGenType, Exports, WitOptions},
js::JS,
providers::Provider,
};
use anyhow::Result;
use walrus::{DataId, DataKind, FunctionBuilder, FunctionId, LocalId, MemoryId, Module, ValType};
Expand Down Expand Up @@ -44,8 +45,8 @@ impl BytecodeMetadata {

/// Dynamic Code Generation.
pub(crate) struct DynamicGenerator {
/// Wasm import namcespace.
pub import_namespace: String,
/// Provider to use.
pub provider: Provider,
/// JavaScript function exports.
function_exports: Exports,
/// Whether to embed the compressed JS source in the generated module.
Expand All @@ -59,37 +60,31 @@ impl DynamicGenerator {
pub fn new() -> Self {
Self {
source_compression: true,
import_namespace: "".into(),
provider: Provider::Default,
function_exports: Default::default(),
wit_opts: Default::default(),
}
}

/// Generate function imports.
pub fn generate_imports(&self, module: &mut Module) -> Result<Imports> {
let import_namespace = self.provider.import_namespace();
let canonical_abi_realloc_type = module.types.add(
&[ValType::I32, ValType::I32, ValType::I32, ValType::I32],
&[ValType::I32],
);
let (canonical_abi_realloc_fn_id, _) = module.add_import_func(
&self.import_namespace,
&import_namespace,
"canonical_abi_realloc",
canonical_abi_realloc_type,
);

let eval_bytecode_type = module.types.add(&[ValType::I32, ValType::I32], &[]);
let (eval_bytecode_fn_id, _) =
module.add_import_func(&self.import_namespace, "eval_bytecode", eval_bytecode_type);
module.add_import_func(&import_namespace, "eval_bytecode", eval_bytecode_type);

let (memory_id, _) = module.add_import_memory(
&self.import_namespace,
"memory",
false,
false,
0,
None,
None,
);
let (memory_id, _) =
module.add_import_memory(&import_namespace, "memory", false, false, 0, None, None);

Ok(Imports::new(
canonical_abi_realloc_fn_id,
Expand All @@ -105,11 +100,7 @@ impl DynamicGenerator {
js: &JS,
imports: &Imports,
) -> Result<BytecodeMetadata> {
let bytecode = if self.import_namespace == "javy_quickjs_provider_v2" {
js.compile_legacy()?
} else {
js.compile()?
};
let bytecode = js.compile(&self.provider)?;
let bytecode_len: i32 = bytecode.len().try_into()?;
let bytecode_data = module.data.add(DataKind::Passive, bytecode);

Expand Down Expand Up @@ -155,7 +146,7 @@ impl DynamicGenerator {
&[],
);
let (invoke_fn, _) =
module.add_import_func(&self.import_namespace, "invoke", invoke_type);
module.add_import_func(&self.provider.import_namespace(), "invoke", invoke_type);

let fn_name_ptr_local = module.locals.add(ValType::I32);
for export in &self.function_exports {
Expand Down Expand Up @@ -311,6 +302,8 @@ fn print_wat(_wasm_binary: &[u8]) -> Result<()> {

#[cfg(test)]
mod test {
use crate::providers::Provider;

use super::DynamicGenerator;
use super::WitOptions;
use anyhow::Result;
Expand All @@ -319,7 +312,7 @@ mod test {
fn default_values() -> Result<()> {
let gen = DynamicGenerator::new();
assert!(gen.source_compression);
assert_eq!(gen.import_namespace, "");
assert!(matches!(gen.provider, Provider::Default));
assert_eq!(gen.wit_opts, WitOptions::default());

Ok(())
Expand Down
22 changes: 4 additions & 18 deletions crates/cli/src/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use swc_core::{
},
};

use crate::bytecode;
use crate::providers::Provider;

#[derive(Clone, Debug)]
pub struct JS {
Expand All @@ -50,23 +50,9 @@ impl JS {
self.source_code.as_bytes()
}

/// Compiles a JavaScript source to bytecode using the QuickJS provider.
pub fn compile(&self) -> Result<Vec<u8>> {
bytecode::compile_source(
bytecode::QUICKJS_PROVIDER_MODULE,
self.source_code.as_bytes(),
)
}

/// Similar to [`Self::compile`]. Instead of using the most up to date
/// provider, it uses the v2 provider.
///
/// NB that this is temporary until the `compile` command is deprecated.
pub fn compile_legacy(&self) -> Result<Vec<u8>> {
bytecode::compile_source(
bytecode::QUICKJS_PROVIDER_V2_MODULE,
self.source_code.as_bytes(),
)
/// Compiles a JavaScript source to bytecode using a QuickJS provider.
pub fn compile(&self, provider: &Provider) -> Result<Vec<u8>> {
provider.compile_source(self.source_code.as_bytes())
}

pub fn compress(&self) -> Result<Vec<u8>> {
Expand Down
8 changes: 5 additions & 3 deletions crates/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod codegen;
mod commands;
mod js;
mod option;
mod providers;
mod wit;

use crate::codegen::WitOptions;
Expand All @@ -13,6 +14,7 @@ use codegen::{CodeGenBuilder, DynamicGenerator, StaticGenerator};
use commands::{CodegenOptionGroup, JsOptionGroup};
use javy_config::Config;
use js::JS;
use providers::Provider;
use std::fs;
use std::fs::File;
use std::io::Write;
Expand Down Expand Up @@ -43,7 +45,7 @@ fn main() -> Result<()> {
opts.wit_world.clone(),
))?)
.source_compression(!opts.no_source_compression)
.provider_version("2");
.provider(Provider::V2);

let config = Config::default();
let mut gen = if opts.dynamic {
Expand All @@ -64,7 +66,7 @@ fn main() -> Result<()> {
builder
.wit_opts(codegen.wit)
.source_compression(codegen.source_compression)
.provider_version("3");
.provider(Provider::Default);

let js_opts: JsOptionGroup = opts.js.clone().into();
let mut gen = if codegen.dynamic {
Expand All @@ -86,6 +88,6 @@ fn emit_provider(opts: &EmitProviderCommandOpts) -> Result<()> {
Some(path) => Box::new(File::create(path)?),
_ => Box::new(std::io::stdout()),
};
file.write_all(bytecode::QUICKJS_PROVIDER_MODULE)?;
file.write_all(Provider::Default.as_bytes())?;
Ok(())
}
41 changes: 41 additions & 0 deletions crates/cli/src/providers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use crate::bytecode;
use anyhow::Result;

const QUICKJS_PROVIDER_MODULE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/provider.wasm"));

/// Use the legacy provider when using the `compile -d` command.
const QUICKJS_PROVIDER_V2_MODULE: &[u8] = include_bytes!("./javy_quickjs_provider_v2.wasm");

/// Different providers that are available.
#[derive(Debug)]
pub enum Provider {
/// The default provider.
Default,
/// A provider for use with the `compile` to maintain backward compatibility.
V2,
}

impl Provider {
/// Returns the provider Wasm module as a byte slice.
pub fn as_bytes(&self) -> &[u8] {
match self {
Self::Default => QUICKJS_PROVIDER_MODULE,
Self::V2 => QUICKJS_PROVIDER_V2_MODULE,
}
}

/// Uses the provider to generate QuickJS bytecode.
pub fn compile_source(&self, js_source_code: &[u8]) -> Result<Vec<u8>> {
bytecode::compile_source(self, js_source_code)
}

/// The import namespace to use for this provider.
pub fn import_namespace(&self) -> String {
let prefix = "javy_quickjs_provider_v";
let version = match self {
Self::Default => 3,
Self::V2 => 2,
};
format!("{prefix}{version}")
}
}

0 comments on commit 447fab5

Please sign in to comment.