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 }