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