Skip to content

Commit

Permalink
Merge pull request wasmerio#2005 from wasmerio/fix/emscripten-exports
Browse files Browse the repository at this point in the history
Improve WasmerEnv, fix emscripten imports
  • Loading branch information
MarkMcCaskey authored Jan 11, 2021
2 parents 7a3d482 + 3654cb1 commit 86574c6
Show file tree
Hide file tree
Showing 15 changed files with 262 additions and 108 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
## **[Unreleased]**

### Added
- [#2005](https://github.com/wasmerio/wasmer/pull/2005) Added the arguments `alias` and `optional` to `WasmerEnv` derive's `export` attribute.

### Changed
- [#1985](https://github.com/wasmerio/wasmer/pull/1985) Bump minimum supported Rust version to 1.48

### Fixed
- [#2005](https://github.com/wasmerio/wasmer/pull/2005) Emscripten is now working again.

## 1.0.0 - 2021-01-05

Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ test-packages:
cargo test -p wasmer-cli --release
cargo test -p wasmer-cache --release
cargo test -p wasmer-engine --release
cargo test -p wasmer-derive --release


# The test-capi rules depend on the build-capi rules to build the .a files to
Expand Down
13 changes: 12 additions & 1 deletion lib/api/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ impl From<ExportError> for HostEnvInitError {
/// memory: LazyInit<Memory>,
/// #[wasmer(export(name = "real_name"))]
/// func: LazyInit<NativeFunc<(i32, i32), i32>>,
/// #[wasmer(export(optional = true, alias = "memory2", alias = "_memory2"))]
/// optional_memory: LazyInit<Memory>,
/// }
///
/// ```
Expand All @@ -51,7 +53,16 @@ impl From<ExportError> for HostEnvInitError {
/// `<field_name>_ref` and `<field_name>_ref_unchecked` for easy access to the
/// data.
///
/// This trait can also be implemented manually:
/// The valid arguments to `export` are:
/// - `name = "string"`: specify the name of this item in the Wasm module. If this is not specified, it will default to the name of the field.
/// - `optional = true`: specify whether this export is optional. Defaults to
/// `false`. Being optional means that if the export can't be found, the
/// [`LazyInit`] will be left uninitialized.
/// - `alias = "string"`: specify additional names to look for in the Wasm module.
/// `alias` may be specified multiple times to search for multiple aliases.
/// -------
///
/// This trait may also be implemented manually:
/// ```
/// # use wasmer::{WasmerEnv, LazyInit, Memory, Instance, HostEnvInitError};
/// #[derive(Clone)]
Expand Down
8 changes: 4 additions & 4 deletions lib/cli/src/commands/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ impl Compile {
pub(crate) fn get_recommend_extension(
engine_type: &EngineType,
target_triple: &Triple,
) -> &'static str {
match engine_type {
) -> Result<&'static str> {
Ok(match engine_type {
#[cfg(feature = "native")]
EngineType::Native => {
wasmer_engine_native::NativeArtifact::get_default_extension(target_triple)
Expand All @@ -55,7 +55,7 @@ impl Compile {
}
#[cfg(not(all(feature = "native", feature = "jit", feature = "object-file")))]
_ => bail!("selected engine type is not compiled in"),
}
})
}

fn inner_execute(&self) -> Result<()> {
Expand All @@ -81,7 +81,7 @@ impl Compile {
.file_stem()
.map(|osstr| osstr.to_string_lossy().to_string())
.unwrap_or_default();
let recommended_extension = Self::get_recommend_extension(&engine_type, target.triple());
let recommended_extension = Self::get_recommend_extension(&engine_type, target.triple())?;
match self.output.extension() {
Some(ext) => {
if ext != recommended_extension {
Expand Down
15 changes: 13 additions & 2 deletions lib/cli/src/commands/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,19 @@ impl Run {
let mut em_env = EmEnv::new(&emscripten_globals.data, Default::default());
let import_object =
generate_emscripten_env(module.store(), &mut emscripten_globals, &mut em_env);
let mut instance = Instance::new(&module, &import_object)
.with_context(|| "Can't instantiate emscripten module")?;
let mut instance = match Instance::new(&module, &import_object) {
Ok(instance) => instance,
Err(e) => {
let err: Result<(), _> = Err(e);
#[cfg(feature = "wasi")]
{
if Wasi::has_wasi_imports(&module) {
return err.with_context(|| "This module has both Emscripten and WASI imports. Wasmer does not currently support Emscripten modules using WASI imports.");
}
}
return err.with_context(|| "Can't instantiate emscripten module");
}
};

run_emscripten_instance(
&mut instance,
Expand Down
10 changes: 9 additions & 1 deletion lib/cli/src/commands/run/wasi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,22 @@ pub struct Wasi {
enable_experimental_io_devices: bool,
}

#[allow(dead_code)]
impl Wasi {
/// Gets the WASI version (if any) for the provided module
pub fn get_version(module: &Module) -> Option<WasiVersion> {
// Get the wasi version on strict mode, so no other imports are
// Get the wasi version in strict mode, so no other imports are
// allowed.
get_wasi_version(&module, true)
}

/// Checks if a given module has any WASI imports at all.
pub fn has_wasi_imports(module: &Module) -> bool {
// Get the wasi version in non-strict mode, so no other imports
// are allowed
get_wasi_version(&module, false).is_some()
}

/// Helper function for executing Wasi from the `Run` command.
pub fn execute(&self, module: Module, program_name: String, args: Vec<String>) -> Result<()> {
let args = args.iter().cloned().map(|arg| arg.into_bytes());
Expand Down
2 changes: 1 addition & 1 deletion lib/compiler-cranelift/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ rayon = "1.5"
serde = { version = "1.0", features = ["derive"] }
more-asserts = "0.2"
gimli = { version = "0.22", optional = true }
smallvec = "1.5"
smallvec = "1.6"

[dev-dependencies]
target-lexicon = { version = "0.11", default-features = false }
Expand Down
2 changes: 1 addition & 1 deletion lib/compiler-llvm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ wasmer-compiler = { path = "../compiler", version = "1.0.0", features = ["transl
wasmer-vm = { path = "../vm", version = "1.0.0" }
wasmer-types = { path = "../wasmer-types", version = "1.0.0" }
target-lexicon = { version = "0.11", default-features = false }
smallvec = "1.5"
smallvec = "1.6"
goblin = "0.2"
libc = { version = "^0.2", default-features = false }
byteorder = "1"
Expand Down
2 changes: 1 addition & 1 deletion lib/compiler-singlepass/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ dynasm = "1.0"
dynasmrt = "1.0"
lazy_static = "1.4"
byteorder = "1.3"
smallvec = "1.5"
smallvec = "1.6"

[dev-dependencies]
target-lexicon = { version = "0.11", default-features = false }
Expand Down
2 changes: 1 addition & 1 deletion lib/compiler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ hashbrown = { version = "0.9", optional = true }
serde = { version = "1.0", features = ["derive"], optional = true }
thiserror = "1.0"
serde_bytes = { version = "0.11", optional = true }
smallvec = "1.5"
smallvec = "1.6"

[target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies]
raw-cpuid = "7.0"
Expand Down
61 changes: 55 additions & 6 deletions lib/derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,22 +123,71 @@ fn derive_struct_fields(data: &DataStruct) -> (TokenStream, TokenStream) {
helpers.push(helper_tokens);
}
match wasmer_attr {
WasmerAttr::Export { identifier, span } => {
WasmerAttr::Export {
identifier,
optional,
aliases,
span,
} => {
let finish_tokens = if let Some(name) = name {
let name_str = name.to_string();
let item_name =
identifier.unwrap_or_else(|| LitStr::new(&name_str, name.span()));
quote_spanned! {f.span()=>
let #name: #inner_type = instance.exports.get_with_generics(#item_name)?;
self.#name.initialize(#name);
let mut access_expr = quote_spanned! {
f.span() =>
instance.exports.get_with_generics::<#inner_type, _, _>(#item_name)
};
for alias in aliases {
access_expr = quote_spanned! {
f.span()=>
#access_expr .or_else(|_| instance.exports.get_with_generics::<#inner_type, _, _>(#alias))
};
}
if optional {
quote_spanned! {
f.span()=>
match #access_expr {
Ok(#name) => { self.#name.initialize(#name); },
Err(_) => (),
};
}
} else {
quote_spanned! {
f.span()=>
let #name: #inner_type = #access_expr?;
self.#name.initialize(#name);
}
}
} else {
if let Some(identifier) = identifier {
let mut access_expr = quote_spanned! {
f.span() =>
instance.exports.get_with_generics::<#inner_type, _, _>(#identifier)
};
for alias in aliases {
access_expr = quote_spanned! {
f.span()=>
#access_expr .or_else(|_| instance.exports.get_with_generics::<#inner_type, _, _>(#alias))
};
}
let local_var =
Ident::new(&format!("field_{}", field_num), identifier.span());
quote_spanned! {f.span()=>
let #local_var: #inner_type = instance.exports.get_with_generics(#identifier)?;
if optional {
quote_spanned! {
f.span()=>
match #access_expr {
Ok(#local_var) => {
self.#field_num.initialize(#local_var);
},
Err(_) => (),
}
}
} else {
quote_spanned! {
f.span()=>
let #local_var: #inner_type = #access_expr?;
self.#field_num.initialize(#local_var);
}
}
} else {
abort!(
Expand Down
78 changes: 59 additions & 19 deletions lib/derive/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,59 +3,97 @@ use proc_macro_error::abort;
use syn::{
parenthesized,
parse::{Parse, ParseStream},
token, Ident, LitStr, Token,
token, Ident, LitBool, LitStr, Token,
};

pub enum WasmerAttr {
Export {
/// The identifier is an override, otherwise we use the field name as the name
/// to lookup in `instance.exports`.
identifier: Option<LitStr>,
optional: bool,
aliases: Vec<LitStr>,
span: Span,
},
}

#[derive(Debug)]
struct ExportExpr {
name: Option<LitStr>,
optional: bool,
aliases: Vec<LitStr>,
}

#[derive(Debug)]
struct ExportOptions {
name: Option<LitStr>,
optional: bool,
aliases: Vec<LitStr>,
}
impl Parse for ExportOptions {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let ident = input.parse::<Ident>()?;
let _ = input.parse::<Token![=]>()?;
let ident_str = ident.to_string();
let name;
let mut name = None;
let mut optional: bool = false;
let mut aliases: Vec<LitStr> = vec![];
loop {
let ident = input.parse::<Ident>()?;
let _ = input.parse::<Token![=]>()?;
let ident_str = ident.to_string();

match ident_str.as_str() {
"name" => {
name = Some(input.parse::<LitStr>()?);
match ident_str.as_str() {
"name" => {
name = Some(input.parse::<LitStr>()?);
}
"optional" => {
optional = input.parse::<LitBool>()?.value;
}
"alias" => {
let alias = input.parse::<LitStr>()?;
aliases.push(alias);
}
otherwise => {
abort!(
ident,
"Unrecognized argument in export options: expected `name = \"string\"`, `optional = bool`, or `alias = \"string\"` found `{}`",
otherwise
);
}
}
otherwise => {
abort!(
ident,
"Unrecognized argument in export options: expected `name` found `{}`",
otherwise
);

match input.parse::<Token![,]>() {
Ok(_) => continue,
Err(_) => break,
}
}

Ok(ExportOptions { name })
Ok(ExportOptions {
name,
optional,
aliases,
})
}
}

impl Parse for ExportExpr {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let name;
let optional;
let aliases;
if input.peek(Ident) {
let options = input.parse::<ExportOptions>()?;
name = options.name;
optional = options.optional;
aliases = options.aliases;
} else {
name = None;
optional = false;
aliases = vec![];
}
Ok(Self { name })
Ok(Self {
name,
optional,
aliases,
})
}
}

Expand All @@ -70,17 +108,19 @@ impl Parse for WasmerAttrInner {
let out = match ident_str.as_str() {
"export" => {
let export_expr;
let name = if input.peek(token::Paren) {
let (name, optional, aliases) = if input.peek(token::Paren) {
let _: token::Paren = parenthesized!(export_expr in input);

let expr = export_expr.parse::<ExportExpr>()?;
expr.name
(expr.name, expr.optional, expr.aliases)
} else {
None
(None, false, vec![])
};

WasmerAttr::Export {
identifier: name,
optional,
aliases,
span,
}
}
Expand Down
Loading

0 comments on commit 86574c6

Please sign in to comment.