Skip to content

Commit b5e04a0

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 <[email protected]>
1 parent 08fd006 commit b5e04a0

File tree

4 files changed

+640
-0
lines changed

4 files changed

+640
-0
lines changed

src/hyperlight_component_macro/src/lib.rs

Lines changed: 88 additions & 0 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
@@ -63,6 +65,7 @@ use hyperlight_component_util::*;
6365
/// `instantiate()` method on the component trait that makes
6466
/// instantiating the sandbox particularly ergonomic in core
6567
/// Hyperlight.
68+
6669
#[proc_macro]
6770
pub fn host_bindgen(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
6871
let _ = env_logger::try_init();
@@ -79,6 +82,48 @@ pub fn host_bindgen(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
7982
})
8083
}
8184

85+
#[proc_macro]
86+
pub fn host_bindgen2(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
87+
let _ = env_logger::try_init();
88+
89+
let parsed_bindgen_input = syn::parse_macro_input!(input as BindgenInputParams);
90+
91+
eprintln!("WE GET BACK FROM THE PARSING");
92+
eprintln!("{:?}", parsed_bindgen_input);
93+
94+
let path = parsed_bindgen_input.path.unwrap_or_else(|| {
95+
let wit_world_env = std::env::var_os("WIT_WORLD");
96+
97+
if let Some(env) = wit_world_env {
98+
std::path::PathBuf::from(env)
99+
} else {
100+
std::path::PathBuf::new()
101+
}
102+
});
103+
104+
let world_name = parsed_bindgen_input.world_name;
105+
106+
// what do we do, do we disturb the function signature or
107+
// put this world_name as an env or a rust static variable.
108+
// keeping as a rust static variable seems to be an appropriate
109+
// choice. frequently changing the OS env will be stupid
110+
111+
eprintln!("PATH = {:?} \n WORLD_NAME = {:?}", path.clone().into_os_string(), world_name.clone());
112+
113+
114+
util::read_world_from_file_1(
115+
path.into_os_string(),
116+
world_name,
117+
|kebab_name, ct: &etypes::Component<'_>| {
118+
let decls = emit::run_state(false, false, |s| {
119+
rtypes::emit_toplevel(s, &kebab_name, ct);
120+
host::emit_toplevel(s, &kebab_name, ct);
121+
});
122+
util::emit_decls(decls).into()
123+
},
124+
)
125+
}
126+
82127
/// Create the hyperlight_guest_init() function (which should be
83128
/// called in hyperlight_main()) for the wasm component type in the
84129
/// file passed in (or `$WIT_WORLD`, if nothing is passed in). This
@@ -107,3 +152,46 @@ pub fn guest_bindgen(input: proc_macro::TokenStream) -> proc_macro::TokenStream
107152
util::emit_decls(decls).into()
108153
})
109154
}
155+
156+
#[derive(Debug)]
157+
struct BindgenInputParams {
158+
world_name: Option<String>,
159+
path: Option<std::path::PathBuf>,
160+
}
161+
162+
impl Parse for BindgenInputParams {
163+
fn parse(input: ParseStream) -> Result<Self> {
164+
let content;
165+
syn::braced!(content in input);
166+
eprintln!("Content = \n {:?}", content);
167+
168+
let mut world_name = None;
169+
let mut path = None;
170+
171+
// Parse key-value pairs inside the braces
172+
while !content.is_empty() {
173+
let key: Ident = content.parse()?;
174+
content.parse::<Token![:]>()?;
175+
176+
match key.to_string().as_str() {
177+
"world_name" => {
178+
let value: LitStr = content.parse()?;
179+
world_name = Some(value.value());
180+
}
181+
"path" => {
182+
let value: LitStr = content.parse()?;
183+
path = Some(std::path::PathBuf::from(value.value()));
184+
}
185+
_ => {
186+
return Err(syn::Error::new(key.span(), format!("Unknown key: {}", key)));
187+
}
188+
}
189+
190+
// Parse optional comma
191+
if content.peek(Token![,]) {
192+
content.parse::<Token![,]>()?;
193+
}
194+
}
195+
Ok(Self { world_name, path })
196+
}
197+
}

src/hyperlight_component_util/src/component.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ pub fn read_component_single_exported_type<'a>(
149149
_ => {}
150150
}
151151
}
152+
153+
// eprintln!("{:?}",ctx.types.into_iter().nth(n));
152154
match last_idx {
153155
None => panic!("no exported type"),
154156
Some(n) => match ctx.types.into_iter().nth(n) {
@@ -157,3 +159,92 @@ pub fn read_component_single_exported_type<'a>(
157159
},
158160
}
159161
}
162+
163+
pub fn read_component_specific_world_name<'a>(
164+
items: impl Iterator<Item = wasmparser::Result<Payload<'a>>>,
165+
world_name: String,
166+
) -> Component<'a> {
167+
let mut ctx = Ctx::new(None, false);
168+
let mut world_idx = None;
169+
170+
for x in items {
171+
match x {
172+
Ok(Version { num, encoding, .. }) => {
173+
if encoding != wasmparser::Encoding::Component {
174+
panic!("wasm file is not a component")
175+
}
176+
if num != 0xd {
177+
panic!("unknown component encoding version 0x{:x}\n", num);
178+
}
179+
}
180+
Ok(ComponentTypeSection(ts)) => {
181+
for t in ts {
182+
match t {
183+
Ok(ComponentType::Component(ct)) => {
184+
let ct_ = ctx.elab_component(&ct);
185+
ctx.types.push(Defined::Component(ct_.unwrap()));
186+
}
187+
_ => panic!("non-component type"),
188+
}
189+
}
190+
}
191+
Ok(ComponentExportSection(es)) => {
192+
for e in es {
193+
match e {
194+
Err(_) => panic!("invalid export section"),
195+
Ok(ce) => {
196+
if ce.kind == ComponentExternalKind::Type {
197+
ctx.types.push(raw_type_export_type(&ctx, &ce).clone());
198+
match ce.name {
199+
wasmparser::ComponentExportName(name) => {
200+
if name.eq_ignore_ascii_case(&world_name) {
201+
eprintln!("WE GOT IN = {}", name);
202+
eprintln!("Found matching world: {} (looking for: {})", name, world_name);
203+
world_idx = Some(ctx.types.len() - 1);
204+
eprintln!("Found matching world: {} (looking for: {:?})", name, world_idx.clone());
205+
}
206+
}
207+
}
208+
}
209+
}
210+
}
211+
}
212+
}
213+
Ok(ComponentAliasSection(r#as)) => {
214+
for a in r#as {
215+
match a {
216+
Ok(ComponentAlias::InstanceExport {
217+
kind: ComponentExternalKind::Type,
218+
..
219+
})
220+
| Ok(ComponentAlias::Outer {
221+
kind: ComponentOuterAliasKind::Type,
222+
..
223+
}) => {
224+
panic!("Component outer type aliases are not supported")
225+
}
226+
// Anything else doesn't affect the index
227+
// space that we are interested in, so we can
228+
// safely ignore
229+
_ => {}
230+
}
231+
}
232+
}
233+
234+
// No other component section should be terribly relevant
235+
// for us. We would not generally expect to find them in
236+
// a file that just represents a type like this, but it
237+
// seems like there are/may be a whole bunch of debugging
238+
// custom sections, etc that might show up, so for now
239+
// let's just ignore anything.
240+
_ => {}
241+
}
242+
}
243+
match world_idx {
244+
None => panic!("expected world name not found"),
245+
Some(n) => match ctx.types.into_iter().nth(n) {
246+
Some(Defined::Component(c)) => c,
247+
_ => panic!("final export is not component"),
248+
},
249+
}
250+
}

src/hyperlight_component_util/src/util.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,39 @@ pub fn read_wit_type_from_file<R, F: FnMut(String, &etypes::Component) -> R>(
5050
cb(export.kebab_name.to_string(), ct)
5151
}
5252

53+
pub fn read_world_from_file_1<R, F: FnMut(String, &etypes::Component) -> R>(
54+
filename: impl AsRef<std::ffi::OsStr>,
55+
world_name: Option<String>,
56+
mut cb: F,
57+
) -> R {
58+
let path = std::path::Path::new(&filename);
59+
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
60+
eprintln!("PATH2 = {:?}", path);
61+
let manifest_dir = std::path::Path::new(&manifest_dir);
62+
let path = manifest_dir.join(path);
63+
64+
let bytes = std::fs::read(path).unwrap();
65+
let i = wasmparser::Parser::new(0).parse_all(&bytes);
66+
let ct = crate::component::read_component_specific_world_name(i, world_name.unwrap());
67+
68+
// because of the two-level encapsulation scheme, we need to look
69+
// for the single export of the component type that we just read
70+
if !ct.uvars.is_empty()
71+
|| !ct.imports.is_empty()
72+
|| !ct.instance.evars.is_empty()
73+
|| ct.instance.unqualified.exports.len() != 1
74+
{
75+
panic!("malformed component type container for wit type");
76+
};
77+
let export = &ct.instance.unqualified.exports[0];
78+
use etypes::ExternDesc;
79+
let ExternDesc::Component(ct) = &export.desc else {
80+
panic!("malformed component type container: does not contain component type");
81+
};
82+
log::debug!("hcm: considering component type {:?}", ct);
83+
cb(export.kebab_name.to_string(), ct)
84+
}
85+
5386
/// Deal with `$HYPERLIGHT_COMPONENT_MACRO_DEBUG`: if it is present,
5487
/// save the given token stream (representing the result of
5588
/// macroexpansion) to the debug file and include that file instead of

0 commit comments

Comments
 (0)