WEBASSEMBLY WASM WAT LOW-LEVEL-PROGRAMMING BROWSER PERFORMANCE WEB-PERFORMANCE JAVASCRIPT COMPILER-INTERNALS WEB-DEVELOPMENT DEEP-DIVE

Memahami WebAssembly Text Format (WAT): Menggali Bahasa Mesin Web Anda

⏱️ 12 menit baca
👨‍💻

Memahami WebAssembly Text Format (WAT): Menggali Bahasa Mesin Web Anda

1. Pendahuluan

Sebagai developer web modern, kita sering berhadapan dengan JavaScript. Namun, pernahkah Anda merasa bahwa ada batasan performa yang tidak bisa ditembus oleh JavaScript, terutama untuk tugas-tugas komputasi berat seperti pemrosesan gambar, video, game, atau simulasi ilmiah? Di sinilah WebAssembly (Wasm) hadir sebagai game-changer. Wasm memungkinkan kita menjalankan kode yang mendekati performa native langsung di browser, membuka gerbang bagi aplikasi web yang jauh lebih powerful dan responsif.

Tapi, Wasm itu sendiri adalah format biner. Sulit dibaca dan dipahami manusia. Bayangkan Anda harus mendebug file .exe atau .dll secara langsung tanpa disassembler! Nah, di sinilah WebAssembly Text Format (WAT) berperan. WAT adalah representasi tekstual dari modul WebAssembly, semacam “bahasa assembly” yang bisa dibaca manusia.

🎯 Artikel ini akan membawa Anda menyelami dunia WAT. Kita akan belajar apa itu WAT, bagaimana strukturnya, tipe data yang digunakannya, instruksi dasar, dan bagaimana ia berinteraksi dengan JavaScript. Memahami WAT tidak hanya akan memperdalam pemahaman Anda tentang WebAssembly, tetapi juga memberikan wawasan berharga tentang bagaimana kode berperforma tinggi bekerja di web.

2. Apa Itu WebAssembly Text Format (WAT)?

Bayangkan Anda memiliki sebuah program yang ditulis dalam bahasa C++. Setelah dicompile, program tersebut akan menjadi file biner .wasm. File biner ini sangat efisien untuk dieksekusi oleh mesin, tetapi sama sekali tidak bisa dibaca oleh manusia. Di sinilah WAT datang sebagai penyelamat.

WAT adalah format teks yang merepresentasikan modul WebAssembly secara human-readable. Jadi, jika Anda punya file .wasm, Anda bisa mengonversinya kembali menjadi .wat untuk melihat instruksi-instruksi assembly di dalamnya. Ini mirip dengan cara Anda bisa melihat kode assembly dari sebuah program C++ yang sudah dicompile.

Keuntungan Memahami WAT:

Sintaksis WAT menggunakan apa yang disebut S-expressions (Symbolic Expressions), yang mungkin familiar bagi Anda yang pernah berinteraksi dengan bahasa seperti Lisp. Ini berarti kode dikelilingi oleh tanda kurung, seperti (module ...) atau (func ...).

3. Struktur Dasar Modul WAT

Setiap file WAT mendefinisikan sebuah module. module adalah unit dasar dalam WebAssembly yang berisi semua definisi fungsi, variabel global, memori, dan lain-lain.

Berikut adalah struktur dasar sebuah modul WAT:

(module
  ;; Bagian-bagian modul ada di sini
  ;; (tipe, fungsi, memori, tabel, global, import, export)
)

Mari kita bedah bagian-bagian pentingnya:

4. Tipe Data dan Instruksi Dasar

WebAssembly adalah mesin berbasis stack. Artinya, semua operasi dilakukan dengan mendorong nilai ke stack (push) dan mengambil nilai dari stack (pop).

Tipe Data Numerik

Wasm memiliki empat tipe data dasar untuk angka:

⚠️ Perhatikan bahwa tidak ada tipe data string atau boolean secara langsung di Wasm. String direpresentasikan sebagai array byte dalam memori linear, dan boolean biasanya direpresentasikan sebagai i32 (0 untuk false, 1 untuk true).

Instruksi Stack-Based

Instruksi Wasm umumnya mengikuti pola tipe.operasi.

📌 Contoh Sederhana: Fungsi Penambahan Dua Angka

Mari kita buat fungsi add dalam WAT yang menerima dua integer 32-bit dan mengembalikan hasil penjumlahannya.

(module
  (func (export "add") (param $a i32) (param $b i32) (result i32)
    local.get $a ;; Mendorong nilai parameter $a ke stack
    local.get $b ;; Mendorong nilai parameter $b ke stack
    i32.add      ;; Mengambil $b dan $a dari stack, menjumlahkan, mendorong hasilnya ke stack
  )
)

Bagaimana cara kerja i32.add?

  1. local.get $a: Stack: [nilai_a]
  2. local.get $b: Stack: [nilai_a, nilai_b]
  3. i32.add: Mengambil nilai_b dan nilai_a dari stack, menjumlahkannya (nilai_a + nilai_b), lalu mendorong hasilnya kembali ke stack. Stack: [hasil] Karena ini adalah instruksi terakhir dan fungsi memiliki (result i32), nilai teratas di stack (hasil) akan menjadi nilai kembalian fungsi.

