Belajar WebAssembly - Bahasa Teks (WAT) & Sintaks
Episode 4 of 23

Belajar WebAssembly - Bahasa Teks (WAT) & Sintaks

Mendalami bahasa teks WebAssembly (WAT): struktur (module), (func), (export), (import), tipe (param)/(result), plus instruksi local.get/set, i32.const, aritmetika stack, call, dan deklarasi (memory) sebagai bekal menulis modul Wasm yang lebih kompleks

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

Setelah di episode 3 kita berhasil menjalankan modul Hello World pertama — satu fungsi add yang di-export — pada episode ini kita naik satu tingkat: menguasai bahasa teks WAT secara sungguhan. WAT adalah jendela paling jujur ke dalam WebAssembly: karena ia setara satu-ke-satu dengan bytecode binary, apa pun yang kalian tulis di WAT itulah yang benar-benar dieksekusi engine.

Mengapa memahami WAT penting? Sebagian besar kode produksi ditulis dalam Rust atau C/C++ lalu dikompilasi ke Wasm — tetapi setiap kali ada bug aneh, masalah ABI, atau optimasi yang gagal, kalian akan kembali membaca output .wat dari compiler untuk mencari tahu apa yang sebenarnya terjadi. WAT adalah bahasa assembly kalian untuk dunia Wasm.

Struktur Modul: (module)

Semua WAT dibungkus dalam satu bentuk (module ...). Di dalamnya bisa ada fungsi, memory, tabel, global, imports, dan exports — masing-masing dalam bentuk S-expression (ungkapan dengan tanda kurung).

struktur-modul.wat
(module
  (type $t (func (param i32) (result i32)))
  (import "env" "log" (func $log (param i32)))
  (func $f (type $t) (param $x i32) (result i32)
    local.get $x
    i32.const 1
    i32.add
  )
  (memory 1)
  (export "f" (func $f))
)

Modul di atas memakai hampir semua komponen yang akan kita bahas: sebuah type, satu import, satu fungsi, satu memory, dan satu export. Mari bedah satu per satu.

Fungsi: (func), (param), (result)

Fungsi adalah unit eksekusi utama. Bentuknya:

sintaks-fungsi.wat
(func $nama (param $a i32) (param $b i64) (result i64)
  ;; body: urutan instruksi
)
  • $nama — nama lokal yang hanya berlaku di dalam modul (opsional; tanpa nama juga valid).
  • (param $a i32) — mendeklarasikan parameter dengan nama dan tipe.
  • (result i64) — tipe kembalian. Wasm mendukung maksimal satu tipe kembalian (tanpa multi-value).
  • ;; — komentar gaya WAT, diabaikan oleh compiler.

Parameter dan Lokal: local.get / local.set

Di dalam body fungsi, parameter dan variabel lokal diakses lewat local.get (membaca) dan local.set (menulis):

local-get-set.wat
(func $ganti (param $nilai i32) (result i32)
  (local $tmp i32)
  local.get $nilai
  local.set $tmp
  local.get $tmp
  i32.const 100
  i32.add
)

Urutannya: baca $nilai → simpan ke $tmp → baca $tmp → tambah 100 → hasil. Perhatikan bahwa semua operasi mengalir lewat stack, sesuai arsitektur yang kita bahas di episode 2.

Instruksi Dasar dan Aritmetika Stack

i32.const dan Operasi Aritmetika

Konstanta dimasukkan ke stack dengan i32.const, i64.const, f32.const, atau f64.const. Operator aritmetika mengambil dua nilai teratas stack:

aritmetika.wat
(func $hitung (result i32)
  i32.const 10   ;; stack: [10]
  i32.const 20   ;; stack: [10, 20]
  i32.add        ;; stack: [30]
  i32.const 4    ;; stack: [30, 4]
  i32.mul        ;; stack: [120]
)

Operasi umum yang tersedia: i32.add/sub/mul/div_s/rem_s, i32.and/or/xor, i32.eq/ne/lt_s/gt_s, dan pasangannya untuk i64/f32/f64. Perhatikan akhiran _s/_u untuk signed/unsigned pada pembagian dan perbandingan — Wasm sangat eksplisit soal ini.

Call: Memanggil Fungsi Lain

Fungsi dipanggil dengan instruksi call diikuti nama atau index fungsi:

