diff --git a/Cargo.toml b/Cargo.toml index b4fd74cd1..8c4641ea2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ js-component-bindgen = { path = "./crates/js-component-bindgen" } wit-component = { workspace = true } [build-dependencies] -anyhow = "1.0.71" +anyhow = { workspace = true } js-component-bindgen = { path = "./crates/js-component-bindgen" } wit-component = { workspace = true } diff --git a/adapter.wasm b/adapter.wasm new file mode 100644 index 000000000..0177c1742 Binary files /dev/null and b/adapter.wasm differ diff --git a/bin/self_build.rs b/bin/self_build.rs index 2633a70fb..cd0d8690d 100644 --- a/bin/self_build.rs +++ b/bin/self_build.rs @@ -19,6 +19,10 @@ fn main() -> Result<()> { encoder = encoder.adapter("wasi_snapshot_preview1", &adapter)?; let adapted_component = encoder.encode()?; + fs::create_dir_all(PathBuf::from("./obj"))?; + let mut component_path = PathBuf::from("./obj").join(&name); + component_path.set_extension("component.wasm"); + fs::write(component_path, &adapted_component)?; let import_map = HashMap::from([ ( diff --git a/crates/js-component-bindgen/src/intrinsics.rs b/crates/js-component-bindgen/src/intrinsics.rs index de2eb51df..178192aae 100644 --- a/crates/js-component-bindgen/src/intrinsics.rs +++ b/crates/js-component-bindgen/src/intrinsics.rs @@ -116,10 +116,10 @@ pub fn render_intrinsics( } "), - Intrinsic::DataView => output.push_str(" - let dv = new DataView(new ArrayBuffer()); - const dataView = mem => dv.buffer === mem.buffer ? dv : dv = new DataView(mem.buffer); - "), + Intrinsic::DataView => output.push_str(" + let dv = new DataView(new ArrayBuffer()); + const dataView = mem => dv.buffer === mem.buffer ? dv : dv = new DataView(mem.buffer); + "), Intrinsic::FetchCompile => if !no_nodejs_compat { output.push_str(" diff --git a/crates/js-component-bindgen/src/transpile_bindgen.rs b/crates/js-component-bindgen/src/transpile_bindgen.rs index aee5f715f..077a2f20d 100644 --- a/crates/js-component-bindgen/src/transpile_bindgen.rs +++ b/crates/js-component-bindgen/src/transpile_bindgen.rs @@ -22,8 +22,9 @@ use wasmtime_environ::{ GlobalInitializer, InstantiateModule, LoweredIndex, RuntimeImportIndex, RuntimeInstanceIndex, StaticModuleIndex, Trampoline, TrampolineIndex, }, + fact::{FixedEncoding, Transcode}, + EntityIndex, PrimaryMap, }; -use wasmtime_environ::{EntityIndex, PrimaryMap}; use wit_bindgen_core::abi::{self, LiftLower}; use wit_component::StringEncoding; use wit_parser::abi::AbiVariant; @@ -431,6 +432,7 @@ impl<'a> Instantiator<'a, '_> { } fn trampoline(&mut self, i: TrampolineIndex, trampoline: &'a Trampoline) { + let i = i.as_u32(); match trampoline { // these are hoisted before initialization Trampoline::LowerImport { .. } => {} @@ -446,15 +448,48 @@ impl<'a> Instantiator<'a, '_> { // for now since it can't be tested and additionally JS doesn't // support multi-memory which transcoders rely on anyway. Trampoline::Transcoder { - op: _, - from: _, - from64: _, - to: _, - to64: _, - } => unimplemented!(), + op, + from, + from64, + to, + to64, + } => { + if *from64 || *to64 { + unimplemented!("memory 64 transcoder"); + } + let from = from.as_u32(); + let to = to.as_u32(); + match op { + Transcode::Copy(FixedEncoding::Utf8) => { + uwriteln!( + self.src.js, + "function trampoline{i} (from_ptr, len, to_ptr) {{ + new Uint8Array(memory{to}.buffer, to_ptr, len).set(new Uint8Array(memory{from}.buffer, from_ptr, len)); + }} + " + ); + } + Transcode::Copy(FixedEncoding::Utf16) => unimplemented!("utf16 copier"), + Transcode::Copy(FixedEncoding::Latin1) => unimplemented!("latin1 copier"), + Transcode::Latin1ToUtf16 => unimplemented!("latin to utf16 transcoder"), + Transcode::Latin1ToUtf8 => unimplemented!("latin to utf8 transcoder"), + Transcode::Utf16ToCompactProbablyUtf16 => { + unimplemented!("utf16 to compact wtf16 transcoder") + } + Transcode::Utf16ToCompactUtf16 => { + unimplemented!("utf16 to compact utf16 transcoder") + } + Transcode::Utf16ToLatin1 => unimplemented!("utf16 to latin1 transcoder"), + Transcode::Utf16ToUtf8 => unimplemented!("utf16 to utf8 transcoder"), + Transcode::Utf8ToCompactUtf16 => { + unimplemented!("utf8 to compact utf16 transcoder") + } + Transcode::Utf8ToLatin1 => unimplemented!("utf8 to latin1 transcoder"), + Transcode::Utf8ToUtf16 => unimplemented!("utf8 to utf16 transcoder"), + }; + } Trampoline::ResourceNew(resource) => { - let i = i.as_u32(); self.ensure_resource_table(*resource); let rid = resource.as_u32(); uwrite!( @@ -468,7 +503,6 @@ impl<'a> Instantiator<'a, '_> { ); } Trampoline::ResourceRep(resource) => { - let i = i.as_u32(); self.ensure_resource_table(*resource); let rid = resource.as_u32(); uwrite!( @@ -484,7 +518,6 @@ impl<'a> Instantiator<'a, '_> { ); } Trampoline::ResourceDrop(resource) => { - let i = i.as_u32(); self.ensure_resource_table(*resource); let rid = resource.as_u32(); let resource = &self.types[*resource]; @@ -585,6 +618,7 @@ impl<'a> Instantiator<'a, '_> { prev.is_none(), "unsupported duplicate import of `{module}::{name}`" ); + assert!(prev.is_none()); } let mut imports = String::new(); if !import_obj.is_empty() { diff --git a/test/cli.js b/test/cli.js index 6ada12659..c98455338 100644 --- a/test/cli.js +++ b/test/cli.js @@ -1,5 +1,5 @@ import { deepStrictEqual, ok, strictEqual } from 'node:assert'; -import { readFile, rm } from 'node:fs/promises'; +import { readFile, rm, writeFile } from 'node:fs/promises'; import { fileURLToPath } from 'url'; import { exec, jcoPath } from './helpers.js'; import { tmpdir } from 'node:os'; @@ -18,6 +18,16 @@ export async function cliTest (fixtures) { catch {} } + test('Transcoding', async () => { + const outDir = fileURLToPath(new URL(`./output/env-allow`, import.meta.url)); + const { stderr } = await exec(jcoPath, 'transpile', `test/fixtures/env-allow.composed.wasm`, '-o', outDir); + strictEqual(stderr, ''); + await writeFile(`${outDir}/package.json`, JSON.stringify({ type: 'module' })); + const source = await readFile(`${outDir}/env-allow.composed.js`); + const m = await import(`${outDir}/env-allow.composed.js`); + deepStrictEqual(m.testGetEnv(), [['CUSTOM', 'VAL']]); + }); + test('Transpile', async () => { try { const name = 'flavorful'; diff --git a/test/fixtures/env-allow.composed.wasm b/test/fixtures/env-allow.composed.wasm new file mode 100644 index 000000000..37e9cb60b Binary files /dev/null and b/test/fixtures/env-allow.composed.wasm differ diff --git a/test/fixtures/virt.wasm b/test/fixtures/virt.wasm new file mode 100644 index 000000000..1a282c1aa Binary files /dev/null and b/test/fixtures/virt.wasm differ diff --git a/update-tests.sh b/update-tests.sh index c8812a846..b80261445 100755 --- a/update-tests.sh +++ b/update-tests.sh @@ -26,3 +26,9 @@ cd .. ./src/jco.js componentize test/fixtures/component-gen/import-fn.js --wit test/fixtures/component-gen/import-fn.wit -o test/fixtures/components/import-fn.component.wasm rm -rf wit-bindgen + +## wasi virt to generate composition case +git clone https://github.com/bytecodealliance/wasi-virt +cd wasi-virt +cargo test +cp tests/generated/env-allow.composed.wasm ../test/fixtures/