diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d7c2370996..f8da0234457 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ ## **[Unreleased]** +### Added + +- [#1649](https://github.com/wasmerio/wasmer/pull/1649) Add outline of migration to 1.0.0 docs. + +### Changed + +### Fixed + ## 1.0.0-alpha5 - 2020-11-06 ### Added diff --git a/docs/deps_dedup.dot b/docs/deps_dedup.dot new file mode 100644 index 00000000000..d4ae34a7709 --- /dev/null +++ b/docs/deps_dedup.dot @@ -0,0 +1,74 @@ +digraph dependencies { + newrank=true; + + n0 [label="wasmer", color=orange]; + n1 [label="wasmer-compiler", color=orange]; + n5 [label="wasmer-engine", color=orange]; + n6 [label="wasmer-engine-jit", color=orange]; + n7 [label="wasmer-engine-native", color=orange]; + n8 [label="wasmer-types", color=orange]; + n9 [label="wasmer-vm", color=orange]; + n10 [label="wasmer-c-api", color=orange]; + n11 [label="wasmer-emscripten", color=orange]; + n12 [label="wasmer-wasi", color=orange]; + n13 [label="wasmer-cache", color=orange]; + n14 [label="wasmer-cli", color=orange]; + + + subgraph cluster_compiler { + label="Compilers"; + color=brown; + + n2 [label="wasmer-compiler-cranelift", color=orange]; + n3 [label="wasmer-compiler-llvm", color=orange]; + n4 [label="wasmer-compiler-singlepass", color=orange]; + } + + subgraph cluster_engine { + label="Engines"; + color=brown; + + n6 [label="wasmer-engine-jit", color=orange]; + n7 [label="wasmer-engine-native", color=orange]; + } + + { + rank=same; + n2; + n3; + n4; + n6; + n7; + } + + + subgraph cluster_abi { + label="Provided ABIs"; + color=brown; + + n12 [label="wasmer-wasi", color=orange]; + n11 [label="wasmer-emscripten", color=orange]; + } + + n14 -> n13 [color=orange, style=dashed]; + n14 -> n12 [color=orange, style=dashed]; + n14 -> n11 [color=orange, style=dashed]; + n13 -> n0 [color=orange, style=dashed]; + n10 -> n11 [color=orange, style=dashed]; + n10 -> n12 [color=orange, style=dashed]; + n11 -> n0 [color=orange, style=dashed]; + n12 -> n0 [color=orange, style=dashed]; + n0 -> n2 [color=orange, style=dashed]; + n0 -> n3 [color=orange, style=dashed]; + n0 -> n4 [color=orange, style=dashed]; + n0 -> n6 [color=orange, style=dashed]; + n0 -> n7 [color=orange, style=dashed]; + n2 -> n1 [color=orange, style=dashed]; + n3 -> n1 [color=orange, style=dashed]; + n4 -> n1 [color=orange, style=dashed]; + n6 -> n5 [color=orange, style=dashed]; + n7 -> n5 [color=orange, style=dashed]; + n5 -> n1 [color=orange, style=dashed]; + n1 -> n9 [color=orange, style=dashed]; + n9 -> n8 [color=orange, style=dashed]; +} diff --git a/docs/deps_dedup.svg b/docs/deps_dedup.svg new file mode 100644 index 00000000000..384e010a431 --- /dev/null +++ b/docs/deps_dedup.svg @@ -0,0 +1,244 @@ + + + + + + +dependencies + + +cluster_compiler + +Compilers + + +cluster_engine + +Engines + + +cluster_abi + +Provided ABIs + + + +n0 + +wasmer + + + +n6 + +wasmer-engine-jit + + + +n0->n6 + + + + + +n7 + +wasmer-engine-native + + + +n0->n7 + + + + + +n2 + +wasmer-compiler-cranelift + + + +n0->n2 + + + + + +n3 + +wasmer-compiler-llvm + + + +n0->n3 + + + + + +n4 + +wasmer-compiler-singlepass + + + +n0->n4 + + + + + +n1 + +wasmer-compiler + + + +n9 + +wasmer-vm + + + +n1->n9 + + + + + +n5 + +wasmer-engine + + + +n5->n1 + + + + + +n6->n5 + + + + + +n7->n5 + + + + + +n8 + +wasmer-types + + + +n9->n8 + + + + + +n10 + +wasmer-c-api + + + +n11 + +wasmer-emscripten + + + +n10->n11 + + + + + +n12 + +wasmer-wasi + + + +n10->n12 + + + + + +n11->n0 + + + + + +n12->n0 + + + + + +n13 + +wasmer-cache + + + +n13->n0 + + + + + +n14 + +wasmer-cli + + + +n14->n11 + + + + + +n14->n12 + + + + + +n14->n13 + + + + + +n2->n1 + + + + + +n3->n1 + + + + + +n4->n1 + + + + + diff --git a/docs/migration_to_1.0.0.md b/docs/migration_to_1.0.0.md new file mode 100644 index 00000000000..ba59eae4988 --- /dev/null +++ b/docs/migration_to_1.0.0.md @@ -0,0 +1,346 @@ +# Migrating from Wasmer 0.x to Wasmer 1.0.0 + +Wasmer 1.0.0 is currently in alpha and is our primary focus. This document will +describe the differences between Wasmer 0.x and Wasmer 1.0.0 and provide examples +to make migrating to the new API as simple as possible. + +Some features are still under development during the alpha of Wasmer 1.0.0. This document +will aim to make clear what these features are. + +## Table of Contents + +- [Rationale for changes in 1.0.0](#rationale-for-changes-in-100) +- [How to use Wasmer 1.0.0](#how-to-use-wasmer-100) + - [Installing Wasmer CLI](#installing-wamser-cli) + - [Using Wasmer 1.0.0](#using-wamser-100) +- [Project structure](#project-structure) +- [Differences](#differences) + - [Instantiating modules](#instantiating-modules) + - [Passing host functions](#passing-host-functions) + - [Accessing the environment as a host function](#accessing-the-environment-as-a-host-function) + - [Error handling](#error-handling) + - [Caching modules](#caching-modules) + +## Rationale for changes in 1.0.0 + +Wasmer 0.x was great but as the WASM community and standards evolve we felt the need to make Wasmer also follow these +changes. + +Wasmer 1.x is what we think a necessary rewrite of a big part of the project to make it more future-proof. + +This version introduces many new features and makes using Wasmer more natural. We did a hard work making it as close +to the standard API as possible while always providing good performances, flexibility and stability. + +The rewrite of the Wasmer Runtime also comes with a rewrite of the languages integrations to achieve the same goals: +providing a clearer API and improving the feature set. + +In this document you will discover the major changes between Wasmer 0.x and Wasmer 1.x by highlighting how to use the +new Rust API. + +## How to use Wasmer 1.0.0 + +### Installing Wasmer CLI + +See [wasmer.io] for installation instructions. + +If you already have wasmer installed, run `wasmer self-update`. + +Install the latest versions of Wasmer with [wasmer-nightly] or by following the steps described in the +documentation: [Getting Started][getting-started]. + +### Using Wasmer 1.0.0 + +The CLI interface for Wasmer 1.0.0 is mostly the same as it was in Wasmer 0.x. + +One difference is that rather than specifying the compiler with `--backend=cranelift`, +in Wasmer 1.0.0 we prefer using the name of the backend as a flag directly, +for example: `--cranelift`. + +The top-level crates that users will usually interface with are: + +- [wasmer] - Wasmer's core runtime API +- [wasmer-wasi] - Wasmer's WASI implementation +- [wasmer-emscripten] - Wasmer's Emscripten implementation +- TODO: + +See the [examples] to find out how to do specific things in Wasmer 1.0.0. + +## Project Structure + +![Wasmer dependencies graph](./deps_dedup.svg) + +The figure above shows the core Wasmer crates and their dependencies with transitive dependencies deduplicated. + +Wasmer 1.0.0 has two core architectural abstractions: engines and compilers. + +An engine is a system that processes WASM with a compiler and prepares it to be executed. + +A compiler is a system that translates WASM into a format that can be understood +more directly by a real computer: machine code. + +For example, in the [examples] you'll see that we are using the JIT engine and the Cranelift compiler. The JIT engine +will generate machine code at runtime, using Cranelift, and then execute it. + +For most uses, users will primarily use the [wasmer] crate directly, perhaps with one of our +provided ABIs such as [wasmer-wasi]. However, for users that need finer grained control over +the behavior of wasmer, other crates such as [wasmer-compiler] and [wasmer-engine] may be used +to implement custom compilers and engines respectively. + +## Differences + +### Instantiating modules + +With Wasmer 0.x, instantiating a module was a matter of calling `wasmer::compiler::compile` and then calling +`instanciate` on the compiled module. + +While simple, this did not give you full-control over Wasmer's configuration. For example, choosing another compiler +was not straightforward. + +With Wasmer 1.x, we changed this part and made the API look more like how Wasmer works internally to give you more +control: + +```diff +- let module = compile(&wasm_bytes[..])?; ++ let engine = JIT::new(&Cranelift::default()).engine(); ++ let store = Store::new(&engine); ++ let module = Module::new(&store, wasm_bytes)?; +- let instance = module.instantiate(&imports)?; ++ let instance = Instance::new(&module, &import_object)?; +``` + +Note that we did not cover how to create the import object here. This is because this part works the same as it used to +with Wasmer 0.x. + +To get more information on how instantiation now works, have a look at the [dedicated example][instance-example] + +### Passing host functions + +With Wasmer 0.x passing host functions to the guest was primarily done using the `func!` macro or by directly using +`Func::new` or `DynamicFunc::new`. + +Given we have a function like: + +```rust +fn sum(a: i32, b: i32) -> i32 { + a + b +} +``` + +We want to import this function in the guest module, let's have a look at how it differs between Wasmer 0.x and +Wasmer 1.x: + +```diff +let import_object = imports! { + "env" => { +- "sum" => func!(sum), ++ "sum" => Function::new_native(&store, sum), + } +} +``` + +The above example illustrates how to import what we call "native functions". There were already available in Wasmer +0.x through the `func!` macro or with `Func::new`. + +There is a second flavor for imported functions: dynamic functions. With Wasmer 0;x you would have created such a +function using `DynamicFunc::new`, here is how it's done with Wasmer 1.x: + +```rust +let sum_signature = FunctionType::new(vec![Type::I32, Type::I32], vec![Type::I32]); +let sum = Function::new(&store, &sum_signature, |args| { + let result = args[0].unwrap_I32() + args[1].unwrap_i32(); + + Ok(vec![Value::I32(result)]) +}); +``` + +Both examples address different needs and have their own pros and cons. We encourage you to have a look at the +dedicated example: [Exposing host functions][host-functions-example]. + +Note that having this API for functions now looks more like the other entities APIs: globals, memories, tables. Here is +a quick example introducing each of them: [Imports & Exports][imports-exports-example] + +### Accessing the environment as a host function + +With Wasmer 0.x each function had its own `vm::Ctx`. This was your entrypoint to the module internals and allowed +access to the context of the currently running instance. + +With Wasmer 1.0.0 this was changed to provide a simpler yet powerful API. + +Let's see an example where we want to have access to the module memory. Here is how we would have done that with Wasmer +0.x: + +```diff +- let get_at = |ctx: &mut vm::Ctx, idx: i32, len: i32| { +- let mem_desc = ctx.memory(0); +- let mem = mem_desc.deref(); +- +- println!("Memory: {:?}", mem); +- +- let view: MemoryView = mem.view(); +- let bytes = view[idx as usize..len as usize].iter().map(Cell::get).collect(); +- +- println!("string: {}", String::from_utf8(bytes).unwrap()); +- }; + +- let import_object = imports! { +- "env" => { +- "get_at" => func!(get_at), +- } +- }; ++ let import_object = imports! {}; + +- let instance = instantiate(wasm_bytes, &import_object)?; ++ let instance = Instance::new(&wasm_bytes, &import_object)?; + ++ let memory = instance.exports.get_memory("mem")?; ++ let get = instance ++ .exports ++ .get_native_function::<(), (WasmPtr, i32)>("get")?; ++ let (ptr, length) = get.call()?; ++ let str = ptr.get_utf8_string(memory, length as u32)?; +``` + +Here we have a module which provides one exported function: `get`. Each time we call this function it will in turn +call our imported function `get_at`. + +The `get_at` function is responsible for reading the guest module-s memory through the `vm::Ctx`. + +With Wasmer 1.0.0 (where the `vm::Ctx` does not exist anymore) we can achieve the same result with something more +natural: we only use imports and exports to read from the memory and write to it. + +Take a look at the following examples to get more details: +* [Interacting with memory][memory] +* [Using memory pointers][memory-pointers] + +The other thing where `vm::Ctx` was useful was to pass data from and to host functions. This has also been made simpler +with Wasmer 1.x: + +```rust +let shared_counter: Arc> = Arc::new(RefCell::new(0)); + +struct Env { + counter: Arc>, +} + +fn get_counter(env: &mut Env) -> i32 { + *env.counter.borrow() +} + +let get_counter_func = Function::new_native_with_env( + &store, + Env { counter: shared_counter.clone() }, + get_counter +); +``` + +A dedicated example describes how to use this feature: [Exposing host functions][host-functions]. + +### Error handling + +Handling errors with Wasmer 0.x was a bit hard, especially, the `wasmer_runtime::error::RuntimeError`. It was rather +complex: it had many variants that you had to handle when pattern matching results. This has been made way simpler with +Wasmer 1.0.0: + +```diff +// Retrieve the `get` function from module's exports and then call it +let result = get.call(0, 13); + +match result { +- Err(RuntimeError::InvokeError(InvokeError::TrapCode { .. })) => { +- // ... +- } +- Err(RuntimeError::InvokeError(InvokeError::FailedWithNoError)) => { +- // ... +- } +- Err(RuntimeError::InvokeError(InvokeError::UnknownTrap { .. })) => { +- // ... +- } +- Err(RuntimeError::InvokeError(InvokeError::UnknownTrapCode { .. })) => { +- // ... +- } +- Err(RuntimeError::InvokeError(InvokeError::EarlyTrap(_))) => { +- // ... +- } +- Err(RuntimeError::InvokeError(InvokeError::Breakpoint(_))) => { +- // ... +- } +- Err(RuntimeError::Metering(_)) => { +- // ... +- } +- Err(RuntimeError::InstanceImage(_)) => { +- // ... +- } +- Err(RuntimeError::User(_)) => { +- // ... +- } ++ Error(e) => { ++ println!("Error caught from `div_by_zero`: {}", e.message()); ++ ++ let frames = e.trace(); ++ let frames_len = frames.len(); ++ ++ // ... ++ } + Ok(_) => { + // ... + }, +} +``` + +As you can see here, handling errors is really easy now! You may find the following examples useful to get more familiar +with this topic: +* [Handling Errors][errors] +* [Interrupting Execution][exit-early] + +Note than with Wasmer 1.0.0, each function that is part of the API has its own kind of error. For example: +* Instantiating a module may return `InstantiationError`s; +* Getting exports from the guest module may return `ExportError`s; +* Calling a exported function may return `RuntimeError`s; +* ... + +### Caching modules + +You may be aware Wasmer, since 0.x, allows you to cache compiled module so that further executions of your program +will be faster. + +Because caching may bring a significant boost when running Wasm modules we wanted to make it easier to use with +Wasmer 1.0.0. + +With Wasmer 0.x you had to handle the whole caching process inside your program's code. With Wasmer 1.0.0 +you'll be able to delegate most of the work to Wasmer: + +```diff +- let artifact = module.cache().unwrap(); +- let bytes = artifact.serialize().unwrap(); +- +- let path = "module_cached.so"; +- fs::write(path, bytes).unwrap(); ++ module.serialize_to_file(path)?; + +- let mut file = File::open(path).unwrap(); +- let cached_bytes = &mut vec![]; +- file.read_to_end(cached_bytes); +- drop(file); +- +- let cached_artifact = Artifact::deserialize(&cached_bytes).unwrap(); +- let cached_module = unsafe { load_cache_with(cached_artifact, &default_compiler()) }.unwrap(); ++ let cached_module = unsafe { Module::deserialize_from_file(&store, path) }?; +``` + +[examples]: https://docs.wasmer.io/integrations/examples +[wasmer]: https://crates.io/crates/wasmer/1.0.0-alpha3 +[wasmer-wasi]: https://crates.io/crates/wasmer-wasi/1.0.0-alpha3 +[wasmer-emscripten]: https://crates.io/crates/wasmer-emscripten/1.0.0-alpha3 +[wasmer-engine]: https://crates.io/crates/wasmer-engine/1.0.0-alpha3 +[wasmer-compiler]: https://crates.io/crates/wasmer-compiler/1.0.0-alpha3 +[wasmer.io]: https://wasmer.io +[wasmer-nightly]: https://github.com/wasmerio/wasmer-nightly/ +[getting-started]: https://docs.wasmer.io/ecosystem/wasmer/getting-started +[instance-example]: https://docs.wasmer.io/integrations/examples/instance +[imports-exports-example]: https://docs.wasmer.io/integrations/examples/imports-and-exports +[host-functions-example]: https://docs.wasmer.io/integrations/examples/host-functions +[memory]: https://docs.wasmer.io/integrations/examples/memory +[memory-pointers]: https://docs.wasmer.io/integrations/examples/memory-pointers +[host-functions]: https://docs.wasmer.io/integrations/examples/host-functions +[errors]: https://docs.wasmer.io/integrations/examples/errors +[exit-early]: https://docs.wasmer.io/integrations/examples/exit-early \ No newline at end of file