Skip to content
This repository has been archived by the owner on Mar 23, 2020. It is now read-only.

WebAssembly chapter in english #51

Merged
merged 2 commits into from
May 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 198 additions & 0 deletions presentation/chapters/en-US/wasm.chapter
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# WebAssembly
[Table of Contents](toc/english.html)

---

## What?

WebAssembly enables running Rust (among others) in Javascript environments like the web browser.

It is the successor to asm.js in many ways.

It is currently a developing standard and is often not enabled by default.

---

## Gotcha

WebAssembly is still not widely supported and has a number of rough edges.

---

## Installing

Setup is a bit unrefined at this moment, but it should improve in the future.

* Fetch `emsdk` from [emscripten](http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html).
* Unpack it somewhere sensible.
* Navigate to the directory in your terminal.

---

## Installing: `emcc`

<pre><code data-source="chapters/shared/code/wasm/1.bash" data-trim="hljs bash"></code></pre>

The output of the third command will offer instructions for what to add to `$PATH` if desired.

> We use `incoming` to utilize the latest refinements.

---

## Installing: `emcc`

The versions of the toolchain are quite important. Verify there are no errors running the following:

<pre><code data-source="chapters/shared/code/wasm/2.bash" data-trim="hljs bash"></code></pre>

---

## Installing: `rustup` Target

`rustup` allows installing multiple compilation targets.

<pre><code data-source="chapters/shared/code/wasm/3.bash" data-trim="hljs bash"></code></pre>

---

# Standalone Executable

---

## Standalone Executable

<pre><code data-source="chapters/shared/code/wasm/4.bash" data-trim="hljs bash"></code></pre>

<pre><code data-source="chapters/shared/code/wasm/5.rs" data-trim="hljs rust"></code></pre>

---

## Standalone Executable

<pre><code data-source="chapters/shared/code/wasm/6.bash" data-trim="hljs bash"></code></pre>

This will create a directory structure like so:

<pre><code data-source="chapters/shared/code/wasm/7.output" data-trim="hljs bash"></code></pre>

---

## Standalone Executable

Once we generate the `wasm` and `js` we want to place them in with a `site` folder. We can use a `Makefile` for this.

<pre><code data-source="chapters/shared/code/wasm/8.makefile" data-trim="hljs makefile"></code></pre>

---

## Standalone Executable

Create `site/index.html`:

<pre><code data-source="chapters/shared/code/wasm/9.html" data-trim="hljs html"></code></pre>

---

## Standalone Executable

Running `python -m SimpleHTTPServer` or equivalent, browsing to `localhost:8000/site`, and opening the console yield the following output:

<pre><code data-source="chapters/shared/code/wasm/10.output" data-trim="hljs bash"></code></pre>

---

# Rust from JS

---

## Rust from JS

Exporting functions for use in Javascript is a bit more complicated.

Additionally, interactions must be handled like interactions to C.

---

## Rust from JS

The nightly channel is currently necessary to get this to work properly:

<pre><code data-source="chapters/shared/code/wasm/11.bash" data-trim="hljs bash"></code></pre>

---

## Rust from JS

<pre><code data-source="chapters/shared/code/wasm/12.bash" data-trim="hljs bash"></code></pre>

<pre><code data-source="chapters/shared/code/wasm/13.rs" data-trim="hljs rust"></code></pre>

---

## Rust from JS

We can use the same Makefile as before.

<pre><code data-source="chapters/shared/code/wasm/8.makefile" data-trim="hljs makefile"></code></pre>

---

## Rust from JS

The `onRuntimeInitialized` hook for `Module` defines what is called after the WebAssembly is loaded.

<pre><code data-source="chapters/shared/code/wasm/14.html" data-trim="hljs html"></code></pre>

---

## Rust from JS

Running `python -m SimpleHTTPServer` or equivalent, browsing to `localhost:8000/site`, and opening the console yield the following output:

<pre><code data-source="chapters/shared/code/wasm/15.output" data-trim="hljs bash"></code></pre>

---

# JS from Rust

---

## JS from Rust

Calling JS code from Rust has similar complications.

It is done primarily through passing the `--js-library` flag at link time, which requires the nightly channel of rust.

Passing numerics is relatively simple, but passing more complex things like strings requires extra effort.

---

## JS from Rust

Returning a string for Rust code:

<pre><code data-source="chapters/shared/code/wasm/17.js" data-trim="hljs javascript"></code></pre>

---

## JS from Rust

Calling the Javascript function:

<pre><code data-source="chapters/shared/code/wasm/18.rs" data-trim="hljs rust"></code></pre>

---

## DOM Interaction

