Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions src/hyperlight_component_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ limitations under the License.
extern crate proc_macro;

use hyperlight_component_util::*;
use syn::parse::{Parse, ParseStream};
use syn::{Ident, LitStr, Result, Token};

/// Create host bindings for the wasm component type in the file
/// passed in (or `$WIT_WORLD`, if nothing is passed in). This will
Expand All @@ -63,6 +65,7 @@ use hyperlight_component_util::*;
/// `instantiate()` method on the component trait that makes
/// instantiating the sandbox particularly ergonomic in core
/// Hyperlight.

#[proc_macro]
pub fn host_bindgen(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let _ = env_logger::try_init();
Expand All @@ -79,6 +82,48 @@ pub fn host_bindgen(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
})
}

#[proc_macro]
pub fn host_bindgen2(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let _ = env_logger::try_init();

let parsed_bindgen_input = syn::parse_macro_input!(input as BindgenInputParams);

eprintln!("WE GET BACK FROM THE PARSING");
eprintln!("{:?}", parsed_bindgen_input);

let path = parsed_bindgen_input.path.unwrap_or_else(|| {
let wit_world_env = std::env::var_os("WIT_WORLD");

if let Some(env) = wit_world_env {
std::path::PathBuf::from(env)
} else {
std::path::PathBuf::new()
}
});

let world_name = parsed_bindgen_input.world_name;

// what do we do, do we disturb the function signature or
// put this world_name as an env or a rust static variable.
// keeping as a rust static variable seems to be an appropriate
// choice. frequently changing the OS env will be stupid

eprintln!("PATH = {:?} \n WORLD_NAME = {:?}", path.clone().into_os_string(), world_name.clone());


util::read_world_from_file_1(
path.into_os_string(),
world_name,
|kebab_name, ct: &etypes::Component<'_>| {
let decls = emit::run_state(false, false, |s| {
rtypes::emit_toplevel(s, &kebab_name, ct);
host::emit_toplevel(s, &kebab_name, ct);
});
util::emit_decls(decls).into()
},
)
}

/// Create the hyperlight_guest_init() function (which should be
/// called in hyperlight_main()) for the wasm component type in the
/// file passed in (or `$WIT_WORLD`, if nothing is passed in). This
Expand Down Expand Up @@ -107,3 +152,46 @@ pub fn guest_bindgen(input: proc_macro::TokenStream) -> proc_macro::TokenStream
util::emit_decls(decls).into()
})
}

#[derive(Debug)]
struct BindgenInputParams {
world_name: Option<String>,
path: Option<std::path::PathBuf>,
}

impl Parse for BindgenInputParams {
fn parse(input: ParseStream) -> Result<Self> {
let content;
syn::braced!(content in input);
eprintln!("Content = \n {:?}", content);

let mut world_name = None;
let mut path = None;

// Parse key-value pairs inside the braces
while !content.is_empty() {
let key: Ident = content.parse()?;
content.parse::<Token![:]>()?;

match key.to_string().as_str() {
"world_name" => {
let value: LitStr = content.parse()?;
world_name = Some(value.value());
}
"path" => {
let value: LitStr = content.parse()?;
path = Some(std::path::PathBuf::from(value.value()));
}
_ => {
return Err(syn::Error::new(key.span(), format!("Unknown key: {}", key)));
}
}

// Parse optional comma
if content.peek(Token![,]) {
content.parse::<Token![,]>()?;
}
}
Ok(Self { world_name, path })
}
}
91 changes: 91 additions & 0 deletions src/hyperlight_component_util/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ pub fn read_component_single_exported_type<'a>(
_ => {}
}
}

// eprintln!("{:?}",ctx.types.into_iter().nth(n));
match last_idx {
None => panic!("no exported type"),
Some(n) => match ctx.types.into_iter().nth(n) {
Expand All @@ -157,3 +159,92 @@ pub fn read_component_single_exported_type<'a>(
},
}
}

pub fn read_component_specific_world_name<'a>(
items: impl Iterator<Item = wasmparser::Result<Payload<'a>>>,
world_name: String,
) -> Component<'a> {
let mut ctx = Ctx::new(None, false);
let mut world_idx = None;

