Skip to content

Commit 7250968

Browse files
authored
Closes #651: (#1020)
- adds support to select a specific world from a WIT file if multiple worlds are specified. Signed-off-by: Shailesh Vashishth <[email protected]>
1 parent 945fada commit 7250968

File tree

6 files changed

+164
-17
lines changed

6 files changed

+164
-17
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
cargo install --locked cargo-hyperlight

src/hyperlight_component_macro/src/lib.rs

Lines changed: 72 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+
let world_name = parsed_bindgen_input.world_name;
77+
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+
let world_name = parsed_bindgen_input.world_name;
103+
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,57 @@ 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(
150+
key.span(),
151+
format!(
152+
"unknown parameter '{}'; expected 'path' or 'world_name'",
153+
key
154+
),
155+
));
156+
}
157+
}
158+
// Parse optional comma
159+
if content.peek(Token![,]) {
160+
content.parse::<Token![,]>()?;
161+
}
162+
}
163+
} else {
164+
let option_path_litstr = input.parse::<Option<syn::LitStr>>()?;
165+
if let Some(concrete_path) = option_path_litstr {
166+
path = Some(std::path::PathBuf::from(concrete_path.value()));
167+
}
168+
}
169+
Ok(Self { world_name, path })
170+
}
171+
}

src/hyperlight_component_util/src/component.rs

Lines changed: 19 additions & 4 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 selected_type_idx = None;
8889
for x in items {
8990
match x {
9091
Ok(Version { num, encoding, .. }) => {
@@ -112,8 +113,18 @@ 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_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+
}
117128
}
118129
}
119130
}
@@ -149,11 +160,15 @@ pub fn read_component_single_exported_type<'a>(
149160
_ => {}
150161
}
151162
}
152-
match last_idx {
153-
None => panic!("no exported type"),
163+
164+
match selected_type_idx {
154165
Some(n) => match ctx.types.into_iter().nth(n) {
155166
Some(Defined::Component(c)) => c,
156167
_ => panic!("final export is not component"),
157168
},
169+
None => match &world_name {
170+
Some(name) => panic!("world '{}' not found in component", name),
171+
None => panic!("no exported type"),
172+
},
158173
}
159174
}

src/hyperlight_component_util/src/util.rs

Lines changed: 3 additions & 3 deletions
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
@@ -52,8 +53,7 @@ pub fn read_wit_type_from_file<R, F: FnMut(String, &etypes::Component) -> R>(
5253

5354
/// Deal with `$HYPERLIGHT_COMPONENT_MACRO_DEBUG`: if it is present,
5455
/// save the given token stream (representing the result of
55-
/// macroexpansion) to the debug file and include that file instead of
56-
/// directly returning the given token stream.
56+
/// macroexpansion) to the debug file and then return the token stream
5757
pub fn emit_decls(decls: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
5858
if let Ok(dbg_out) = std::env::var("HYPERLIGHT_COMPONENT_MACRO_DEBUG") {
5959
if let Ok(file) = syn::parse2(decls.clone()) {

src/hyperlight_host/tests/wit_test.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,49 @@ 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+
mod pick_world_binding_test {
434+
use crate::pick_world_bindings::r#twoworlds::r#wit::r#first_import::RecFirstImport;
435+
436+
impl crate::pick_world_bindings::r#twoworlds::r#wit::r#first_import::RecFirstImport {
437+
fn new() -> Self {
438+
Self {
439+
r#key: String::from("dummyKey"),
440+
r#value: String::from("dummyValue"),
441+
}
442+
}
443+
}
444+
445+
#[test]
446+
fn test_first_import_instance() {
447+
let first_import = RecFirstImport::new();
448+
assert_eq!(first_import.r#key, "dummyKey");
449+
assert_eq!(first_import.r#value, "dummyValue");
450+
}
451+
}
452+
453+
mod pick_world_bindings2 {
454+
hyperlight_component_macro::host_bindgen!({path: "../tests/rust_guests/witguest/twoworlds.wasm", world_name: "secondworld"});
455+
}
456+
mod pick_world_binding_test2 {
457+
use crate::pick_world_bindings2::r#twoworlds::r#wit::r#second_export::RecSecondExport;
458+
459+
impl crate::pick_world_bindings2::r#twoworlds::r#wit::r#second_export::RecSecondExport {
460+
fn new() -> Self {
461+
Self {
462+
r#key: String::from("dummyKey"),
463+
r#value: String::from("dummyValue"),
464+
}
465+
}
466+
}
467+
468+
#[test]
469+
fn test_second_export_instance() {
470+
let first_import = RecSecondExport::new();
471+
assert_eq!(first_import.r#key, "dummyKey");
472+
assert_eq!(first_import.r#value, "dummyValue");
473+
}
474+
}
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)