/** A WebAssembly module that parses WebAssembly modules. */ // Common constants shared between AssemblyScript and TypeScript import { Type, SectionId, ExternalKind, NameType, MAX_PAGES, MAX_ELEMS, Opcode } from "../src/common"; import * as opt from "./options"; /** Current offset in memory. */ var off: usize = 0; /** Reads an unsigned integer from memory. */ function readUint(): u32 { var pos = off; var val = load(pos); off = pos + sizeof(); return val; } /** Reads an unsigned 64-bit integer from memory. */ function readUint64(): u64 { var pos = off; var val = load(pos); off = pos + 8; return val; } /** Reads a LEB128-encoded unsigned integer from memory. */ function readVaruint(size: u32): u32 { var val: u32 = 0; var shl: u32 = 0; var byt: u32; var pos = off; do { byt = load(pos++); val |= (byt & 0x7F) << shl; if (!(byt & 0x80)) break; shl += 7; } while (true); off = pos; return val; } /** Reads a LEB128-encoded signed integer from memory. */ function readVarint(size: u32): i32 { var val: u32 = 0; var shl: u32 = 0; var byt: u32; var pos = off; do { byt = load(pos++); val |= (byt & 0x7F) << shl; shl += 7; } while (byt & 0x80); off = pos; return select(val | (~0 << shl), val, shl < size && (byt & 0x40) != 0); } /** Reads a LEB128-encoded signed 64-bit integer from memory. */ function readVarint64(): i64 { var val: u64 = 0; var shl: u64 = 0; var byt: u64; var pos = off; do { byt = load(pos++); val |= (byt & 0x7F) << shl; shl += 7; } while (byt & 0x80); off = pos; return select(val | (~0 << shl), val, shl < 64 && (byt & 0x40) != 0); } function skipInitExpr(): void { var op = readUint(); switch (op) { case Opcode.i32_const: { readVarint(32); break; } case Opcode.i64_const: { readVarint64(); break; } case Opcode.f32_const: { readUint(); break; } case Opcode.f64_const: { readUint64(); break; } case Opcode.get_global: { readVaruint(32); break; } default: unreachable(); // MVP } if (readUint() != Opcode.end) unreachable(); } /** Starts parsing the module that has been placed in memory. */ export function parse(begin: usize, end: usize): void { off = begin; var magic = readUint(); if (magic != 0x6D736100) unreachable(); var version = readUint(); if (version != 1) unreachable(); var fun_space_index: u32 = 0; var glo_space_index: u32 = 0; var mem_space_index: u32 = 0; var tbl_space_index: u32 = 0; while (off < end) { let section_off = off; let id = readVaruint(7); let payload_len = readVaruint(32); let name_off = 0; let name_len = 0; if (!id) { let before = off; name_len = readVaruint(32); name_off = off; off += name_len; payload_len -= off - before; } else if (id > SectionId.Data) unreachable(); let payload_off = off; if (opt.onSection( id, payload_off, payload_len, name_off, name_len )) { switch (id) { case SectionId.Type: { let count = readVaruint(32); for (let index: u32 = 0; index < count; ++index) { let form = readVarint(7) & 0x7f; opt.onType( index, form ); let paramCount = readVaruint(32); for (let paramIndex: u32 = 0; paramIndex < paramCount; ++paramIndex) { let paramType = readVarint(7) & 0x7f; opt.onTypeParam( index, paramIndex, paramType ); } let returnCount = readVaruint(1); // MVP for (let returnIndex: u32 = 0; returnIndex < returnCount; ++returnIndex) { let returnType = readVarint(7) & 0x7f; opt.onTypeReturn( index, returnIndex, returnType ); } } break; } case SectionId.Import: { let count = readVaruint(32); for (let index: u32 = 0; index < count; ++index) { let module_len = readVaruint(32); let module_off = off; off += module_len; let field_len = readVaruint(32); let field_off = off; off += field_len; let kind = readUint(); opt.onImport( index, kind, module_off, module_len, field_off, field_len ); switch (kind) { case ExternalKind.Function: { let type = readVaruint(32); opt.onFunctionImport( fun_space_index++, type ); break; } case ExternalKind.Table: { let type = readVarint(7) & 0x7f; let flags = readVaruint(1); let initial = readVaruint(32); let maximum = flags & 1 ? readVaruint(32) : MAX_ELEMS; opt.onTableImport( tbl_space_index++, type, initial, maximum, flags ); break; } case ExternalKind.Memory: { let flags = readVaruint(1); let initial = readVaruint(32); let maximum = flags & 1 ? readVaruint(32) : MAX_PAGES; opt.onMemoryImport( mem_space_index++, initial, maximum, flags ); break; } case ExternalKind.Global: { let type = readVarint(7) & 0x7f; let mutability = readVaruint(1); opt.onGlobalImport( glo_space_index++, type, mutability ); break; } default: unreachable(); } } break; } case SectionId.Function: { let count = readVaruint(32); for (let i: u32 = 0; i < count; ++i) { let typeIndex = readVaruint(32); opt.onFunction( fun_space_index++, typeIndex ); } break; } case SectionId.Table: { let count = readVaruint(32); for (let index: u32 = 0; index < count; ++index) { let type = readVaruint(7) & 0x7f; let flags = readVaruint(1); let initial = readVaruint(32); let maximum = flags & 1 ? readVaruint(32) : MAX_ELEMS; opt.onTable( tbl_space_index++, type, initial, maximum, flags ); } break; } case SectionId.Memory: { let count = readVaruint(32); for (let index: u32 = 0; index < count; ++index) { let flags = readVaruint(1); let initial = readVaruint(32); let maximum = flags & 1 ? readVaruint(32) : MAX_PAGES; opt.onMemory( mem_space_index++, initial, maximum, flags ); } break; } case SectionId.Global: { let count = readVaruint(32); for (let i: u32 = 0; i < count; ++i) { let type = readVarint(7) & 0x7f; let mutability = readVaruint(1); skipInitExpr(); opt.onGlobal( glo_space_index++, type, mutability ); } break; } case SectionId.Export: { let count = readVaruint(32); for (let index: u32 = 0; index < count; ++index) { let field_len = readVaruint(32); let field_off = off; off += field_len; let kind = readUint(); let kind_index = readVaruint(32); opt.onExport( index, kind, kind_index, field_off, field_len ); } break; } case SectionId.Start: { let index = readVaruint(32); opt.onStart( index ); break; } case SectionId.Custom: { if ( name_len == 4 && load(name_off) == 0x656D616E // "name" ) { let name_type = readVaruint(7); let name_payload_len = readVaruint(32); let name_payload_off = off; switch (name_type) { case NameType.Module: { let module_name_len = readVaruint(32); let module_name_off = off; opt.onModuleName( module_name_off, module_name_len ); break; } case NameType.Function: { let count = readVaruint(32); for (let i: u32 = 0; i < count; ++i) { let fn_index = readVaruint(32); let fn_name_len = readVaruint(32); let fn_name_off = off; off += fn_name_len; opt.onFunctionName( fn_index, fn_name_off, fn_name_len ); } break; } case NameType.Local: { let count = readVaruint(32); for (let i: u32 = 0; i < count; ++i) { let fn_index = readVaruint(32); let lc_count = readVaruint(32); for (let j: u32 = 0; j < lc_count; ++j) { let lc_index = readVaruint(32); let lc_name_len = readVaruint(32); let lc_name_off = off; off += lc_name_len; opt.onLocalName( fn_index, lc_index, lc_name_off, lc_name_len ); } } break; } default: unreachable(); } off = name_payload_off + name_payload_len; // ignore errors break; } else if ( name_len == 16 && load(name_off ) == 0x614D656372756F73 && // "sourceMa" load(name_off + 8) == 0x4C5255676E697070 // "ppingURL" ) { let url_len = readVaruint(32); let url_off = off; off += url_len; opt.onSourceMappingURL( url_off, url_len ); } off = payload_off + payload_len; // ignore errors break; } case SectionId.Element: case SectionId.Code: case SectionId.Data: { // skip off += payload_len; break; } default: unreachable(); } } else { // skip off += payload_len; } } if (off != end) unreachable(); }