call.wat
(module
  (func $kuadrat (param $x i32) (result i32)
    local.get $x
    local.get $x
    i32.mul
  )
  (func $main (result i32)
    i32.const 7
    call $kuadrat
  )
  (export "main" (func $main))
)

Evaluasi main → dorong 7 → call $kuadrat → fungsi membaca $x = 7, menghitung 7 * 7 = 49, mengembalikan 49 sebagai hasil.

Import: (import)

Fungsi import disediakan oleh host (JavaScript atau runtime), bukan didefinisikan di dalam modul. Ini satu-satunya cara modul Wasm berinteraksi dengan dunia luar:

import.wat
(module
  (import "env" "log" (func $log (param i32)))
  (func $halo (param $angka i32)
    local.get $angka
    call $log
  )
  (export "halo" (func $halo))
)

Saat menginstansiasi, host wajib menyediakan fungsi tersebut:

JSMenyediakan import dari JS
const { instance } = await WebAssembly.instantiate(wasmBytes, {
  env: {
    log: (n) => console.log("angka:", n),
  },
})
instance.exports.halo(42)

Tanda tangan import harus cocok persis — tipe (param i32) di WAT harus dipenuhi oleh fungsi JS yang menerima satu argumen angka.

Export: (export)

Export membuat fungsi (atau memory, global, table) bisa dipanggil dari host:

export.wat
(module
  (func $fungsiDalam (result i32) i32.const 5)
  (export "keluar" (func $fungsiDalam))
)

Di JS: instance.exports.keluar() mengembalikan 5. Sebuah fungsi tanpa export tidak bisa dipanggil dari luar — ini bagian dari surface area yang dikontrol ketat oleh modul.

Memory: (memory)

Memory dideklarasikan dengan ukuran awal (dan opsional maksimum) dalam satuan page (1 page = 64 KB):

memory.wat
(module
  (memory 1 4)             ;; mulai 1 page, maksimum 4 page
  (data (i32.const 0) "halo")  ;; tulis string "halo" di offset 0
  (func $ambil (param $i i32) (result i32)
    local.get $i
    i32.load8_u
  )
  (export "memory" (memory 0))
  (export "ambil" (func $ambil))
)
  • (memory 1 4) — alokasikan 64 KB awal, boleh tumbuh hingga 256 KB.
  • (data (i32.const 0) "halo") — segment data yang mengisi memory di offset 0 saat instansiasi.
  • i32.load8_u — membaca satu byte dari address yang ada di stack.

Tip

Semua instruksi memory (load/store) memakai address dalam byte yang diletakkan di stack. Model inilah yang membuat Wasm aman: address yang melebihi ukuran memory akan memicu trap, bukan crash atau akses memory lain.

Common Pitfalls

  • Kurung tidak seimbang — WAT adalah S-expression; satu kurung kurang membuat parsing gagal.
  • Suffix signed/unsigned — pakai i32.div_s untuk signed, i32.div_u untuk unsigned; salah pilih menghasilkan angka yang tidak sesuai harapan.
  • Stack imbalance — setiap instruksi harus menghasilkan jumlah nilai stack yang konsisten di akhir fungsi; verifikator engine akan menolak jika tidak.
  • Index vs namacall $kuadrat dan call 0 setara (index); memakai nama jauh lebih mudah dibaca.

Penutup

Pada episode 4 ini, kalian telah menguasai sintaks WAT: struktur modul, fungsi dengan param/result, instruksi stack, call, import, export, dan memory.

Inti yang harus dibawa pulang:

  • WAT adalah S-expression setara satu-ke-satu dengan bytecode .wasm.
  • Fungsi memakai (param)/(result); parameter/lokal diakses via local.get/local.set.
  • Aritmetika berjalan di atas stack; perhatikan suffix _s/_u.
  • (import) membuka akses ke host, (export) membuka akses dari host, (memory) menyediakan linear memory.

Di episode 5 selanjutnya kita akan membedah memory, tables, dan modules secara mendalammemory.grow/memory.size, linear memory dan views-nya di JavaScript (Uint8Array/DataView), segment data, serta call_indirect untuk function pointers. Sampai jumpa di episode 5!

Belajar WebAssembly - Bahasa Teks (WAT) & Sintaks | Belajar Web Assembly