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) -> Component<'a> {
86    let mut ctx = Ctx::new(None, false);
87    let mut last_idx = None;
88    for x in items {
89        match x {
90            Ok(Version { num, encoding, .. }) => {
91                if encoding != wasmparser::Encoding::Component {
92                    panic!("wasm file is not a component")
93                }
94                if num != 0xd {
95                    panic!("unknown component encoding version 0x{:x}\n", num);
96                }
97            }
98            Ok(ComponentTypeSection(ts)) => {
99                for t in ts {
100                    match t {
101                        Ok(ComponentType::Component(ct)) => {
102                            let ct_ = ctx.elab_component(&ct);
103                            ctx.types.push(Defined::Component(ct_.unwrap()));
104                        }
105                        _ => panic!("non-component type"),
106                    }
107                }
108            }
109            Ok(ComponentExportSection(es)) => {
110                for e in es {
111                    match e {
112                        Err(_) => panic!("invalid export section"),
113                        Ok(ce) => {
114                            if ce.kind == ComponentExternalKind::Type {
115                                last_idx = Some(ctx.types.len());
116                                ctx.types.push(raw_type_export_type(&ctx, &ce).clone());
117                            }
118                        }
119                    }
120                }
121            }
122            Ok(ComponentAliasSection(r#as)) => {
123                for a in r#as {
124                    match a {
125                        Ok(ComponentAlias::InstanceExport {
126                            kind: ComponentExternalKind::Type,
127                            ..
128                        })
129                        | Ok(ComponentAlias::Outer {
130                            kind: ComponentOuterAliasKind::Type,
131                            ..
132                        }) => {
133                            panic!("Component outer type aliases are not supported")
134                        }
135                        // Anything else doesn't affect the index
136                        // space that we are interested in, so we can
137                        // safely ignore
138                        _ => {}
139                    }
140                }
141            }
142
143            // No other component section should be terribly relevant
144            // for us.  We would not generally expect to find them in
145            // a file that just represents a type like this, but it
146            // seems like there are/may be a whole bunch of debugging
147            // custom sections, etc that might show up, so for now
148            // let's just ignore anything.
149            _ => {}
150        }
151    }
152    match last_idx {
153        None => panic!("no exported type"),
154        Some(n) => match ctx.types.into_iter().nth(n) {
155            Some(Defined::Component(c)) => c,
156            _ => panic!("final export is not component"),
157        },
158    }
159}