Available numeric types:
- i32
- i64
- f32
- f64
(module
(func $sum (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add
)
(export "sum" (func $sum))
)Show assembly code from WAT code from above snippet:
xxd -g1 -c8 sum.wasm
for b in $(xxd -ps -c1 sum.wasm); do echo -n "0x$b, "; doneCheck how many bites program have:
stat sum.wasmCheck WebAssembly code in web site https://webassembly.github.io/wabt/demo/wat2wasm
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Hello world</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>hello world</h1>
<script>
function init() {
alert("Hi There!")
}
init()
</script>
</body>
</html>Create int8 array and paste bytes from wasm. Put wasm function to the site and download code. Take bytes using xxd -g1 sum.wasm In browser byteArray will be in decimal format insead of hex decimal
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Hello world</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>hello world</h1>
<script>
async function init() {
const byteArray = new Int8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07, 0x01, 0x03, 0x73, 0x75, 0x6d, 0x00, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b, 0x00, 0x18, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x01, 0x06, 0x01, 0x00, 0x03, 0x73, 0x75, 0x6d, 0x02, 0x09, 0x01, 0x00, 0x02, 0x00, 0x01, 0x61, 0x01, 0x01, 0x62])
const wasm = await WebAssembly.instantiate(byteArray.buffer);
debugger
const sumFunction = wasm.instance.exports.sum;
const result = sumFunction(10, 50);
console.log(result);
}
init()
</script>
</body>
</html>npm init -y
npm install --save webpack webpack-cli webpack-dev-server
cat <<EOF > .gitignore
node_modules
EOF
const path = require("path");
module.exports = {
entry: () => "./index.js",
output: {
path: path.resolve(__dirname, "public"),
filename: "index.js"
},
mode: "development"
}async function init() {
const byteArray = new Int8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07, 0x01, 0x03, 0x73, 0x75, 0x6d, 0x00, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b, 0x00, 0x18, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x01, 0x06, 0x01, 0x00, 0x03, 0x73, 0x75, 0x6d, 0x02, 0x09, 0x01, 0x00, 0x02, 0x00, 0x01, 0x61, 0x01, 0x01, 0x62])
const wasm = await WebAssembly.instantiate(byteArray.buffer);
const sumFunction = wasm.instance.exports.sum;
const result = sumFunction(10, 60);
console.log(result);
}
init()<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Hello world</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>hello world</h1>
<script src="./index.js"></script>
</body>
</html>Add to package.json
"script": {
"dev": "webpack-dev-server"
"build": "webpack build"
}
Keep only static files in public. index.html should be in www
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Hello world</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>hello world</h1>
<script src="./index.js"></script>
</body>
</html>Install copy webpack plugin
npm install --save copy-webpack-pluginconst path = require("path");
const CopyWebpackPlugin = require("copy-webpack-plugin");
module.exports = {
entry: () => "./index.js",
output: {
path: path.resolve(__dirname, "public"),
filename: "index.js"
},
mode: "development",
plugins: [
new CopyWebpackPlugin({
patterns: [
{ from: "./index.html", to: "./" }
]
})
]
}async function init() {
const response = await fetch("sum.wasm");
const buffer = await response.arrayBuffer();
const wasm = await WebAssembly.instantiate(buffer);
const sumFunction = wasm.instance.exports.sum;
const result = sumFunction(100, 60);
console.log(result);
}
init()async function init() {
const importObject = {
console: {
log: () => {
console.log("Just logging something!");
},
error: () => {
console.log("I am just error");
}
}
}
const response = await fetch("sum.wasm");
const buffer = await response.arrayBuffer();
const wasm = await WebAssembly.instantiate(buffer, importObject);
const sumFunction = wasm.instance.exports.sum;
debugger
const result = sumFunction(100, 60);
console.log(result);
}
init()(module
(import "console" "log" (func $log))
(import "console" "error" (func $error))
(func $sum (param $a i32) (param $b i32) (result i32)
call $log
call $error
local.get $a
local.get $b
i32.add
)
(export "sum" (func $sum))
)Memory can be crated in WebAssembly and exported to JS Memory can be crated in JS and exported to WebAssembly
memory 1 means 1 page of memory. Page has around 64KB
Exporting memory. (export “mem” (memory 0)) - mem -handler in js. (memory 0) means export first created memory, so (memory 1) - line 4 from below snippet.
(module
(import "console" "log" (func $log))
(import "console" "error" (func $error))
(memory 1)
(func $sum (param $a i32) (param $b i32) (result i32)
call $log
call $error
local.get $a
local.get $b
i32.add
)
(export "mem" (memory 0))
(export "sum" (func $sum))
)Create alias for memory Insted of (export “mem” (memory 0)) in line 4 there is alias $mem and we can use it as (export “mem” (memory $mem))
(module
(import "console" "log" (func $log))
(import "console" "error" (func $error))
(memory $mem 1)
(func $sum (param $a i32) (param $b i32) (result i32)
call $log
call $error
local.get $a
local.get $b
i32.add
)
(export "mem" (memory $mem))
(export "sum" (func $sum))
)Load to memory
(data (i32.const 0) “Hi”) - At index 0 in memory put “Hi”
(module
(import "console" "log" (func $log))
(import "console" "error" (func $error))
(memory $mem 1)
(data (i32.const 0) "Hi")
(func $sum (param $a i32) (param $b i32) (result i32)
call $log
call $error
local.get $a
local.get $b
i32.add
)
(export "mem" (memory $mem))
(export "sum" (func $sum))
)Access memory from js Create array of two bits. Because all 64KB are empty, thats why only 2 bits. Decode array to plain text.
async function init() {
const importObject = {
console: {
log: () => {
console.log("Just logging something!");
},
error: () => {
console.log("I am just error");
}
}
}
const response = await fetch("sum.wasm");
const buffer = await response.arrayBuffer();
const wasm = await WebAssembly.instantiate(buffer, importObject);
const sumFunction = wasm.instance.exports.sum;
const wasmMemory = wasm.instance.exports.mem;
const uint8Array = new Uint8Array(wasmMemory.buffer, 0, 2);
const hiText = new TextDecoder().decode(uint8Array);
console.log(hiText);
}
init()const memory = new WebAssembly.Memory({initial: 1}); Create a memory in js. This memory will be read by WebAssembly. In WebAssembly we put “Hello”. Back in JS we can read this memory.
async function init() {
const memory = new WebAssembly.Memory({initial: 1});
const importObject = {
js: {
mem: memory
},
console: {
log: () => {
console.log("Just logging something!");
},
error: () => {
console.log("I am just error");
}
}
}
const response = await fetch("sum.wasm");
const buffer = await response.arrayBuffer();
const wasm = await WebAssembly.instantiate(buffer, importObject);
const uint8Array = new Uint8Array(memory.buffer, 0, 5);
const hiText = new TextDecoder().decode(uint8Array);
console.log(hiText);
}
init()(module
(import "console" "log" (func $log))
(import "console" "error" (func $error))
(memory (import "js" "mem") 1)
(data (i32.const 0) "Hello")
(func $sum (param $a i32) (param $b i32) (result i32)
call $log
call $error
local.get $a
local.get $b
i32.add
)
(export "sum" (func $sum))
)cat << EOF > Cargo.toml
[package]
name = "wa-snake"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
wasm-bindgen = "0.2.83"
[lib]
crate-type = ["cdylib"]
EOF#[wasm_bindgen] macro for sharing for JS
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn great(name: &str) {
println!("Hi there {}", name);
}cargo install wasm-packwasm-pack build --target web
npm installInit is exported as default. Greet is exported as function.
import init, { great } from "wa-snake";
async function start() {
const wasm = await init();
great("Wojtek");
console.log("Hello");
}
start()Alternative
import init from "wa-snake";
async function start() {
const wasm = await init();
wasm.great("Wojtek");
console.log("Hello");
}
start()Alternative
import init, { great } from "wa-snake";
init().then(wasm => {
wasm.great("Wojtek");
console.log("Hello");
})Add ability to print to web browser console from rust Run external code from rust using extern
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn great(name: &str) {
alert(name);
}
#[wasm_bindgen]
extern {
pub fn alert(s: &str);
}import("./index.js").catch(e => console.error("Error importing index.js :", e))Change webpack for bootstraping
const path = require("path");
module.exports = {
entry: () => "./bootstrap.js",
output: {
path: path.resolve(__dirname, "public"),
filename: "bootstrap.js"
},
mode: "development"
}Change index.html for bootstraping
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Hello world</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>hello world</h1>
<script src="./bootstrap.js"></script>
</body>
</html>cat << EOF > Cargo.toml
[package]
name = "wa-snake"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
wasm-bindgen = "0.2.83"
wee_alloc = "0.4.5"
[lib]
crate-type = ["cdylib", "rlib"]
EOFuse wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn great(name: &str) {
alert(name);
}
#[wasm_bindgen]
extern {
pub fn alert(s: &str);
}wasm-pack build --target web