Skip to content

Commit 4109355

Browse files
committed
feat(runtime): implement WASI Preview2 stdout/stderr output
- Add wasip2_host module with output stream resource management - Implement get-stdout, get-stderr, blocking-write-and-flush functions - Add import_order field to Module for index-based import lookup - Fix WASI version matching to handle 0.2.x version differences - Fix memory buffer sizing for WASI write operations (up to 16MB) - Support element segment lookup for call_indirect resolution - hello_rust_host.wasm now prints "Hello wasm component world from Rust!"
1 parent 2a59507 commit 4109355

File tree

17 files changed

+2777
-473
lines changed

17 files changed

+2777
-473
lines changed
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
//! Canonical Function Executor for WebAssembly Components
2+
//!
3+
//! This module provides execution of canonical functions (canon.lift and canon.lower)
4+
//! and integrates with the wasip2 host implementation for WASI preview2 support.
5+
6+
extern crate alloc;
7+
8+
use crate::bounded_component_infra::ComponentProvider;
9+
use crate::canonical_abi::ComponentValue;
10+
use wrt_error::{Error, Result};
11+
use wrt_runtime::wasip2_host::Wasip2Host;
12+
use wrt_foundation::float_repr::{FloatBits32, FloatBits64};
13+
use alloc::vec::Vec;
14+
use alloc::string::String;
15+
16+
/// Canonical function types
17+
#[derive(Debug, Clone)]
18+
pub enum CanonicalFunction {
19+
/// Canon.lift - exports a component function by lifting core WASM function
20+
Lift {
21+
func_idx: u32,
22+
opts: CanonicalOptions,
23+
},
24+
/// Canon.lower - imports a host function by lowering to core WASM
25+
Lower {
26+
func_idx: u32,
27+
interface: String,
28+
function: String,
29+
opts: CanonicalOptions,
30+
},
31+
}
32+
33+
/// Canonical ABI options
34+
#[derive(Debug, Clone, Default)]
35+
pub struct CanonicalOptions {
36+
pub memory: Option<u32>,
37+
pub realloc: Option<u32>,
38+
pub post_return: Option<u32>,
39+
pub string_encoding: StringEncoding,
40+
}
41+
42+
/// String encoding for canonical ABI
43+
#[derive(Debug, Clone)]
44+
pub enum StringEncoding {
45+
Utf8,
46+
Utf16,
47+
Latin1,
48+
}
49+
50+
impl Default for StringEncoding {
51+
fn default() -> Self {
52+
StringEncoding::Utf8
53+
}
54+
}
55+
56+
/// Executor for canonical functions with wasip2 support
57+
pub struct CanonicalExecutor {
58+
/// WASI Preview2 host implementation
59+
wasip2: Wasip2Host,
60+
/// Registered canonical functions
61+
canonicals: Vec<CanonicalFunction>,
62+
}
63+
64+
impl CanonicalExecutor {
65+
pub fn new() -> Self {
66+
Self {
67+
wasip2: Wasip2Host::new(),
68+
canonicals: Vec::new(),
69+
}
70+
}
71+
72+
/// Register a canonical function
73+
pub fn register_canonical(&mut self, func: CanonicalFunction) -> u32 {
74+
let idx = self.canonicals.len() as u32;
75+
self.canonicals.push(func);
76+
idx
77+
}
78+
79+
/// Execute a canon.lower import (host function call)
80+
pub fn execute_canon_lower(
81+
&mut self,
82+
interface: &str,
83+
function: &str,
84+
args: Vec<ComponentValue>,
85+
memory: Option<&mut [u8]>,
86+
) -> Result<Vec<ComponentValue>> {
87+
eprintln!("[CANON_EXECUTOR] Executing canon.lower: {}::{}", interface, function);
88+
89+
// Convert ComponentValue to wasip2 Value format
90+
let wasip2_args = self.convert_to_wasip2_values(args)?;
91+
92+
// Dispatch to wasip2 host
93+
let wasip2_results = self.wasip2.dispatch(interface, function, wasip2_args, memory)?;
94+
95+
// Convert results back to ComponentValue
96+
let results = self.convert_from_wasip2_values(wasip2_results)?;
97+
98+
eprintln!("[CANON_EXECUTOR] Canon.lower completed with {} results", results.len());
99+
Ok(results)
100+
}
101+
102+
/// Execute a canon.lift export (component function export)
103+
pub fn execute_canon_lift(
104+
&mut self,
105+
func_idx: u32,
106+
args: Vec<ComponentValue>,
107+
_opts: &CanonicalOptions,
108+
) -> Result<Vec<ComponentValue>> {
109+
eprintln!("[CANON_EXECUTOR] Executing canon.lift: func_idx={}", func_idx);
110+
111+
// Canon.lift would call the underlying core WASM function
112+
// This requires integration with the core WASM engine
113+
// For now, return a placeholder
114+
115+
Err(Error::runtime_not_implemented("Canon.lift execution not yet implemented"))
116+
}
117+
118+
/// Convert ComponentValue to wasip2 Value
119+
fn convert_to_wasip2_values(&self, values: Vec<ComponentValue>) -> Result<Vec<wrt_foundation::values::Value>> {
120+
use wrt_foundation::values::Value;
121+
122+
let mut result = Vec::new();
123+
for val in values {
124+
let wasip2_val = match val {
125+
ComponentValue::Bool(b) => Value::I32(if b { 1 } else { 0 }),
126+
ComponentValue::S8(i) => Value::I32(i as i32),
127+
ComponentValue::U8(u) => Value::I32(u as i32),
128+
ComponentValue::S16(i) => Value::I32(i as i32),
129+
ComponentValue::U16(u) => Value::I32(u as i32),
130+
ComponentValue::S32(i) => Value::I32(i),
131+
ComponentValue::U32(u) => Value::I32(u as i32),
132+
ComponentValue::S64(i) => Value::I64(i),
133+
ComponentValue::U64(u) => Value::I64(u as i64),
134+
ComponentValue::F32(f) => Value::F32(FloatBits32::from_f32(f)),
135+
ComponentValue::F64(f) => Value::F64(FloatBits64::from_f64(f)),
136+
_ => {
137+
eprintln!("[CANON_EXECUTOR] Unsupported ComponentValue type for conversion");
138+
return Err(Error::runtime_error("Unsupported value type"));
139+
}
140+
};
141+
result.push(wasip2_val);
142+
}
143+
Ok(result)
144+
}
145+
146+
/// Convert wasip2 Value to ComponentValue
147+
fn convert_from_wasip2_values(&self, values: Vec<wrt_foundation::values::Value>) -> Result<Vec<ComponentValue>> {
148+
use wrt_foundation::values::Value;
149+
150+
let mut result = Vec::new();
151+
for val in values {
152+
let comp_val = match val {
153+
Value::I32(i) => ComponentValue::S32(i),
154+
Value::I64(i) => ComponentValue::S64(i),
155+
Value::F32(f) => ComponentValue::F32(f.to_f32()),
156+
Value::F64(f) => ComponentValue::F64(f.to_f64()),
157+
_ => {
158+
eprintln!("[CANON_EXECUTOR] Unsupported Value type for conversion");
159+
return Err(Error::runtime_error("Unsupported value type"));
160+
}
161+
};
162+
result.push(comp_val);
163+
}
164+
Ok(result)
165+
}
166+
}
167+
168+
/// Create a global canonical executor instance
169+
#[cfg(feature = "std")]
170+
pub fn create_canonical_executor() -> CanonicalExecutor {
171+
CanonicalExecutor::new()
172+
}
173+
174+
/// Check if a function name represents a wasip2 canonical import
175+
pub fn is_wasip2_canonical(name: &str) -> bool {
176+
name.starts_with("wasi:") && name.contains("@0.2")
177+
}

