Skip to content

Commit 14976ae

Browse files
committed
fix(component): implement proper alias indexing and global section parsing
Fixes #118 Major changes: - Implement global section parsing in streaming decoder (was previously stubbed) - Add dest_idx field to Alias struct to track actual index in component - Separate core and component function index spaces during parsing - Assign alias indices based on section order, accounting for canon definitions - Create stub instances for InlineExports referencing canon-lowered functions - Update alias map to use computed dest_idx instead of flat counter Technical details: - Global section now properly parses value type, mutability, and init expressions - Parser tracks separate counters for core vs component items during section processing - Canon lower/lift/resource operations correctly increment appropriate counters - Section 0x06 (Aliases) and 0x08 (Canonicals) processed in binary order - Stub instances allow component instantiation to proceed without full canon runtime Impact: - TinyGo component now instantiates and executes successfully - Alias map correctly resolves Function[0-25, 50-51] with canon functions between - Component executes _initialize() and main entry point without errors - I/O output pending full Canonical ABI runtime implementation Related changes: - Make WASI default feature in wrtd - Update CLAUDE.md with anti-fallback guidelines - Fix import linking to delegate to inner StacklessEngine
1 parent 6a9004a commit 14976ae

File tree

17 files changed

+1230
-747
lines changed

17 files changed

+1230
-747
lines changed

CLAUDE.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ cargo-wrt --help
2020
- Any example code MUST be in the `example/` directory or test files, not in the runtime implementation.
2121
- The runtime should be able to execute real WebAssembly modules without special-casing specific files.
2222
- Only use placeholders when absolutely necessary and clearly document them.
23+
- **NO FALLBACK LOGIC**: NEVER add fallback code that masks bugs or incomplete implementations. Fallbacks prevent us from seeing real issues and create technical debt. If something doesn't work, fix the root cause - don't add a fallback. Examples of banned fallbacks:
24+
- `unwrap_or_else(|| default_value)` when the value should always exist
25+
- `if lookup_fails { use_hardcoded_assumption }` instead of proper lookup
26+
- "Fallback to instance X" when proper mapping should exist
27+
- Any code that says "this is a temporary workaround"
28+
- **FAIL LOUD AND EARLY**: If data is missing or incorrect, return an error immediately. Don't silently substitute defaults or guess values.
2329

2430
## Memory System Architecture
2531

wrt-component/src/components/component_instantiation.rs

Lines changed: 167 additions & 56 deletions
Large diffs are not rendered by default.

wrt-component/src/linker/wasi_provider.rs

