Skip to content

Commit 9a45c2f

Browse files
committed
wat component passes tests, idk whats going on with rust one
1 parent 6a5a4fb commit 9a45c2f

File tree

8 files changed

+1036
-57
lines changed

8 files changed

+1036
-57
lines changed

Cargo.lock

Lines changed: 743 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ test-programs = { path = "./test-programs" }
1717
test-programs-artifacts = { path = "./test-programs/artifacts" }
1818
wasm-encoder = { version = "0.235.0", features = ["wasmparser"] }
1919
wasmparser = "0.235.0"
20+
wat = "1.235.0"
2021
wasmtime = "34.0.1"
21-
# wasmtime-wasi = "34.0.1"
22-
# wasmtime-wasi-http = "34.0.1"
22+
wasmtime-wasi = "34.0.1"
23+
wasmtime-wasi-http = "34.0.1"
2324
wit-bindgen = "0.43.0"
2425

2526
[package]

src/lib.rs

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,6 @@ pub async fn initialize_staged(
7878
// - No runtime table operations
7979
// - No reference type globals
8080
// - Each module instantiated at most once
81-
// - If a module exports a memory, a single module must export a mutable `__stack_pointer` global of type I32
82-
//
83-
// Note that we use `__stack_pointer` to allocate 8 bytes to store the canonical `list<u8>` representation of
84-
// memory.
8581

8682
let copy_component_section = |section, component: &[u8], result: &mut Component| {
8783
if let Some((id, range)) = section {
@@ -374,7 +370,6 @@ pub async fn initialize_staged(
374370
let mut lifts = CanonicalFunctionSection::new();
375371
let mut component_types = ComponentTypeSection::new();
376372
let mut component_exports = ComponentExportSection::new();
377-
let mut stack_pointers = Vec::new();
378373
for (module_index, globals_to_export) in &globals_to_export {
379374
for (global_index, (name, ty)) in globals_to_export {
380375
let ty = Encode.val_type(*ty)?;
@@ -390,9 +385,6 @@ pub async fn initialize_staged(
390385
shared: false,
391386
},
392387
);
393-
if name == "__stack_pointer" {
394-
stack_pointers.push((offset, ty));
395-
}
396388
functions.function(offset);
397389
let mut function = Function::new([]);
398390
function.instruction(&Ins::GlobalGet(offset));
@@ -432,14 +424,6 @@ pub async fn initialize_staged(
432424
}
433425

434426
if let Some((module_index, name, ty)) = memory_info {
435-
let stack_pointer = match stack_pointers.as_slice() {
436-
[(offset, ValType::I32)] => *offset,
437-
438-
_ => bail!(
439-
"component with memory must contain exactly one module which \
440-
exports a mutable `__stack_pointer` global of type I32"
441-
),
442-
};
443427
let offset = types.len();
444428
types.ty().function([], [wasm_encoder::ValType::I32]);
445429
imports.import(
@@ -450,19 +434,41 @@ pub async fn initialize_staged(
450434
functions.function(offset);
451435

452436
let mut function = Function::new([(1, wasm_encoder::ValType::I32)]);
453-
function.instruction(&Ins::GlobalGet(stack_pointer));
454-
function.instruction(&Ins::I32Const(8));
455-
function.instruction(&Ins::I32Sub);
437+
function.instruction(&Ins::MemorySize(0));
438+
// stack[0] = current memory, in pages
439+
440+
function.instruction(&Ins::I32Const(PAGE_SIZE_BYTES));
441+
function.instruction(&Ins::I32Mul);
456442
function.instruction(&Ins::LocalTee(0));
443+
// stack[0] = local[0] = current memory, in bytes
444+
445+
function.instruction(&Ins::I32Const(1));
446+
function.instruction(&Ins::MemoryGrow(0));
447+
// stack[1] = old memory, in bytes
448+
// stack[0] = grown memory, in pages, or -1 if failed
457449
function.instruction(&Ins::I32Const(0));
458-
function.instruction(&Ins::I32Store(mem_arg(0, 2)));
459-
function.instruction(&Ins::LocalGet(0));
450+
function.instruction(&Ins::I32LtS);
451+
function.instruction(&Ins::If(wasm_encoder::BlockType::Empty));
452+
// Trap if memory grow failed
453+
function.instruction(&Ins::Unreachable);
454+
function.instruction(&Ins::Else);
455+
function.instruction(&Ins::End);
456+
457+
// stack[0] = old memory, in bytes
460458
function.instruction(&Ins::I32Const(0));
461-
function.instruction(&Ins::MemoryGrow(0));
462-
function.instruction(&Ins::I32Const(PAGE_SIZE_BYTES));
463-
function.instruction(&Ins::I32Mul);
464-
function.instruction(&Ins::I32Store(mem_arg(4, 2)));
459+
// stack[1] = old memory in bytes
460+
// stack[0] = 0 (start of memory)
461+
function.instruction(&Ins::I32Store(mem_arg(0, 1)));
462+
// 0 stored at end of old memory
463+
function.instruction(&Ins::LocalGet(0));
464+
function.instruction(&Ins::LocalGet(0));
465+
// stack[1] = old memory in bytes
466+
// stack[0] = old memory in bytes
467+
function.instruction(&Ins::I32Store(mem_arg(4, 1)));
468+
// old memory size, stored at old memory + 4
469+
465470
function.instruction(&Ins::LocalGet(0));
471+
// stack[0] = old memory in bytes
466472
function.instruction(&Ins::End);
467473
code.function(&function);
468474

@@ -524,6 +530,7 @@ pub async fn initialize_staged(
524530

525531
let instrumented_component = instrumented_component.finish();
526532

533+
std::fs::write("instrumented.wasm", &instrumented_component)?;
527534
Validator::new().validate_all(&instrumented_component)?;
528535

529536
let mut invoker = initialize(instrumented_component).await?;
@@ -541,26 +548,26 @@ pub async fn initialize_staged(
541548
invoker
542549
.call_s32(name)
543550
.await
544-
.with_context(|| name.to_owned())?,
551+
.with_context(|| format!("retrieving global value {name}"))?,
545552
),
546553
wasmparser::ValType::I64 => ConstExpr::i64_const(
547554
invoker
548555
.call_s64(name)
549556
.await
550-
.with_context(|| name.to_owned())?,
557+
.with_context(|| format!("retrieving global value {name}"))?,
551558
),
552559
wasmparser::ValType::F32 => ConstExpr::f32_const(
553560
invoker
554561
.call_f32(name)
555562
.await
556-
.with_context(|| name.to_owned())?
563+
.with_context(|| format!("retrieving global value {name}"))?
557564
.into(),
558565
),
559566
wasmparser::ValType::F64 => ConstExpr::f64_const(
560567
invoker
561568
.call_f64(name)
562569
.await
563-
.with_context(|| name.to_owned())?
570+
.with_context(|| format!("retrieving global value {name}"))?
564571
.into(),
565572
),
566573
wasmparser::ValType::V128 => bail!("V128 not yet supported"),
@@ -573,7 +580,12 @@ pub async fn initialize_staged(
573580

574581
let memory_value = if memory_info.is_some() {
575582
let name = "component-init-get-memory";
576-
Some(invoker.call_list_u8(name).await.context(name)?)
583+
Some(
584+
invoker
585+
.call_list_u8(name)
586+
.await
587+
.with_context(|| format!("retrieving memory with {name}"))?,
588+
)
577589
} else {
578590
None
579591
};

wasmtime/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ anyhow.workspace = true
88
async-trait.workspace = true
99
component-init.workspace = true
1010
wasmtime.workspace = true
11-
# wasmtime-wasi.workspace = true
12-
# wasmtime-wasi-http.workspace = true
1311

1412
[dev-dependencies]
1513
tokio.workspace = true
1614
test-programs-artifacts.workspace = true
15+
wat.workspace = true
16+
wasmtime-wasi.workspace = true

wasmtime/src/lib.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use wasmtime::{
88
pub async fn initialize(component: &[u8]) -> Result<Vec<u8>> {
99
component_init::initialize(component, |instrumented| {
1010
Box::pin(async move {
11-
let i = invoker(instrumented).await?;
11+
let i = invoker(instrumented)
12+
.await
13+
.context("running instrumented component")?;
1214
let i: Box<dyn Invoker> = Box::new(i);
1315
Ok(i)
1416
})
@@ -32,11 +34,13 @@ async fn invoker(component: Vec<u8>) -> Result<Impl> {
3234
.await
3335
.context("instantiate")?;
3436
let mut this = Impl { instance, store };
35-
this.call::<()>("component-init").await?;
37+
this.call::<()>("component-init")
38+
.await
39+
.context("running the component-init export func")?;
3640
Ok(this)
3741
}
3842

39-
struct Ctx;
43+
pub struct Ctx;
4044

4145
struct Impl {
4246
instance: Instance,
@@ -56,10 +60,14 @@ impl Impl {
5660
let func = func
5761
.typed::<(), T>(&mut self.store)
5862
.with_context(|| format!("type of {name} func"))?;
59-
Ok(func
63+
let r = func
6064
.call_async(&mut self.store, ())
6165
.await
62-
.with_context(|| format!("executing {name}"))?)
66+
.with_context(|| format!("executing {name}"))?;
67+
func.post_return_async(&mut self.store)
68+
.await
69+
.with_context(|| format!("post-return {name}"))?;
70+
Ok(r)
6371
}
6472
}
6573

wasmtime/tests/rust.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use anyhow::{anyhow, Context, Result};
2+
use test_programs_artifacts::TEST;
3+
4+
async fn execute(component: &[u8]) -> Result<()> {
5+
use wasmtime::{
6+
component::{Component, Linker, ResourceTable},
7+
Config, Engine, Store,
8+
};
9+
use wasmtime_wasi::p2::WasiCtx;
10+
11+
let mut config = Config::new();
12+
config.async_support(true);
13+
let engine = Engine::new(&config).context("create engine")?;
14+
let component = Component::new(&engine, component).context("load component")?;
15+
16+
struct Ctx {
17+
table: ResourceTable,
18+
wasi: WasiCtx,
19+
}
20+
impl wasmtime_wasi::p2::IoView for Ctx {
21+
fn table(&mut self) -> &mut ResourceTable {
22+
&mut self.table
23+
}
24+
}
25+
impl wasmtime_wasi::p2::WasiView for Ctx {
26+
fn ctx(&mut self) -> &mut WasiCtx {
27+
&mut self.wasi
28+
}
29+
}
30+
31+
let mut linker = Linker::new(&engine);
32+
wasmtime_wasi::p2::add_to_linker_async(&mut linker).context("add wasi to linker")?;
33+
let mut store = Store::new(
34+
&engine,
35+
Ctx {
36+
table: ResourceTable::new(),
37+
wasi: WasiCtx::builder().inherit_stdout().inherit_stderr().build(),
38+
},
39+
);
40+
41+
let instance = linker
42+
.instantiate_async(&mut store, &component)
43+
.await
44+
.context("instantiate")?;
45+
46+
let wasi_cli_run = instance
47+
.get_export_index(&mut store, None, "wasi:cli/run@0.2.3")
48+
.ok_or_else(|| anyhow!("`wasi:cli/run` is not exported"))?;
49+
let export = instance
50+
.get_export_index(&mut store, Some(&wasi_cli_run), "run")
51+
.ok_or_else(|| anyhow!("`wasi:cli/run.run` is not exported"))?;
52+
let func = instance
53+
.get_func(&mut store, export)
54+
.ok_or_else(|| anyhow!("`wasi:cli/run.run` export is not a func"))?;
55+
let func = func
56+
.typed::<(), (Result<(), ()>,)>(&mut store)
57+
.with_context(|| format!("type of run func"))?;
58+
func.call_async(&mut store, ())
59+
.await
60+
.with_context(|| format!("executing run"))?
61+
.0
62+
.map_err(|()| anyhow!("run returned error"))?;
63+
func.post_return_async(&mut store)
64+
.await
65+
.with_context(|| format!("post-return run"))?;
66+
Ok(())
67+
}
68+
69+
#[tokio::test]
70+
async fn init_rust() -> Result<()> {
71+
println!("test component: {TEST:?}");
72+
73+
let component = std::fs::read(TEST)?;
74+
75+
// Without initialization, run will trap.
76+
let err = execute(&component)
77+
.await
78+
.err()
79+
.context("uninitialized run should trap")?;
80+
assert!(
81+
format!("{err:?}").contains("unreachable"),
82+
"should die with an unreachable trap, got: {err:?}"
83+
);
84+
85+
let initialized_component = component_init_wasmtime::initialize(&component).await?;
86+
87+
/*
88+
// TODO: execute `component`. it should not trap.
89+
execute(&initialized_component)
90+
.await
91+
.context("execute initialized component")?;
92+
*/
93+
94+
Ok(())
95+
}

wasmtime/tests/test.rs

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)