5. Mengelola Memori dengan WAT

Salah satu kekuatan WebAssembly adalah kemampuannya untuk berinteraksi dengan memori linear. Ini adalah array byte yang bisa dibagi antara Wasm dan JavaScript, memungkinkan transfer data yang efisien.

💡 Contoh: Menyimpan dan Memuat dari Memori

Mari kita buat fungsi yang menyimpan integer ke memori pada offset tertentu, lalu memuatnya kembali.

(module
  (memory (export "mem") 1) ;; Export memori 1 halaman (64KB)

  (func (export "storeAndLoad") (param $offset i32) (param $value i32) (result i32)
    ;; Simpan $value ke memori pada $offset
    local.get $offset  ;; Mendorong offset ke stack: [$offset]
    local.get $value   ;; Mendorong value ke stack: [$offset, $value]
    i32.store          ;; Mengambil $value dan $offset, menyimpan $value di memori[$offset]. Stack kosong.

    ;; Muat kembali nilai dari $offset
    local.get $offset  ;; Mendorong offset ke stack: [$offset]
    i32.load           ;; Mengambil $offset, membaca memori[$offset], mendorong hasilnya ke stack: [loaded_value]
  )
)

6. Interaksi dengan JavaScript: Import dan Export

Modul WebAssembly tidak hidup dalam isolasi. Mereka perlu berinteraksi dengan lingkungan host (biasanya JavaScript di browser). Ini dilakukan melalui mekanisme import dan export.

📌 Contoh Interaksi Lengkap

Mari kita buat modul WAT yang mengekspor fungsi add dan storeAndLoad, serta mengimpor fungsi log dari JavaScript.

;; file: my_module.wat
(module
  ;; Import fungsi log dari JavaScript
  (import "js" "log" (func $log_message (param i32)))

  ;; Definisikan memori
  (memory (export "mem") 1)

  ;; Fungsi penambahan
  (func (export "add") (param $a i32) (param $b i32) (result i32)
    local.get $a
    local.get $b
    i32.add
    call $log_message ;; Panggil fungsi log dari JS dengan hasil penjumlahan
  )

  ;; Fungsi simpan dan muat memori
  (func (export "storeAndLoad") (param $offset i32) (param $value i32) (result i32)
    local.get $offset
    local.get $value
    i32.store
    local.get $offset
    i32.load
  )
)

Sekarang, bagaimana kita memuat dan menjalankan ini di JavaScript?

// index.js
async function loadWasm() {
  // Konversi WAT ke binary .wasm (biasanya ini dilakukan oleh toolchain)
  // Untuk demo, kita bisa pakai wabt (WebAssembly Binary Toolkit) atau online converter
  // Di sini kita asumsikan sudah ada my_module.wasm
  const watCode = `
    (module
      (import "js" "log" (func $log_message (param i32)))
      (memory (export "mem") 1)
      (func (export "add") (param $a i32) (param $b i32) (result i32)
        local.get $a
        local.get $b
        i32.add
        call $log_message
      )
      (func (export "storeAndLoad") (param $offset i32) (param $value i32) (result i32)
        local.get $offset
        local.get $value
        i32.store
        local.get $offset
        i32.load
      )
    )
  `;

  // Untuk menjalankan WAT langsung di browser tanpa kompilasi eksternal,
  // kita perlu mengonversinya ke binary terlebih dahulu.
  // Ini bisa dilakukan dengan tool seperti `wat2wasm` dari Wabt.
  // Untuk contoh sederhana ini, kita akan menganggap `watCode` ini sudah dikompilasi
  // menjadi array buffer atau bisa langsung di-instantiate oleh WebAssembly.instantiate.
  // Namun, WebAssembly.instantiate tidak menerima string WAT, melainkan array buffer binary WASM.
  // Jadi, kita perlu step kompilasi WAT ke WASM binary.
  // Untuk tujuan edukasi, kita akan menyederhanakan dengan menganggap
  // kita memiliki cara untuk mendapatkan binary dari WAT string.

  // Realistisnya, Anda akan memuat file .wasm:
  // const response = await fetch('my_module.wasm');
  // const buffer = await response.arrayBuffer();
  // const module = await WebAssembly.compile(buffer);

  // Atau, untuk demo WAT string, kita bisa menggunakan TextEncoder dan WebAssembly.Module
  // yang secara teknis membutuhkan format binary, jadi kita harus "menulis" binary-nya
  // atau menggunakan library. Untuk tutorial ini, kita akan melewati detail kompilasi WAT-to-WASM
  // dan fokus pada interaksi setelah modul siap.
  // Jika Anda ingin mencoba, Anda bisa menggunakan online WAT to WASM converter,
  // atau menginstal `wabt` dan menjalankan `wat2wasm my_module.wat -o my_module.wasm`.

  // Anggap `wasmBytes` adalah ArrayBuffer dari `my_module.wasm`
  // Untuk demo ini, mari kita gunakan contoh WAT yang lebih sederhana yang bisa dikompilasi on-the-fly
  // (meskipun ini bukan cara standar di browser, tapi untuk ilustrasi)
  // Sebenarnya WebAssembly.Module hanya menerima binary, bukan text format.
  // Untuk demo langsung di JS, kita perlu library