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 }