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 import std.algorithm : copy; 9 10 version(unittest) { 11 import unit_threaded; 12 } 13 14 struct encoding(T) { } 15 struct condition(string cond) { } 16 struct length(alias field) { } 17 struct positionMark(string name) { } 18 19 struct header { 20 uint version_; 21 } 22 23 struct Section { 24 static auto calcPayloadLength(ref Section section, size_t[string] marks) { 25 if (section.id != 0) 26 return section.payload_len; 27 return section.payload_len - (marks["b"] - marks["a"]); 28 } 29 @encoding!varuint7 30 uint id; 31 @encoding!varuint32 32 uint payload_len; 33 @condition!"id == 0" { 34 @positionMark!"a" @encoding!varuint32 35 uint name_len; 36 @length!"name_len" 37 string name; 38 } 39 @positionMark!"b" @length!calcPayloadLength 40 ubyte[] payload; 41 uint size() { 42 return 1 + sizeOf!(varuint32)(payload_len) + payload_len; 43 } 44 } 45 46 struct import_section { 47 @encoding!varuint32 48 uint count; 49 @length!"count" 50 import_entry[] entries; 51 } 52 53 enum external_kind : ubyte { 54 Function = 0, 55 Table = 1, 56 Memory = 2, 57 Global = 3 58 } 59 60 @encoding!varint7 61 enum value_type : byte { 62 i32 = -0x01, 63 i64 = -0x02, 64 f32 = -0x03, 65 f64 = -0x04, 66 anyfunc = -0x10, 67 func = -0x20, 68 empty = -0x40 69 } 70 71 struct resizable_limits { 72 @encoding!varuint1 bool flags; 73 @encoding!varuint32 uint initial; 74 @condition!"flags == true" @varuint32 uint maximum; 75 } 76 @encoding!varint7 77 enum elem_type : byte { 78 anyfunc = -0x10 79 } 80 81 struct global_type { 82 value_type content_type; 83 @encoding!varuint1 bool mutability; 84 } 85 86 struct table_type { 87 elem_type element_type; 88 resizable_limits limits; 89 } 90 91 struct memory_type { 92 resizable_limits limits; 93 } 94 struct import_entry { 95 @encoding!varuint32 96 uint module_len; 97 @length!"module_len" 98 string module_str; 99 @encoding!varuint32 100 uint field_len; 101 @length!"field_len" 102 string field_str; 103 external_kind kind; 104 @condition!"kind == external_kind.Function" @encoding!varuint32 uint functionType; 105 @condition!"kind == external_kind.Table" table_type tableType; 106 @condition!"kind == external_kind.Memory" memory_type memoryType; 107 @condition!"kind == external_kind.Global" global_type globalType; 108 } 109 110 struct Sections(Range) { 111 private { 112 Range range; 113 bool initialized = false; 114 Section section; 115 void readSection() { 116 section = range.read!Section; 117 initialized = true; 118 } 119 } 120 this(ref Range range) { 121 this.range = range; 122 } 123 @property bool empty() { 124 return range.empty; 125 } 126 Section front() { 127 if (!initialized) 128 readSection(); 129 return section; 130 } 131 void popFront() { 132 readSection(); 133 } 134 } 135 136 static assert(isInputRange!(Sections!(ubyte[]))); 137 138 auto readSections(Range)(auto ref Range range) { 139 return Sections!(Range)(range); 140 } 141 142 @("file") 143 unittest { 144 import std.stdio; 145 import std.algorithm; 146 import std.range : take; 147 auto input = File("resource/example.wasm").byChunk(4096).joiner().drop(8); 148 auto sections = input.readSections.take(2).array(); 149 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])]); 150 } 151 152 struct RangeWithPosition(Range) if (is(ElementType!Range == ubyte)) { 153 private { Range* range; size_t pos = 0; } 154 this(Range* range) { 155 this.range = range; 156 } 157 auto front() { 158 return (*range).front(); 159 } 160 auto empty() { 161 return range.empty; 162 } 163 void popFront() { 164 (*range).popFront(); 165 pos += 1; 166 } 167 auto getPosition() { 168 return pos; 169 } 170 } 171 172 template read(T) { 173 import std.traits; 174 T read(Range)(ref Range range, size_t cnt) if (isArray!T) { 175 static if(is(T : U[], U)) {} 176 static if (isAggregateType!U) { 177 Unqual!U[] items = new Unqual!U[cnt]; 178 foreach(idx; 0..cnt) { 179 items[idx] = .read!U(range); 180 } 181 return cast(T)items; 182 } else { 183 Unqual!U[] items = new Unqual!U[cnt]; 184 foreach(idx; 0..cnt) { 185 items[idx] = range.front(); 186 range.popFront(); 187 } 188 return cast(T)items; 189 } 190 } 191 T read(Range)(auto ref Range plainRange) { 192 static if (isAggregateType!T) { 193 auto range = RangeWithPosition!Range(&plainRange); 194 T t; 195 size_t[string] marks; 196 assert(range.getPosition() == 0); 197 static foreach(field; T.tupleof) {{ 198 static if (hasUDA!(field, positionMark)) { 199 alias positionMarks = getUDAs!(field, positionMark); 200 static foreach(marker; positionMarks) {{ 201 enum mark = TemplateArgsOf!(marker)[0]; 202 marks[mark] = range.getPosition(); 203 }} 204 } 205 static if (hasUDA!(field, condition)) { 206 alias conditions = getUDAs!(field, condition); 207 static foreach(c; conditions) {{ 208 enum condition = TemplateArgsOf!(c)[0]; 209 mixin("if (t."~condition~") readMember!(field.stringof)(range, t, marks);"); 210 }} 211 } else { 212 readMember!(field.stringof)(range, t, marks); 213 } 214 }} 215 return t; 216 } else static if (is(T == enum)) { 217 static if (hasUDA!(T, encoding)) { 218 alias encodingType = TemplateArgsOf!(getUDAs!(T, encoding)[0])[0]; 219 return readEncoding!(encodingType, T)(plainRange); 220 } else { 221 T t; 222 ubyte[] mem = (cast(ubyte[])(&t)[0..1]); 223 foreach(idx; 0..mem.length) { 224 mem[idx] = plainRange.front; 225 plainRange.popFront(); 226 } 227 return t; 228 } 229 } else { 230 T t; 231 ubyte[] mem = (cast(ubyte[])(&t)[0..1]); 232 foreach(idx; 0..mem.length) { 233 mem[idx] = plainRange.front; 234 plainRange.popFront(); 235 } 236 return t; 237 } 238 } 239 } 240 241 Type readEncoding(Encoding, Type, Range)(auto ref Range range) { 242 return cast(Type)Encoding.decode(range); 243 } 244 245 template readMember(string name) { 246 void readMember(Range, T)(auto ref Range range, ref T t, ref size_t[string] marks) { 247 alias field = AliasSeq!(__traits(getMember, t, name))[0]; 248 alias Field = typeof(field); 249 static if (is(Field : U[], U)) { 250 assert(hasUDA!(field, length)); 251 alias lengths = getUDAs!(field, length); 252 static if (is(typeof(TemplateArgsOf!(lengths[0])[0]) == string)) { 253 enum length = TemplateArgsOf!(lengths[0])[0]; 254 size_t len = __traits(getMember, t, length); 255 __traits(getMember, t, name) = read!(Field)(range, len); 256 } else { 257 alias lengthFun = TemplateArgsOf!(lengths[0])[0]; 258 size_t len = lengthFun(t, marks); 259 __traits(getMember, t, name) = read!(Field)(range, len); 260 } 261 } else static if (hasUDA!(field, encoding)) { 262 alias EncodingType = TemplateArgsOf!(getUDAs!(field, encoding)[0])[0]; 263 __traits(getMember, t, name) = readEncoding!(EncodingType, Field)(range); 264 } else { 265 __traits(getMember, t, name) = read!(Field)(range); 266 } 267 } 268 } 269 270 unittest { 271 ubyte[] data = [0x01,'A',0x0,0x0,0x0,0x0,0x0,0x0,0x0]; 272 data.read!import_entry.shouldEqual(import_entry(1,"A",0,"",external_kind.Function,0)); 273 } 274 275 unittest { 276 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]; 277 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])); 278 data.length.shouldEqual(0); 279 } 280 281 unittest { 282 ubyte[] data = [0x00, 0x1f, 0x01, 65, 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]; 283 data.read!Section.shouldEqual(Section(0, 31, 1, "A", [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])); 284 data.length.shouldEqual(0); 285 } 286 287 unittest { 288 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]; 289 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])); 290 data.length.shouldEqual(0); 291 } 292 293 unittest { 294 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]; 295 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))])); 296 data.length.shouldEqual(0); 297 }