Update dist files; Also parse name and source mapping sections in lib/parse

This commit is contained in:
dcodeIO
2018-04-04 03:41:04 +02:00
parent c45a35b1c1
commit acfef646ef
15 changed files with 290 additions and 89 deletions

View File

@ -1,6 +1,6 @@
# ![AS](https://avatars1.githubusercontent.com/u/28916798?s=48) parse
A WebAssembly binary parser in WebAssembly.
A WebAssembly binary parser in WebAssembly. Super small, super fast, with TypeScript support.
API
---
@ -53,6 +53,18 @@ API
* **onExport**?(index: `number`, kind: `ExternalKind`, kindIndex: `number`, nameOff: `number`, nameLen: `number`): `void`<br />
Called with each export if the export section is evaluated.
* **onSourceMappingURL**?(offset: `number`, length: `number`): `void`<br />
Called with the source map URL if the 'sourceMappingURL' section is evaluated.
* **onModuleName**?(offset: `number`, length: `number`): `void`<br />
Called with the module name if present and the 'name' section is evaluated.
* **onFunctionName**?(index: `number`, offset: `number`, length: `number`): `void`<br />
Called with each function name if present and the 'name' section is evaluated.
* **onLocalName**?(funcIndex: `number`, index: `number`, offset: `number`, length: `number`): `void`<br />
Called with each local name if present and the 'name' section is evaluated.
* **Type**<br />
A value or element type, depending on context.

View File

