Skip to content

Commit c716af8

Browse files
committed
[component_util] Add Component Model elaboration/bindgen utilities
This commit adds a general implementation of WebAssembly Component Model type elaboration, closely derived from the formal specification. It also adds a number of functions which can generate Rust binding code from appropriately-structured (roughly: WIT-like) component types. Signed-off-by: Lucy Menon <[email protected]>
1 parent 1562354 commit c716af8

File tree

19 files changed

+6092
-2
lines changed

19 files changed

+6092
-2
lines changed

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ members = [
1111
"src/hyperlight_host",
1212
"src/hyperlight_guest_capi",
1313
"src/hyperlight_testing",
14-
"fuzz",
14+
"fuzz",
1515
"src/hyperlight_guest_bin",
16+
"src/hyperlight_component_util",
1617
]
1718
# Guests have custom linker flags, so we need to exclude them from the workspace
1819
exclude = [
@@ -36,6 +37,7 @@ hyperlight-host = { path = "src/hyperlight_host", version = "0.5.1", default-fea
3637
hyperlight-guest = { path = "src/hyperlight_guest", version = "0.5.1", default-features = false }
3738
hyperlight-guest-bin = { path = "src/hyperlight_guest_bin", version = "0.5.1", default-features = false }
3839
hyperlight-testing = { path = "src/hyperlight_testing", default-features = false }
40+
hyperlight-component-util = { path = "src/hyperlight_component_util" }
3941

4042
[workspace.lints.rust]
4143
unsafe_op_in_unsafe_fn = "deny"
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[package]
2+
name = "hyperlight-component-util"
3+
version.workspace = true
4+
edition.workspace = true
5+
rust-version.workspace = true
6+
license.workspace = true
7+
homepage.workspace = true
8+
repository.workspace = true
9+
readme.workspace = true
10+
description = """
11+
Shared implementation for the procedural macros that generate Hyperlight host and guest bindings from component types
12+
"""
13+
14+
[lib]
15+
name = "hyperlight_component_util"
16+
17+
[dependencies]
18+
wasmparser = { version = "0.224.0" }
19+
quote = { version = "1.0.38" }
20+
proc-macro2 = { version = "1.0.93" }
21+
syn = { version = "2.0.96" }
22+
itertools = { version = "0.14.0" }
23+
prettyplease = { version = "0.2.31" }
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
Copyright 2024 The Hyperlight Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
//! Just enough component parsing support to get at the actual types
18+
19+
use wasmparser::Payload::{ComponentExportSection, ComponentTypeSection, Version};
20+
use wasmparser::{ComponentExternalKind, ComponentType, ComponentTypeRef, Payload};
21+
22+
use crate::etypes::{Component, Ctx, Defined};
23+
24+
/// From [`wasmparser::ComponentExport`], elaborate a deftype_e as per
25+
/// the specification.
26+
fn raw_type_export_type<'p, 'a, 'c>(
27+
ctx: &'c Ctx<'p, 'a>,
28+
ce: &'c wasmparser::ComponentExport<'a>,
29+
) -> &'c Defined<'a> {
30+
match ce.ty {
31+
Some(ComponentTypeRef::Component(n)) => match ctx.types.get(n as usize) {
32+
Some(t) => t,
33+
t => panic!("bad component type 1 {:?}", t),
34+
},
35+
None => match ctx.types.get(ce.index as usize) {
36+
Some(t) => t,
37+
t => panic!("bad component type 2 {:?}", t),
38+
},
39+
_ => panic!("non-component ascribed type"),
40+
}
41+
}
42+
43+
/// Find the last exported type in a component, since in wasm-encoded
44+
/// WIT this is typically the main world to use. This is a very
45+
/// special case that just lets us pull a type out of a value-level
46+
/// component easily; it is not a full component typechecker.
47+
///
48+
/// TODO: Encode even more assumptions about WIT package structure
49+
/// (which are already there in rtypes/host/guest) and allow looking
50+
/// for a specific named world, instead of simply grabbing the last
51+
/// export.
52+
pub fn read_component_single_exported_type<'a>(
53+
items: impl Iterator<Item = wasmparser::Result<Payload<'a>>>,
54+
) -> Component<'a> {
55+
let mut ctx = Ctx::new(None, false);
56+
let mut last_idx = None;
57+
for x in items {
58+
match x {
59+
Ok(Version { .. }) => (),
60+
Ok(ComponentTypeSection(ts)) => {
61+
for t in ts {
62+
match t {
63+
Ok(ComponentType::Component(ct)) => {
64+
let ct_ = ctx.elab_component(&ct);
65+
ctx.types.push(Defined::Component(ct_.unwrap()));
66+
}
67+
_ => panic!("non-component type"),
68+
}
69+
}
70+
}
71+
Ok(ComponentExportSection(es)) => {
72+
for e in es {
73+
match e {
74+
Err(_) => panic!("invalid export section"),
75+
Ok(ce) => {
76+
if ce.kind == ComponentExternalKind::Type {
77+
last_idx = Some(ctx.types.len());
78+
ctx.types.push(raw_type_export_type(&ctx, &ce).clone());
79+
}
80+
}
81+
}
82+
}
83+
}
84+
_ => {}
85+
}
86+
}
87+
match last_idx {
88+
None => panic!("no exported type"),
89+
Some(n) => match ctx.types.into_iter().nth(n) {
90+
Some(Defined::Component(c)) => c,
91+
_ => panic!("final export is not component"),
92+
},
93+
}
94+
}

0 commit comments

Comments
 (0)