There is a [WebPlatform crate](https://github.com/tcr/rust-webplatform) to explore and contribute to.

<pre><code data-source="chapters/shared/code/wasm/19.rs" data-trim="hljs rust"></code></pre>

---

## Future

WebAssembly is rapidly becoming more refined and mature. Rust's integration is also under active work.

Keep your eyes peeled for more, better support!
4 changes: 4 additions & 0 deletions presentation/chapters/shared/code/wasm/1.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
./emsdk update
./emsdk install sdk-incoming-64bit
./emsdk activate sdk-incoming-64bit
source ./emsdk_env.sh
7 changes: 7 additions & 0 deletions presentation/chapters/shared/code/wasm/10.output
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
trying binaryen method: native-wasm
asynchronously preparing wasm
binaryen method succeeded.
run() called, but dependencies remain, so not running
pre-main prep time: 108 ms
South
false
2 changes: 2 additions & 0 deletions presentation/chapters/shared/code/wasm/11.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
rustup override set nightly
rustup target add wasm32-unknown-emscripten
1 change: 1 addition & 0 deletions presentation/chapters/shared/code/wasm/12.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cargo init wasm-demo --bin
23 changes: 23 additions & 0 deletions presentation/chapters/shared/code/wasm/13.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use std::os::raw::c_char;
use std::ffi::CString;
use std::collections::HashMap;

#[no_mangle]
pub fn get_data() -> *mut c_char {
let mut data = HashMap::new();
data.insert("Alice", "send");
data.insert("Bob", "receive");
data.insert("Carol", "intercept");

let descriptions = data.iter()
.map(|(p,a)| format!("{} likes to {} messages", p, a))
.collect::<Vec<_>>();

CString::new(descriptions.join(", "))
.unwrap()
.into_raw()
}

fn main() {
// Deliberately blank.
}
17 changes: 17 additions & 0 deletions presentation/chapters/shared/code/wasm/14.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<html>
<head>
<script>
// This is read and used by `site.js`
var Module = {
wasmBinaryFile: "site.wasm",
onRuntimeInitialized: main,
}
function main() {
let get_data = Module.cwrap('get_data', 'string', []);
console.log(get_data());
}
</script>
<script src="site.js"></script>
</head>
<body></body>
</html>
4 changes: 4 additions & 0 deletions presentation/chapters/shared/code/wasm/15.output
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
trying binaryen method: native-wasm
asynchronously preparing wasm
binaryen method succeeded.
Bob likes to receive messages, Alice likes to send messages, Carol likes to intercept messages
17 changes: 17 additions & 0 deletions presentation/chapters/shared/code/wasm/17.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use strict';

var library = {
get_data: function() {
var str = "Hello from JS.";
alert(str);

// Not needed for numerics.
var len = lengthBytesUTF8(str);
var buffer = Module._malloc(len);
Module.stringToUTF8(str, buffer, len);

return buffer;
},
};

mergeInto(LibraryManager.library, library);
26 changes: 26 additions & 0 deletions presentation/chapters/shared/code/wasm/18.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#![feature(link_args)]

#[cfg_attr(target_arch="wasm32", link_args = "\
--js-library site/utilities.js\
")]
extern {}

use std::os::raw::c_char;
use std::ffi::CStr;

extern {
fn get_data() -> *mut c_char;
}

fn get_data_safe() -> String {
let data = unsafe {
CStr::from_ptr(get_data())
};
data.to_string_lossy()
.into_owned()
}

fn main() {
let data = get_data_safe();
println!("{:?}", data);
}
24 changes: 24 additions & 0 deletions presentation/chapters/shared/code/wasm/19.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
extern crate webplatform;

fn main() {
let document = webplatform::init();
let body = document.element_query("body")
.unwrap();
body.html_append("\
<h1>This header brought to you by Rust</h1>\
<button>Click me!</button>\
");

let button = document.element_query("button")
.unwrap();
button.on("mouseenter", move |_| {
println!("Mouse entered!");
body.html_append("<p>Mouse entered!</p>");
});
button.on("click", |_| {
println!("Clicked!");
webplatform::alert("Clicked!");
});

webplatform::spin();
}
7 changes: 7 additions & 0 deletions presentation/chapters/shared/code/wasm/2.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
$ emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 1.37.9
clang version 4.0.0 (emscripten 1.37.9 : 1.37.9)
Target: x86_64-apple-darwin16.4.0
Thread model: posix
InstalledDir: /Users/$USER/emsdk/clang/e1.37.9_64bit
INFO:root:(Emscripten: Running sanity checks)
1 change: 1 addition & 0 deletions presentation/chapters/shared/code/wasm/3.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rustup target add wasm32-unknown-emscripten
1 change: 1 addition & 0 deletions presentation/chapters/shared/code/wasm/4.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cargo init wasm-demo --bin
16 changes: 16 additions & 0 deletions presentation/chapters/shared/code/wasm/5.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#[derive(Debug)]
pub enum Direction { North, South, East, West }

fn is_north(dir: Direction) -> bool {
match dir {
Direction::North => true,
_ => false,
}
}

fn main() {
let points = Direction::South;
println!("{:?}", points);
let compass = is_north(points);
println!("{}", compass);
}
1 change: 1 addition & 0 deletions presentation/chapters/shared/code/wasm/6.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cargo build --target=wasm32-unknown-emscripten --release
13 changes: 13 additions & 0 deletions presentation/chapters/shared/code/wasm/7.output
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
target
└── wasm32-unknown-emscripten
└── release
├── build
├── deps
│   ├── wasm_demo-9c23ae9a241f12fa.asm.js
│   ├── wasm_demo-9c23ae9a241f12fa.js
│   └── wasm_demo-9c23ae9a241f12fa.wasm
├── examples
├── incremental
├── native
├── wasm-demo.d
└── wasm-demo.js
Loading