@ -5,6 +5,7 @@ import {
Type,
SectionId,
ExternalKind,
NameType,
MAX_PAGES,
MAX_TABLES,
Opcode
@ -90,14 +91,22 @@ declare function onFunction(index: u32, typeIndex: u32): void;
declare function onGlobal(index: u32, type: u32, mutability: u32): void;
declare function onExport(index: u32, kind: u32, kindIndex: u32, nameOffset: u32, nameLength: u32): void;
declare function onStart(index: u32): void;
declare function onSourceMappingURL(offset: u32, length: u32): void;
declare function onModuleName(offset: u32, length: u32): void;
declare function onFunctionName(index: u32, offset: u32, length: u32): void;
declare function onLocalName(funcIndex: u32, index: u32, offset: u32, length: u32): void;
/** Starts parsing the module that has been placed in memory. */
function parse(): void {
export function parse(begin: usize, end: usize): void {
off = begin;
var magic = readUint<u32>();
if (magic != 0x6D736100) unreachable();
var version = readUint<u32>();
if (version != 1) unreachable();
var end: usize = current_memory() << 16;
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);
@ -105,13 +114,11 @@ function parse(): void {
let name_off = 0;
let name_len = 0;
if (!id) {
let before = off;
name_len = readVaruint(32);
if (!name_len) {
off = section_off;
break;
}
name_off = off;
off += name_len;
payload_len -= off - before;
} else if (id > <u32>SectionId.Data) unreachable();
let payload_off = off;
if (onSection(
@ -126,16 +133,27 @@ function parse(): void {
let count = readVaruint(32);
for (let index: u32 = 0; index < count; ++index) {
let form = readVarint(7) & 0x7f;
onType(index, form);
onType(
index,
form
);
let paramCount = readVaruint(32);
for (let paramIndex: u32 = 0; paramIndex < paramCount; ++paramIndex) {
let paramType = readVarint(7) & 0x7f;
onTypeParam(index, paramIndex, paramType);
onTypeParam(
index,
paramIndex,
paramType
);
}
let returnCount = readVaruint(1); // MVP
for (let returnIndex: u32 = 0; returnIndex < returnCount; ++returnIndex) {
let returnType = readVarint(7) & 0x7f;
onTypeReturn(index, returnIndex, returnType);
onTypeReturn(
index,
returnIndex,
returnType
);
}
}
break;
@ -145,10 +163,10 @@ function parse(): void {
for (let index: u32 = 0; index < count; ++index) {
let module_len = readVaruint(32);
let module_off = off;
off += module_off;
off += module_len;
let field_len = readVaruint(32);
let field_off = off;
off += field_off;
off += field_len;
let kind = readUint<u8>();
onImport(
index,
@ -161,7 +179,10 @@ function parse(): void {
switch (kind) {
case ExternalKind.Function: {
let type = readVaruint(32);
onFunctionImport(index, type);
onFunctionImport(
fun_space_index++,
type
);
break;
}
case ExternalKind.Table: {
@ -169,20 +190,35 @@ function parse(): void {
let flags = readVaruint(1);
let initial = readVaruint(32);
let maximum: u32 = flags & 1 ? readVaruint(32) : MAX_TABLES;
onTableImport(index, type, initial, maximum, flags);
onTableImport(
tbl_space_index++,
type,
initial,
maximum,
flags
);
break;
}
case ExternalKind.Memory: {
let flags = readVaruint(1);
let initial = readVaruint(32);
let maximum: u32 = flags & 1 ? readVaruint(32) : MAX_PAGES;
onMemoryImport(index, initial, maximum, flags);
onMemoryImport(
mem_space_index++,
initial,
maximum,
flags
);
break;
}
case ExternalKind.Global: {
let type = readVarint(7) & 0x7f;
let mutability = readVaruint(1);
onGlobalImport(index, type, mutability);
onGlobalImport(
glo_space_index++,
type,
mutability
);
break;
}
default: unreachable();
@ -192,9 +228,12 @@ function parse(): void {
}
case SectionId.Function: {
let count = readVaruint(32);
for (let index: u32 = 0; index < count; ++index) {
for (let i: u32 = 0; i < count; ++i) {
let typeIndex = readVaruint(32);
onFunction(index, typeIndex);
onFunction(
fun_space_index++,
typeIndex
);
}
break;
}
@ -204,13 +243,18 @@ function parse(): void {
let flags = readVaruint(1);
let initial = readVaruint(32);
let maximum: u32 = flags ? readVaruint(32) : MAX_PAGES;
onMemory(index, initial, maximum, flags);
onMemory(
mem_space_index++,
initial,
maximum,
flags
);
}
break;
}
case SectionId.Global: {
let count = readVaruint(32);
for (let index: u32 = 0; index < count; ++index) {
for (let i: u32 = 0; i < count; ++i) {
let type = readVarint(7) & 0x7f;
let mutability = readVaruint(1);
let op = readUint<u8>();
@ -239,7 +283,11 @@ function parse(): void {
}
op = readUint<u8>();
if (op != Opcode.end) unreachable();
onGlobal(index, type, mutability);
onGlobal(
glo_space_index++,
type,
mutability
);
}
break;
}
@ -268,8 +316,80 @@ function parse(): void {
);
break;
}
case SectionId.Custom:
case SectionId.Code: { // TODO
case SectionId.Custom: {
if (
name_len == 4 &&
load<u32>(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;
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;
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;
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<u64>(name_off ) == 0x614D656372756F73 && // "sourceMa"
load<u64>(name_off + 8) == 0x4C5255676E697070 // "ppingURL"
) {
let url_len = readVaruint(32);
let url_off = off;
off += url_len;
onSourceMappingURL(
url_off,
url_len
);
}
off = payload_off + payload_len; // ignore errors
break;
}
case SectionId.Code: { // skip
off += payload_len;
break;
}
@ -279,7 +399,5 @@ function parse(): void {
off += payload_len;
}
}
if (off != end) unreachable();
}
// Start parsing immediately
parse();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,7 @@
"main": "index.js",
"types": "index.d.ts",
"scripts": {
"build:as": "asc assembly/index.ts -O3 -b build/index.wasm --noMemory --importMemory --sourceMap --noDebug --validate",
"build:as": "asc assembly/index.ts -O3 -b build/index.wasm --noMemory --importMemory --noDebug --sourceMap --validate",
"build": "npm run build:as && webpack --mode production --display-modules",
"test": "ts-node tests/"
},

View File

@ -35,6 +35,13 @@ export enum ExternalKind {
Global = 3
}
/** Name section types. */
export enum NameType {
Module = 0,
Function = 1,
Local = 2
}
/** Maximum number of pages. */
export const MAX_PAGES = 0xffff;

View File

@ -36,7 +36,15 @@ export interface ParseOptions {
/** Called with the start function index if the start section is evaluated. */
onStart?(index: number): void;
/** Called with each export if the export section is evaluated. */
onExport?(index: number, kind: ExternalKind, kindIndex: number, nameOff: number, nameLen: number);
onExport?(index: number, kind: ExternalKind, kindIndex: number, nameOff: number, nameLen: number): void;
/** Called with the source map URL if the 'sourceMappingURL' section is evaluated. */
onSourceMappingURL?(offset: number, length: number): void;
/** Called with the module name if present and the 'name' section is evaluated. */
onModuleName?(offset: number, length: number): void;
/** Called with each function name if present and the 'name' section is evaluated. */
onFunctionName?(index: number, offset: number, length: number): void;
/** Called with each local name if present and the 'name' section is evaluated. */
onLocalName?(funcIndex: number, index: number, offset: number, length: number): void;
}
/** Parses the contents of a WebAssembly binary according to the specified options. */
@ -47,9 +55,9 @@ export function parse(binary: Uint8Array, options?: ParseOptions): void {
if (!compiled) compiled = new WebAssembly.Module(base64_decode(WASM_DATA));
// use the binary as the parser's memory
var bytes = binary.length + 5; // leave space for u8 + u32 zero terminator
var pages = ((bytes + 0xffff) & ~0xffff) >> 16;
var memory = new WebAssembly.Memory({ initial: pages });
var nBytes = binary.length;
var nPages = ((nBytes + 0xffff) & ~0xffff) >> 16;
var memory = new WebAssembly.Memory({ initial: nPages });
var buffer = new Uint8Array(memory.buffer);
buffer.set(binary);
@ -58,13 +66,8 @@ export function parse(binary: Uint8Array, options?: ParseOptions): void {
// instantiate the parser and return its exports
function nop(): void {}
var imports = {
env: {
memory: memory
}
};
[
"onSection",
var imports = { env: { memory } };
[ "onSection",
"onType",
"onTypeParam",
"onTypeReturn",
@ -77,10 +80,14 @@ export function parse(binary: Uint8Array, options?: ParseOptions): void {
"onFunction",
"onGlobal",
"onExport",
"onStart"
"onStart",
"onSourceMappingURL",
"onModuleName",
"onFunctionName",
"onLocalName"
].forEach((name: string): void => imports.env[name] = options[name] || nop);
var instance = new WebAssembly.Instance(compiled, imports);
return instance.exports;
instance.exports.parse(0, nBytes);
}
export declare namespace parse {

View File

@ -6,58 +6,56 @@ import {
parse
} from "..";
const testBinary = fs.readFileSync(__dirname + "/libm.wasm");
function onSection(id: SectionId, offset: number, length: number, nameOffset: number, nameLength: number): boolean {
var name = id == 0 ? parse.readString(nameOffset, nameLength) : SectionId[id];
var name = id == 0 ? "'" + parse.readString(nameOffset, nameLength) + "'" : SectionId[id];
console.log(name + " section at " + offset + ".." + (offset + length));
return true;
}
function onType(index: number, form: Type): void {
console.log("- FunctionType[" + index + "] is " + Type[form]);
console.log("- FunctionType[" + index + "]: " + Type[form]);
}
function onTypeParam(index: number, paramIndex: number, paramType: Type): void {
console.log(" > param[" + paramIndex + "] = " + Type[paramType]);
console.log(" > param[" + paramIndex + "] -> " + Type[paramType]);
}
function onTypeReturn(index: number, returnIndex: number, returnType: Type): void {
console.log(" > return[" + returnIndex + "] = " + Type[returnType]);
console.log(" > return[" + returnIndex + "] -> " + Type[returnType]);
}
function onImport(index: number, kind: ExternalKind, moduleOff: number, moduleLen: number, fieldOff: number, fieldLen: number): void {
var moduleName = parse.readString(moduleOff, moduleLen);
var fieldName = parse.readString(fieldOff, fieldLen);
console.log("- Import[" + index + "] is '" + moduleName + "." + fieldName + "'");
console.log("- Import[" + index + "]: '" + moduleName + "." + fieldName + "'");
}
function onFunctionImport(index: number, type: number): void {
console.log(" > FunctionType[" + type + "]");
function onFunctionImport(funIndex: number, type: number): void {
console.log(" - Function[" + funIndex + "] -> FunctionType[" + type + "]");
}
function onTableImport(index: number, type: Type, initial: number, maximum: number, flags: number): void {
console.log(" > " + Type[type] + ", initial=" + initial + ", maximum=" + maximum);
function onTableImport(tblIndex: number, type: Type, initial: number, maximum: number, flags: number): void {
console.log(" - Table[" + tblIndex + "] -> " + Type[type] + ": initial=" + initial + ", maximum=" + maximum);
}
function onMemoryImport(index: number, initial: number, maximum: number, flags: number): void {
console.log(" > initial=" + initial + ", maximum=" + maximum);
function onMemoryImport(memIndex: number, initial: number, maximum: number, flags: number): void {
console.log(" - Memory[" + memIndex + "]: initial=" + initial + ", maximum=" + maximum);
}
function onGlobalImport(index: number, type: Type, mutability: number): void {
console.log(" > " + (mutability & 1 ? "mutable " : "const ") + Type[type]);
function onGlobalImport(gloIndex: number, type: Type, mutability: number): void {
console.log(" - Global[" + gloIndex + "]: " + (mutability & 1 ? "mutable " : "const ") + Type[type]);
}
function onMemory(index: number, initial: number, maximum: number, flags: number): void {
console.log("- Memory[" + index + "]: initial=" + initial + ", maximum=" + maximum);
function onMemory(memIndex: number, initial: number, maximum: number, flags: number): void {
console.log("- Memory[" + memIndex + "]: initial=" + initial + ", maximum=" + maximum);
}
function onFunction(index: number, typeIndex: number): void {
console.log("- Function[" + index + "]: FunctionType[" + typeIndex + "]");
function onFunction(funIndex: number, typeIndex: number): void {
console.log("- Function[" + funIndex + "] -> FunctionType[" + typeIndex + "]");
}
function onGlobal(index: number, type: Type, mutability: number): void {
console.log("- Global[" + index + "]: " + (mutability & 1 ? "mutable " : "const ") + Type[type]);
function onGlobal(gloIndex: number, type: Type, mutability: number): void {
console.log("- Global[" + gloIndex + "]: " + (mutability & 1 ? "mutable " : "const ") + Type[type]);
}
function onStart(index: number): void {
@ -66,22 +64,53 @@ function onStart(index: number): void {
function onExport(index: number, kind: ExternalKind, kindIndex: number, fieldOffset: number, fieldLength: number): void {
var field = parse.readString(fieldOffset, fieldLength);
console.log("- Export[" + index + "]: '" + field + "' -> " + ExternalKind[kind] + "[" + kindIndex + "]");
console.log("- Export[" + index + "], '" + field + "' -> " + ExternalKind[kind] + "[" + kindIndex + "]");
}
const result = parse(testBinary, {
onSection,
onType,
onTypeParam,
onTypeReturn,
onImport,
onFunctionImport,
onTableImport,
onMemoryImport,
onGlobalImport,
onMemory,
onFunction,
onGlobal,
onStart,
onExport
function onSourceMappingURL(offset: number, length: number): void {
var url = parse.readString(offset, length);
console.log("- sourceMap: " + url);
}
function onModuleName(offset: number, length: number): void {
var name = parse.readString(offset, length);
console.log("- moduleName: " + name);
}
function onFunctionName(index: number, offset: number, length: number): void {
var name = parse.readString(offset, length);
console.log(" - Function[" + index + "] name: " + name);
}
function onLocalName(funcIndex: number, index: number, offset: number, length: number): void {
var name = parse.readString(offset, length);
console.log(" - Function[" + funcIndex + "].local[" + index + "] name: " + name);
}
[ "../build/index.wasm",
"libm.wasm"
].forEach((filename: string): void => {
const binary: Uint8Array = fs.readFileSync(__dirname + "/" + filename);
console.log("Testing '" + filename + "' ...");
parse(binary, {
onSection,
onType,
onTypeParam,
onTypeReturn,
onImport,
onFunctionImport,
onTableImport,
onMemoryImport,
onGlobalImport,
onMemory,
onFunction,
onGlobal,
onStart,
onExport,
onSourceMappingURL,
onModuleName,
onFunctionName,
onLocalName
});
console.log();
});