Skip to content

chodkows/ws-snake

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Readme

Raw WebAssembly

Numeric types

Available numeric types:

  • i32
  • i64
  • f32
  • f64

WebAssembly function

(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, "; done

Check how many bites program have:

stat sum.wasm

Check WebAssembly code in web site https://webassembly.github.io/wabt/demo/wat2wasm

Run webassembly in the browser

Run simple js function

<!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>

Run wasm function in 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>

Run webassebly using webpack

npm init -y
npm install --save webpack webpack-cli webpack-dev-server
cat <<EOF > .gitignore
node_modules
EOF

Webpack configuration

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"
}

Webpack config plugin

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-plugin
const 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: "./" }
        ]
      })
    ]
}

Fetch wasm

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()

Import wasm

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))
)

WebAssembly memory

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()

JS memory

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))
)

Preparing the Game

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-pack
wasm-pack build --target web

npm install

Init 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);
}

Bootstrap js

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>

Wasm size - wee_alloc

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"]
EOF
use 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

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors