Skip to content

Commit 835395b

Browse files
committed
Closes #651:
- adds support to select a specific world from a WIT file if multiple worlds are specified. Signed-off-by: Shailesh Vashishth <shavashishth@gmail.com>
1 parent 2b3f386 commit 835395b

File tree

6 files changed

+135
-14
lines changed

6 files changed

+135
-14
lines changed

Justfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ guests: build-and-move-rust-guests build-and-move-c-guests
4545
witguest-wit:
4646
cargo install --locked wasm-tools
4747
cd src/tests/rust_guests/witguest && wasm-tools component wit guest.wit -w -o interface.wasm
48+
cd src/tests/rust_guests/witguest && wasm-tools component wit two_worlds.wit -w -o twoworlds.wasm
4849

4950
build-rust-guests target=default-target features="": (witguest-wit)
5051
cd src/tests/rust_guests/simpleguest && cargo build {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }}

src/hyperlight_component_macro/src/lib.rs

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ limitations under the License.
5050
extern crate proc_macro;
5151

5252
use hyperlight_component_util::*;
53+
use syn::parse::{Parse, ParseStream};
54+
use syn::{Ident, LitStr, Result, Token};
5355

5456
/// Create host bindings for the wasm component type in the file
5557
/// passed in (or `$WIT_WORLD`, if nothing is passed in). This will
@@ -66,11 +68,14 @@ use hyperlight_component_util::*;
6668
#[proc_macro]
6769
pub fn host_bindgen(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
6870
let _ = env_logger::try_init();
69-
let path: Option<syn::LitStr> = syn::parse_macro_input!(input as Option<syn::LitStr>);
70-
let path = path
71-
.map(|x| x.value().into())
72-
.unwrap_or_else(|| std::env::var_os("WIT_WORLD").unwrap());
73-
util::read_wit_type_from_file(path, |kebab_name, ct| {
71+
let parsed_bindgen_input = syn::parse_macro_input!(input as BindgenInputParams);
72+
let path = match parsed_bindgen_input.path {
73+
Some(path_env) => path_env.into_os_string(),
74+
None => std::env::var_os("WIT_WORLD").unwrap(),
75+
};
76+
77+
let world_name = parsed_bindgen_input.world_name;
78+
util::read_wit_type_from_file(path, world_name, |kebab_name, ct| {
7479
let decls = emit::run_state(false, false, |s| {
7580
rtypes::emit_toplevel(s, &kebab_name, ct);
7681
host::emit_toplevel(s, &kebab_name, ct);
@@ -89,11 +94,14 @@ pub fn host_bindgen(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
8994
#[proc_macro]
9095
pub fn guest_bindgen(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
9196
let _ = env_logger::try_init();
92-
let path: Option<syn::LitStr> = syn::parse_macro_input!(input as Option<syn::LitStr>);
93-
let path = path
94-
.map(|x| x.value().into())
95-
.unwrap_or_else(|| std::env::var_os("WIT_WORLD").unwrap());
96-
util::read_wit_type_from_file(path, |kebab_name, ct| {
97+
let parsed_bindgen_input = syn::parse_macro_input!(input as BindgenInputParams);
98+
let path = match parsed_bindgen_input.path {
99+
Some(path_env) => path_env.into_os_string(),
100+
None => std::env::var_os("WIT_WORLD").unwrap(),
101+
};
102+
103+
let world_name = parsed_bindgen_input.world_name;
104+
util::read_wit_type_from_file(path, world_name, |kebab_name, ct| {
97105
let decls = emit::run_state(true, false, |s| {
98106
// Emit type/trait definitions for all instances in the world
99107
rtypes::emit_toplevel(s, &kebab_name, ct);
@@ -107,3 +115,51 @@ pub fn guest_bindgen(input: proc_macro::TokenStream) -> proc_macro::TokenStream
107115
util::emit_decls(decls).into()
108116
})
109117
}
118+
119+
#[derive(Debug)]
120+
struct BindgenInputParams {
121+
world_name: Option<String>,
122+
path: Option<std::path::PathBuf>,
123+
}
124+
125+
impl Parse for BindgenInputParams {
126+
fn parse(input: ParseStream) -> Result<Self> {
127+
let mut path = None;
128+
let mut world_name = None;
129+
130+
if input.peek(syn::token::Brace) {
131+
let content;
132+
syn::braced!(content in input);
133+
134+
// Parse key-value pairs inside the braces
135+
while !content.is_empty() {
136+
let key: Ident = content.parse()?;
137+
content.parse::<Token![:]>()?;
138+
139+
match key.to_string().as_str() {
140+
"world_name" => {
141+
let value: LitStr = content.parse()?;
142+
world_name = Some(value.value());
143+
}
144+
"path" => {
145+
let value: LitStr = content.parse()?;
146+
path = Some(std::path::PathBuf::from(value.value()));
147+
}
148+
_ => {
149+
return Err(syn::Error::new(key.span(), format!("Unknown key: {}", key)));
150+
}
151+
}
152+
// Parse optional comma
153+
if content.peek(Token![,]) {
154+
content.parse::<Token![,]>()?;
155+
}
156+
}
157+
} else {
158+
let option_path_litstr = input.parse::<Option<syn::LitStr>>()?;
159+
if let Some(concrete_path) = option_path_litstr {
160+
path = Some(std::path::PathBuf::from(concrete_path.value()));
161+
}
162+
}
163+
Ok(Self { world_name, path })
164+
}
165+
}

src/hyperlight_component_util/src/component.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,10 @@ fn raw_type_export_type<'p, 'a, 'c>(
8282
/// export.
8383
pub fn read_component_single_exported_type<'a>(
8484
items: impl Iterator<Item = wasmparser::Result<Payload<'a>>>,
85+
world_name: Option<String>,
8586
) -> Component<'a> {
8687
let mut ctx = Ctx::new(None, false);
87-
let mut last_idx = None;
88+
let mut picks_one_world_or_last_idx = None;
8889
for x in items {
8990
match x {
9091
Ok(Version { num, encoding, .. }) => {
@@ -112,8 +113,23 @@ pub fn read_component_single_exported_type<'a>(
112113
Err(_) => panic!("invalid export section"),
113114
Ok(ce) => {
114115
if ce.kind == ComponentExternalKind::Type {
115-
last_idx = Some(ctx.types.len());
116116
ctx.types.push(raw_type_export_type(&ctx, &ce).clone());
117+
118+
// picks the world index if world is passed in the proc_macro
119+
// else picks the index of last type, exported by core module
120+
if world_name.is_some() {
121+
let world = world_name.clone().unwrap();
122+
match ce.name {
123+
wasmparser::ComponentExportName(name) => {
124+
if name.eq_ignore_ascii_case(&world) {
125+
picks_one_world_or_last_idx =
126+
Some(ctx.types.len() - 1);
127+
}
128+
}
129+
}
130+
} else {
131+
picks_one_world_or_last_idx = Some(ctx.types.len() - 1);
132+
}
117133
}
118134
}
119135
}
@@ -149,7 +165,8 @@ pub fn read_component_single_exported_type<'a>(
149165
_ => {}
150166
}
151167
}
152-
match last_idx {
168+
169+
match picks_one_world_or_last_idx {
153170
None => panic!("no exported type"),
154171
Some(n) => match ctx.types.into_iter().nth(n) {
155172
Some(Defined::Component(c)) => c,

src/hyperlight_component_util/src/util.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use crate::etypes;
2121
/// given filename, relative to the cargo manifest directory.
2222
pub fn read_wit_type_from_file<R, F: FnMut(String, &etypes::Component) -> R>(
2323
filename: impl AsRef<std::ffi::OsStr>,
24+
world_name: Option<String>,
2425
mut cb: F,
2526
) -> R {
2627
let path = std::path::Path::new(&filename);
@@ -30,7 +31,7 @@ pub fn read_wit_type_from_file<R, F: FnMut(String, &etypes::Component) -> R>(
3031

3132
let bytes = std::fs::read(path).unwrap();
3233
let i = wasmparser::Parser::new(0).parse_all(&bytes);
33-
let ct = crate::component::read_component_single_exported_type(i);
34+
let ct = crate::component::read_component_single_exported_type(i, world_name);
3435

3536
// because of the two-level encapsulation scheme, we need to look
3637
// for the single export of the component type that we just read

src/hyperlight_host/tests/wit_test.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,26 @@ mod wit_test {
426426
drop(guard);
427427
}
428428
}
429+
430+
mod pick_world_bindings {
431+
hyperlight_component_macro::host_bindgen!({path: "../tests/rust_guests/witguest/twoworlds.wasm", world_name: "firstworld"});
432+
}
433+
434+
mod pick_world_binding_test {
435+
use crate::pick_world_bindings::r#twoworlds::r#wit::r#first_import::RecFirstImport;
436+
437+
impl crate::pick_world_bindings::r#twoworlds::r#wit::r#first_import::RecFirstImport {
438+
fn new() -> Option<Self> {
439+
Some(Self {
440+
r#key: String::from("dummyKey"),
441+
r#value: String::from("dummyValue"),
442+
})
443+
}
444+
}
445+
446+
#[test]
447+
fn test_first_import_instance() {
448+
let first_import = RecFirstImport::new();
449+
assert!(first_import.is_some());
450+
}
451+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package twoworlds:wit;
2+
3+
world secondworld {
4+
export second-export;
5+
}
6+
7+
world firstworld {
8+
import first-import;
9+
}
10+
11+
interface second-export {
12+
record rec-second-export {
13+
key : string,
14+
value: string
15+
}
16+
}
17+
18+
interface first-import {
19+
record rec-first-import {
20+
key: string,
21+
value : string
22+
}
23+
}

0 commit comments

Comments
 (0)