Ditulisan sebelumnya kita sudah membahas "sedikit" tentang WebAssembly, ditulisan ini kita akan mulai berkenalan dengan Wasm karena ku anggap kalian—para pembaca—tertarik dengan Wasm.

Wasm adalah format instruksi binary untuk stack-based virtual machine, sebuah abstraksi dari komputer yang "ber-perilaku" seperti mesin sungguhan. Salah satu contoh populer nya adalah JVM, yang ditulisan sebelumnya pernah kita bahas sedikit.

Wasm di-desain se-portable mungkin untuk target kompilasi dari bahasa progam high-level seperti C, C++, atau Rust. Jika sebelumnya C/C++/Rust target kompilasinya hanya ke sistem operasi, dengan Wasm sekarang target kompilasi tersebut bisa dieksekusi di dunia web.

Yeay or Nay?

Kita tidak akan membahas banyak seputar Wasm yang sudah ditulis di situs resmi nya, namun sebagai motivasi, fitur-fitur yang dijanjikan oleh Wasm antara lain:

Ditulisan sebelumnya kita pernah membahas sedikit seputar efisiensi & kecepatan (performa) di web, dan Wasm hadir untuk menyelesaikan masalah tersebut (jika kamu memiliki masalahnya).

Show me some hello world!

Tidak se-sederhana itu, sayang.

WebAssembly adalah format instruksi binary, kita harus menulis kode high-level yang bisa di-compile menjadi format instruksi tersebut. Tapi tenang, WebAssembly memiliki WAT (Web Assembly Text) yang syntax terlihat seperti ehm pseudo-code dan lisp.

Kita coba menampilkan hello world di Wasm, ini kode WAT nya:

(module
  (memory (export "memory") 1)

  (data (i32.const 0) "hello world")

  (global (export "length") i32 (i32.const 11))
  (global (export "position") i32 (i32.const 0))
)

Hahaha kode apa itu ya? Kita bahas dikit-dikit.

Memory Allocation

Pertama, kita alokasikan memory sekitar 64KB atau 65,536 Bytes (1Byte = 8 bit).

[ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]  ...   [ ]

 0   1   2   3   4   5   6   7   8   9   10       65535

Lalu kita "push" string "hello world" dari lokasi 0 sampai "hello world".length()

[h] [e] [l] [l] [o] [ ] [w] [o] [r] [l] [d]  ...   [ ]

 0   1   2   3   4   5   6   7   8   9   10       65535

Bagaimana bila data yang dimasukkan melebihi memory sudah di alokasikan?

Coba ubah "hello world" menjadi string yang di-generate oleh kode berikut:

Array.from(Array(15329).keys()).toString().replace(/\,/g,'') + '666'

Nanti akan ada pesan error seperti data segment does not fit in memory.

Lanjut, length dibutuhkan untuk menampilkan "cursor" terakhir dari string hello world dan position untuk "meng-point cursor" awal yang ingin ditunjuk.

JavaScript part

Sekarang kita sudah memiliki memory dari memory, cursor awal, dan cursor akhir. Mari kita ke bagian JavaScript. Kita sudah meng-export nya agar bisa di akses oleh JavaScript, kode WAT diatas bila kita instanisasi di JavaScript (via new WebAssembly.Instance), akan membentuk object:

WebAssembly.Instance: {
  exports: {
    length: WebAssembly.Global { value: 11 }
    memory: WebAssembly.Memory { buffer: ArrayBuffer }
    position: WebAssembly.Global { value: 0 }
  }
}

Jadi kita bisa meng-import nya via:

const { memory, length, position } = wasmInstance.exports

console.log(memory) // { buffer: ArrayBuffer { byteLength: 65536 } }
console.log(length) // 11
console.log(position) // 0

Untuk bisa melhat nilai yang ada di memory.buffer kita bisa membuat array baru menggunakan Uint8Array yang mana sebuah array yang merepresentasikan sebuah array dengan nilai 8-bit integer.

