1 module wasm_reader.reader;
2 
3 import wasm_reader.leb;
4 import std.typecons;
5 import std.meta;
6 import std.traits;
7 import std.range;
8 
9 version(unittest) {
10   import unit_threaded;
11 }
12 
13 struct encoding(T) { }
14 struct condition(string cond) { }
15 struct length(string field) { }
16 
17 struct header {
18   uint version_;
19 }
20 
21 struct Section {
22   @encoding!varuint7
23   uint id;
24   @encoding!varuint32
25   uint payload_len;
26   @condition!"id == 0" {
27     @encoding!varuint32
28       uint name_len;
29     @length!"name_len"
30       string name;
31   }
32   // TODO: length is not payload_len, length is payload_len - name_len - name
33   @length!"payload_len"
34   ubyte[] payload;
35 }
36 
37 struct import_section {
38   @encoding!varuint32
39   uint count;
40   @length!"count"
41   import_entry[] entries;
42 }
43 
44 enum external_kind : ubyte  {
45   Function = 0,
46     Table = 1,
47     Memory = 2,
48     Global = 3
49 }
50 
51 @encoding!varint7
52 enum value_type : byte {
53   i32 = -0x01,
54     i64 = -0x02,
55     f32 = -0x03,
56     f64 = -0x04,
57     anyfunc = -0x10,
58     func = -0x20,
59     empty = -0x40
60 }
61 
62 struct resizable_limits {
63   @encoding!varuint1 bool flags;
64   @encoding!varuint32 uint initial;
65   @condition!"flags == true" @varuint32 uint maximum;
66 }
67 @encoding!varint7
68 enum elem_type : byte {
69   anyfunc = -0x10
70 }
71 
72 struct global_type {
73   value_type content_type;
74   @encoding!varuint1 bool mutability;
75 }
76 
77 struct table_type {
78   elem_type element_type;
79   resizable_limits limits;
80 }
81 
82 struct memory_type {
83   resizable_limits limits;
84 }
85 struct import_entry {
86   @encoding!varuint32
87   uint module_len;
88   @length!"module_len"
89   string module_str;
90   @encoding!varuint32
91   uint field_len;
92   @length!"field_len"
93   string field_str;
94   external_kind kind;
95   @condition!"kind == external_kind.Function" @encoding!varuint32 uint functionType;
96   @condition!"kind == external_kind.Table" table_type tableType;
97   @condition!"kind == external_kind.Memory" memory_type memoryType;
98   @condition!"kind == external_kind.Global" global_type globalType;
99 }
100 
101 struct Sections(Range) {
102   private {
103     Range range;
104     bool initialized = false;
105     Section section;
106     void readSection() {
107       section = range.read!Section;
108       initialized = true;
109     }
110   }
111   this(ref Range range) {
112     this.range = range;
113   }
114   @property bool empty() {
115     return range.empty;
116   }
117   Section front() {
118     if (!initialized)
119       readSection();
120     return section;
121   }
122   void popFront() {
123     readSection();
124   }
125 }
126 
127 static assert(isInputRange!(Sections!(ubyte[])));
128 
129 auto readSections(Range)(auto ref Range range) {
130   return Sections!(Range)(range);
131 }
132 
133 unittest {
134   import std.stdio;
135   import std.algorithm;
136   import std.range : take;
137   auto input = File("resource/example.wasm").byChunk(4096).joiner().drop(8);
138   auto sections = input.readSections.take(2).array();
139   sections.shouldEqual([Section(1, 29, 0, "", [6, 96, 0, 0, 96, 2, 127, 127, 1, 127, 96, 1, 127, 0, 96, 1, 127, 1, 127, 96, 1, 125, 1, 127, 96, 2, 127, 127, 0]), Section(2, 181, 0, "", [7, 3, 101, 110, 118, 26, 115, 112, 97, 115, 109, 95, 97, 100, 100, 80, 114, 105, 109, 105, 116, 105, 118, 101, 95, 95, 115, 116, 114, 105, 110, 103, 0, 1, 3, 101, 110, 118, 11, 99, 111, 110, 115, 111, 108, 101, 95, 108, 111, 103, 0, 2, 3, 101, 110, 118, 23, 115, 112, 97, 115, 109, 95, 97, 100, 100, 80, 114, 105, 109, 105, 116, 105, 118, 101, 95, 95, 105, 110, 116, 0, 3, 3, 101, 110, 118, 25, 115, 112, 97, 115, 109, 95, 97, 100, 100, 80, 114, 105, 109, 105, 116, 105, 118, 101, 95, 95, 102, 108, 111, 97, 116, 0, 4, 3, 101, 110, 118, 21, 68, 111, 99, 117, 109, 101, 110, 116, 95, 108, 111, 99, 97, 116, 105, 111, 110, 95, 71, 101, 116, 0, 5, 3, 101, 110, 118, 18, 115, 112, 97, 115, 109, 95, 114, 101, 109, 111, 118, 101, 79, 98, 106, 101, 99, 116, 0, 2, 3, 101, 110, 118, 6, 109, 101, 109, 111, 114, 121, 2, 0, 2])]);
140 }
141 
142 template read(T) {
143   import std.traits;
144   T read(Range)(auto ref Range range, size_t cnt) if (isArray!T) {
145     static if(is(T : U[], U)) {}
146     static if (isAggregateType!U) {
147       U[] items = new U[cnt];
148       foreach(idx; 0..cnt) {
149         items[idx] = .read!U(range);
150       }
151       return items;
152     } else {
153       auto t = cast(T)(range.take(U.sizeof*cnt).array);
154       range.popFrontN(U.sizeof*cnt);
155       return t;
156     }
157   }
158   T read(Range)(auto ref Range range) {
159     static if (isAggregateType!T) {
160       T t;
161       static foreach(field; T.tupleof) {{
162           static if (hasUDA!(field, condition)) {
163             alias conditions = getUDAs!(field, condition);
164             static foreach(c; conditions) {{
165                 enum condition = TemplateArgsOf!(c)[0];
166                 mixin("if (t."~condition~") readMember!(field.stringof)(range, t);");
167               }}
168           } else {
169             readMember!(field.stringof)(range, t);
170           }
171         }}
172       return t;
173     } else static if (is(T == enum)) {
174       static if (hasUDA!(T, encoding)) {
175         alias encodingType = TemplateArgsOf!(getUDAs!(T, encoding)[0])[0];
176         return readEncoding!(encodingType, T)(range);
177       } else {
178         T t = (cast(T[])(range[0 .. T.sizeof]))[0];
179         range.popFrontN(T.sizeof);
180         return t;
181       }
182     } else {
183       T t = (cast(T[])(range[0 .. T.sizeof]))[0];
184       range.popFrontN(T.sizeof);
185       return t;
186     }
187   }
188 }
189 
190 Type readEncoding(Encoding, Type, Range)(auto ref Range range) {
191   return cast(Type)Encoding.decode(range);
192 }
193 
194 template readMember(string name) {
195   void readMember(Range, T)(auto ref Range range, ref T t) {
196     alias field = AliasSeq!(__traits(getMember, t, name))[0];
197     alias Field = typeof(field);
198     static if (is(Field : U[], U)) {
199       assert(hasUDA!(field, length));
200       alias lengths = getUDAs!(field, length);
201       enum length = TemplateArgsOf!(lengths)[0];
202       size_t len = __traits(getMember, t, length);
203       __traits(getMember, t, name) = read!(Field)(range, len);
204     } else static if (hasUDA!(field, encoding)) {
205       alias EncodingType = TemplateArgsOf!(getUDAs!(field, encoding)[0])[0];
206       __traits(getMember, t, name) = readEncoding!(EncodingType, Field)(range);
207     } else {
208       __traits(getMember, t, name) = read!(Field)(range);
209     }
210   }
211 }
212 
213 unittest {
214   ubyte[] data = [0x01,'A',0x0,0x0,0x0,0x0,0x0,0x0,0x0];
215   data.read!import_entry.shouldEqual(import_entry(1,"A",0,"",external_kind.Function,0));
216 }
217 
218 unittest {
219   ubyte[] data = [0x01, 0x1d, 0x06, 0x60, 0x01, 0x7f, 0x00, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x60, 0x01, 0x7d, 0x01, 0x7f, 0x60, 0x02, 0x7f, 0x7f, 0x00, 0x60, 0x00, 0x00];
220   data.read!Section.shouldEqual(Section(1, 29, 0, "", [6, 96, 1, 127, 0, 96, 2, 127, 127, 1, 127, 96, 1, 127, 1, 127, 96, 1, 125, 1, 127, 96, 2, 127, 127, 0, 96, 0, 0]));
221   data.length.shouldEqual(0);
222 }
223 
224 unittest {
225   ubyte[] data = [0x02, 0xb5, 0x01, 0x07, 0x03, 0x65, 0x6e, 0x76, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x02, 0x03, 0x65, 0x6e, 0x76, 0x0b, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x5f, 0x6c, 0x6f, 0x67, 0x00, 0x00, 0x03, 0x65, 0x6e, 0x76, 0x1a, 0x73, 0x70, 0x61, 0x73, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x73, 0x70, 0x61, 0x73, 0x6d, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x00, 0x00, 0x03, 0x65, 0x6e, 0x76, 0x17, 0x73, 0x70, 0x61, 0x73, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x5f, 0x69, 0x6e, 0x74, 0x00, 0x02, 0x03, 0x65, 0x6e, 0x76, 0x19, 0x73, 0x70, 0x61, 0x73, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x00, 0x03, 0x03, 0x65, 0x6e, 0x76, 0x15, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x47, 0x65, 0x74, 0x00, 0x04];
226   data.read!Section.shouldEqual(Section(2, 181, 0, "", [7, 3, 101, 110, 118, 6, 109, 101, 109, 111, 114, 121, 2, 0, 2, 3, 101, 110, 118, 11, 99, 111, 110, 115, 111, 108, 101, 95, 108, 111, 103, 0, 0, 3, 101, 110, 118, 26, 115, 112, 97, 115, 109, 95, 97, 100, 100, 80, 114, 105, 109, 105, 116, 105, 118, 101, 95, 95, 115, 116, 114, 105, 110, 103, 0, 1, 3, 101, 110, 118, 18, 115, 112, 97, 115, 109, 95, 114, 101, 109, 111, 118, 101, 79, 98, 106, 101, 99, 116, 0, 0, 3, 101, 110, 118, 23, 115, 112, 97, 115, 109, 95, 97, 100, 100, 80, 114, 105, 109, 105, 116, 105, 118, 101, 95, 95, 105, 110, 116, 0, 2, 3, 101, 110, 118, 25, 115, 112, 97, 115, 109, 95, 97, 100, 100, 80, 114, 105, 109, 105, 116, 105, 118, 101, 95, 95, 102, 108, 111, 97, 116, 0, 3, 3, 101, 110, 118, 21, 68, 111, 99, 117, 109, 101, 110, 116, 95, 108, 111, 99, 97, 116, 105, 111, 110, 95, 71, 101, 116, 0, 4]));
227   data.length.shouldEqual(0);
228 }
229 
230 unittest {
231   ubyte[] data = [0x07, 0x03, 0x65, 0x6e, 0x76, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x02, 0x03, 0x65, 0x6e, 0x76, 0x0b, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x5f, 0x6c, 0x6f, 0x67, 0x00, 0x00, 0x03, 0x65, 0x6e, 0x76, 0x1a, 0x73, 0x70, 0x61, 0x73, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x73, 0x70, 0x61, 0x73, 0x6d, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x00, 0x00, 0x03, 0x65, 0x6e, 0x76, 0x17, 0x73, 0x70, 0x61, 0x73, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x5f, 0x69, 0x6e, 0x74, 0x00, 0x02, 0x03, 0x65, 0x6e, 0x76, 0x19, 0x73, 0x70, 0x61, 0x73, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x00, 0x03, 0x03, 0x65, 0x6e, 0x76, 0x15, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x47, 0x65, 0x74, 0x00, 0x04];
232   data.read!import_section.shouldEqual(import_section(7, [import_entry(3, "env", 6, "memory", external_kind.Memory, 0, table_type(elem_type.anyfunc, resizable_limits(false, 0, 0)), memory_type(resizable_limits(false, 2, 0)), global_type(value_type.i32, false)), import_entry(3, "env", 11, "console_log", external_kind.Function, 0, table_type(elem_type.anyfunc, resizable_limits(false, 0, 0)), memory_type(resizable_limits(false, 0, 0)), global_type(value_type.i32, false)), import_entry(3, "env", 26, "spasm_addPrimitive__string", external_kind.Function, 1, table_type(elem_type.anyfunc, resizable_limits(false, 0, 0)), memory_type(resizable_limits(false, 0, 0)), global_type(value_type.i32, false)), import_entry(3, "env", 18, "spasm_removeObject", external_kind.Function, 0, table_type(elem_type.anyfunc, resizable_limits(false, 0, 0)), memory_type(resizable_limits(false, 0, 0)), global_type(value_type.i32, false)), import_entry(3, "env", 23, "spasm_addPrimitive__int", external_kind.Function, 2, table_type(elem_type.anyfunc, resizable_limits(false, 0, 0)), memory_type(resizable_limits(false, 0, 0)), global_type(value_type.i32, false)), import_entry(3, "env", 25, "spasm_addPrimitive__float", external_kind.Function, 3, table_type(elem_type.anyfunc, resizable_limits(false, 0, 0)), memory_type(resizable_limits(false, 0, 0)), global_type(value_type.i32, false)), import_entry(3, "env", 21, "Document_location_Get", external_kind.Function, 4, table_type(elem_type.anyfunc, resizable_limits(false, 0, 0)), memory_type(resizable_limits(false, 0, 0)), global_type(value_type.i32, false))]));
233   data.length.shouldEqual(0);
234 }