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 }