Skip to main content

hyperlight_component_util/
component.rs

1/*
2Copyright 2025 The Hyperlight Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15 */
16
17//! Just enough component parsing support to get at the actual types
18
19use wasmparser::Payload::{
20    ComponentAliasSection, ComponentExportSection, ComponentTypeSection, Version,
21};
22use wasmparser::{
23    ComponentAlias, ComponentExternalKind, ComponentOuterAliasKind, ComponentType,
24    ComponentTypeRef, Payload,
25};
26
27use crate::etypes::{Component, Ctx, Defined};
28
29/// From [`wasmparser::ComponentExport`], elaborate a deftype_e as per
30/// the specification.
31fn raw_type_export_type<'p, 'a, 'c>(
32    ctx: &'c Ctx<'p, 'a>,
33    ce: &'c wasmparser::ComponentExport<'a>,
34) -> &'c Defined<'a> {
35    match ce.ty {
36        Some(ComponentTypeRef::Component(n)) => match ctx.types.get(n as usize) {
37            Some(t) => t,
38            None => {
39                panic!("malformed component type export: ascription does not refer to a type");
40            }
41        },
42        Some(_) => {
43            panic!(
44                "malformed component type export: ascription does not refer to a component type"
45            );
46        }
47        None => match ctx.types.get(ce.index as usize) {
48            Some(t) => t,
49            None => {
50                panic!("malformed component type export: does not refer to a type");
51            }
52        },
53    }
54}
55
56/// Find the last exported type in a component, since in wasm-encoded
57/// WIT this is typically the main world to use.  This is a very
58/// special case that just lets us pull a type out of a value-level
59///
60/// Precondition: The given iterator is
61/// - a component, whose
62/// - encoding version is 0xd exactly, and who
63/// - does not contain any value-level aliases, and whose
64/// - final export is a component type
65///
66/// Anything that is a "binary-encoded WIT" produced by a recent
67/// toolchain should satisfy this. On violation, this function will
68/// panic with an error message.
69///
70/// The reason we look for the last export is that the WIT binary
71/// encoding encodes any instance type imported/exported from the main
72/// component (a/k/a WIT world) as a type export, followed by a final
73/// type export for the type of the main component/world.
74///
75/// TODO: Allow the user to specify a specific export to use (or a WIT
76/// world name), since current WIT tooling can generate encoded
77/// packages with multiple component types in them.
78///
79/// TODO: Encode even more assumptions about WIT package structure
80/// (which are already there in rtypes/host/guest) and allow looking
81/// for a specific named world, instead of simply grabbing the last
82/// export.
83pub fn read_component_single_exported_type<'a>(
84    items: impl Iterator<Item = wasmparser::Result<Payload<'a>>>,
85    world_name: Option<String>,
86) -> Component<'a> {
87    let mut ctx = Ctx::new(None, false);
88    let mut selected_type_idx = None;
89    for x in items {
90        match x {
91            Ok(Version { num, encoding, .. }) => {
92                if encoding != wasmparser::Encoding::Component {
93                    panic!("wasm file is not a component")
94                }
95                if num != 0xd {
96                    panic!("unknown component encoding version 0x{:x}\n", num);
97                }
98            }
99            Ok(ComponentTypeSection(ts)) => {
100                for t in ts {
101                    match t {
102                        Ok(ComponentType::Component(ct)) => {
103                            let ct_ = ctx.elab_component(&ct);
104                            ctx.types.push(Defined::Component(ct_.unwrap()));
105                        }
106                        _ => panic!("non-component type"),
107                    }
108                }
109            }
110            Ok(ComponentExportSection(es)) => {
111                for e in es {
112                    match e {
113                        Err(_) => panic!("invalid export section"),
114                        Ok(ce) => {
115                            if ce.kind == ComponentExternalKind::Type {
116                                ctx.types.push(raw_type_export_type(&ctx, &ce).clone());
117
118                                // picks the world index if world_name is passed in the proc_macro
119                                // else picks the index of last type, exported by core module
120                                if let Some(world) = world_name.as_ref() {
121                                    let name = ce.name.0;
122                                    if name.eq_ignore_ascii_case(world) {
123                                        selected_type_idx = Some(ctx.types.len() - 1);
124                                    }
125                                } else {
126                                    selected_type_idx = Some(ctx.types.len() - 1);
127                                }
128                            }
129                        }
130                    }
131                }
132            }
133            Ok(ComponentAliasSection(r#as)) => {
134                for a in r#as {
135                    match a {
136                        Ok(ComponentAlias::InstanceExport {
137                            kind: ComponentExternalKind::Type,
138                            ..
139                        })
140                        | Ok(ComponentAlias::Outer {
141                            kind: ComponentOuterAliasKind::Type,
142                            ..
143                        }) => {
144                            panic!("Component outer type aliases are not supported")
145                        }
146                        // Anything else doesn't affect the index
147                        // space that we are interested in, so we can
148                        // safely ignore
149                        _ => {}
150                    }
151                }
152            }
153
154            // No other component section should be terribly relevant
155            // for us.  We would not generally expect to find them in
156            // a file that just represents a type like this, but it
157            // seems like there are/may be a whole bunch of debugging
158            // custom sections, etc that might show up, so for now
159            // let's just ignore anything.
160            _ => {}
161        }
162    }
163
164    match selected_type_idx {
165        Some(n) => match ctx.types.into_iter().nth(n) {
166            Some(Defined::Component(c)) => c,
167            _ => panic!("final export is not component"),
168        },
169        None => match &world_name {
170            Some(name) => panic!("world '{}' not found in component", name),
171            None => panic!("no exported type"),
172        },
173    }
174}