Skip to content

Commit

Permalink
Fix multiword exports (#424)
Browse files Browse the repository at this point in the history
* Fix multiword exports

* Rename exports function
  • Loading branch information
jeffcharles authored Jul 11, 2023
1 parent 34de4ed commit f83174e
Show file tree
Hide file tree
Showing 11 changed files with 73 additions and 36 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ wasi-common = { workspace = true }
walrus = "0.20.1"
swc_core = { version = "0.78.15", features = ["common_sourcemap", "ecma_ast", "ecma_parser"] }
wit-parser = "0.8.0"
convert_case = "0.4.0"

[dev-dependencies]
serde_json = "1.0"
Expand Down
28 changes: 28 additions & 0 deletions crates/cli/src/exports.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use anyhow::{anyhow, Result};
use convert_case::{Case, Casing};
use std::path::Path;

use crate::{js::JS, wit};

pub struct Export {
pub wit: String,
pub js: String,
}

pub fn process_exports(js: &JS, wit: &Path, wit_world: &str) -> Result<Vec<Export>> {
let js_exports = js.exports()?;
wit::parse_exports(wit, wit_world)?
.into_iter()
.map(|wit_export| {
let export = wit_export.from_case(Case::Kebab).to_case(Case::Camel);
if !js_exports.contains(&export) {
Err(anyhow!("JS module does not export {export}"))
} else {
Ok(Export {
wit: wit_export,
js: export,
})
}
})
.collect::<Result<Vec<Export>>>()
}
32 changes: 10 additions & 22 deletions crates/cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
mod bytecode;
mod commands;
mod exports;
mod js;
mod wasm_generator;
mod wit;

use crate::commands::{Command, CompileCommandOpts, EmitProviderCommandOpts};
use crate::wasm_generator::r#static as static_generator;
use anyhow::{anyhow, bail, Result};
use anyhow::{bail, Result};
use exports::Export;
use js::JS;
use std::env;
use std::fs::File;
Expand All @@ -23,7 +25,12 @@ fn main() -> Result<()> {
Command::EmitProvider(opts) => emit_provider(opts),
Command::Compile(opts) => {
let js = JS::from_file(&opts.input)?;
let exports = determine_js_exports(&js, opts)?;
let exports = match (&opts.wit, &opts.wit_world) {
(None, None) => Ok(vec![]),
(None, Some(_)) => Ok(vec![]),
(Some(_), None) => bail!("Must provide WIT world when providing WIT file"),
(Some(wit), Some(world)) => exports::process_exports(&js, wit, world),
}?;
if opts.dynamic {
let wasm = dynamic_generator::generate(&js, exports)?;
fs::write(&opts.output, wasm)?;
Expand All @@ -44,7 +51,7 @@ fn emit_provider(opts: &EmitProviderCommandOpts) -> Result<()> {
Ok(())
}

fn create_statically_linked_module(opts: &CompileCommandOpts, exports: Vec<String>) -> Result<()> {
fn create_statically_linked_module(opts: &CompileCommandOpts, exports: Vec<Export>) -> Result<()> {
// The javy-core `main.rs` pre-initializer uses WASI to read the JS source
// code from stdin. Wizer doesn't let us customize its WASI context so we
// don't have a better option right now. Since we can't set the content of
Expand Down Expand Up @@ -83,22 +90,3 @@ fn create_statically_linked_module(opts: &CompileCommandOpts, exports: Vec<Strin
fs::write(&opts.output, wasm)?;
Ok(())
}

fn determine_js_exports(js: &JS, opts: &CompileCommandOpts) -> Result<Vec<String>> {
let js_exports = js.exports()?;
match (&opts.wit, &opts.wit_world) {
(None, None) => Ok(vec![]),
(None, Some(_)) => Ok(vec![]),
(Some(_), None) => bail!("Must provide WIT world when providing WIT file"),
(Some(path), Some(world)) => wit::parse_exports(path, world)?
.into_iter()
.map(|wit_export| {
if !js_exports.contains(&wit_export) {
Err(anyhow!("JS module does not export {wit_export}"))
} else {
Ok(wit_export)
}
})
.collect::<Result<Vec<String>>>(),
}
}
10 changes: 5 additions & 5 deletions crates/cli/src/wasm_generator/dynamic.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::js::JS;
use crate::{exports::Export, js::JS};

use super::transform::{self, SourceCodeSection};
use anyhow::Result;
Expand Down Expand Up @@ -64,7 +64,7 @@ use walrus::{DataKind, FunctionBuilder, Module, ValType};
// (data (;0;) "\02\05\18function.mjs\06foo\0econsole\06log\06bar\0f\bc\03\00\01\00\00\be\03\00\00\0e\00\06\01\a0\01\00\00\00\03\01\01\1a\00\be\03\00\01\08\ea\05\c0\00\e1)8\e0\00\00\00B\e1\00\00\00\04\e2\00\00\00$\01\00)\bc\03\01\04\01\00\07\0a\0eC\06\01\be\03\00\00\00\03\00\00\13\008\e0\00\00\00B\e1\00\00\00\04\df\00\00\00$\01\00)\bc\03\01\02\03]")
// (data (;1;) "foo")
// )
pub fn generate(js: &JS, exported_functions: Vec<String>) -> Result<Vec<u8>> {
pub fn generate(js: &JS, exported_functions: Vec<Export>) -> Result<Vec<u8>> {
let mut module = Module::with_config(transform::module_config());

const IMPORT_NAMESPACE: &str = "javy_quickjs_provider_v1";
Expand Down Expand Up @@ -123,9 +123,9 @@ pub fn generate(js: &JS, exported_functions: Vec<String>) -> Result<Vec<u8>> {
let (invoke_fn, _) = module.add_import_func(IMPORT_NAMESPACE, "invoke", invoke_type);

let fn_name_ptr_local = module.locals.add(ValType::I32);
for js_export in exported_functions {
for export in exported_functions {
// For each JS function export, add an export that copies the name of the function into memory and invokes it.
let js_export_bytes = js_export.as_bytes();
let js_export_bytes = export.js.as_bytes();
let js_export_len: i32 = js_export_bytes.len().try_into().unwrap();
let fn_name_data = module.data.add(DataKind::Passive, js_export_bytes.to_vec());

Expand Down Expand Up @@ -161,7 +161,7 @@ pub fn generate(js: &JS, exported_functions: Vec<String>) -> Result<Vec<u8>> {
.i32_const(js_export_len)
.call(invoke_fn);
let export_fn = export_fn.finish(vec![], &mut module.funcs);
module.exports.add(&js_export, export_fn);
module.exports.add(&export.wit, export_fn);
}
}

Expand Down
12 changes: 6 additions & 6 deletions crates/cli/src/wasm_generator/static.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use binaryen::{CodegenConfig, Module};
use walrus::{DataKind, ExportItem, FunctionBuilder, FunctionId, MemoryId, ValType};
use wizer::Wizer;

use crate::js::JS;
use crate::{exports::Export, js::JS};

use super::transform::{self, SourceCodeSection};

Expand All @@ -26,7 +26,7 @@ pub fn generate() -> Result<Vec<u8>> {
/// Takes Wasm created by `Generator` and makes additional changes.
///
/// This is intended to be run in the parent process after generating the Wasm.
pub fn refine(wasm: Vec<u8>, js: &JS, exports: Vec<String>) -> Result<Vec<u8>> {
pub fn refine(wasm: Vec<u8>, js: &JS, exports: Vec<Export>) -> Result<Vec<u8>> {
let mut module = transform::module_config().parse(&wasm)?;

let (realloc, invoke, memory) = {
Expand Down Expand Up @@ -82,12 +82,12 @@ fn export_exported_js_functions(
realloc_fn: FunctionId,
invoke_fn: FunctionId,
memory: MemoryId,
js_exports: Vec<String>,
js_exports: Vec<Export>,
) {
let ptr_local = module.locals.add(ValType::I32);
for js_export in js_exports {
for export in js_exports {
// For each JS function export, add an export that copies the name of the function into memory and invokes it.
let js_export_bytes = js_export.as_bytes();
let js_export_bytes = export.js.as_bytes();
let js_export_len: i32 = js_export_bytes.len().try_into().unwrap();
let fn_name_data = module.data.add(DataKind::Passive, js_export_bytes.to_vec());

Expand All @@ -108,6 +108,6 @@ fn export_exported_js_functions(
.i32_const(js_export_len)
.call(invoke_fn);
let export_fn = export_fn.finish(vec![], &mut module.funcs);
module.exports.add(&js_export, export_fn);
module.exports.add(&export.wit, export_fn);
}
}
6 changes: 3 additions & 3 deletions crates/cli/tests/dynamic_linking_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ pub fn test_dynamic_linking() -> Result<()> {

#[test]
pub fn test_dynamic_linking_with_func() -> Result<()> {
let js_src = "export function foo() { console.log('In foo'); }; console.log('Toplevel');";
let js_src = "export function fooBar() { console.log('In foo'); }; console.log('Toplevel');";
let wit = "
package local:main
world foo-test {
export foo: func()
export foo-bar: func()
}
";
let log_output = invoke_fn_on_generated_module(js_src, "foo", Some((wit, "foo-test")))?;
let log_output = invoke_fn_on_generated_module(js_src, "foo-bar", Some((wit, "foo-test")))?;
assert_eq!("Toplevel\nIn foo\n", &log_output);
Ok(())
}
Expand Down
2 changes: 2 additions & 0 deletions crates/cli/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ fn test_exported_functions() {
let (_, logs, fuel_consumed) = run_fn(&mut runner, "foo", &[]);
assert_eq!("Hello from top-level\nHello from foo\n", logs);
assert_fuel_consumed_within_threshold(54610, fuel_consumed);
let (_, logs, _) = run_fn(&mut runner, "foo-bar", &[]);
assert_eq!("Hello from top-level\nHello from fooBar\n", logs);
}

#[cfg(feature = "experimental_event_loop")]
Expand Down
4 changes: 4 additions & 0 deletions crates/cli/tests/sample-scripts/exported-fn.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ export function foo() {
console.log("Hello from foo");
}

export function fooBar() {
console.log("Hello from fooBar");
}

console.log("Hello from top-level");
1 change: 1 addition & 0 deletions crates/cli/tests/sample-scripts/exported-fn.wit
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ package local:test
world exported-fn {
export foo: func()
export bar: func()
export foo-bar: func()
}
6 changes: 6 additions & 0 deletions supply-chain/imports.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1130,6 +1130,12 @@ who = "Johan Andersson <[email protected]>"
criteria = "safe-to-deploy"
version = "1.0.58"

[[audits.embark-studios.audits.convert_case]]
who = "Johan Andersson <[email protected]>"
criteria = "safe-to-deploy"
version = "0.4.0"
notes = "No unsafe usage or ambient capabilities"

[[audits.embark-studios.audits.epaint]]
who = "Johan Andersson <[email protected]>"
criteria = "safe-to-deploy"
Expand Down

0 comments on commit f83174e

Please sign in to comment.