for x in items {
match x {
Ok(Version { num, encoding, .. }) => {
if encoding != wasmparser::Encoding::Component {
panic!("wasm file is not a component")
}
if num != 0xd {
panic!("unknown component encoding version 0x{:x}\n", num);
}
}
Ok(ComponentTypeSection(ts)) => {
for t in ts {
match t {
Ok(ComponentType::Component(ct)) => {
let ct_ = ctx.elab_component(&ct);
ctx.types.push(Defined::Component(ct_.unwrap()));
}
_ => panic!("non-component type"),
}
}
}
Ok(ComponentExportSection(es)) => {
for e in es {
match e {
Err(_) => panic!("invalid export section"),
Ok(ce) => {
if ce.kind == ComponentExternalKind::Type {
ctx.types.push(raw_type_export_type(&ctx, &ce).clone());
match ce.name {
wasmparser::ComponentExportName(name) => {
if name.eq_ignore_ascii_case(&world_name) {
eprintln!("WE GOT IN = {}", name);
eprintln!("Found matching world: {} (looking for: {})", name, world_name);
world_idx = Some(ctx.types.len() - 1);
eprintln!("Found matching world: {} (looking for: {:?})", name, world_idx.clone());
}
}
}
}
}
}
}
}
Ok(ComponentAliasSection(r#as)) => {
for a in r#as {
match a {
Ok(ComponentAlias::InstanceExport {
kind: ComponentExternalKind::Type,
..
})
| Ok(ComponentAlias::Outer {
kind: ComponentOuterAliasKind::Type,
..
}) => {
panic!("Component outer type aliases are not supported")
}
// Anything else doesn't affect the index
// space that we are interested in, so we can
// safely ignore
_ => {}
}
}
}

// No other component section should be terribly relevant
// for us. We would not generally expect to find them in
// a file that just represents a type like this, but it
// seems like there are/may be a whole bunch of debugging
// custom sections, etc that might show up, so for now
// let's just ignore anything.
_ => {}
}
}
match world_idx {
None => panic!("expected world name not found"),
Some(n) => match ctx.types.into_iter().nth(n) {
Some(Defined::Component(c)) => c,
_ => panic!("final export is not component"),
},
}
}
33 changes: 33 additions & 0 deletions src/hyperlight_component_util/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,39 @@ pub fn read_wit_type_from_file<R, F: FnMut(String, &etypes::Component) -> R>(
cb(export.kebab_name.to_string(), ct)
}

pub fn read_world_from_file_1<R, F: FnMut(String, &etypes::Component) -> R>(
filename: impl AsRef<std::ffi::OsStr>,
world_name: Option<String>,
mut cb: F,
) -> R {
let path = std::path::Path::new(&filename);
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
eprintln!("PATH2 = {:?}", path);
let manifest_dir = std::path::Path::new(&manifest_dir);
let path = manifest_dir.join(path);

let bytes = std::fs::read(path).unwrap();
let i = wasmparser::Parser::new(0).parse_all(&bytes);
let ct = crate::component::read_component_specific_world_name(i, world_name.unwrap());

// because of the two-level encapsulation scheme, we need to look
// for the single export of the component type that we just read
if !ct.uvars.is_empty()
|| !ct.imports.is_empty()
|| !ct.instance.evars.is_empty()
|| ct.instance.unqualified.exports.len() != 1
{
panic!("malformed component type container for wit type");
};
let export = &ct.instance.unqualified.exports[0];
use etypes::ExternDesc;
let ExternDesc::Component(ct) = &export.desc else {
panic!("malformed component type container: does not contain component type");
};
log::debug!("hcm: considering component type {:?}", ct);
cb(export.kebab_name.to_string(), ct)
}

/// Deal with `$HYPERLIGHT_COMPONENT_MACRO_DEBUG`: if it is present,
/// save the given token stream (representing the result of
/// macroexpansion) to the debug file and include that file instead of
Expand Down
Loading
Loading