Kita menggunakan Uint8Array karena data array yang kita tunjuk berjenis ArrayBuffer.

Sekarang, mari kita lihat sekilas nilai yang dihasilkan:

const data = new Uint8Array(memory.buffer)

console.log(data) // [104,101,108,108,111,32,119,111,114,108,100,0,...0]
console.log(data.length) // 65536

Sudah ada yang menarik?

Jika belum, nilai ini: 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100 adalah bentuk dari decimal (bytes) yang jika diterjemahkan menjadi text adalah "hello world"

Table

Yang mana, string "hello world" tersebut disimpan memory langsung.

Ini pratinjau nya:

Sudah oke lah ya untuk pembahasan hello world kita?

So I NeEd To WrItE WaT?

Tentu tidak.

Bahasa high-level (but lower-level) seperti C, C++, dan Rust mendukung target kompilasi menjadi file wasm. Kita bisa menggunakan contoh Rust disini, tapi tidak akan se-simple contoh di wat karena pertama kita tidak tau string "hello world" berada di memory yang mana dan kedua kita tidak bisa mengaturnya semerdeka ketika menggunakan wat, dan ketiga wasm_bindgen tidak mendukung variable.

Jadi, kita simpan string "hello world" tersebut disebuah function bernama hello_world yang nantinya akan kita panggil di file JS kita.

#![feature(proc_macro, wasm_custom_section, wasm_import_module)]

extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn hello_world() -> String {
  format!("hello world")
}

Sekarang, daripada kita ngambil alamat lokasi dari memory, kita bisa langsung memanggil fungsi hello_world tersebut. Di contoh ini menggunakan WebAssembly Studio, namun penggunaan nya harusnya tidak jauh beda dengan contoh dibawah.

wasm_bindgen('../out/main_bg.wasm').then(() => {
  const { hello_world } = wasm_bindgen

  document.getElementById('container').textContent = hello_world()
})

Dan ini hasilnya:

Sama, kan? Tapi file .wasm nya memiliki konten berbeda dari yang sebelumnya karena sudah tercampuri oleh hal-hal yang berkaitan dengan Rust.

Juga, di wasm_bindgen meng-alokasikan memori sekitar 1MB, yang berarti kita bisa menyimpan sekitar ~1,000,000 data ke memori.

Bahasa yang mendukung WebAssembly

Yang stabil dan relatif banyak digunakan antara lain C, C++, C#, Rust, Lua, Go dan TypeScript (via AssemblyScript).

Jadi, jika ingin membuat "file" ber-ekstensi .wasm selain menggunakan WAT, kamu bisa menggunakan bahasa-bahasa tersebut di project kamu, yang artinya, kamu/tim kamu harus menguasai bahasa pemrograman tersebut.

Contoh-contoh project yang ditenagai WebAssembly antara lain Squoosh (client-side image compression by chromelabs team), SketchUp Web (SketchUp Web Editor by Trimble, ref), PSPDFKit Web (client-side PDF editor by PSPDFKit team, ref), dan masih banyak lagi.

Penutup

Dengan menggunakan Wasm, kode yang dieksekusi lebih optimal & cepat karena sudah berbentuk "binary instruction". Kita bisa membuat file wasm untuk proses-proses yang biasa dilakukan di backend namun sebenarnya bisa dilakukan tanpa backend (seperti image editor, image compression, game, dsb).

Atau juga, bisa untuk fungsionalitas-fungsionalitas yang performa nya lebih cepat bila menggunakan native-implementation seperti kripto, grafis, games dan sebagainya.

Gue optimis dengan Wasm, Mozilla & Google (pemain besar di dunia web) optimis dengan Wasm, tapi Apple meskipun mendukung Wasm sayangnya belum terlihat optimisnya terhadap Wasm (IMO).

Wasm is a future, and at evilfactorylabs, the future is now.

Thanks for stopping by.