kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

wat.c (66163B)


      1 #include <kit/source.h>
      2 
      3 #include "wasm/wasm.h"
      4 #include "wasm/wasm_insn_table.h"
      5 
      6 typedef struct WasmTok {
      7   const char* p;
      8   size_t len;
      9   uint32_t line;
     10   uint32_t col;
     11   uint8_t kind;
     12 } WasmTok;
     13 
     14 enum {
     15   WT_EOF = 0,
     16   WT_LPAREN,
     17   WT_RPAREN,
     18   WT_ATOM,
     19   WT_STRING,
     20 };
     21 
     22 typedef struct WatParser {
     23   KitCompiler* c;
     24   const char* name;
     25   const char* src;
     26   size_t len;
     27   size_t pos;
     28   uint32_t line;
     29   uint32_t col;
     30   WasmTok tok;
     31   KitSrcLoc field_loc;
     32   WasmModule* module;
     33 } WatParser;
     34 
     35 static KitSrcLoc wat_loc(WatParser* p, uint32_t line, uint32_t col) {
     36   KitSrcLoc loc = wasm_loc(line, col);
     37   if (p && p->module) loc.file_id = p->module->file_id;
     38   return loc;
     39 }
     40 
     41 static KitSrcLoc wat_tok_loc(WatParser* p, WasmTok t) {
     42   return wat_loc(p, t.line, t.col);
     43 }
     44 
     45 static int tok_is(WasmTok t, const char* s) {
     46   size_t n = kit_slice_cstr(s).len;
     47   return t.kind == WT_ATOM && t.len == n && memcmp(t.p, s, n) == 0;
     48 }
     49 
     50 static int wasm_name_eq(const char* name, WasmTok t) {
     51   size_t n;
     52   if (!name || t.kind != WT_ATOM) return 0;
     53   n = kit_slice_cstr(name).len;
     54   return t.len == n && memcmp(name, t.p, n) == 0;
     55 }
     56 
     57 static int wat_hex(char ch) {
     58   if (ch >= '0' && ch <= '9') return ch - '0';
     59   if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
     60   if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
     61   return -1;
     62 }
     63 
     64 static char* wat_dup_string(WatParser* p, WasmTok t, size_t* len_out) {
     65   char* out;
     66   size_t i, n = 0;
     67   if (t.kind != WT_STRING)
     68     wasm_error(p->c, wasm_loc(t.line, t.col), "wasm wat: expected string");
     69   out = (char*)p->module->heap->alloc(p->module->heap, t.len + 1u, 1);
     70   if (!out) wasm_error(p->c, wasm_loc(t.line, t.col), "wasm: out of memory");
     71   for (i = 0; i < t.len; ++i) {
     72     char ch = t.p[i];
     73     if (ch != '\\') {
     74       out[n++] = ch;
     75       continue;
     76     }
     77     if (++i >= t.len)
     78       wasm_error(p->c, wasm_loc(t.line, t.col),
     79                  "wasm wat: unterminated string escape");
     80     ch = t.p[i];
     81     switch (ch) {
     82       case 'n':
     83         out[n++] = '\n';
     84         break;
     85       case 'r':
     86         out[n++] = '\r';
     87         break;
     88       case 't':
     89         out[n++] = '\t';
     90         break;
     91       case '"':
     92       case '\'':
     93       case '\\':
     94         out[n++] = ch;
     95         break;
     96       default: {
     97         int hi = wat_hex(ch);
     98         int lo = (i + 1u < t.len) ? wat_hex(t.p[i + 1u]) : -1;
     99         if (hi < 0 || lo < 0)
    100           wasm_error(p->c, wasm_loc(t.line, t.col),
    101                      "wasm wat: unsupported string escape");
    102         out[n++] = (char)((hi << 4) | lo);
    103         i++;
    104         break;
    105       }
    106     }
    107   }
    108   out[n] = '\0';
    109   if (len_out) *len_out = n;
    110   return out;
    111 }
    112 
    113 static char* wat_dup_atom(WatParser* p, WasmTok t) {
    114   char* out = wasm_strdup(p->module->heap, t.p, t.len);
    115   if (!out) wasm_error(p->c, wasm_loc(t.line, t.col), "wasm: out of memory");
    116   return out;
    117 }
    118 
    119 static void wat_next(WatParser* p) {
    120   const char* s = p->src;
    121   while (p->pos < p->len) {
    122     char ch = s[p->pos];
    123     if (ch == '\n') {
    124       p->pos++;
    125       p->line++;
    126       p->col = 1;
    127       continue;
    128     }
    129     if (ch == ' ' || ch == '\t' || ch == '\r') {
    130       p->pos++;
    131       p->col++;
    132       continue;
    133     }
    134     if (ch == ';' && p->pos + 1u < p->len && s[p->pos + 1u] == ';') {
    135       while (p->pos < p->len && s[p->pos] != '\n') {
    136         p->pos++;
    137         p->col++;
    138       }
    139       continue;
    140     }
    141     if (ch == '(' && p->pos + 1u < p->len && s[p->pos + 1u] == ';') {
    142       uint32_t depth = 1;
    143       p->pos += 2u;
    144       p->col += 2u;
    145       while (depth && p->pos < p->len) {
    146         if (s[p->pos] == '\n') {
    147           p->pos++;
    148           p->line++;
    149           p->col = 1;
    150         } else if (s[p->pos] == '(' && p->pos + 1u < p->len &&
    151                    s[p->pos + 1u] == ';') {
    152           p->pos += 2u;
    153           p->col += 2u;
    154           depth++;
    155         } else if (s[p->pos] == ';' && p->pos + 1u < p->len &&
    156                    s[p->pos + 1u] == ')') {
    157           p->pos += 2u;
    158           p->col += 2u;
    159           depth--;
    160         } else {
    161           p->pos++;
    162           p->col++;
    163         }
    164       }
    165       if (depth)
    166         wasm_error(p->c, wasm_loc(p->line, p->col),
    167                    "wasm wat: unterminated block comment");
    168       continue;
    169     }
    170     break;
    171   }
    172   p->tok.p = s + p->pos;
    173   p->tok.len = 0;
    174   p->tok.line = p->line;
    175   p->tok.col = p->col;
    176   p->tok.kind = WT_EOF;
    177   if (p->pos >= p->len) return;
    178   if (s[p->pos] == '(') {
    179     p->tok.kind = WT_LPAREN;
    180     p->tok.len = 1;
    181     p->pos++;
    182     p->col++;
    183     return;
    184   }
    185   if (s[p->pos] == ')') {
    186     p->tok.kind = WT_RPAREN;
    187     p->tok.len = 1;
    188     p->pos++;
    189     p->col++;
    190     return;
    191   }
    192   if (s[p->pos] == '"') {
    193     size_t start = ++p->pos;
    194     p->col++;
    195     p->tok.kind = WT_STRING;
    196     p->tok.p = s + start;
    197     while (p->pos < p->len && s[p->pos] != '"') {
    198       if (s[p->pos] == '\\') {
    199         p->pos++;
    200         p->col++;
    201         if (p->pos >= p->len) break;
    202       } else if ((unsigned char)s[p->pos] < 0x20) {
    203         wasm_error(p->c, wasm_loc(p->line, p->col),
    204                    "wasm wat: unsupported string escape/control character");
    205       }
    206       p->pos++;
    207       p->col++;
    208     }
    209     if (p->pos >= p->len)
    210       wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    211                  "wasm wat: unterminated string");
    212     p->tok.len = (size_t)(s + p->pos - p->tok.p);
    213     p->pos++;
    214     p->col++;
    215     return;
    216   }
    217   p->tok.kind = WT_ATOM;
    218   while (p->pos < p->len) {
    219     char ch = s[p->pos];
    220     if (ch == '(' || ch == ')' || ch == ' ' || ch == '\t' || ch == '\r' ||
    221         ch == '\n')
    222       break;
    223     p->pos++;
    224     p->col++;
    225   }
    226   p->tok.len = (size_t)(s + p->pos - p->tok.p);
    227 }
    228 
    229 static void wat_expect(WatParser* p, uint8_t kind, const char* what) {
    230   if (p->tok.kind != kind)
    231     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    232                "wasm wat: expected %.*s", KIT_SLICE_ARG(kit_slice_cstr(what)));
    233   wat_next(p);
    234 }
    235 
    236 static int wat_parse_i64(WatParser* p, int64_t* out) {
    237   const char* s = p->tok.p;
    238   size_t n = p->tok.len, i = 0;
    239   uint64_t v = 0;
    240   int neg = 0;
    241   unsigned base = 10;
    242   if (p->tok.kind != WT_ATOM || n == 0) return 0;
    243   if (s[0] == '-' || s[0] == '+') {
    244     neg = s[0] == '-';
    245     i = 1;
    246   }
    247   if (i + 2u <= n && s[i] == '0' && (s[i + 1u] == 'x' || s[i + 1u] == 'X')) {
    248     base = 16;
    249     i += 2u;
    250   }
    251   if (i == n) return 0;
    252   for (; i < n; ++i) {
    253     int hd;
    254     unsigned d;
    255     if (s[i] == '_') continue;
    256     hd = wat_hex(s[i]);
    257     if (hd < 0 || (unsigned)hd >= base) return 0;
    258     d = (unsigned)hd;
    259     if (v > (UINT64_MAX - d) / base) return 0;
    260     v = v * base + d;
    261   }
    262   *out = neg ? -(int64_t)v : (int64_t)v;
    263   return 1;
    264 }
    265 
    266 static int wat_parse_f64(WatParser* p, double* out) {
    267   char buf[128];
    268   char* end = NULL;
    269   if (p->tok.kind != WT_ATOM || p->tok.len == 0 || p->tok.len >= sizeof buf)
    270     return 0;
    271   memcpy(buf, p->tok.p, p->tok.len);
    272   buf[p->tok.len] = '\0';
    273   *out = strtod(buf, &end);
    274   return end && *end == '\0';
    275 }
    276 
    277 static int wat_val_type(WasmTok t, WasmValType* out) {
    278   if (tok_is(t, "i32")) {
    279     *out = WASM_VAL_I32;
    280     return 1;
    281   }
    282   if (tok_is(t, "i64")) {
    283     *out = WASM_VAL_I64;
    284     return 1;
    285   }
    286   if (tok_is(t, "f32")) {
    287     *out = WASM_VAL_F32;
    288     return 1;
    289   }
    290   if (tok_is(t, "f64")) {
    291     *out = WASM_VAL_F64;
    292     return 1;
    293   }
    294   if (tok_is(t, "funcref")) {
    295     *out = WASM_VAL_FUNCREF;
    296     return 1;
    297   }
    298   if (tok_is(t, "externref")) {
    299     *out = WASM_VAL_EXTERNREF;
    300     return 1;
    301   }
    302   return 0;
    303 }
    304 
    305 static void wat_require_feature(WatParser* p, WasmFeatureSet feature,
    306                                 const char* feature_name, const char* what) {
    307   if (!wasm_feature_enabled(p->module, feature))
    308     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    309                "wasm wat: %.*s requires %.*s",
    310                KIT_SLICE_ARG(kit_slice_cstr(what)),
    311                KIT_SLICE_ARG(kit_slice_cstr(feature_name)));
    312 }
    313 
    314 static uint8_t wasm_export_kind_from_tok(WasmTok t) {
    315   if (tok_is(t, "func")) return 0;
    316   if (tok_is(t, "table")) return 1;
    317   if (tok_is(t, "memory")) return 2;
    318   if (tok_is(t, "global")) return 3;
    319   return 0xffu;
    320 }
    321 
    322 static void wat_skip_list(WatParser* p) {
    323   uint32_t depth = 1;
    324   while (depth && p->tok.kind != WT_EOF) {
    325     if (p->tok.kind == WT_LPAREN)
    326       depth++;
    327     else if (p->tok.kind == WT_RPAREN)
    328       depth--;
    329     wat_next(p);
    330   }
    331 }
    332 
    333 /* A handful of mnemonics have legacy spellings that map to the same kind as
    334  * their canonical WASM_INSN_TABLE name; the table carries only the canonical
    335  * spelling, so these aliases are checked separately. */
    336 static const struct {
    337   const char* mnemonic;
    338   WasmInsnKind kind;
    339 } WAT_INSN_ALIASES[] = {
    340     {"i32.atomic.wait", WASM_INSN_I32_ATOMIC_WAIT},
    341     {"i64.atomic.wait", WASM_INSN_I64_ATOMIC_WAIT},
    342     {"atomic.notify", WASM_INSN_MEMORY_ATOMIC_NOTIFY},
    343 };
    344 
    345 /* Resolve a WAT mnemonic token to its instruction kind via a linear scan over
    346  * the single WASM_INSN_TABLE map (same set, same semantics as the former
    347  * 200-arm strcmp chain), plus a tiny alias list. `has_imm` reflects whether the
    348  * grammar parses one trailing immediate token, derived from the operand
    349  * class. */
    350 static int wat_instr_kind(WasmTok t, WasmInsnKind* out, int* has_imm) {
    351   *has_imm = 0;
    352   if (t.kind != WT_ATOM) return 0;
    353   for (WasmInsnKind k = 0; k <= WASM_INSN_TABLE_FILL; ++k) {
    354     const WasmInsnInfo* info = wasm_insn_info(k);
    355     if (!info) continue;
    356     if (tok_is(t, info->mnemonic)) {
    357       *out = k;
    358       *has_imm = wasm_operand_class_has_imm((WasmOperandClass)info->operand_class);
    359       return 1;
    360     }
    361   }
    362   for (size_t i = 0; i < sizeof(WAT_INSN_ALIASES) / sizeof(WAT_INSN_ALIASES[0]);
    363        ++i) {
    364     if (tok_is(t, WAT_INSN_ALIASES[i].mnemonic)) {
    365       const WasmInsnInfo* info = wasm_insn_info(WAT_INSN_ALIASES[i].kind);
    366       *out = WAT_INSN_ALIASES[i].kind;
    367       *has_imm =
    368           info && wasm_operand_class_has_imm((WasmOperandClass)info->operand_class);
    369       return 1;
    370     }
    371   }
    372   return 0;
    373 }
    374 
    375 static void wat_parse_func_index(WatParser* p, int64_t* out) {
    376   uint32_t i;
    377   if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
    378     for (i = 0; i < p->module->nfuncs; ++i) {
    379       if (wasm_name_eq(p->module->funcs[i].name, p->tok)) {
    380         *out = i;
    381         return;
    382       }
    383     }
    384     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    385                "wasm wat: unknown function name");
    386   }
    387   if (!wat_parse_i64(p, out))
    388     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    389                "wasm wat: expected instruction immediate");
    390 }
    391 
    392 static void wat_parse_type_index(WatParser* p, int64_t* out) {
    393   uint32_t i;
    394   if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
    395     for (i = 0; i < p->module->ntypes; ++i) {
    396       if (wasm_name_eq(p->module->types[i].name, p->tok)) {
    397         *out = i;
    398         return;
    399       }
    400     }
    401     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    402                "wasm wat: unknown type name");
    403   }
    404   if (!wat_parse_i64(p, out))
    405     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    406                "wasm wat: expected type index");
    407 }
    408 
    409 static void wat_parse_local_index(WatParser* p, WasmFunc* f, int64_t* out) {
    410   uint32_t i, nlocals = f->nparams + f->nlocals;
    411   if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
    412     for (i = 0; i < nlocals; ++i) {
    413       if (wasm_name_eq(f->local_names[i], p->tok)) {
    414         *out = i;
    415         return;
    416       }
    417     }
    418     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    419                "wasm wat: unknown local name");
    420   }
    421   if (!wat_parse_i64(p, out))
    422     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    423                "wasm wat: expected instruction immediate");
    424 }
    425 
    426 static void wat_parse_global_index(WatParser* p, int64_t* out) {
    427   uint32_t i;
    428   if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
    429     for (i = 0; i < p->module->nglobals; ++i) {
    430       if (wasm_name_eq(p->module->globals[i].name, p->tok)) {
    431         *out = i;
    432         return;
    433       }
    434     }
    435     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    436                "wasm wat: unknown global name");
    437   }
    438   if (!wat_parse_i64(p, out))
    439     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    440                "wasm wat: expected global index");
    441 }
    442 
    443 static void wat_parse_instr_imm(WatParser* p, WasmFunc* f, WasmInsnKind kind,
    444                                 int64_t* out) {
    445   switch (kind) {
    446     case WASM_INSN_CALL:
    447     case WASM_INSN_RETURN_CALL:
    448       wat_parse_func_index(p, out);
    449       break;
    450     case WASM_INSN_GLOBAL_GET:
    451     case WASM_INSN_GLOBAL_SET:
    452       wat_parse_global_index(p, out);
    453       break;
    454     case WASM_INSN_LOCAL_GET:
    455     case WASM_INSN_LOCAL_SET:
    456     case WASM_INSN_LOCAL_TEE:
    457       wat_parse_local_index(p, f, out);
    458       break;
    459     default:
    460       if (!wat_parse_i64(p, out))
    461         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    462                    "wasm wat: expected instruction immediate");
    463       break;
    464   }
    465 }
    466 
    467 static int wat_atom_prefix(WasmTok t, const char* prefix) {
    468   size_t n = kit_slice_cstr(prefix).len;
    469   return t.kind == WT_ATOM && t.len >= n && memcmp(t.p, prefix, n) == 0;
    470 }
    471 
    472 static int wat_parse_u32_atom(WasmTok t, uint32_t* out) {
    473   uint64_t v = 0;
    474   size_t i = 0;
    475   unsigned base = 10;
    476   if (t.kind != WT_ATOM || t.len == 0) return 0;
    477   if (i + 2u <= t.len && t.p[i] == '0' &&
    478       (t.p[i + 1u] == 'x' || t.p[i + 1u] == 'X')) {
    479     base = 16;
    480     i += 2u;
    481   }
    482   if (i == t.len) return 0;
    483   for (; i < t.len; ++i) {
    484     int hd;
    485     if (t.p[i] == '_') continue;
    486     hd = wat_hex(t.p[i]);
    487     if (hd < 0 || (unsigned)hd >= base) return 0;
    488     if (v > (UINT32_MAX - (uint32_t)hd) / base) return 0;
    489     v = v * base + (uint32_t)hd;
    490   }
    491   *out = (uint32_t)v;
    492   return 1;
    493 }
    494 
    495 static int wat_parse_mem_align_log2(WasmTok t, uint32_t* out) {
    496   uint32_t bytes;
    497   uint32_t lg = 0;
    498   if (!wat_parse_u32_atom(t, &bytes) || bytes == 0u) return 0;
    499   if (bytes & (bytes - 1u)) return 0;
    500   while (bytes > 1u) {
    501     bytes >>= 1u;
    502     lg++;
    503   }
    504   *out = lg;
    505   return 1;
    506 }
    507 
    508 static int wat_parse_u64_atom(WasmTok t, uint64_t* out) {
    509   uint64_t v = 0;
    510   size_t i = 0;
    511   unsigned base = 10;
    512   if (t.kind != WT_ATOM || t.len == 0) return 0;
    513   if (i + 2u <= t.len && t.p[i] == '0' &&
    514       (t.p[i + 1u] == 'x' || t.p[i + 1u] == 'X')) {
    515     base = 16;
    516     i += 2u;
    517   }
    518   if (i == t.len) return 0;
    519   for (; i < t.len; ++i) {
    520     int hd;
    521     if (t.p[i] == '_') continue;
    522     hd = wat_hex(t.p[i]);
    523     if (hd < 0 || (unsigned)hd >= base) return 0;
    524     if (v > (UINT64_MAX - (uint64_t)hd) / base) return 0;
    525     v = v * base + (uint64_t)hd;
    526   }
    527   *out = v;
    528   return 1;
    529 }
    530 
    531 static void wat_parse_memory_index(WatParser* p, uint32_t* out) {
    532   uint32_t i;
    533   int64_t idx;
    534   if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
    535     for (i = 0; i < p->module->nmemories; ++i) {
    536       if (wasm_name_eq(p->module->memories[i].name, p->tok)) {
    537         *out = i;
    538         return;
    539       }
    540     }
    541     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    542                "wasm wat: unknown memory name");
    543   }
    544   if (!wat_parse_i64(p, &idx) || idx < 0 || idx > UINT32_MAX)
    545     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    546                "wasm wat: expected memory index");
    547   *out = (uint32_t)idx;
    548 }
    549 
    550 static void wat_parse_mem_attrs(WatParser* p, uint32_t* align, uint64_t* offset,
    551                                 uint32_t* memidx) {
    552   while (p->tok.kind == WT_ATOM) {
    553     WasmTok val;
    554     if (wat_atom_prefix(p->tok, "align=")) {
    555       val = p->tok;
    556       val.p += 6;
    557       val.len -= 6;
    558       if (!wat_parse_mem_align_log2(val, align))
    559         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    560                    "wasm wat: bad memory alignment");
    561       wat_next(p);
    562     } else if (wat_atom_prefix(p->tok, "offset=")) {
    563       val = p->tok;
    564       val.p += 7;
    565       val.len -= 7;
    566       if (!wat_parse_u64_atom(val, offset))
    567         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    568                    "wasm wat: bad memory offset");
    569       wat_next(p);
    570     } else if (wat_atom_prefix(p->tok, "memory=")) {
    571       val = p->tok;
    572       val.p += 7;
    573       val.len -= 7;
    574       if (!wat_parse_u32_atom(val, memidx))
    575         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    576                    "wasm wat: bad memory index");
    577       wat_next(p);
    578     } else if (wat_atom_prefix(p->tok, "mem=")) {
    579       val = p->tok;
    580       val.p += 4;
    581       val.len -= 4;
    582       if (!wat_parse_u32_atom(val, memidx))
    583         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    584                    "wasm wat: bad memory index");
    585       wat_next(p);
    586     } else {
    587       break;
    588     }
    589   }
    590 }
    591 
    592 static void wat_reject_inline_result(WatParser* p, const char* what) {
    593   if (p->tok.kind != WT_LPAREN) return;
    594   wat_next(p);
    595   if (!tok_is(p->tok, "result")) {
    596     p->pos = (size_t)(p->tok.p - p->src);
    597     p->line = p->tok.line;
    598     p->col = p->tok.col;
    599     p->tok.kind = WT_LPAREN;
    600     p->tok.p = p->src + p->pos - 1u;
    601     p->tok.len = 1;
    602     p->tok.line = p->line;
    603     p->tok.col = p->col - 1u;
    604     return;
    605   }
    606   wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    607              "wasm wat: %.*s results are unsupported",
    608              KIT_SLICE_ARG(kit_slice_cstr(what)));
    609 }
    610 
    611 static void wat_parse_instr(WatParser* p, WasmFunc* f);
    612 
    613 static void wat_check_instr_feature(WatParser* p, WasmInsnKind kind) {
    614   if (kind == WASM_INSN_RETURN_CALL || kind == WASM_INSN_RETURN_CALL_INDIRECT ||
    615       kind == WASM_INSN_RETURN_CALL_REF)
    616     wat_require_feature(p, WASM_FEATURE_TAIL_CALLS, "tail calls",
    617                         "tail-call instruction");
    618   if (kind == WASM_INSN_REF_NULL || kind == WASM_INSN_REF_FUNC ||
    619       kind == WASM_INSN_REF_IS_NULL || kind == WASM_INSN_CALL_REF ||
    620       kind == WASM_INSN_RETURN_CALL_REF)
    621     wat_require_feature(p, WASM_FEATURE_TYPED_FUNC_REFS,
    622                         "typed function references",
    623                         "typed-reference instruction");
    624   if (kind == WASM_INSN_ATOMIC_FENCE || wasm_insn_is_atomic_mem(kind))
    625     wat_require_feature(p, WASM_FEATURE_THREADS, "threads",
    626                         "atomic instruction");
    627 }
    628 
    629 static uint32_t wat_parse_call_indirect_type(WatParser* p) {
    630   int64_t typeidx;
    631   wat_expect(p, WT_LPAREN, "'('");
    632   if (!tok_is(p->tok, "type"))
    633     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    634                "wasm wat: expected call_indirect type");
    635   wat_next(p);
    636   wat_parse_type_index(p, &typeidx);
    637   if (typeidx < 0 || (uint64_t)typeidx >= p->module->ntypes)
    638     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    639                "wasm wat: type index out of range");
    640   wat_next(p);
    641   wat_expect(p, WT_RPAREN, "')'");
    642   return (uint32_t)typeidx;
    643 }
    644 
    645 static void wat_parse_ref_null_type(WatParser* p, int64_t* out) {
    646   if (tok_is(p->tok, "func") || tok_is(p->tok, "nofunc") ||
    647       tok_is(p->tok, "funcref")) {
    648     *out = WASM_VAL_FUNCREF;
    649     return;
    650   }
    651   if (tok_is(p->tok, "extern") || tok_is(p->tok, "noextern") ||
    652       tok_is(p->tok, "externref")) {
    653     *out = WASM_VAL_EXTERNREF;
    654     return;
    655   }
    656   wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    657              "wasm wat: expected reference type");
    658 }
    659 
    660 /* True for bulk-memory and table.* ops whose WAT form takes one or two
    661  * index immediates rather than a memarg. */
    662 static int wat_is_bulk_op(WasmInsnKind k) {
    663   switch (k) {
    664     case WASM_INSN_MEMORY_INIT:
    665     case WASM_INSN_DATA_DROP:
    666     case WASM_INSN_MEMORY_COPY:
    667     case WASM_INSN_MEMORY_FILL:
    668     case WASM_INSN_TABLE_INIT:
    669     case WASM_INSN_ELEM_DROP:
    670     case WASM_INSN_TABLE_COPY:
    671     case WASM_INSN_TABLE_GROW:
    672     case WASM_INSN_TABLE_SIZE:
    673     case WASM_INSN_TABLE_FILL:
    674       return 1;
    675     default:
    676       return 0;
    677   }
    678 }
    679 
    680 /* Optionally parse a uleb-style index immediate. Accepts:
    681  *   - a numeric literal (any base/sign accepted by wat_parse_i64)
    682  *   - a `$name` resolved against the segment lookup callback `resolver`,
    683  *     which returns 1 with the resolved index in *out on success.
    684  * Returns 0 if the token isn't a recognizable immediate. */
    685 typedef int (*WatNameResolver)(WatParser*, WasmTok name, uint32_t* out);
    686 static int wat_try_parse_uleb_imm_named(WatParser* p, WatNameResolver resolver,
    687                                         uint32_t* out) {
    688   int64_t v;
    689   WasmInsnKind next_kind;
    690   int next_has_imm;
    691   if (p->tok.kind != WT_ATOM) return 0;
    692   if (p->tok.len && p->tok.p[0] == '$') {
    693     if (!resolver) return 0;
    694     if (!resolver(p, p->tok, out)) return 0;
    695     wat_next(p);
    696     return 1;
    697   }
    698   if (wat_instr_kind(p->tok, &next_kind, &next_has_imm)) return 0;
    699   if (!wat_parse_i64(p, &v) || v < 0 || v > UINT32_MAX) return 0;
    700   *out = (uint32_t)v;
    701   wat_next(p);
    702   return 1;
    703 }
    704 
    705 static int wat_resolve_data_name(WatParser* p, WasmTok name, uint32_t* out) {
    706   for (uint32_t i = 0; i < p->module->ndata; ++i) {
    707     if (wasm_name_eq(p->module->data[i].name, name)) {
    708       *out = i;
    709       return 1;
    710     }
    711   }
    712   return 0;
    713 }
    714 
    715 static int wat_resolve_elem_name(WatParser* p, WasmTok name, uint32_t* out) {
    716   for (uint32_t i = 0; i < p->module->nelems; ++i) {
    717     if (wasm_name_eq(p->module->elems[i].name, name)) {
    718       *out = i;
    719       return 1;
    720     }
    721   }
    722   return 0;
    723 }
    724 
    725 /* Emit a bulk-memory or table op, parsing 0..2 index immediates depending on
    726  * the kind. Caller has already consumed the opcode token. Named refs of the
    727  * form `$name` resolve against the appropriate segment table (data segments
    728  * for memory.init/data.drop, elem segments for table.init/elem.drop). */
    729 static void wat_emit_bulk_op(WatParser* p, WasmFunc* f, WasmInsnKind kind) {
    730   uint32_t a = 0, b = 0;
    731   int got_a, got_b;
    732   WatNameResolver primary = NULL;
    733   switch (kind) {
    734     case WASM_INSN_MEMORY_INIT:
    735     case WASM_INSN_DATA_DROP:
    736       primary = wat_resolve_data_name;
    737       break;
    738     case WASM_INSN_TABLE_INIT:
    739     case WASM_INSN_ELEM_DROP:
    740       primary = wat_resolve_elem_name;
    741       break;
    742     default:
    743       primary = NULL;
    744       break;
    745   }
    746   got_a = wat_try_parse_uleb_imm_named(p, primary, &a);
    747   got_b = wat_try_parse_uleb_imm_named(p, primary, &b);
    748   wasm_func_add_insn(p->c, p->module, f, kind, 0);
    749   switch (kind) {
    750     case WASM_INSN_MEMORY_INIT:
    751       /* memory.init [memidx] dataidx — single arg means dataidx, two args
    752        * is (memidx dataidx). */
    753       if (got_b) {
    754         f->insns[f->ninsns - 1u].memidx = a;
    755         f->insns[f->ninsns - 1u].imm = (int64_t)b;
    756       } else {
    757         f->insns[f->ninsns - 1u].memidx = 0;
    758         f->insns[f->ninsns - 1u].imm = (int64_t)a;
    759       }
    760       break;
    761     case WASM_INSN_DATA_DROP:
    762       f->insns[f->ninsns - 1u].imm = (int64_t)a;
    763       break;
    764     case WASM_INSN_MEMORY_COPY:
    765       /* memory.copy [dst src] — both default to 0. */
    766       f->insns[f->ninsns - 1u].memidx = got_a ? a : 0;
    767       f->insns[f->ninsns - 1u].aux_idx = got_b ? b : 0;
    768       break;
    769     case WASM_INSN_MEMORY_FILL:
    770       f->insns[f->ninsns - 1u].memidx = got_a ? a : 0;
    771       break;
    772     case WASM_INSN_TABLE_INIT:
    773       /* table.init [tableidx] elemidx. */
    774       if (got_b) {
    775         f->insns[f->ninsns - 1u].aux_idx = a;
    776         f->insns[f->ninsns - 1u].imm = (int64_t)b;
    777       } else {
    778         f->insns[f->ninsns - 1u].aux_idx = 0;
    779         f->insns[f->ninsns - 1u].imm = (int64_t)a;
    780       }
    781       break;
    782     case WASM_INSN_ELEM_DROP:
    783       f->insns[f->ninsns - 1u].imm = (int64_t)a;
    784       break;
    785     case WASM_INSN_TABLE_COPY:
    786       f->insns[f->ninsns - 1u].imm = (int64_t)(got_a ? a : 0);
    787       f->insns[f->ninsns - 1u].aux_idx = got_b ? b : 0;
    788       break;
    789     case WASM_INSN_TABLE_GROW:
    790     case WASM_INSN_TABLE_SIZE:
    791     case WASM_INSN_TABLE_FILL:
    792       f->insns[f->ninsns - 1u].imm = (int64_t)(got_a ? a : 0);
    793       break;
    794     default:
    795       break;
    796   }
    797 }
    798 
    799 static void wat_parse_instr_list(WatParser* p, WasmFunc* f) {
    800   WasmInsnKind kind;
    801   int has_imm;
    802   int64_t imm = 0;
    803   WasmTok head;
    804   wat_expect(p, WT_LPAREN, "'('");
    805   head = p->tok;
    806   p->module->current_loc = wat_tok_loc(p, head);
    807   if (tok_is(head, "block") || tok_is(head, "loop")) {
    808     kind = tok_is(head, "block") ? WASM_INSN_BLOCK : WASM_INSN_LOOP;
    809     wat_next(p);
    810     if (p->tok.kind == WT_LPAREN) {
    811       wat_next(p);
    812       if (tok_is(p->tok, "result")) {
    813         wat_next(p);
    814         if (!tok_is(p->tok, "i32") && !tok_is(p->tok, "i64"))
    815           wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    816                      "wasm wat: unsupported block result type");
    817         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    818                    "wasm wat: block results are unsupported");
    819       }
    820       p->pos = (size_t)(p->tok.p - p->src);
    821       p->line = p->tok.line;
    822       p->col = p->tok.col;
    823       p->tok.kind = WT_LPAREN;
    824       p->tok.p = p->src + p->pos - 1u;
    825       p->tok.len = 1;
    826       p->tok.line = p->line;
    827       p->tok.col = p->col - 1u;
    828     }
    829     wasm_func_add_insn(p->c, p->module, f, kind, 0);
    830     while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF)
    831       wat_parse_instr(p, f);
    832     wasm_func_add_insn(p->c, p->module, f, WASM_INSN_END, 0);
    833     wat_expect(p, WT_RPAREN, "')'");
    834     return;
    835   }
    836   if (tok_is(head, "if")) {
    837     wat_next(p);
    838     while (p->tok.kind == WT_LPAREN) {
    839       WasmTok save_head;
    840       size_t save_pos = p->pos;
    841       uint32_t save_line = p->line, save_col = p->col;
    842       wat_next(p);
    843       save_head = p->tok;
    844       p->pos = save_pos;
    845       p->line = save_line;
    846       p->col = save_col;
    847       p->tok.kind = WT_LPAREN;
    848       p->tok.p = p->src + p->pos - 1u;
    849       p->tok.len = 1;
    850       p->tok.line = save_line;
    851       p->tok.col = save_col - 1u;
    852       if (tok_is(save_head, "then") || tok_is(save_head, "else")) break;
    853       if (tok_is(save_head, "result"))
    854         wasm_error(p->c, wasm_loc(save_head.line, save_head.col),
    855                    "wasm wat: if results are unsupported");
    856       wat_parse_instr(p, f);
    857     }
    858     wasm_func_add_insn(p->c, p->module, f, WASM_INSN_IF, 0);
    859     if (p->tok.kind == WT_LPAREN) {
    860       wat_next(p);
    861       if (!tok_is(p->tok, "then"))
    862         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    863                    "wasm wat: expected then");
    864       wat_next(p);
    865       while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF)
    866         wat_parse_instr(p, f);
    867       wat_expect(p, WT_RPAREN, "')'");
    868     }
    869     if (p->tok.kind == WT_LPAREN) {
    870       wat_next(p);
    871       if (!tok_is(p->tok, "else"))
    872         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    873                    "wasm wat: expected else");
    874       wasm_func_add_insn(p->c, p->module, f, WASM_INSN_ELSE, 0);
    875       wat_next(p);
    876       while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF)
    877         wat_parse_instr(p, f);
    878       wat_expect(p, WT_RPAREN, "')'");
    879     }
    880     wasm_func_add_insn(p->c, p->module, f, WASM_INSN_END, 0);
    881     wat_expect(p, WT_RPAREN, "')'");
    882     return;
    883   }
    884   if (!wat_instr_kind(head, &kind, &has_imm))
    885     wasm_error(p->c, wasm_loc(head.line, head.col),
    886                "wasm wat: unsupported instruction");
    887   wat_check_instr_feature(p, kind);
    888   wat_next(p);
    889   if (wasm_insn_is_mem(kind)) {
    890     uint32_t align = 0, memidx = 0;
    891     uint64_t offset = 0;
    892     wat_parse_mem_attrs(p, &align, &offset, &memidx);
    893     while (p->tok.kind == WT_LPAREN) {
    894       wat_next(p);
    895       if (tok_is(p->tok, "memory")) {
    896         wat_next(p);
    897         wat_parse_memory_index(p, &memidx);
    898         wat_next(p);
    899         wat_expect(p, WT_RPAREN, "')'");
    900       } else {
    901         p->pos = (size_t)(p->tok.p - p->src);
    902         p->line = p->tok.line;
    903         p->col = p->tok.col;
    904         p->tok.kind = WT_LPAREN;
    905         p->tok.p = p->src + p->pos - 1u;
    906         p->tok.len = 1;
    907         p->tok.line = p->line;
    908         p->tok.col = p->col - 1u;
    909         wat_parse_instr(p, f);
    910       }
    911     }
    912     wasm_func_add_mem_insn(p->c, p->module, f, kind, align, offset, memidx);
    913     wat_expect(p, WT_RPAREN, "')'");
    914     return;
    915   }
    916   if (kind == WASM_INSN_BR_TABLE) {
    917     KitHeap* heap = p->module->heap;
    918     uint32_t n = 0, cap = 8u;
    919     uint32_t* tmp = (uint32_t*)heap->alloc(heap, sizeof(uint32_t) * cap,
    920                                            _Alignof(uint32_t));
    921     if (!tmp)
    922       wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm wat: oom");
    923     while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
    924       int64_t target;
    925       if (!wat_parse_i64(p, &target) || target < 0 || target > UINT32_MAX)
    926         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
    927                    "wasm wat: bad br_table target");
    928       if (n == cap) {
    929         uint32_t nc = cap * 2u;
    930         tmp =
    931             (uint32_t*)heap->realloc(heap, tmp, sizeof(uint32_t) * cap,
    932                                      sizeof(uint32_t) * nc, _Alignof(uint32_t));
    933         if (!tmp)
    934           wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm wat: oom");
    935         cap = nc;
    936       }
    937       tmp[n++] = (uint32_t)target;
    938       wat_next(p);
    939     }
    940     wasm_func_add_insn(p->c, p->module, f, WASM_INSN_BR_TABLE, 0);
    941     wasm_insn_set_targets(p->c, p->module, &f->insns[f->ninsns - 1u], tmp, n);
    942     heap->free(heap, tmp, sizeof(uint32_t) * cap);
    943     wat_expect(p, WT_RPAREN, "')'");
    944     return;
    945   }
    946   if (kind == WASM_INSN_CALL_INDIRECT ||
    947       kind == WASM_INSN_RETURN_CALL_INDIRECT) {
    948     uint32_t typeidx = wat_parse_call_indirect_type(p);
    949     while (p->tok.kind == WT_LPAREN) wat_parse_instr(p, f);
    950     wasm_func_add_insn(p->c, p->module, f, kind, typeidx);
    951     wat_expect(p, WT_RPAREN, "')'");
    952     return;
    953   }
    954   if (kind == WASM_INSN_CALL_REF || kind == WASM_INSN_RETURN_CALL_REF) {
    955     uint32_t typeidx = wat_parse_call_indirect_type(p);
    956     while (p->tok.kind == WT_LPAREN) wat_parse_instr(p, f);
    957     wasm_func_add_insn(p->c, p->module, f, kind, typeidx);
    958     wat_expect(p, WT_RPAREN, "')'");
    959     return;
    960   }
    961   if (kind == WASM_INSN_REF_NULL) {
    962     wat_parse_ref_null_type(p, &imm);
    963     wat_next(p);
    964     wasm_func_add_insn(p->c, p->module, f, kind, imm);
    965     wat_expect(p, WT_RPAREN, "')'");
    966     return;
    967   }
    968   if (kind == WASM_INSN_REF_FUNC) {
    969     wat_parse_func_index(p, &imm);
    970     wat_next(p);
    971     wasm_func_add_insn(p->c, p->module, f, kind, imm);
    972     wat_expect(p, WT_RPAREN, "')'");
    973     return;
    974   }
    975   if (wat_is_bulk_op(kind)) {
    976     wat_emit_bulk_op(p, f, kind);
    977     while (p->tok.kind == WT_LPAREN) wat_parse_instr(p, f);
    978     wat_expect(p, WT_RPAREN, "')'");
    979     return;
    980   }
    981   if (kind == WASM_INSN_MEMORY_SIZE || kind == WASM_INSN_MEMORY_GROW) {
    982     uint32_t memidx = 0;
    983     while (p->tok.kind == WT_LPAREN) {
    984       wat_next(p);
    985       if (!tok_is(p->tok, "memory")) {
    986         p->pos = (size_t)(p->tok.p - p->src);
    987         p->line = p->tok.line;
    988         p->col = p->tok.col;
    989         p->tok.kind = WT_LPAREN;
    990         p->tok.p = p->src + p->pos - 1u;
    991         p->tok.len = 1;
    992         p->tok.line = p->line;
    993         p->tok.col = p->col - 1u;
    994         wat_parse_instr(p, f);
    995         continue;
    996       }
    997       wat_next(p);
    998       wat_parse_memory_index(p, &memidx);
    999       wat_next(p);
   1000       wat_expect(p, WT_RPAREN, "')'");
   1001     }
   1002     {
   1003       WasmInsnKind next_kind;
   1004       int next_has_imm;
   1005       if (p->tok.kind == WT_ATOM &&
   1006           !wat_instr_kind(p->tok, &next_kind, &next_has_imm)) {
   1007         wat_parse_memory_index(p, &memidx);
   1008         wat_next(p);
   1009       }
   1010     }
   1011     wasm_func_add_insn(p->c, p->module, f,
   1012                        kind == WASM_INSN_MEMORY_GROW ? WASM_INSN_MEMORY_GROW
   1013                                                      : WASM_INSN_MEMORY_SIZE,
   1014                        0);
   1015     f->insns[f->ninsns - 1u].memidx = memidx;
   1016     wat_expect(p, WT_RPAREN, "')'");
   1017     return;
   1018   }
   1019   if (has_imm) {
   1020     if (kind == WASM_INSN_F32_CONST || kind == WASM_INSN_F64_CONST) {
   1021       double fv;
   1022       if (!wat_parse_f64(p, &fv))
   1023         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1024                    "wasm wat: expected float immediate");
   1025       wat_next(p);
   1026       while (p->tok.kind == WT_LPAREN) wat_parse_instr(p, f);
   1027       wasm_func_add_fp_insn(p->c, p->module, f, kind, fv);
   1028       wat_expect(p, WT_RPAREN, "')'");
   1029       return;
   1030     } else {
   1031       wat_parse_instr_imm(p, f, kind, &imm);
   1032       wat_next(p);
   1033     }
   1034   }
   1035   while (p->tok.kind == WT_LPAREN) wat_parse_instr(p, f);
   1036   wasm_func_add_insn(p->c, p->module, f, kind, imm);
   1037   wat_expect(p, WT_RPAREN, "')'");
   1038 }
   1039 
   1040 static void wat_parse_instr(WatParser* p, WasmFunc* f) {
   1041   WasmInsnKind kind;
   1042   int has_imm;
   1043   if (p->tok.kind == WT_LPAREN) {
   1044     wat_parse_instr_list(p, f);
   1045     return;
   1046   }
   1047   if (!wat_instr_kind(p->tok, &kind, &has_imm))
   1048     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1049                "wasm wat: unsupported instruction");
   1050   p->module->current_loc = wat_tok_loc(p, p->tok);
   1051   wat_check_instr_feature(p, kind);
   1052   wat_next(p);
   1053   if (kind == WASM_INSN_BLOCK || kind == WASM_INSN_LOOP)
   1054     wat_reject_inline_result(p, "block");
   1055   else if (kind == WASM_INSN_IF)
   1056     wat_reject_inline_result(p, "if");
   1057   if (wasm_insn_is_mem(kind)) {
   1058     uint32_t align = 0, memidx = 0;
   1059     uint64_t offset = 0;
   1060     wat_parse_mem_attrs(p, &align, &offset, &memidx);
   1061     wasm_func_add_mem_insn(p->c, p->module, f, kind, align, offset, memidx);
   1062     return;
   1063   }
   1064   if (kind == WASM_INSN_BR_TABLE) {
   1065     KitHeap* heap = p->module->heap;
   1066     uint32_t n = 0, cap = 8u;
   1067     uint32_t* tmp = (uint32_t*)heap->alloc(heap, sizeof(uint32_t) * cap,
   1068                                            _Alignof(uint32_t));
   1069     if (!tmp)
   1070       wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm wat: oom");
   1071     wasm_func_add_insn(p->c, p->module, f, WASM_INSN_BR_TABLE, 0);
   1072     while (p->tok.kind == WT_ATOM) {
   1073       WasmInsnKind next_kind;
   1074       int next_has_imm;
   1075       int64_t target;
   1076       if (wat_instr_kind(p->tok, &next_kind, &next_has_imm)) break;
   1077       if (!wat_parse_i64(p, &target) || target < 0 || target > UINT32_MAX)
   1078         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1079                    "wasm wat: bad br_table target");
   1080       if (n == cap) {
   1081         uint32_t nc = cap * 2u;
   1082         tmp =
   1083             (uint32_t*)heap->realloc(heap, tmp, sizeof(uint32_t) * cap,
   1084                                      sizeof(uint32_t) * nc, _Alignof(uint32_t));
   1085         if (!tmp)
   1086           wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col), "wasm wat: oom");
   1087         cap = nc;
   1088       }
   1089       tmp[n++] = (uint32_t)target;
   1090       wat_next(p);
   1091     }
   1092     wasm_insn_set_targets(p->c, p->module, &f->insns[f->ninsns - 1u], tmp, n);
   1093     heap->free(heap, tmp, sizeof(uint32_t) * cap);
   1094     return;
   1095   }
   1096   if (kind == WASM_INSN_CALL_INDIRECT ||
   1097       kind == WASM_INSN_RETURN_CALL_INDIRECT) {
   1098     uint32_t typeidx = wat_parse_call_indirect_type(p);
   1099     wasm_func_add_insn(p->c, p->module, f, kind, typeidx);
   1100     return;
   1101   }
   1102   if (kind == WASM_INSN_CALL_REF || kind == WASM_INSN_RETURN_CALL_REF) {
   1103     uint32_t typeidx = wat_parse_call_indirect_type(p);
   1104     wasm_func_add_insn(p->c, p->module, f, kind, typeidx);
   1105     return;
   1106   }
   1107   if (kind == WASM_INSN_REF_NULL) {
   1108     int64_t imm;
   1109     wat_parse_ref_null_type(p, &imm);
   1110     wasm_func_add_insn(p->c, p->module, f, kind, imm);
   1111     wat_next(p);
   1112     return;
   1113   }
   1114   if (kind == WASM_INSN_REF_FUNC) {
   1115     int64_t imm;
   1116     wat_parse_func_index(p, &imm);
   1117     wasm_func_add_insn(p->c, p->module, f, kind, imm);
   1118     wat_next(p);
   1119     return;
   1120   }
   1121   if (kind == WASM_INSN_MEMORY_SIZE || kind == WASM_INSN_MEMORY_GROW) {
   1122     uint32_t memidx = 0;
   1123     WasmInsnKind next_kind;
   1124     int next_has_imm;
   1125     if (p->tok.kind == WT_ATOM &&
   1126         !wat_instr_kind(p->tok, &next_kind, &next_has_imm)) {
   1127       wat_parse_memory_index(p, &memidx);
   1128       wat_next(p);
   1129     }
   1130     wasm_func_add_insn(p->c, p->module, f, kind, 0);
   1131     f->insns[f->ninsns - 1u].memidx = memidx;
   1132     return;
   1133   }
   1134   if (wat_is_bulk_op(kind)) {
   1135     wat_emit_bulk_op(p, f, kind);
   1136     return;
   1137   }
   1138   if (has_imm) {
   1139     if (kind == WASM_INSN_F32_CONST || kind == WASM_INSN_F64_CONST) {
   1140       double fv;
   1141       if (!wat_parse_f64(p, &fv))
   1142         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1143                    "wasm wat: expected float immediate");
   1144       wasm_func_add_fp_insn(p->c, p->module, f, kind, fv);
   1145       wat_next(p);
   1146     } else {
   1147       int64_t imm;
   1148       wat_parse_instr_imm(p, f, kind, &imm);
   1149       wasm_func_add_insn(p->c, p->module, f, kind, imm);
   1150       wat_next(p);
   1151     }
   1152   } else {
   1153     wasm_func_add_insn(p->c, p->module, f, kind, 0);
   1154   }
   1155 }
   1156 
   1157 static void wat_parse_func(WatParser* p) {
   1158   WasmFunc* f = wasm_add_func(p->c, p->module);
   1159   uint32_t checked_params = 0;
   1160   uint32_t checked_results = 0;
   1161   f->loc = p->field_loc;
   1162   wat_expect(p, WT_LPAREN, "'('");
   1163   if (!tok_is(p->tok, "func"))
   1164     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1165                "wasm wat: expected func");
   1166   wat_next(p);
   1167   if (p->tok.kind == WT_ATOM && p->tok.len > 0 && p->tok.p[0] == '$') {
   1168     f->name = wat_dup_atom(p, p->tok);
   1169     wat_next(p);
   1170   }
   1171   while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
   1172     if (p->tok.kind == WT_LPAREN) {
   1173       wat_next(p);
   1174       if (tok_is(p->tok, "export")) {
   1175         wat_next(p);
   1176         if (p->tok.kind != WT_STRING)
   1177           wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1178                      "wasm wat: expected export string");
   1179         f->export_name = wat_dup_string(p, p->tok, NULL);
   1180         {
   1181           WasmExport* ex = wasm_add_export(p->c, p->module);
   1182           ex->name = wasm_strdup(p->module->heap, f->export_name,
   1183                                  kit_slice_cstr(f->export_name).len);
   1184           if (!ex->name)
   1185             wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1186                        "wasm: out of memory");
   1187           ex->kind = 0;
   1188           ex->index = p->module->nfuncs - 1u;
   1189         }
   1190         wat_next(p);
   1191         wat_expect(p, WT_RPAREN, "')'");
   1192       } else if (tok_is(p->tok, "type")) {
   1193         int64_t typeidx;
   1194         wat_next(p);
   1195         wat_parse_type_index(p, &typeidx);
   1196         if (typeidx < 0 || (uint64_t)typeidx >= p->module->ntypes)
   1197           wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1198                      "wasm wat: type index out of range");
   1199         f->typeidx = (uint32_t)typeidx;
   1200         f->has_typeidx = 1;
   1201         wasm_func_set_params(p->c, p->module, f,
   1202                              p->module->types[typeidx].params,
   1203                              p->module->types[typeidx].nparams);
   1204         f->nresults = p->module->types[typeidx].nresults;
   1205         memcpy(f->results, p->module->types[typeidx].results,
   1206                sizeof(f->results[0]) * f->nresults);
   1207         wat_next(p);
   1208         wat_expect(p, WT_RPAREN, "')'");
   1209       } else if (tok_is(p->tok, "param")) {
   1210         WasmTok pending_name;
   1211         int have_name = 0;
   1212         memset(&pending_name, 0, sizeof pending_name);
   1213         wat_next(p);
   1214         while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
   1215           WasmValType vt;
   1216           if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
   1217             pending_name = p->tok;
   1218             have_name = 1;
   1219             wat_next(p);
   1220             continue;
   1221           }
   1222           if (!wat_val_type(p->tok, &vt))
   1223             wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1224                        "wasm wat: expected parameter type");
   1225           if (!wasm_is_frontend_value_type(vt))
   1226             wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1227                        "wasm wat: unsupported parameter type");
   1228           if (f->has_typeidx) {
   1229             if (checked_params >= f->nparams || f->params[checked_params] != vt)
   1230               wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1231                          "wasm wat: parameter does not match type");
   1232             if (have_name) {
   1233               wasm_func_set_local_name(p->c, p->module, f, checked_params,
   1234                                        pending_name.p, pending_name.len);
   1235               have_name = 0;
   1236             }
   1237             checked_params++;
   1238             wat_next(p);
   1239             continue;
   1240           }
   1241           if (have_name) {
   1242             wasm_func_set_local_name(p->c, p->module, f, f->nparams,
   1243                                      pending_name.p, pending_name.len);
   1244             have_name = 0;
   1245           }
   1246           wasm_func_push_param(p->c, p->module, f, vt);
   1247           wat_next(p);
   1248         }
   1249         wat_expect(p, WT_RPAREN, "')'");
   1250       } else if (tok_is(p->tok, "result")) {
   1251         WasmValType vt;
   1252         wat_next(p);
   1253         if (!wat_val_type(p->tok, &vt))
   1254           wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1255                      "wasm wat: expected result type");
   1256         if (!wasm_is_frontend_value_type(vt))
   1257           wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1258                      "wasm wat: unsupported result type");
   1259         if (f->has_typeidx) {
   1260           if (checked_results >= f->nresults ||
   1261               f->results[checked_results] != vt)
   1262             wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1263                        "wasm wat: result does not match type");
   1264           checked_results++;
   1265         } else {
   1266           f->results[0] = vt;
   1267           f->nresults = 1;
   1268         }
   1269         wat_next(p);
   1270         wat_expect(p, WT_RPAREN, "')'");
   1271       } else if (tok_is(p->tok, "local")) {
   1272         WasmTok pending_name;
   1273         int have_name = 0;
   1274         memset(&pending_name, 0, sizeof pending_name);
   1275         wat_next(p);
   1276         while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
   1277           WasmValType vt;
   1278           if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
   1279             pending_name = p->tok;
   1280             have_name = 1;
   1281             wat_next(p);
   1282             continue;
   1283           }
   1284           if (!wat_val_type(p->tok, &vt))
   1285             wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1286                        "wasm wat: expected local type");
   1287           if (have_name) {
   1288             uint32_t index = f->nparams + f->nlocals;
   1289             wasm_func_set_local_name(p->c, p->module, f, index, pending_name.p,
   1290                                      pending_name.len);
   1291             have_name = 0;
   1292           }
   1293           wasm_func_push_local(p->c, p->module, f, vt);
   1294           wat_next(p);
   1295         }
   1296         wat_expect(p, WT_RPAREN, "')'");
   1297       } else {
   1298         p->pos = (size_t)(p->tok.p - p->src);
   1299         p->line = p->tok.line;
   1300         p->col = p->tok.col;
   1301         p->tok.kind = WT_LPAREN;
   1302         p->tok.p = p->src + p->pos - 1u;
   1303         p->tok.len = 1;
   1304         p->tok.line = p->line;
   1305         p->tok.col = p->col - 1u;
   1306         wat_parse_instr(p, f);
   1307       }
   1308     } else {
   1309       wat_parse_instr(p, f);
   1310     }
   1311   }
   1312   if (!f->has_typeidx)
   1313     f->typeidx = wasm_intern_func_type(p->c, p->module, f);
   1314   else if (checked_params && checked_params != f->nparams)
   1315     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1316                "wasm wat: parameter list does not match type");
   1317   wat_expect(p, WT_RPAREN, "')'");
   1318 }
   1319 
   1320 static void wat_parse_type_field(WatParser* p) {
   1321   WasmFuncType* t = wasm_add_type(p->c, p->module);
   1322   if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
   1323     t->name = wat_dup_atom(p, p->tok);
   1324     wat_next(p);
   1325   }
   1326   wat_expect(p, WT_LPAREN, "'('");
   1327   if (!tok_is(p->tok, "func"))
   1328     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1329                "wasm wat: expected func type");
   1330   wat_next(p);
   1331   while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
   1332     wat_expect(p, WT_LPAREN, "'('");
   1333     if (tok_is(p->tok, "param")) {
   1334       wat_next(p);
   1335       while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
   1336         WasmValType vt;
   1337         if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
   1338           wat_next(p);
   1339           continue;
   1340         }
   1341         if (!wat_val_type(p->tok, &vt) || !wasm_is_num_type(vt))
   1342           wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1343                      "wasm wat: expected parameter type");
   1344         wasm_type_push_param(p->c, p->module, t, vt);
   1345         wat_next(p);
   1346       }
   1347     } else if (tok_is(p->tok, "result")) {
   1348       wat_next(p);
   1349       while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
   1350         WasmValType vt;
   1351         if (!wat_val_type(p->tok, &vt) || !wasm_is_num_type(vt))
   1352           wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1353                      "wasm wat: expected result type");
   1354         if (t->nresults >= 1u)
   1355           wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1356                      "wasm wat: multi-result unsupported");
   1357         t->results[t->nresults++] = vt;
   1358         wat_next(p);
   1359       }
   1360     } else {
   1361       wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1362                  "wasm wat: expected type field");
   1363     }
   1364     wat_expect(p, WT_RPAREN, "')'");
   1365   }
   1366   wat_expect(p, WT_RPAREN, "')'");
   1367   wat_expect(p, WT_RPAREN, "')'");
   1368 }
   1369 
   1370 static void wat_parse_export_field(WatParser* p) {
   1371   char* name;
   1372   int64_t idx = 0;
   1373   uint8_t kind;
   1374   if (p->tok.kind != WT_STRING)
   1375     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1376                "wasm wat: expected export string");
   1377   name = wat_dup_string(p, p->tok, NULL);
   1378   wat_next(p);
   1379   wat_expect(p, WT_LPAREN, "'('");
   1380   kind = wasm_export_kind_from_tok(p->tok);
   1381   if (kind == 0xffu)
   1382     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1383                "wasm wat: expected export kind");
   1384   wat_next(p);
   1385   if (kind == 0)
   1386     wat_parse_func_index(p, &idx);
   1387   else if (!wat_parse_i64(p, &idx))
   1388     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1389                "wasm wat: expected export index");
   1390   if (idx < 0 || idx > UINT32_MAX)
   1391     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1392                "wasm wat: export index out of range");
   1393   {
   1394     WasmExport* ex = wasm_add_export(p->c, p->module);
   1395     ex->name = name;
   1396     ex->kind = kind;
   1397     ex->index = (uint32_t)idx;
   1398   }
   1399   if (kind == 0 && (uint64_t)idx < p->module->nfuncs) {
   1400     wasm_free_str(p->module->heap, &p->module->funcs[idx].export_name);
   1401     p->module->funcs[idx].export_name =
   1402         wasm_strdup(p->module->heap, name, kit_slice_cstr(name).len);
   1403   } else if (kind == 2 && (uint64_t)idx < p->module->nmemories) {
   1404     wasm_free_str(p->module->heap,
   1405                   &p->module->memories[(uint32_t)idx].export_name);
   1406     p->module->memories[(uint32_t)idx].export_name =
   1407         wasm_strdup(p->module->heap, name, kit_slice_cstr(name).len);
   1408   } else if (kind == 1 && (uint64_t)idx < p->module->ntables) {
   1409     wasm_free_str(p->module->heap, &p->module->tables[idx].export_name);
   1410     p->module->tables[idx].export_name =
   1411         wasm_strdup(p->module->heap, name, kit_slice_cstr(name).len);
   1412   } else if (kind == 3 && (uint64_t)idx < p->module->nglobals) {
   1413     wasm_free_str(p->module->heap, &p->module->globals[idx].export_name);
   1414     p->module->globals[idx].export_name =
   1415         wasm_strdup(p->module->heap, name, kit_slice_cstr(name).len);
   1416   }
   1417   wat_next(p);
   1418   wat_expect(p, WT_RPAREN, "')'");
   1419   wat_expect(p, WT_RPAREN, "')'");
   1420 }
   1421 
   1422 static void wat_parse_memory_field(WatParser* p) {
   1423   int64_t min_pages, max_pages;
   1424   WasmMemory* mem = wasm_add_memory(p->c, p->module);
   1425   if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
   1426     mem->name = wat_dup_atom(p, p->tok);
   1427     wat_next(p);
   1428   }
   1429   while (p->tok.kind == WT_ATOM &&
   1430          (tok_is(p->tok, "i64") || tok_is(p->tok, "shared"))) {
   1431     if (tok_is(p->tok, "i64"))
   1432       mem->is64 = 1;
   1433     else
   1434       mem->shared = 1;
   1435     wat_next(p);
   1436   }
   1437   if (!wat_parse_i64(p, &min_pages) || min_pages < 0)
   1438     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1439                "wasm wat: expected memory minimum");
   1440   mem->min_pages = (uint64_t)min_pages;
   1441   wat_next(p);
   1442   while (p->tok.kind == WT_ATOM &&
   1443          (tok_is(p->tok, "i64") || tok_is(p->tok, "shared"))) {
   1444     if (tok_is(p->tok, "i64"))
   1445       mem->is64 = 1;
   1446     else
   1447       mem->shared = 1;
   1448     wat_next(p);
   1449   }
   1450   if (p->tok.kind != WT_RPAREN) {
   1451     if (!wat_parse_i64(p, &max_pages) || max_pages < min_pages)
   1452       wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1453                  "wasm wat: bad memory maximum");
   1454     mem->has_max = 1;
   1455     mem->max_pages = (uint64_t)max_pages;
   1456     wat_next(p);
   1457     while (p->tok.kind == WT_ATOM &&
   1458            (tok_is(p->tok, "i64") || tok_is(p->tok, "shared"))) {
   1459       if (tok_is(p->tok, "i64"))
   1460         mem->is64 = 1;
   1461       else
   1462         mem->shared = 1;
   1463       wat_next(p);
   1464     }
   1465   }
   1466   (void)mem;
   1467   wat_expect(p, WT_RPAREN, "')'");
   1468 }
   1469 
   1470 static void wat_parse_memory_limits(WatParser* p, WasmMemory* mem) {
   1471   int64_t lo, hi;
   1472   while (p->tok.kind == WT_ATOM &&
   1473          (tok_is(p->tok, "i64") || tok_is(p->tok, "shared"))) {
   1474     if (tok_is(p->tok, "i64"))
   1475       mem->is64 = 1;
   1476     else
   1477       mem->shared = 1;
   1478     wat_next(p);
   1479   }
   1480   if (!wat_parse_i64(p, &lo) || lo < 0)
   1481     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1482                "wasm wat: expected memory minimum");
   1483   mem->min_pages = (uint64_t)lo;
   1484   wat_next(p);
   1485   while (p->tok.kind == WT_ATOM &&
   1486          (tok_is(p->tok, "i64") || tok_is(p->tok, "shared"))) {
   1487     if (tok_is(p->tok, "i64"))
   1488       mem->is64 = 1;
   1489     else
   1490       mem->shared = 1;
   1491     wat_next(p);
   1492   }
   1493   if (p->tok.kind != WT_RPAREN) {
   1494     if (!wat_parse_i64(p, &hi) || hi < lo)
   1495       wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1496                  "wasm wat: bad memory maximum");
   1497     mem->has_max = 1;
   1498     mem->max_pages = (uint64_t)hi;
   1499     wat_next(p);
   1500     while (p->tok.kind == WT_ATOM &&
   1501            (tok_is(p->tok, "i64") || tok_is(p->tok, "shared"))) {
   1502       if (tok_is(p->tok, "i64"))
   1503         mem->is64 = 1;
   1504       else
   1505         mem->shared = 1;
   1506       wat_next(p);
   1507     }
   1508   }
   1509 }
   1510 
   1511 static void wat_parse_table_limits_and_type(WatParser* p, WasmTable* t) {
   1512   int64_t lo, hi;
   1513   if (!wat_parse_i64(p, &lo) || lo < 0 || lo > UINT32_MAX)
   1514     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1515                "wasm wat: expected table minimum");
   1516   t->min = (uint32_t)lo;
   1517   wat_next(p);
   1518   if (p->tok.kind == WT_ATOM) {
   1519     WasmValType maybe_type;
   1520     if (!wat_val_type(p->tok, &maybe_type)) {
   1521       if (!wat_parse_i64(p, &hi) || hi < lo || hi > UINT32_MAX)
   1522         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1523                    "wasm wat: bad table maximum");
   1524       t->has_max = 1;
   1525       t->max = (uint32_t)hi;
   1526       wat_next(p);
   1527     }
   1528   }
   1529   if (!wat_val_type(p->tok, &t->elem_type))
   1530     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1531                "wasm wat: expected table element type");
   1532   wat_next(p);
   1533 }
   1534 
   1535 static void wat_parse_import_field(WatParser* p) {
   1536   char *mod, *name;
   1537   if (p->tok.kind != WT_STRING)
   1538     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1539                "wasm wat: expected import module string");
   1540   mod = wat_dup_string(p, p->tok, NULL);
   1541   wat_next(p);
   1542   if (p->tok.kind != WT_STRING)
   1543     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1544                "wasm wat: expected import name string");
   1545   name = wat_dup_string(p, p->tok, NULL);
   1546   wat_next(p);
   1547   wat_expect(p, WT_LPAREN, "'('");
   1548   if (tok_is(p->tok, "func")) {
   1549     WasmFunc* f = wasm_add_func(p->c, p->module);
   1550     f->is_import = 1;
   1551     f->import_module = mod;
   1552     f->import_name = name;
   1553     wat_next(p);
   1554     if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
   1555       f->name = wat_dup_atom(p, p->tok);
   1556       wat_next(p);
   1557     }
   1558     while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
   1559       wat_expect(p, WT_LPAREN, "'('");
   1560       if (tok_is(p->tok, "type")) {
   1561         int64_t typeidx;
   1562         wat_next(p);
   1563         wat_parse_type_index(p, &typeidx);
   1564         if (typeidx < 0 || (uint64_t)typeidx >= p->module->ntypes)
   1565           wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1566                      "wasm wat: type index out of range");
   1567         f->typeidx = (uint32_t)typeidx;
   1568         f->has_typeidx = 1;
   1569         wasm_func_set_params(p->c, p->module, f,
   1570                              p->module->types[typeidx].params,
   1571                              p->module->types[typeidx].nparams);
   1572         f->nresults = p->module->types[typeidx].nresults;
   1573         memcpy(f->results, p->module->types[typeidx].results,
   1574                sizeof(f->results[0]) * f->nresults);
   1575         wat_next(p);
   1576       } else if (tok_is(p->tok, "param")) {
   1577         wat_next(p);
   1578         while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
   1579           WasmValType vt;
   1580           if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
   1581             wat_next(p);
   1582             continue;
   1583           }
   1584           if (!wat_val_type(p->tok, &vt) || !wasm_is_frontend_value_type(vt))
   1585             wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1586                        "wasm wat: expected parameter type");
   1587           wasm_func_push_param(p->c, p->module, f, vt);
   1588           wat_next(p);
   1589         }
   1590       } else if (tok_is(p->tok, "result")) {
   1591         wat_next(p);
   1592         while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
   1593           WasmValType vt;
   1594           if (!wat_val_type(p->tok, &vt) || !wasm_is_frontend_value_type(vt))
   1595             wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1596                        "wasm wat: expected result type");
   1597           if (f->nresults >= 1u)
   1598             wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1599                        "wasm wat: multi-result unsupported");
   1600           f->results[f->nresults++] = vt;
   1601           wat_next(p);
   1602         }
   1603       } else {
   1604         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1605                    "wasm wat: expected import func type field");
   1606       }
   1607       wat_expect(p, WT_RPAREN, "')'");
   1608     }
   1609     if (!f->has_typeidx) f->typeidx = wasm_intern_func_type(p->c, p->module, f);
   1610   } else if (tok_is(p->tok, "memory")) {
   1611     WasmMemory* mem = wasm_add_memory(p->c, p->module);
   1612     mem->is_import = 1;
   1613     mem->import_module = mod;
   1614     mem->import_name = name;
   1615     wat_next(p);
   1616     if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
   1617       mem->name = wat_dup_atom(p, p->tok);
   1618       wat_next(p);
   1619     }
   1620     wat_parse_memory_limits(p, mem);
   1621   } else if (tok_is(p->tok, "table")) {
   1622     WasmTable* t = wasm_add_table(p->c, p->module);
   1623     t->is_import = 1;
   1624     t->import_module = mod;
   1625     t->import_name = name;
   1626     wat_next(p);
   1627     if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
   1628       t->name = wat_dup_atom(p, p->tok);
   1629       wat_next(p);
   1630     }
   1631     wat_parse_table_limits_and_type(p, t);
   1632   } else if (tok_is(p->tok, "global")) {
   1633     WasmGlobal* g = wasm_add_global(p->c, p->module);
   1634     g->is_import = 1;
   1635     g->import_module = mod;
   1636     g->import_name = name;
   1637     wat_next(p);
   1638     if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
   1639       g->name = wat_dup_atom(p, p->tok);
   1640       wat_next(p);
   1641     }
   1642     if (p->tok.kind == WT_LPAREN) {
   1643       wat_next(p);
   1644       if (!tok_is(p->tok, "mut"))
   1645         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1646                    "wasm wat: expected mut");
   1647       g->mutable_ = 1;
   1648       wat_next(p);
   1649       if (!wat_val_type(p->tok, &g->type) || !wasm_is_num_type(g->type))
   1650         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1651                    "wasm wat: expected global type");
   1652       wat_next(p);
   1653       wat_expect(p, WT_RPAREN, "')'");
   1654     } else {
   1655       if (!wat_val_type(p->tok, &g->type) || !wasm_is_num_type(g->type))
   1656         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1657                    "wasm wat: expected global type");
   1658       wat_next(p);
   1659     }
   1660   } else {
   1661     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1662                "wasm wat: unsupported import kind");
   1663   }
   1664   wat_expect(p, WT_RPAREN, "')'");
   1665   wat_expect(p, WT_RPAREN, "')'");
   1666 }
   1667 
   1668 static void wat_parse_table_field(WatParser* p) {
   1669   WasmTable* t = wasm_add_table(p->c, p->module);
   1670   if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
   1671     t->name = wat_dup_atom(p, p->tok);
   1672     wat_next(p);
   1673   }
   1674   wat_parse_table_limits_and_type(p, t);
   1675   wat_expect(p, WT_RPAREN, "')'");
   1676 }
   1677 
   1678 static void wat_parse_const_expr(WatParser* p, WasmInsn* out) {
   1679   WasmInsnKind kind;
   1680   int has_imm;
   1681   memset(out, 0, sizeof *out);
   1682   wat_expect(p, WT_LPAREN, "'('");
   1683   if (!wat_instr_kind(p->tok, &kind, &has_imm) ||
   1684       (kind != WASM_INSN_I32_CONST && kind != WASM_INSN_I64_CONST &&
   1685        kind != WASM_INSN_F32_CONST && kind != WASM_INSN_F64_CONST))
   1686     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1687                "wasm wat: expected constant expression");
   1688   out->kind = (uint8_t)kind;
   1689   out->loc = wat_tok_loc(p, p->tok);
   1690   wat_next(p);
   1691   if (kind == WASM_INSN_F32_CONST || kind == WASM_INSN_F64_CONST) {
   1692     if (!wat_parse_f64(p, &out->fp))
   1693       wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1694                  "wasm wat: expected float immediate");
   1695   } else if (!wat_parse_i64(p, &out->imm)) {
   1696     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1697                "wasm wat: expected integer immediate");
   1698   }
   1699   wat_next(p);
   1700   wat_expect(p, WT_RPAREN, "')'");
   1701 }
   1702 
   1703 static void wat_parse_global_field(WatParser* p) {
   1704   WasmGlobal* g = wasm_add_global(p->c, p->module);
   1705   g->loc = p->field_loc;
   1706   if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
   1707     g->name = wat_dup_atom(p, p->tok);
   1708     wat_next(p);
   1709   }
   1710   if (p->tok.kind == WT_LPAREN) {
   1711     wat_next(p);
   1712     if (tok_is(p->tok, "export")) {
   1713       wat_next(p);
   1714       if (p->tok.kind != WT_STRING)
   1715         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1716                    "wasm wat: expected export string");
   1717       g->export_name = wat_dup_string(p, p->tok, NULL);
   1718       {
   1719         WasmExport* ex = wasm_add_export(p->c, p->module);
   1720         ex->name = wasm_strdup(p->module->heap, g->export_name,
   1721                                kit_slice_cstr(g->export_name).len);
   1722         if (!ex->name)
   1723           wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1724                      "wasm: out of memory");
   1725         ex->kind = 3;
   1726         ex->index = p->module->nglobals - 1u;
   1727       }
   1728       wat_next(p);
   1729       wat_expect(p, WT_RPAREN, "')'");
   1730     } else if (tok_is(p->tok, "mut")) {
   1731       g->mutable_ = 1;
   1732       wat_next(p);
   1733       if (!wat_val_type(p->tok, &g->type) || !wasm_is_num_type(g->type))
   1734         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1735                    "wasm wat: expected global type");
   1736       wat_next(p);
   1737       wat_expect(p, WT_RPAREN, "')'");
   1738     } else {
   1739       wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1740                  "wasm wat: expected global type");
   1741     }
   1742   } else {
   1743     if (!wat_val_type(p->tok, &g->type) || !wasm_is_num_type(g->type))
   1744       wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1745                  "wasm wat: expected global type");
   1746     wat_next(p);
   1747   }
   1748   if (!g->type)
   1749     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1750                "wasm wat: missing global type");
   1751   wat_parse_const_expr(p, &g->init);
   1752   wat_expect(p, WT_RPAREN, "')'");
   1753 }
   1754 
   1755 static void wat_parse_elem_field(WatParser* p) {
   1756   WasmElemSegment* e = wasm_add_elem(p->c, p->module);
   1757   int has_offset = 0;
   1758   int is_declarative = 0;
   1759   e->tableidx = 0;
   1760   e->elem_type = WASM_VAL_FUNCREF;
   1761   /* Optional `$name` or "declare" keyword for declarative segments. */
   1762   if (p->tok.kind == WT_ATOM && p->tok.len && p->tok.p[0] == '$') {
   1763     e->name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
   1764     wat_next(p);
   1765   }
   1766   if (tok_is(p->tok, "declare")) {
   1767     is_declarative = 1;
   1768     wat_next(p);
   1769   } else if (p->tok.kind == WT_ATOM) {
   1770     int64_t tableidx;
   1771     if (wat_parse_i64(p, &tableidx)) {
   1772       if (tableidx < 0 || tableidx > UINT32_MAX)
   1773         wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1774                    "wasm wat: table index out of range");
   1775       e->tableidx = (uint32_t)tableidx;
   1776       wat_next(p);
   1777     }
   1778   }
   1779   if (!is_declarative && p->tok.kind == WT_LPAREN) {
   1780     WasmInsn off;
   1781     wat_parse_const_expr(p, &off);
   1782     if (off.kind != WASM_INSN_I32_CONST || off.imm < 0)
   1783       wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1784                  "wasm wat: element offset must be i32.const");
   1785     e->offset = off.imm;
   1786     has_offset = 1;
   1787   }
   1788   if (is_declarative)
   1789     e->mode = WASM_SEG_DECLARATIVE;
   1790   else if (has_offset)
   1791     e->mode = WASM_SEG_ACTIVE;
   1792   else
   1793     e->mode = WASM_SEG_PASSIVE;
   1794   if (tok_is(p->tok, "func")) wat_next(p);
   1795   while (p->tok.kind != WT_RPAREN && p->tok.kind != WT_EOF) {
   1796     int64_t idx;
   1797     wat_parse_func_index(p, &idx);
   1798     if (idx < 0 || idx > UINT32_MAX)
   1799       wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1800                  "wasm wat: element function index out of range");
   1801     wasm_elem_push_func(p->c, p->module, e, (uint32_t)idx);
   1802     wat_next(p);
   1803   }
   1804   wat_expect(p, WT_RPAREN, "')'");
   1805 }
   1806 
   1807 static void wat_parse_start_field(WatParser* p) {
   1808   int64_t idx;
   1809   p->module->start_field_loc = p->field_loc;
   1810   wat_parse_func_index(p, &idx);
   1811   if (idx < 0 || idx > UINT32_MAX)
   1812     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1813                "wasm wat: start function index out of range");
   1814   p->module->has_start = 1;
   1815   p->module->start_func = (uint32_t)idx;
   1816   wat_next(p);
   1817   wat_expect(p, WT_RPAREN, "')'");
   1818 }
   1819 
   1820 static void wat_parse_custom_field(WatParser* p) {
   1821   WasmCustom* cs;
   1822   size_t n = 0;
   1823   char* bytes;
   1824   if (p->tok.kind != WT_STRING)
   1825     wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1826                "wasm wat: expected custom section name");
   1827   cs = wasm_add_custom(p->c, p->module);
   1828   cs->name = wat_dup_string(p, p->tok, NULL);
   1829   if (kit_slice_eq_cstr(kit_slice_cstr(cs->name), "target_features") ||
   1830       kit_slice_eq_cstr(kit_slice_cstr(cs->name), "target-feature"))
   1831     p->module->has_target_features = 1;
   1832   wat_next(p);
   1833   if (p->tok.kind == WT_STRING) {
   1834     bytes = wat_dup_string(p, p->tok, &n);
   1835     cs->data = (uint8_t*)p->module->heap->alloc(p->module->heap, n ? n : 1u, 1);
   1836     if (!cs->data)
   1837       wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1838                  "wasm: out of memory");
   1839     memcpy(cs->data, bytes, n);
   1840     cs->len = (uint32_t)n;
   1841     p->module->heap->free(p->module->heap, bytes, p->tok.len + 1u);
   1842     wat_next(p);
   1843   }
   1844   wat_expect(p, WT_RPAREN, "')'");
   1845 }
   1846 
   1847 static void wat_parse_data_field(WatParser* p) {
   1848   int64_t offset = 0;
   1849   uint32_t memidx = 0;
   1850   int has_offset = 0;
   1851   char* bytes = NULL;
   1852   size_t nbytes = 0;
   1853   size_t alloc_len = 0;
   1854   char* seg_name = NULL;
   1855   WasmDataSegment* d;
   1856   if (p->tok.kind == WT_ATOM) {
   1857     WasmInsnKind next_kind;
   1858     int next_has_imm;
   1859     if (p->tok.len && p->tok.p[0] == '$') {
   1860       seg_name = wasm_strdup(p->module->heap, p->tok.p, p->tok.len);
   1861       wat_next(p);
   1862     } else if (!wat_instr_kind(p->tok, &next_kind, &next_has_imm)) {
   1863       wat_parse_memory_index(p, &memidx);
   1864       wat_next(p);
   1865     } else {
   1866       wat_next(p);
   1867     }
   1868   }
   1869   if (p->tok.kind == WT_LPAREN) {
   1870     size_t save_pos = p->pos;
   1871     uint32_t save_line = p->line, save_col = p->col;
   1872     wat_next(p);
   1873     if (tok_is(p->tok, "memory")) {
   1874       wat_next(p);
   1875       wat_parse_memory_index(p, &memidx);
   1876       wat_next(p);
   1877       wat_expect(p, WT_RPAREN, "')'");
   1878     } else {
   1879       p->pos = save_pos;
   1880       p->line = save_line;
   1881       p->col = save_col;
   1882       p->tok.kind = WT_LPAREN;
   1883       p->tok.p = p->src + p->pos - 1u;
   1884       p->tok.len = 1;
   1885       p->tok.line = save_line;
   1886       p->tok.col = save_col - 1u;
   1887     }
   1888   }
   1889   /* The offset expression `(i32.const N)` is optional: when absent the
   1890    * segment is passive. */
   1891   if (p->tok.kind == WT_LPAREN) {
   1892     wat_next(p);
   1893     if (!tok_is(p->tok, "i32.const") && !tok_is(p->tok, "i64.const"))
   1894       wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1895                  "wasm wat: expected const data offset");
   1896     wat_next(p);
   1897     if (!wat_parse_i64(p, &offset) || offset < 0 || offset > UINT32_MAX)
   1898       wasm_error(p->c, wasm_loc(p->tok.line, p->tok.col),
   1899                  "wasm wat: bad data offset");
   1900     wat_next(p);
   1901     wat_expect(p, WT_RPAREN, "')'");
   1902     has_offset = 1;
   1903   }
   1904   if (p->tok.kind == WT_STRING) {
   1905     alloc_len = p->tok.len + 1u;
   1906     bytes = wat_dup_string(p, p->tok, &nbytes);
   1907     wat_next(p);
   1908   }
   1909   d = wasm_add_data(p->c, p->module);
   1910   d->name = seg_name;
   1911   if (has_offset) {
   1912     d->mode = WASM_SEG_ACTIVE;
   1913     d->memidx = memidx;
   1914     d->offset = offset;
   1915   } else {
   1916     d->mode = WASM_SEG_PASSIVE;
   1917   }
   1918   if (nbytes)
   1919     wasm_data_set_bytes(p->c, p->module, d, (const uint8_t*)bytes, nbytes);
   1920   if (bytes) p->module->heap->free(p->module->heap, bytes, alloc_len);
   1921   wat_expect(p, WT_RPAREN, "')'");
   1922 }
   1923 
   1924 void wasm_parse_wat_body(KitCompiler* c, WasmModule* m, WasmFunc* f,
   1925                          const char* src, size_t len, KitSrcLoc loc) {
   1926   WatParser p;
   1927   memset(&p, 0, sizeof p);
   1928   p.c = c;
   1929   p.name = "<asm template>";
   1930   p.src = src;
   1931   p.len = len;
   1932   p.line = loc.line ? loc.line : 1u;
   1933   p.col = loc.col ? loc.col : 1u;
   1934   p.module = m;
   1935   p.field_loc = loc;
   1936   wat_next(&p);
   1937   while (p.tok.kind != WT_EOF) wat_parse_instr(&p, f);
   1938 }
   1939 
   1940 void wasm_parse_wat(KitCompiler* c, KitSlice name, const KitSlice* input,
   1941                     WasmModule* out) {
   1942   WatParser p;
   1943   memset(&p, 0, sizeof p);
   1944   if (name.s) (void)kit_source_add_memory(c, name, &out->file_id);
   1945   p.c = c;
   1946   p.name = name.s;
   1947   p.src = input->s;
   1948   p.len = input->len;
   1949   p.line = 1;
   1950   p.col = 1;
   1951   p.module = out;
   1952   wat_next(&p);
   1953   wat_expect(&p, WT_LPAREN, "'('");
   1954   if (!tok_is(p.tok, "module"))
   1955     wasm_error(c, wasm_loc(p.tok.line, p.tok.col), "wasm wat: expected module");
   1956   wat_next(&p);
   1957   while (p.tok.kind != WT_RPAREN && p.tok.kind != WT_EOF) {
   1958     if (p.tok.kind != WT_LPAREN)
   1959       wasm_error(c, wasm_loc(p.tok.line, p.tok.col),
   1960                  "wasm wat: expected module field");
   1961     wat_next(&p);
   1962     p.field_loc = wat_tok_loc(&p, p.tok);
   1963     if (tok_is(p.tok, "type")) {
   1964       wat_next(&p);
   1965       wat_parse_type_field(&p);
   1966     } else if (tok_is(p.tok, "func")) {
   1967       p.pos = (size_t)(p.tok.p - p.src);
   1968       p.line = p.tok.line;
   1969       p.col = p.tok.col;
   1970       p.tok.kind = WT_LPAREN;
   1971       p.tok.p = p.src + p.pos - 1u;
   1972       p.tok.len = 1;
   1973       p.tok.line = p.line;
   1974       p.tok.col = p.col - 1u;
   1975       wat_parse_func(&p);
   1976     } else if (tok_is(p.tok, "export")) {
   1977       wat_next(&p);
   1978       wat_parse_export_field(&p);
   1979     } else if (tok_is(p.tok, "memory")) {
   1980       wat_next(&p);
   1981       wat_parse_memory_field(&p);
   1982     } else if (tok_is(p.tok, "data")) {
   1983       wat_next(&p);
   1984       wat_parse_data_field(&p);
   1985     } else if (tok_is(p.tok, "import")) {
   1986       wat_next(&p);
   1987       wat_parse_import_field(&p);
   1988     } else if (tok_is(p.tok, "table")) {
   1989       wat_next(&p);
   1990       wat_parse_table_field(&p);
   1991     } else if (tok_is(p.tok, "global")) {
   1992       wat_next(&p);
   1993       wat_parse_global_field(&p);
   1994     } else if (tok_is(p.tok, "elem")) {
   1995       wat_next(&p);
   1996       wat_parse_elem_field(&p);
   1997     } else if (tok_is(p.tok, "start")) {
   1998       wat_next(&p);
   1999       wat_parse_start_field(&p);
   2000     } else if (tok_is(p.tok, "custom") || tok_is(p.tok, "@custom")) {
   2001       wat_next(&p);
   2002       wat_parse_custom_field(&p);
   2003     } else {
   2004       wat_skip_list(&p);
   2005     }
   2006   }
   2007   wat_expect(&p, WT_RPAREN, "')'");
   2008   if (p.tok.kind != WT_EOF)
   2009     wasm_error(c, wasm_loc(p.tok.line, p.tok.col),
   2010                "wasm wat: trailing tokens after module");
   2011 }