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}