1 module tagion.wasm.WasmReader; 2 3 import std.array : join; 4 import std.bitmanip : Endian, peek, binread = read, binwrite = write; 5 import std.conv : emplace, to; 6 import std.exception : assumeUnique; 7 import std.format; 8 import std.format; 9 import std.meta : AliasSeq; 10 import std.range : enumerate; 11 import std.range.primitives : isForwardRange, isInputRange, isRandomAccessRange; 12 import std.stdio; 13 import std.traits : EnumMembers, ForeachType, PointerTarget, Unqual, getUDAs; 14 import std.uni : toLower; 15 import tagion.wasm.WasmBase; 16 import tagion.wasm.WasmException; 17 18 @safe struct WasmReader { 19 protected { 20 immutable(ubyte)[] _data; 21 } 22 23 immutable(ubyte[]) data() const pure nothrow { 24 return _data; 25 } 26 27 alias serialize = data; 28 29 this(immutable(ubyte[]) data) pure nothrow { 30 _data = data; 31 } 32 33 alias Sections = SectionsT!(WasmRange.WasmSection); 34 35 alias InterfaceModule = InterfaceModuleT!(Sections); 36 37 @trusted void opCall(InterfaceModule iter) const { 38 auto range = opSlice; 39 verbose("WASM '%s'", range.magic); 40 verbose("VERSION %d", range.vernum); 41 verbose("Index %d", range.index); 42 43 while (!range.empty) { 44 auto a = range.front; 45 with (Section) { 46 final switch (a.section) { 47 foreach (E; EnumMembers!(Section)) { 48 case E: 49 const sec = a.sec!E; 50 verbose("Begin(%d)", range.index); 51 verbose.down; 52 verbose("Section(%s) size %d", a.section, a.data.length); 53 verbose.hex(range.index, a.data); 54 enum code = format(q{iter.%s(sec);}, secname(E)); 55 mixin(code); 56 verbose.println("%s", sec); 57 verbose.up; 58 verbose("End"); 59 break; 60 } 61 } 62 } 63 range.popFront; 64 } 65 } 66 67 struct Limit { 68 Limits lim; 69 uint from; 70 uint to; 71 this(immutable(ubyte[]) data, ref size_t index) pure nothrow { 72 lim = cast(Limits) data[index]; 73 index += Limits.sizeof; 74 from = u32(data, index); // LEB128 -> uint.max 75 const uint get_to(const uint lim) { 76 with (Limits) { 77 final switch (lim) { 78 case INFINITE: 79 return to.max; 80 case RANGE: 81 return u32(data, index); 82 } 83 } 84 } 85 86 to = get_to(lim); 87 } 88 } 89 90 @trusted static immutable(T[]) Vector(T)(immutable(ubyte[]) data, ref size_t index) { 91 immutable len = u32(data, index); 92 static if (T.sizeof is ubyte.sizeof) { 93 immutable vec_mem = data[index .. index + len * T.sizeof]; 94 index += len * T.sizeof; 95 immutable result = cast(immutable(T*))(vec_mem.ptr); 96 return result[0 .. len]; 97 } 98 else { 99 auto result = new T[len]; 100 foreach (ref a; result) { 101 a = decode!T(data, index); 102 } 103 return assumeUnique(result); 104 } 105 } 106 107 WasmRange opSlice() const pure nothrow { 108 return WasmRange(data); 109 } 110 111 static assert(isInputRange!WasmRange); 112 static assert(isForwardRange!WasmRange); 113 //static assert(isRandomAccessRange!WasmRange); 114 115 auto get(Section S)() const nothrow { 116 alias T = Sections[S]; 117 auto range = opSlice; 118 auto sec = range[S]; 119 import std.exception; 120 121 debug { 122 assumeWontThrow( 123 { writefln("sec.data=%s", sec.data); } 124 ); 125 } 126 127 return new T(sec.data); 128 } 129 130 @safe struct WasmRange { 131 immutable(ubyte[]) data; 132 protected size_t _index; 133 immutable(string) magic; 134 immutable(uint) vernum; 135 this(immutable(ubyte[]) data) pure nothrow @nogc @trusted { 136 this.data = data; 137 magic = cast(string)(data[0 .. uint.sizeof]); 138 _index = uint.sizeof; 139 vernum = data[_index .. $].peek!(uint, Endian.littleEndian); 140 _index += uint.sizeof; 141 _index = 2 * uint.sizeof; 142 } 143 144 pure nothrow { 145 bool empty() const @nogc { 146 return _index >= data.length; 147 } 148 149 WasmSection front() const @nogc { 150 return WasmSection(data[_index .. $]); 151 } 152 153 void popFront() @nogc { 154 _index += Section.sizeof; 155 const size = u32(data, _index); 156 _index += size; 157 } 158 159 WasmRange save() @nogc { 160 WasmRange result = this; 161 return result; 162 } 163 164 WasmSection opIndex(const Section index) const 165 in { 166 assert(index < EnumMembers!(Section).length); 167 } 168 do { 169 auto index_range = WasmRange(data); 170 // (() @trusted { 171 foreach (ref sec; index_range) { 172 if (index == sec.section) { 173 return sec; 174 } 175 else if (index < sec.section) { 176 break; 177 } 178 } 179 return WasmSection.emptySection(index); 180 } 181 182 size_t index() const @nogc { 183 return _index; 184 } 185 186 } 187 188 @nogc struct WasmSection { 189 immutable(ubyte[]) data; 190 immutable(Section) section; 191 192 static WasmSection emptySection(const Section sectype) pure nothrow { 193 immutable(ubyte[]) data = [sectype, 0]; 194 return WasmSection(data); 195 } 196 197 this(immutable(ubyte[]) data) @nogc pure nothrow { 198 section = cast(Section) data[0]; 199 size_t index = Section.sizeof; 200 const size = u32(data, index); 201 this.data = data[index .. index + size]; 202 } 203 204 auto sec(Section S)() pure 205 in { 206 assert(S is section); 207 } 208 do { 209 alias T = Sections[S]; 210 return new T(data); 211 } 212 213 @nogc struct VectorRange(ModuleSection, Element) { 214 const ModuleSection owner; 215 protected size_t pos; 216 protected uint index; 217 this(const ModuleSection owner) pure { 218 this.owner = owner; 219 } 220 221 pure { 222 Element front() const { 223 return Element(owner.data[pos .. $]); 224 } 225 226 bool empty() const nothrow { 227 return index >= owner.length; 228 } 229 230 void popFront() { 231 pos += front.size; 232 index++; 233 } 234 235 VectorRange save() nothrow { 236 VectorRange result = this; 237 return result; 238 } 239 240 } 241 const(Element) opIndex(const size_t index) const pure { 242 auto range = VectorRange(owner); 243 size_t i; 244 while (!range.empty) { 245 // foreach (i, ref e; range.enumerate) { 246 if (i is index) { 247 return range.front; 248 } 249 range.popFront; 250 i++; 251 } 252 throw new WasmException(format!"Index %d out of range"(index)); 253 assert(0); 254 } 255 } 256 257 static class SectionT(SecType) { 258 immutable uint length; 259 immutable(ubyte[]) data; 260 this(immutable(ubyte[]) data) @nogc pure nothrow { 261 size_t index; 262 length = u32(data, index); 263 this.data = data[index .. $]; 264 } 265 266 protected this(const(SectionT) that) @nogc pure nothrow { 267 data = that.data; 268 length = that.length; 269 } 270 // static assert(isInputRange!SecRange); 271 // static assert(isForwardRange!SecRange); 272 alias SecRange = VectorRange!(SectionT, SecType); 273 SecRange opSlice() const pure nothrow { 274 return SecRange(this); 275 } 276 277 SecType opIndex(const size_t index) const pure { 278 return SecRange(this).opIndex(index); 279 } 280 281 SectionT dup() const pure nothrow { 282 return new SectionT(this); 283 } 284 285 @trusted override string toString() const { 286 string[] result; 287 foreach (i, sec; opSlice.enumerate) { 288 result ~= format("\t%3d %s", i, sec).idup; 289 } 290 return result.join("\n"); 291 } 292 } 293 294 static class Custom { 295 import tagion.hibon.Document; 296 297 immutable(char[]) name; 298 immutable(ubyte[]) bytes; 299 const(Document) doc; 300 immutable(size_t) size; 301 this(immutable(ubyte[]) data) pure nothrow { 302 size_t index; 303 name = Vector!char(data, index); 304 import LEB128 = tagion.utils.LEB128; 305 306 doc = Document(data[index .. $]); 307 index += LEB128.decode!uint(data[index .. $]).size; 308 bytes = data[index .. $]; 309 size = data.length; 310 } 311 } 312 313 struct FuncType { 314 immutable(Types) type; 315 immutable(Types[]) params; 316 immutable(Types[]) results; 317 immutable(size_t) size; 318 this(immutable(ubyte[]) data) pure nothrow { 319 type = cast(Types) data[0]; 320 size_t index = Types.sizeof; 321 params = Vector!Types(data, index); 322 results = Vector!Types(data, index); 323 size = index; 324 } 325 } 326 327 alias Type = SectionT!(FuncType); 328 329 struct ImportType { 330 immutable(char[]) mod; 331 immutable(char[]) name; 332 immutable(ImportDesc) importdesc; 333 immutable(size_t) size; 334 struct ImportDesc { 335 struct FuncDesc { 336 uint funcidx; 337 this(immutable(ubyte[]) data, ref size_t index) pure nothrow { 338 funcidx = u32(data, index); 339 } 340 } 341 342 struct TableDesc { 343 Types type; 344 Limit limit; 345 this(immutable(ubyte[]) data, ref size_t index) pure nothrow { 346 type = cast(Types) data[index]; 347 index += Types.sizeof; 348 limit = Limit(data, index); 349 } 350 } 351 352 struct MemoryDesc { 353 Limit limit; 354 this(immutable(ubyte[]) data, ref size_t index) pure nothrow { 355 limit = Limit(data, index); 356 } 357 } 358 359 struct GlobalDesc { 360 Types type; 361 Mutable mut; 362 this(immutable(ubyte[]) data, ref size_t index) pure nothrow { 363 type = cast(Types) data[index]; 364 index += Types.sizeof; 365 mut = cast(Mutable) data[index]; 366 index += Mutable.sizeof; 367 } 368 } 369 370 protected union { 371 @(IndexType.FUNC) FuncDesc _funcdesc; 372 @(IndexType.TABLE) TableDesc _tabledesc; 373 @(IndexType.MEMORY) MemoryDesc _memorydesc; 374 @(IndexType.GLOBAL) GlobalDesc _globaldesc; 375 } 376 377 protected IndexType _desc; 378 379 auto get(IndexType IType)() const pure 380 in { 381 assert(_desc is IType); 382 } 383 do { 384 foreach (E; EnumMembers!IndexType) { 385 static if (E is IType) { 386 enum code = format("return _%sdesc;", toLower(E.to!string)); 387 mixin(code); 388 } 389 } 390 } 391 392 @property IndexType desc() const pure nothrow { 393 return _desc; 394 } 395 396 this(immutable(ubyte[]) data, ref size_t index) pure nothrow { 397 _desc = cast(IndexType) data[index]; 398 index += IndexType.sizeof; 399 with (IndexType) { 400 final switch (_desc) { 401 case FUNC: 402 _funcdesc = FuncDesc(data, index); 403 break; 404 case TABLE: 405 _tabledesc = TableDesc(data, index); 406 break; 407 case MEMORY: 408 _memorydesc = MemoryDesc(data, index); 409 break; 410 case GLOBAL: 411 _globaldesc = GlobalDesc(data, index); 412 break; 413 } 414 } 415 } 416 417 } 418 419 this(immutable(ubyte[]) data) pure nothrow { 420 size_t index; 421 mod = Vector!char(data, index); 422 name = Vector!char(data, index); 423 importdesc = ImportDesc(data, index); 424 size = index; 425 } 426 427 } 428 429 alias Import = SectionT!(ImportType); 430 431 struct TypeIndex { 432 immutable(uint) idx; 433 immutable(size_t) size; 434 this(immutable(ubyte[]) data) pure nothrow { 435 size_t index; 436 idx = u32(data, index); 437 size = index; 438 } 439 } 440 441 alias Function = SectionT!(TypeIndex); 442 443 struct TableType { 444 immutable(Types) type; 445 immutable(Limit) limit; 446 immutable(size_t) size; 447 this(immutable(ubyte[]) data) pure nothrow { 448 type = cast(Types) data[0]; 449 size_t index = Types.sizeof; 450 limit = Limit(data, index); 451 size = index; 452 } 453 454 } 455 456 alias Table = SectionT!(TableType); 457 458 struct MemoryType { 459 immutable(Limit) limit; 460 immutable(size_t) size; 461 this(immutable(ubyte[]) data) pure nothrow { 462 size_t index; 463 limit = Limit(data, index); 464 size = index; 465 } 466 } 467 468 alias Memory = SectionT!(MemoryType); 469 470 struct GlobalType { 471 immutable(ImportType.ImportDesc.GlobalDesc) global; 472 immutable(ubyte[]) expr; 473 immutable(size_t) size; 474 this(immutable(ubyte[]) data) pure { 475 size_t index; 476 global = ImportType.ImportDesc.GlobalDesc(data, index); 477 auto range = ExprRange(data[index .. $]); 478 while (!range.empty) { 479 const elm = range.front; 480 if ((elm.code is IR.END) && (elm.level == 0)) { 481 break; 482 } 483 range.popFront; 484 } 485 expr = range.data[0 .. range.index]; 486 index += range.index; 487 size = index; 488 } 489 490 ExprRange opSlice() const { 491 return ExprRange(expr); 492 } 493 } 494 495 alias Global = SectionT!(GlobalType); 496 497 struct ExportType { 498 immutable(char[]) name; 499 immutable(IndexType) desc; 500 immutable(uint) idx; 501 immutable(size_t) size; 502 this(immutable(ubyte[]) data) pure { 503 size_t index; 504 name = Vector!char(data, index); 505 desc = cast(IndexType) data[index]; 506 index += IndexType.sizeof; 507 idx = u32(data, index); 508 size = index; 509 } 510 } 511 512 alias Export = SectionT!(ExportType); 513 514 static class Start { 515 immutable(uint) idx; // Function index 516 this(immutable(ubyte[]) data) pure nothrow { 517 size_t u32_size; 518 idx = u32(data, u32_size); 519 } 520 } 521 522 struct ElementType { 523 immutable(uint) tableidx; 524 immutable(ubyte[]) expr; 525 immutable(uint[]) funcs; 526 immutable(size_t) size; 527 static immutable(ubyte[]) exprBlock(immutable(ubyte[]) data) pure { 528 auto range = ExprRange(data); 529 while (!range.empty) { 530 const elm = range.front; 531 if ((elm.code is IR.END) && (elm.level == 0)) { 532 return data[0 .. range.index]; 533 } 534 range.popFront; 535 } 536 //check(0, format("Expression in Element section expected an end code")); 537 assert(0); 538 } 539 540 this(immutable(ubyte[]) data) pure { 541 size_t index; 542 tableidx = u32(data, index); 543 expr = exprBlock(data[index .. $]); 544 index += expr.length; 545 funcs = Vector!uint(data, index); 546 size = index; 547 } 548 549 ExprRange opSlice() const { 550 return ExprRange(expr); 551 } 552 } 553 554 alias Element = SectionT!(ElementType); 555 556 struct CodeType { 557 immutable size_t size; 558 immutable(ubyte[]) data; 559 this(immutable(ubyte[]) data, ref size_t index) pure nothrow { 560 size = u32(data, index); 561 this.data = data[index .. index + size]; 562 } 563 564 struct Local { 565 uint count; 566 Types type; 567 this(immutable(ubyte[]) data, ref size_t index) pure nothrow { 568 count = u32(data, index); 569 type = cast(Types) data[index]; 570 index += Types.sizeof; 571 } 572 } 573 574 LocalRange locals() const pure nothrow { 575 return LocalRange(data); 576 } 577 578 static assert(isInputRange!LocalRange); 579 struct LocalRange { 580 immutable uint length; 581 immutable(ubyte[]) data; 582 private { 583 size_t index; 584 uint j; 585 } 586 587 protected Local _local; 588 this(immutable(ubyte[]) data) pure nothrow { 589 length = u32(data, index); 590 this.data = data; 591 popFront; 592 } 593 594 protected void set_front(ref size_t local_index) pure nothrow { 595 _local = Local(data, local_index); 596 } 597 598 const(Local) front() const pure nothrow { 599 return _local; 600 } 601 602 bool empty() const pure nothrow { 603 return (j > length); 604 } 605 606 void popFront() pure nothrow { 607 if (j < length) { 608 set_front(index); 609 } 610 j++; 611 } 612 } 613 614 ExprRange opSlice() pure const { 615 auto range = LocalRange(data); 616 while (!range.empty) { 617 range.popFront; 618 } 619 return ExprRange(data[range.index .. $]); 620 } 621 622 this(immutable(ubyte[]) data) pure nothrow { 623 size_t index; 624 auto byte_size = u32(data, index); 625 this.data = data[index .. index + byte_size]; 626 index += byte_size; 627 size = index; 628 } 629 630 } 631 632 alias Code = SectionT!(CodeType); 633 634 struct DataType { 635 immutable uint idx; 636 immutable(ubyte[]) expr; 637 immutable(char[]) base; // init value 638 immutable(size_t) size; 639 640 this(immutable(ubyte[]) data) pure { 641 size_t index; 642 idx = u32(data, index); 643 auto range = ExprRange(data[index .. $]); 644 while (!range.empty) { 645 const elm = range.front; 646 if ((elm.code is IR.END) && (elm.level == 0)) { 647 break; 648 } 649 range.popFront; 650 } 651 expr = range.data[0 .. range.index]; 652 index += range.index; 653 base = Vector!char(data, index); 654 size = index; 655 } 656 657 ExprRange opSlice() const { 658 return ExprRange(expr); 659 } 660 } 661 662 alias Data = SectionT!(DataType); 663 664 } 665 } 666 667 version (none) unittest { 668 import std.exception : assumeUnique; 669 import std.file; 670 import std.stdio; 671 672 @trusted static immutable(ubyte[]) fread(R)(R name, size_t upTo = size_t.max) { 673 import std.file : _read = read; 674 675 auto data = cast(ubyte[]) _read(name, upTo); 676 return assumeUnique(data); 677 } 678 679 writeln("WAVM Started"); 680 { 681 immutable code = fread(filename); 682 auto wasm = Wasm(code); 683 auto range = wasm[]; 684 writefln("WasmRange %s %d %d", range.empty, wasm.data.length, code.length); 685 foreach (a; range) { 686 687 writefln("%s length=%d data=%s", a.section, a.data.length, a.data); 688 if (a.section == Section.TYPE) { 689 auto _type = a.sec!(Section.TYPE); 690 writefln("Type types length %d %s", _type.length, _type[]); 691 } 692 else if (a.section == Section.IMPORT) { 693 auto _import = a.sec!(Section.IMPORT); 694 writefln("Import types length %d %s", _import.length, _import[]); 695 } 696 else if (a.section == Section.EXPORT) { 697 auto _export = a.sec!(Section.EXPORT); 698 writefln("Export types length %d %s", _export.length, _export[]); 699 } 700 else if (a.section == Section.FUNCTION) { 701 auto _function = a.sec!(Section.FUNCTION); 702 writefln("Function types length %d %s", _function.length, _function[]); 703 } 704 else if (a.section == Section.TABLE) { 705 auto _table = a.sec!(Section.TABLE); 706 writefln("Table types length %d %s", _table.length, _table[]); 707 } 708 else if (a.section == Section.MEMORY) { 709 auto _memory = a.sec!(Section.MEMORY); 710 writefln("Memory types length %d %s", _memory.length, _memory[]); 711 } 712 else if (a.section == Section.GLOBAL) { 713 auto _global = a.sec!(Section.GLOBAL); 714 writefln("Global types length %d %s", _global.length, _global[]); 715 } 716 else if (a.section == Section.START) { 717 auto _start = a.sec!(Section.START); 718 writefln("Start types %s", _start); 719 } 720 else if (a.section == Section.ELEMENT) { 721 auto _element = a.sec!(Section.ELEMENT); 722 writefln("Element types %s", _element); 723 } 724 else if (a.section == Section.CODE) { 725 auto _code = a.sec!(Section.CODE); 726 writefln("Code types length=%s", _code.length); 727 foreach (c; _code[]) { 728 writefln("c.size=%d c.data.length=%d c.locals=%s c[]=%s", 729 c.size, c.data.length, c.locals, c[]); 730 } 731 } 732 else if (a.section == Section.DATA) { 733 auto _data = a.sec!(Section.DATA); 734 writefln("Data types length=%s", _data.length); 735 foreach (d; _data[]) { 736 writefln("d.size=%d d.data.length=%d d.lodals=%s d[]=%s", 737 d.size, d.init.length, d.init, d[]); 738 } 739 } 740 } 741 742 } 743 } 744 }