Skip to content

Commit

Permalink
Create example for limiting memory with Tunables
Browse files Browse the repository at this point in the history
  • Loading branch information
webmaster128 committed Oct 27, 2020
1 parent 58413f2 commit 52cc995
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.lock

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

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ wasmer-wasi = { version = "1.0.0-alpha4", path = "lib/wasi", optional = true }
wasmer-wast = { version = "1.0.0-alpha4", path = "tests/lib/wast", optional = true }
wasmer-cache = { version = "1.0.0-alpha4", path = "lib/cache", optional = true }
wasmer-types = { version = "1.0.0-alpha4", path = "lib/wasmer-types" }
wasmer-vm = { version = "1.0.0-alpha4", path = "lib/vm" }
cfg-if = "0.1"

[workspace]
Expand Down Expand Up @@ -211,6 +212,11 @@ name = "imported-global"
path = "examples/imports_global.rs"
required-features = ["cranelift"]

[[example]]
name = "tunables-limit-memory"
path = "examples/tunables_limit_memory.rs"
required-features = ["cranelift"]

[[example]]
name = "wasi"
path = "examples/wasi.rs"
Expand Down
19 changes: 18 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,26 @@ example.

</details>

### Tunables

15. **Limit memory**, explains how to use Tunables to limit the
size of an exported Wasm Memories

_Keywords_: basic, tunables, memory

<details>
<summary><em>Execute the example</em></summary>

```shell
$ cargo run --example tunables-limit-memory --release --features "cranelift"
```

</details>


### Integrations

15. [**WASI**][wasi], explains how to use the [WebAssembly System
16. [**WASI**][wasi], explains how to use the [WebAssembly System
Interface][WASI] (WASI), i.e. the [`wasmer-wasi`] crate.

_Keywords_: wasi, system, interface
Expand Down
131 changes: 131 additions & 0 deletions examples/tunables_limit_memory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use std::sync::Arc;
use wasmer::{
imports, wat2wasm, Instance, Memory, MemoryError, MemoryType, Module, Pages, Store, TableType,
Tunables as BaseTunables,
};
use wasmer_compiler::Target;
use wasmer_compiler_cranelift::Cranelift;
use wasmer_engine::Tunables;
use wasmer_engine_jit::JIT;
use wasmer_vm::{Memory as MemoryTrait, MemoryStyle, Table, TableStyle};

/// A custom tunables that allows you to set a memory limit
struct LimitingTunables {
/// The maxium a linear memory is allowed to be (in Wasm pages, 65 KiB each).
/// Since Wasmer ensures there is only none or one memory, this is practically
/// an upper limit for the guest memory.
max_memory: Pages,
/// The base implementation we delegate all the logic to
base: BaseTunables,
}

impl LimitingTunables {
pub fn for_target(target: &Target, limit: Pages) -> Self {
Self {
max_memory: limit,
base: BaseTunables::for_target(target),
}
}
}

impl Tunables for LimitingTunables {
/// Construct a `MemoryStyle` for the provided `MemoryType`
///
/// Delegated to base.
fn memory_style(&self, memory: &MemoryType) -> MemoryStyle {
self.base.memory_style(memory)
}

/// Construct a `TableStyle` for the provided `TableType`
///
/// Delegated to base.
fn table_style(&self, table: &TableType) -> TableStyle {
self.base.table_style(table)
}

/// Create a memory given a memory type
///
/// The requested memory type is validated, adjusted to the limited and then passed to base.
fn create_memory(
&self,
requested: &MemoryType,
style: &MemoryStyle,
) -> Result<Arc<dyn MemoryTrait>, MemoryError> {
if requested.minimum > self.max_memory {
return Err(MemoryError::Generic(
"Minimum of requested memory exceeds the allowed memory limit".to_string(),
));
}

if let Some(max) = requested.maximum {
if max > self.max_memory {
return Err(MemoryError::Generic(
"Maximum of requested memory exceeds the allowed memory limit".to_string(),
));
}
}

let mut adjusted = requested.clone();
adjusted.maximum = Some(self.max_memory);

self.base.create_memory(&adjusted, style)
}

/// Create a memory given a memory type
///
/// Delegated to base.
fn create_table(&self, ty: &TableType, style: &TableStyle) -> Result<Arc<dyn Table>, String> {
self.base.create_table(ty, style)
}
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
// A Wasm module with one exported memory (min: 7 pages, max: unset)
let wat = br#"(module (memory 7) (export "memory" (memory 0)))"#;

// Alternatively: A Wasm module with one exported memory (min: 7 pages, max: 80 pages)
// let wat = br#"(module (memory 7 80) (export "memory" (memory 0)))"#;

let wasm_bytes = wat2wasm(wat)?;

// Any compiler and any engine do the job here
let compiler = Cranelift::default();
let engine = JIT::new(&compiler).engine();

// Here is where the fun begins

let target = Target::default(); // TODO: should this use engine.target(), which is private?
let tunables = LimitingTunables::for_target(&target, Pages(24));

// Create a store, that holds the engine and our custom tunables
let store = Store::new_with_tunables(&engine, tunables);

println!("Compiling module...");
let module = Module::new(&store, wasm_bytes)?;

println!("Instantiating module...");
let import_object = imports! {};

// Now at this point, our custom tunables are used
let instance = Instance::new(&module, &import_object)?;

// Check what happened
let mut memories: Vec<Memory> = instance
.exports
.iter()
.memories()
.map(|pair| pair.1.clone())
.collect();
assert_eq!(memories.len(), 1);

let first_memory = memories.pop().unwrap();
println!("Memory of this instance: {:?}", first_memory);
assert_eq!(first_memory.ty().maximum.unwrap(), Pages(24));

Ok(())
}

#[test]
fn test_tunables_limit_memory() -> Result<(), Box<dyn std::error::Error>> {
main()
}

0 comments on commit 52cc995

Please sign in to comment.