Lines changed: 132 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,42 @@ impl WasiInstanceProvider {
6565
name if name.starts_with("wasi:cli/stdout") => {
6666
self.add_stdout_exports(&mut instance)?;
6767
}
68+
name if name.starts_with("wasi:cli/stdin") => {
69+
self.add_stdin_exports(&mut instance)?;
70+
}
71+
name if name.starts_with("wasi:cli/stderr") => {
72+
self.add_stderr_exports(&mut instance)?;
73+
}
74+
name if name.starts_with("wasi:cli/environment") => {
75+
self.add_environment_exports(&mut instance)?;
76+
}
77+
name if name.starts_with("wasi:cli/exit") => {
78+
self.add_exit_exports(&mut instance)?;
79+
}
6880
name if name.starts_with("wasi:io/streams") => {
6981
self.add_streams_exports(&mut instance)?;
7082
}
83+
name if name.starts_with("wasi:io/error") => {
84+
self.add_error_exports(&mut instance)?;
85+
}
86+
name if name.starts_with("wasi:io/poll") => {
87+
self.add_poll_exports(&mut instance)?;
88+
}
89+
name if name.starts_with("wasi:filesystem/types") => {
90+
self.add_filesystem_types_exports(&mut instance)?;
91+
}
92+
name if name.starts_with("wasi:filesystem/preopens") => {
93+
self.add_filesystem_preopens_exports(&mut instance)?;
94+
}
95+
name if name.starts_with("wasi:clocks/monotonic-clock") => {
96+
self.add_monotonic_clock_exports(&mut instance)?;
97+
}
98+
name if name.starts_with("wasi:clocks/wall-clock") => {
99+
self.add_wall_clock_exports(&mut instance)?;
100+
}
101+
name if name.starts_with("wasi:random/random") => {
102+
self.add_random_exports(&mut instance)?;
103+
}
71104
_ => {
72105
#[cfg(feature = "std")]
73106
println!("[WASI-PROVIDER] Warning: No exports for unknown interface '{}'", interface_name);
@@ -114,36 +147,129 @@ impl WasiInstanceProvider {
114147
Ok(())
115148
}
116149

150+
/// Add stdin interface exports (wasi:cli/stdin@0.2.0)
151+
fn add_stdin_exports(&mut self, instance: &mut InstanceImport) -> Result<()> {
152+
// Export: get-stdin() -> input-stream
153+
self.add_simple_export(instance, "get-stdin")
154+
}
155+
156+
/// Add stderr interface exports (wasi:cli/stderr@0.2.0)
157+
fn add_stderr_exports(&mut self, instance: &mut InstanceImport) -> Result<()> {
158+
// Export: get-stderr() -> output-stream
159+
self.add_simple_export(instance, "get-stderr")
160+
}
161+
162+
/// Add environment interface exports (wasi:cli/environment@0.2.0)
163+
fn add_environment_exports(&mut self, instance: &mut InstanceImport) -> Result<()> {
164+
// Export: get-environment() -> list<tuple<string, string>>
165+
self.add_simple_export(instance, "get-environment")?;
166+
// Export: get-arguments() -> list<string>
167+
self.add_simple_export(instance, "get-arguments")
168+
}
169+
170+
/// Add exit interface exports (wasi:cli/exit@0.2.0)
171+
fn add_exit_exports(&mut self, instance: &mut InstanceImport) -> Result<()> {
172+
// Export: exit(status: result) -> ()
173+
self.add_simple_export(instance, "exit")
174+
}
175+
117176
/// Add streams interface exports (wasi:io/streams@0.2.0)
118177
fn add_streams_exports(&mut self, instance: &mut InstanceImport) -> Result<()> {
119178
// Export: [method]output-stream.blocking-write-and-flush
120-
let write_flush_index = self.next_function_index;
179+
self.add_simple_export(instance, "[method]output-stream.blocking-write-and-flush")?;
180+
// Export: [method]input-stream.blocking-read
181+
self.add_simple_export(instance, "[method]input-stream.blocking-read")?;
182+
// Export: [method]output-stream.blocking-flush
183+
self.add_simple_export(instance, "[method]output-stream.blocking-flush")?;
184+
// Export: [method]output-stream.write
185+
self.add_simple_export(instance, "[method]output-stream.write")?;
186+
// Export: [method]input-stream.read
187+
self.add_simple_export(instance, "[method]input-stream.read")
188+
}
189+
190+
/// Add error interface exports (wasi:io/error@0.2.0)
191+
fn add_error_exports(&mut self, instance: &mut InstanceImport) -> Result<()> {
192+
// Export: [method]error.to-debug-string
193+
self.add_simple_export(instance, "[method]error.to-debug-string")
194+
}
195+
196+
/// Add poll interface exports (wasi:io/poll@0.2.0)
197+
fn add_poll_exports(&mut self, instance: &mut InstanceImport) -> Result<()> {
198+
// Export: poll(pollables: list<pollable>) -> list<u32>
199+
self.add_simple_export(instance, "poll")
200+
}
201+
202+
/// Add filesystem types interface exports (wasi:filesystem/types@0.2.0)
203+
fn add_filesystem_types_exports(&mut self, instance: &mut InstanceImport) -> Result<()> {
204+
// Export: [method]descriptor.read-via-stream
205+
self.add_simple_export(instance, "[method]descriptor.read-via-stream")?;
206+
// Export: [method]descriptor.write-via-stream
207+
self.add_simple_export(instance, "[method]descriptor.write-via-stream")?;
208+
// Export: [method]descriptor.stat
209+
self.add_simple_export(instance, "[method]descriptor.stat")?;
210+
// Export: [method]descriptor.open-at
211+
self.add_simple_export(instance, "[method]descriptor.open-at")
212+
}
213+
214+
/// Add filesystem preopens interface exports (wasi:filesystem/preopens@0.2.0)
215+
fn add_filesystem_preopens_exports(&mut self, instance: &mut InstanceImport) -> Result<()> {
216+
// Export: get-directories() -> list<tuple<descriptor, string>>
217+
self.add_simple_export(instance, "get-directories")
218+
}
219+
220+
/// Add monotonic clock interface exports (wasi:clocks/monotonic-clock@0.2.0)
221+
fn add_monotonic_clock_exports(&mut self, instance: &mut InstanceImport) -> Result<()> {
222+
// Export: now() -> instant
223+
self.add_simple_export(instance, "now")?;
224+
// Export: resolution() -> duration
225+
self.add_simple_export(instance, "resolution")
226+
}
227+
228+
/// Add wall clock interface exports (wasi:clocks/wall-clock@0.2.0)
229+
fn add_wall_clock_exports(&mut self, instance: &mut InstanceImport) -> Result<()> {
230+
// Export: now() -> datetime
231+
self.add_simple_export(instance, "now")?;
232+
// Export: resolution() -> datetime
233+
self.add_simple_export(instance, "resolution")
234+
}
235+
236+
/// Add random interface exports (wasi:random/random@0.2.0)
237+
fn add_random_exports(&mut self, instance: &mut InstanceImport) -> Result<()> {
238+
// Export: get-random-bytes(len: u64) -> list<u8>
239+
self.add_simple_export(instance, "get-random-bytes")?;
240+
// Export: get-random-u64() -> u64
241+
self.add_simple_export(instance, "get-random-u64")
242+
}
243+
244+
/// Helper to add a simple function export
245+
fn add_simple_export(&mut self, instance: &mut InstanceImport, name: &str) -> Result<()> {
246+
let func_index = self.next_function_index;
121247
self.next_function_index += 1;
122248

123249
let provider = ComponentProvider::default();
124250
let signature = WrtComponentType::Unit(provider)?;
125251

126252
let func_export = FunctionExport {
127253
signature,
128-
index: write_flush_index,
254+
index: func_index,
129255
};
130256

131257
#[cfg(feature = "std")]
132258
instance.exports.insert(
133-
"[method]output-stream.blocking-write-and-flush".to_string(),
259+
name.to_string(),
134260
Box::new(ExportValue::Function(func_export)),
135261
);
136262

137263
#[cfg(not(feature = "std"))]
138264
{
139265
use wrt_foundation::bounded::BoundedString;
140-
let name = BoundedString::try_from_str("[method]output-stream.blocking-write-and-flush")?;
141-
instance.exports.push((name, Box::new(ExportValue::Function(func_export))))
266+
let bounded_name = BoundedString::try_from_str(name)?;
267+
instance.exports.push((bounded_name, Box::new(ExportValue::Function(func_export))))
142268
.map_err(|_| wrt_error::Error::resource_exhausted("Too many exports"))?;
143269
}
144270

145271
#[cfg(feature = "std")]
146-
println!("[WASI-PROVIDER] Added export: [method]output-stream.blocking-write-and-flush (index {})", write_flush_index);
272+
println!("[WASI-PROVIDER] Added export: {} (index {})", name, func_index);
147273

148274
Ok(())
149275
}

wrt-decoder/src/component/mod.rs

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,27 @@ pub fn decode_component(binary: &[u8]) -> Result<Component> {
271271
fn parse_component_sections(data: &[u8], component: &mut Component) -> Result<()> {
272272
let mut offset = 0;
273273

274+
// Track index counters for each sort to assign dest_idx to aliases
275+
// These are incremented for both aliases AND canon definitions
276+
// IMPORTANT: Core and component index spaces are SEPARATE
277+
use wrt_format::component::{CoreSort, CanonOperation};
278+
279+
// Core-level counters (for CoreInstanceExport aliases)
280+
let mut core_func_counter = 0u32;
281+
let mut core_table_counter = 0u32;
282+
let mut core_memory_counter = 0u32;
283+
let mut core_global_counter = 0u32;
284+
let mut core_type_counter = 0u32;
285+
let mut core_module_counter = 0u32;
286+
let mut core_instance_counter = 0u32;
287+
288+
// Component-level counters (for InstanceExport aliases)
289+
let mut component_func_counter = 0u32;
290+
let mut component_type_counter = 0u32;
291+
let mut component_instance_counter = 0u32;
292+
let mut component_counter = 0u32;
293+
let mut value_counter = 0u32;
294+
274295
// Parse each section
275296
while offset < data.len() {
276297
// Read section ID
@@ -379,9 +400,57 @@ fn parse_component_sections(data: &[u8], component: &mut Component) -> Result<()
379400
0x06 => {
380401
// Section 6: Aliases
381402
match parse::parse_alias_section(section_data) {
382-
Ok((aliases, _)) => {
403+
Ok((mut aliases, _)) => {
383404
#[cfg(feature = "std")]
384405
eprintln!("[SECTION_PARSER] Section 0x06 (Aliases): parsed {} aliases", aliases.len());
406+
407+
// Assign dest_idx to each alias based on its sort and current counter
408+
for alias in &mut aliases {
409+
use wrt_format::component::{AliasTarget, Sort};
410+
match &alias.target {
411+
AliasTarget::CoreInstanceExport { kind, .. } => {
412+
// Core-level aliases use core counters
413+
let idx = match kind {
414+
CoreSort::Function => { let i = core_func_counter; core_func_counter += 1; i },
415+
CoreSort::Table => { let i = core_table_counter; core_table_counter += 1; i },
416+
CoreSort::Memory => { let i = core_memory_counter; core_memory_counter += 1; i },
417+
CoreSort::Global => { let i = core_global_counter; core_global_counter += 1; i },
418+
CoreSort::Type => { let i = core_type_counter; core_type_counter += 1; i },
419+
CoreSort::Module => { let i = core_module_counter; core_module_counter += 1; i },
420+
CoreSort::Instance => { let i = core_instance_counter; core_instance_counter += 1; i },
421+
};
422+
alias.dest_idx = Some(idx);
423+
#[cfg(feature = "std")]
424+
eprintln!("[SECTION_PARSER] Assigned CoreInstanceExport {:?}[{}]", kind, idx);
425+
},
426+
AliasTarget::InstanceExport { kind, .. } => {
427+
// Component-level aliases use component counters
428+
let idx = match kind {
429+
Sort::Function => { let i = component_func_counter; component_func_counter += 1; i },
430+
Sort::Component => { let i = component_counter; component_counter += 1; i },
431+
Sort::Instance => { let i = component_instance_counter; component_instance_counter += 1; i },
432+
Sort::Value => { let i = value_counter; value_counter += 1; i },
433+
Sort::Type => { let i = component_type_counter; component_type_counter += 1; i },
434+
Sort::Core(_) => {
435+
// Core sorts in InstanceExport are unusual, skip for now
436+
#[cfg(feature = "std")]
437+
eprintln!("[SECTION_PARSER] WARNING: InstanceExport with Core sort");
438+
continue;
439+
},
440+
};
441+
alias.dest_idx = Some(idx);
442+
#[cfg(feature = "std")]
443+
eprintln!("[SECTION_PARSER] Assigned InstanceExport {:?}[{}]", kind, idx);
444+
},
445+
AliasTarget::Outer { .. } => {
446+
// Outer aliases reference parent component's index space
447+
// These don't consume indices in the current component
448+
#[cfg(feature = "std")]
449+
eprintln!("[SECTION_PARSER] Outer alias (no index assigned)");
450+
}
451+
}
452+
}
453+
385454
component.aliases.extend(aliases);
386455
},
387456
Err(e) => {
@@ -407,6 +476,36 @@ fn parse_component_sections(data: &[u8], component: &mut Component) -> Result<()
407476
// Section 8: Canonical (Canon ABI operations: lift, lower, resource)
408477
match parse::parse_canon_section(section_data) {
409478
Ok((canons, _)) => {
479+
#[cfg(feature = "std")]
480+
eprintln!("[SECTION_PARSER] Section 0x08 (Canonicals): parsed {} canons", canons.len());
481+
482+
// Increment counters for canon definitions that create items
483+
for canon in &canons {
484+
match &canon.operation {
485+
CanonOperation::Lower { .. } => {
486+
// Canon lower creates a CORE function
487+
#[cfg(feature = "std")]
488+
eprintln!("[SECTION_PARSER] Canon lower creates core func[{}]", core_func_counter);
489+
core_func_counter += 1;
490+
},
491+
CanonOperation::Lift { .. } => {
492+
// Canon lift creates a COMPONENT function
493+
#[cfg(feature = "std")]
494+
eprintln!("[SECTION_PARSER] Canon lift creates component func[{}]", component_func_counter);
495+
component_func_counter += 1;
496+
},
497+
CanonOperation::Resource(_) => {
498+
// Resource operations (like resource.drop) create CORE functions
499+
#[cfg(feature = "std")]
500+
eprintln!("[SECTION_PARSER] Canon resource creates core func[{}]", core_func_counter);
501+
core_func_counter += 1;
502+
},
503+
_ => {
504+
// Other canon operations may not create items
505+
}
506+
}
507+
}
508+
410509
component.canonicals.extend(canons);
411510
},
412511
Err(_) => {

0 commit comments

Comments
 (0)