wrt-component/src/components/component_instantiation.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3187,6 +3187,13 @@ impl ComponentInstance {
31873187
println!("[TEST-THREAD] ✗ Failed to create imports: {:?}", e);
31883188
Error::runtime_execution_error("Failed to create imports")
31893189
})?,
3190+
#[cfg(feature = "std")]
3191+
import_order: Vec::new(),
3192+
#[cfg(not(feature = "std"))]
3193+
import_order: wrt_foundation::bounded::BoundedVec::new(provider.clone()).map_err(|e| {
3194+
println!("[TEST-THREAD] ✗ Failed to create import_order: {:?}", e);
3195+
Error::runtime_execution_error("Failed to create import_order")
3196+
})?,
31903197
functions: Vec::new(),
31913198
tables: wrt_foundation::bounded::BoundedVec::new(provider.clone()).map_err(|e| {
31923199
println!("[TEST-THREAD] ✗ Failed to create tables: {:?}", e);
@@ -3197,10 +3204,16 @@ impl ComponentInstance {
31973204
println!("[TEST-THREAD] ✗ Failed to create globals: {:?}", e);
31983205
Error::runtime_execution_error("Failed to create globals")
31993206
})?,
3207+
#[cfg(feature = "std")]
3208+
elements: Vec::new(),
3209+
#[cfg(not(feature = "std"))]
32003210
elements: wrt_foundation::bounded::BoundedVec::new(provider.clone()).map_err(|e| {
32013211
println!("[TEST-THREAD] ✗ Failed to create elements: {:?}", e);
32023212
Error::runtime_execution_error("Failed to create elements")
32033213
})?,
3214+
#[cfg(feature = "std")]
3215+
data: Vec::new(),
3216+
#[cfg(not(feature = "std"))]
32043217
data: wrt_foundation::bounded::BoundedVec::new(provider.clone()).map_err(|e| {
32053218
println!("[TEST-THREAD] ✗ Failed to create data: {:?}", e);
32063219
Error::runtime_execution_error("Failed to create data")

wrt-decoder/src/component/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,9 @@ fn parse_component_sections(data: &[u8], component: &mut Component) -> Result<()
508508

509509
component.canonicals.extend(canons);
510510
},
511-
Err(_) => {
511+
Err(e) => {
512+
#[cfg(feature = "std")]
513+
eprintln!("[SECTION_PARSER] ERROR parsing canon section: {:?}", e);
512514
// Continue parsing other sections
513515
}
514516
}

0 commit comments

Comments
 (0)