Skip to content

Commit 0dad0d8

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 9b8ade9 commit 0dad0d8

File tree

6 files changed

+147
-14
lines changed

6 files changed

+147
-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: 77 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,19 @@ 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 = parsed_bindgen_input.path.unwrap_or_else(|| {
73+
let wit_world_env = std::env::var_os("WIT_WORLD");
74+
75+
if let Some(env) = wit_world_env {
76+
std::path::PathBuf::from(env)
77+
} else {
78+
std::path::PathBuf::new()
79+
}
80+
});
81+
let world_name = parsed_bindgen_input.world_name;
82+
83+
util::read_wit_type_from_file(path, world_name, |kebab_name, ct| {
7484
let decls = emit::run_state(false, false, |s| {
7585
rtypes::emit_toplevel(s, &kebab_name, ct);
7686
host::emit_toplevel(s, &kebab_name, ct);
@@ -89,11 +99,19 @@ pub fn host_bindgen(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
8999
#[proc_macro]
90100
pub fn guest_bindgen(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
91101
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| {
102+
let parsed_bindgen_input = syn::parse_macro_input!(input as BindgenInputParams);
103+
let path = parsed_bindgen_input.path.unwrap_or_else(|| {
104+
let wit_world_env = std::env::var_os("WIT_WORLD");
105+
106+
if let Some(env) = wit_world_env {
107+
std::path::PathBuf::from(env)
108+
} else {
109+
std::path::PathBuf::new()
110+
}
111+
});
112+
let world_name = parsed_bindgen_input.world_name;
113+
114+
util::read_wit_type_from_file(path, world_name, |kebab_name, ct| {
97115
let decls = emit::run_state(true, false, |s| {
98116
// Emit type/trait definitions for all instances in the world
99117
rtypes::emit_toplevel(s, &kebab_name, ct);
@@ -107,3 +125,52 @@ pub fn guest_bindgen(input: proc_macro::TokenStream) -> proc_macro::TokenStream
107125
util::emit_decls(decls).into()
108126
})
109127
}
128+
129+
#[derive(Debug)]
130+
struct BindgenInputParams {
131+
world_name: Option<String>,
132+
path: Option<std::path::PathBuf>,
133+
}
134+
135+
impl Parse for BindgenInputParams {
136+
fn parse(input: ParseStream) -> Result<Self> {
137+
let mut path = None;
138+
let mut world_name = None;
139+
140+
if input.peek(syn::token::Brace) {
141+
let content;
142+
syn::braced!(content in input);
143+
eprintln!("Content = \n {:?}", content);
144+
145+
// Parse key-value pairs inside the braces
146+
while !content.is_empty() {
147+
let key: Ident = content.parse()?;
148+
content.parse::<Token![:]>()?;
149+
150+
match key.to_string().as_str() {
151+
"world_name" => {
152+
let value: LitStr = content.parse()?;
153+
world_name = Some(value.value());
154+
}
155+
"path" => {
156+
let value: LitStr = content.parse()?;
157+
path = Some(std::path::PathBuf::from(value.value()));
158+
}
159+
_ => {
160+
return Err(syn::Error::new(key.span(), format!("Unknown key: {}", key)));
161+
}
162+
}
163+
// Parse optional comma
164+
if content.peek(Token![,]) {
165+
content.parse::<Token![,]>()?;
166+
}
167+
}
168+
} else {
169+
let option_path_litstr = input.parse::<Option<syn::LitStr>>()?;
170+
if let Some(concrete_path) = option_path_litstr {
171+
path = Some(std::path::PathBuf::from(concrete_path.value()));
172+
}
173+
}
174+
Ok(Self { world_name, path })
175+
}
176+
}

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: 3 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,8 @@ 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);
35+
let ct = crate::component::read_component_single_exported_type(i, world_name);
3436

3537
// because of the two-level encapsulation scheme, we need to look
3638
// 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)