Title: [WebAssembly introduction] Hello, world!
date: 2018-3-29 14:45:00
Categories: WebAssembly, notes
tags: WebAssembly, JavaScript, Rust, LLVM toolchain
auther: Yiniau
[WebAssembly introduction] Hello, world!
After a while of learning the basics, it’s time to start programming WebAssembly in earnest!
I spent about two months learning Rust and writing a Sudoku game out of it. So it was natural for me to choose Rust -> LLVM Toolchain -> WebAssembly.
To prepare
First, create a new Lib project through Cargo
cargo new hello_world --lib && cd hello_world
Copy the code
Second, you need an HTML to host the JS code and display the effects
vim index.html
Copy the code
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebAssembly</title>
</head>
<body>
<script src="main.js"></script>
</body>
</html>
Copy the code
Third, create the JS file
vim main.js
Copy the code
The adventure begins!
Before we start. Confirm the current status of the catalog
ll .
-rw-r--r-- 1 yiniau staff 52B Mar 25 22:14 Cargo.lock
-rw-r--r-- 1 yiniau staff 112B Mar 25 22:14 Cargo.toml
-rw-r--r-- 1 yiniau staff 165B Mar 29 15:00 index.html
-rw-r--r-- 1 yiniau staff 743B Mar 29 15:00 main.js
drwxr-xr-x 3 yiniau staff 96B Mar 29 14:58 src
ll src
-rw-r--r-- 1 yiniau staff 494B Mar 29 14:35 lib.rs
Copy the code
From previous translations we know that we can fetch.wasm files and compile them by converting them to typed arrays or array buffers and passing them to Webassembly.instantiate but we don’t have a.wasm file yet, The immediate task is to edit the Rust code in lib.rs and compile the output.wasm file through RUSTC
Rustup tool chain please search for installation, very simple.
Thousands of troops, annotation first
/ /! hello world with WebAssembly
Copy the code
As a small step to start, we’ll start with the simplest ———— in WebAssembly we call the functions passed from JS and their closures, with the main logic encapsulated in the JS functions
Declare imports to import resources
We can do this by adding environment attributes to the Imports import resource, starting by creating a Imports object
const imports = {
env: {}}Copy the code
Add functions/closures
Add the function you want to pass to the env attribute
env: {
function hello_world() {
const h1 = document.createElement('h1');
h1.innerHTML = 'Hello, world';
const body = document.querySelector('body'); body.appendChild(h1); }}Copy the code
Then let’s move on to Lib.rs
Declare external functions
To use JS functions, we need to use FFI, declaring external functions in extern first
/ /! a WebAssembly module with Rust
extern {
fn hello_world(a); }Copy the code
This can be called in the body of the function
WebAssembly function body
Implement the function interface called in JS
/// call js function to access DOM
#[no_mangle]
pub extern fn hello_call_js() { // equal pub extern "C" fn ...
unsafe{ hello_insert_dom(); }}Copy the code
Ok, so far lib.rs should look like this
/ /! a WebAssembly module with Rust
extern {
fn hello_insert_dom(a); }/// return "Hello, world"'s bytes array
#[no_mangle]
pub extern fn hello_call_js() { // equal pub extern "C" fn ...
unsafe{ hello_insert_dom(); }}Copy the code
At this point, rust is ready, and the next step is compilation
compile
Enter in ZSH
mkdir build
rustc +nightly --target=wasm32-unknown-unknown -O --crate-type=cdylib src/lib.rs -o build/hello.wasm
# +nightly Indicates the use of the nightly version
# --target specifies the compiler
# -O == -C opt-level=2
# -c opt-level=2 Specifies the optimization level as 2. The range is 0-3
# --create-type=cdylib Adds a crate type accepted by the compiler, called cdylib, which corresponds to the C interface exported from the Rust dynamic library.
# -o Specifies the output file name
Copy the code
// TODO: Record the first question, what is a dynamic library and what is the use of this library.
DONE!!
We can find the Hello. wasm file in the build/ directory
If you want to see what it looks like, you can use hexdump, okay
Import the. Wasm file in JS and use it
fetch('build/hello.wasm') // Get a binary file in hexadecimal format
.then(res= > res.arrayBuffer()) // Put it into the array buffer
.then(bytes= > WebAssembly.instantiate(bytes, imports)) // Pass the binary data to the JIT for processing, along with imports to import resources
.then(results= > { // There are two attributes in the result returned
// One is' module ', which is compiled for 'webassembly.module'
// One is' instance ', 'webassembly. instance', which is the first instance of webassembly. module
const exports = results.instance.exports; // instance.exports carries functions declared by pub extern in rust.
exports.hello_call_js(); / / call
})
Copy the code
Our main.js should now look like this:
const imports = {
env: {
hello_insert_dom: (a)= > {
const h1 = document.createElement('h1');
h1.innerHTML = 'Hello, world';
const body = document.querySelector('body'); body.appendChild(h1); }}}; fetch('build/hello.wasm')
.then(response= > response.arrayBuffer())
.then(bytes= > WebAssembly.instantiate(bytes, imports))
.then(results= > {
console.log(results);
const exports = results.instance.exports;
exports.hello_call_js();
// exports.hello_call_js_pass_data();
});
Copy the code
Testing, exciting!
Start with a local service. I use Lighttpd
localhost!
What’s next
Hello_world doesn’t end there, and to learn more about WebAssembly I’m going to practice WebAssembly by implementing Hello_world in various ways
E.g. Hello, world is implemented by passing arguments to JS functions in WebAssembly