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