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

2
dist/asc.js vendored

File diff suppressed because one or more lines are too long

2
dist/asc.js.map vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
# ![AS](https://avatars1.githubusercontent.com/u/28916798?s=48) parse # ![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 API
--- ---
@ -53,6 +53,18 @@ API
* **onExport**?(index: `number`, kind: `ExternalKind`, kindIndex: `number`, nameOff: `number`, nameLen: `number`): `void`<br /> * **onExport**?(index: `number`, kind: `ExternalKind`, kindIndex: `number`, nameOff: `number`, nameLen: `number`): `void`<br />
Called with each export if the export section is evaluated. 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 /> * **Type**<br />
A value or element type, depending on context. A value or element type, depending on context.

View File

@ -5,6 +5,7 @@ import {
Type, Type,
SectionId, SectionId,
ExternalKind, ExternalKind,
NameType,
MAX_PAGES, MAX_PAGES,
MAX_TABLES, MAX_TABLES,
Opcode 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 onGlobal(index: u32, type: u32, mutability: u32): void;
declare function onExport(index: u32, kind: u32, kindIndex: u32, nameOffset: u32, nameLength: u32): void; declare function onExport(index: u32, kind: u32, kindIndex: u32, nameOffset: u32, nameLength: u32): void;
declare function onStart(index: 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. */ /** 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>(); var magic = readUint<u32>();
if (magic != 0x6D736100) unreachable(); if (magic != 0x6D736100) unreachable();
var version = readUint<u32>(); var version = readUint<u32>();
if (version != 1) unreachable(); 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) { while (off < end) {
let section_off = off; let section_off = off;
let id = readVaruint(7); let id = readVaruint(7);
@ -105,13 +114,11 @@ function parse(): void {
let name_off = 0; let name_off = 0;
let name_len = 0; let name_len = 0;
if (!id) { if (!id) {
let before = off;
name_len = readVaruint(32); name_len = readVaruint(32);
if (!name_len) {
off = section_off;
break;
}
name_off = off; name_off = off;
off += name_len; off += name_len;
payload_len -= off - before;
} else if (id > <u32>SectionId.Data) unreachable(); } else if (id > <u32>SectionId.Data) unreachable();
let payload_off = off; let payload_off = off;
if (onSection( if (onSection(
@ -126,16 +133,27 @@ function parse(): void {
let count = readVaruint(32); let count = readVaruint(32);
for (let index: u32 = 0; index < count; ++index) { for (let index: u32 = 0; index < count; ++index) {
let form = readVarint(7) & 0x7f; let form = readVarint(7) & 0x7f;
onType(index, form); onType(
index,
form
);
let paramCount = readVaruint(32); let paramCount = readVaruint(32);
for (let paramIndex: u32 = 0; paramIndex < paramCount; ++paramIndex) { for (let paramIndex: u32 = 0; paramIndex < paramCount; ++paramIndex) {
let paramType = readVarint(7) & 0x7f; let paramType = readVarint(7) & 0x7f;
onTypeParam(index, paramIndex, paramType); onTypeParam(
index,
paramIndex,
paramType
);
} }
let returnCount = readVaruint(1); // MVP let returnCount = readVaruint(1); // MVP
for (let returnIndex: u32 = 0; returnIndex < returnCount; ++returnIndex) { for (let returnIndex: u32 = 0; returnIndex < returnCount; ++returnIndex) {
let returnType = readVarint(7) & 0x7f; let returnType = readVarint(7) & 0x7f;
onTypeReturn(index, returnIndex, returnType); onTypeReturn(
index,
returnIndex,
returnType
);
} }
} }
break; break;
@ -145,10 +163,10 @@ function parse(): void {
for (let index: u32 = 0; index < count; ++index) { for (let index: u32 = 0; index < count; ++index) {
let module_len = readVaruint(32); let module_len = readVaruint(32);
let module_off = off; let module_off = off;
off += module_off; off += module_len;
let field_len = readVaruint(32); let field_len = readVaruint(32);
let field_off = off; let field_off = off;
off += field_off; off += field_len;
let kind = readUint<u8>(); let kind = readUint<u8>();
onImport( onImport(
index, index,
@ -161,7 +179,10 @@ function parse(): void {
switch (kind) { switch (kind) {
case ExternalKind.Function: { case ExternalKind.Function: {
let type = readVaruint(32); let type = readVaruint(32);
onFunctionImport(index, type); onFunctionImport(
fun_space_index++,
type
);
break; break;
} }
case ExternalKind.Table: { case ExternalKind.Table: {
@ -169,20 +190,35 @@ function parse(): void {
let flags = readVaruint(1); let flags = readVaruint(1);
let initial = readVaruint(32); let initial = readVaruint(32);
let maximum: u32 = flags & 1 ? readVaruint(32) : MAX_TABLES; let maximum: u32 = flags & 1 ? readVaruint(32) : MAX_TABLES;
onTableImport(index, type, initial, maximum, flags); onTableImport(
tbl_space_index++,
type,
initial,
maximum,
flags
);
break; break;
} }
case ExternalKind.Memory: { case ExternalKind.Memory: {
let flags = readVaruint(1); let flags = readVaruint(1);
let initial = readVaruint(32); let initial = readVaruint(32);
let maximum: u32 = flags & 1 ? readVaruint(32) : MAX_PAGES; let maximum: u32 = flags & 1 ? readVaruint(32) : MAX_PAGES;
onMemoryImport(index, initial, maximum, flags); onMemoryImport(
mem_space_index++,
initial,
maximum,
flags
);
break; break;
} }
case ExternalKind.Global: { case ExternalKind.Global: {
let type = readVarint(7) & 0x7f; let type = readVarint(7) & 0x7f;
let mutability = readVaruint(1); let mutability = readVaruint(1);
onGlobalImport(index, type, mutability); onGlobalImport(
glo_space_index++,
type,
mutability
);
break; break;
} }
default: unreachable(); default: unreachable();
@ -192,9 +228,12 @@ function parse(): void {
} }
case SectionId.Function: { case SectionId.Function: {
let count = readVaruint(32); let count = readVaruint(32);
for (let index: u32 = 0; index < count; ++index) { for (let i: u32 = 0; i < count; ++i) {
let typeIndex = readVaruint(32); let typeIndex = readVaruint(32);
onFunction(index, typeIndex); onFunction(
fun_space_index++,
typeIndex
);
} }
break; break;
} }
@ -204,13 +243,18 @@ function parse(): void {
let flags = readVaruint(1); let flags = readVaruint(1);
let initial = readVaruint(32); let initial = readVaruint(32);
let maximum: u32 = flags ? readVaruint(32) : MAX_PAGES; let maximum: u32 = flags ? readVaruint(32) : MAX_PAGES;
onMemory(index, initial, maximum, flags); onMemory(
mem_space_index++,
initial,
maximum,
flags
);
} }
break; break;
} }
case SectionId.Global: { case SectionId.Global: {
let count = readVaruint(32); 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 type = readVarint(7) & 0x7f;
let mutability = readVaruint(1); let mutability = readVaruint(1);
let op = readUint<u8>(); let op = readUint<u8>();
@ -239,7 +283,11 @@ function parse(): void {
} }
op = readUint<u8>(); op = readUint<u8>();
if (op != Opcode.end) unreachable(); if (op != Opcode.end) unreachable();
onGlobal(index, type, mutability); onGlobal(
glo_space_index++,
type,
mutability
);
} }
break; break;
} }
@ -268,8 +316,80 @@ function parse(): void {
); );
break; break;
} }
case SectionId.Custom: case SectionId.Custom: {
case SectionId.Code: { // TODO 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; off += payload_len;
break; break;
} }
@ -279,7 +399,5 @@ function parse(): void {
off += payload_len; 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", "main": "index.js",
"types": "index.d.ts", "types": "index.d.ts",
"scripts": { "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", "build": "npm run build:as && webpack --mode production --display-modules",
"test": "ts-node tests/" "test": "ts-node tests/"
}, },

View File

@ -35,6 +35,13 @@ export enum ExternalKind {
Global = 3 Global = 3
} }
/** Name section types. */
export enum NameType {
Module = 0,
Function = 1,
Local = 2
}
/** Maximum number of pages. */ /** Maximum number of pages. */
export const MAX_PAGES = 0xffff; 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. */ /** Called with the start function index if the start section is evaluated. */
onStart?(index: number): void; onStart?(index: number): void;
/** Called with each export if the export section is evaluated. */ /** 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. */ /** 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)); if (!compiled) compiled = new WebAssembly.Module(base64_decode(WASM_DATA));
// use the binary as the parser's memory // use the binary as the parser's memory
var bytes = binary.length + 5; // leave space for u8 + u32 zero terminator var nBytes = binary.length;
var pages = ((bytes + 0xffff) & ~0xffff) >> 16; var nPages = ((nBytes + 0xffff) & ~0xffff) >> 16;
var memory = new WebAssembly.Memory({ initial: pages }); var memory = new WebAssembly.Memory({ initial: nPages });
var buffer = new Uint8Array(memory.buffer); var buffer = new Uint8Array(memory.buffer);
buffer.set(binary); buffer.set(binary);
@ -58,13 +66,8 @@ export function parse(binary: Uint8Array, options?: ParseOptions): void {
// instantiate the parser and return its exports // instantiate the parser and return its exports
function nop(): void {} function nop(): void {}
var imports = { var imports = { env: { memory } };
env: { [ "onSection",
memory: memory
}
};
[
"onSection",
"onType", "onType",
"onTypeParam", "onTypeParam",
"onTypeReturn", "onTypeReturn",
@ -77,10 +80,14 @@ export function parse(binary: Uint8Array, options?: ParseOptions): void {
"onFunction", "onFunction",
"onGlobal", "onGlobal",
"onExport", "onExport",
"onStart" "onStart",
"onSourceMappingURL",
"onModuleName",
"onFunctionName",
"onLocalName"
].forEach((name: string): void => imports.env[name] = options[name] || nop); ].forEach((name: string): void => imports.env[name] = options[name] || nop);
var instance = new WebAssembly.Instance(compiled, imports); var instance = new WebAssembly.Instance(compiled, imports);
return instance.exports; instance.exports.parse(0, nBytes);
} }
export declare namespace parse { export declare namespace parse {

View File

@ -6,58 +6,56 @@ import {
parse parse
} from ".."; } from "..";
const testBinary = fs.readFileSync(__dirname + "/libm.wasm");
function onSection(id: SectionId, offset: number, length: number, nameOffset: number, nameLength: number): boolean { 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)); console.log(name + " section at " + offset + ".." + (offset + length));
return true; return true;
} }
function onType(index: number, form: Type): void { 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 { 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 { 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 { function onImport(index: number, kind: ExternalKind, moduleOff: number, moduleLen: number, fieldOff: number, fieldLen: number): void {
var moduleName = parse.readString(moduleOff, moduleLen); var moduleName = parse.readString(moduleOff, moduleLen);
var fieldName = parse.readString(fieldOff, fieldLen); 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 { function onFunctionImport(funIndex: number, type: number): void {
console.log(" > FunctionType[" + type + "]"); console.log(" - Function[" + funIndex + "] -> FunctionType[" + type + "]");
} }
function onTableImport(index: number, type: Type, initial: number, maximum: number, flags: number): void { function onTableImport(tblIndex: number, type: Type, initial: number, maximum: number, flags: number): void {
console.log(" > " + Type[type] + ", initial=" + initial + ", maximum=" + maximum); console.log(" - Table[" + tblIndex + "] -> " + Type[type] + ": initial=" + initial + ", maximum=" + maximum);
} }
function onMemoryImport(index: number, initial: number, maximum: number, flags: number): void { function onMemoryImport(memIndex: number, initial: number, maximum: number, flags: number): void {
console.log(" > initial=" + initial + ", maximum=" + maximum); console.log(" - Memory[" + memIndex + "]: initial=" + initial + ", maximum=" + maximum);
} }
function onGlobalImport(index: number, type: Type, mutability: number): void { function onGlobalImport(gloIndex: number, type: Type, mutability: number): void {
console.log(" > " + (mutability & 1 ? "mutable " : "const ") + Type[type]); console.log(" - Global[" + gloIndex + "]: " + (mutability & 1 ? "mutable " : "const ") + Type[type]);
} }
function onMemory(index: number, initial: number, maximum: number, flags: number): void { function onMemory(memIndex: number, initial: number, maximum: number, flags: number): void {
console.log("- Memory[" + index + "]: initial=" + initial + ", maximum=" + maximum); console.log("- Memory[" + memIndex + "]: initial=" + initial + ", maximum=" + maximum);
} }
function onFunction(index: number, typeIndex: number): void { function onFunction(funIndex: number, typeIndex: number): void {
console.log("- Function[" + index + "]: FunctionType[" + typeIndex + "]"); console.log("- Function[" + funIndex + "] -> FunctionType[" + typeIndex + "]");
} }
function onGlobal(index: number, type: Type, mutability: number): void { function onGlobal(gloIndex: number, type: Type, mutability: number): void {
console.log("- Global[" + index + "]: " + (mutability & 1 ? "mutable " : "const ") + Type[type]); console.log("- Global[" + gloIndex + "]: " + (mutability & 1 ? "mutable " : "const ") + Type[type]);
} }
function onStart(index: number): void { 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 { function onExport(index: number, kind: ExternalKind, kindIndex: number, fieldOffset: number, fieldLength: number): void {
var field = parse.readString(fieldOffset, fieldLength); 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, { function onSourceMappingURL(offset: number, length: number): void {
onSection, var url = parse.readString(offset, length);
onType, console.log("- sourceMap: " + url);
onTypeParam, }
onTypeReturn,
onImport, function onModuleName(offset: number, length: number): void {
onFunctionImport, var name = parse.readString(offset, length);
onTableImport, console.log("- moduleName: " + name);
onMemoryImport, }
onGlobalImport,
onMemory, function onFunctionName(index: number, offset: number, length: number): void {
onFunction, var name = parse.readString(offset, length);
onGlobal, console.log(" - Function[" + index + "] name: " + name);
onStart, }
onExport
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();
}); });

View File

@ -2047,7 +2047,9 @@ export namespace NativeMathf {
} }
export function random(): f32 { export function random(): f32 {
return <f32>NativeMath.random(); var f: f32; // FIXME: demoting from f64 to f32 might yield 1.0f
do f = <f32>NativeMath.random(); while (f == 1.0);
return f;
} }
export function round(x: f32): f32 { // see: musl/src/math/roundf.c export function round(x: f32): f32 { // see: musl/src/math/roundf.c

View File

@ -12221,9 +12221,20 @@
) )
) )
(func $~lib/math/NativeMathf.random (; 143 ;) (type $f) (result f32) (func $~lib/math/NativeMathf.random (; 143 ;) (type $f) (result f32)
(f32.demote/f64 (local $0 f32)
(call $~lib/math/NativeMath.random) (loop $continue|0
(br_if $continue|0
(f32.eq
(tee_local $0
(f32.demote/f64
(call $~lib/math/NativeMath.random)
)
)
(f32.const 1)
)
)
) )
(get_local $0)
) )
(func $~lib/math/NativeMath.round (; 144 ;) (type $FF) (param $0 f64) (result f64) (func $~lib/math/NativeMath.round (; 144 ;) (type $FF) (param $0 f64) (result f64)
(local $1 f64) (local $1 f64)

View File

@ -14455,11 +14455,26 @@
) )
) )
(func $~lib/math/NativeMathf.random (; 143 ;) (type $f) (result f32) (func $~lib/math/NativeMathf.random (; 143 ;) (type $f) (result f32)
(return (local $0 f32)
(f32.demote/f64 (nop)
(call $~lib/math/NativeMath.random) (block $break|0
(loop $continue|0
(set_local $0
(f32.demote/f64
(call $~lib/math/NativeMath.random)
)
)
(br_if $continue|0
(f32.eq
(get_local $0)
(f32.const 1)
)
)
) )
) )
(return
(get_local $0)
)
) )
(func $~lib/math/NativeMath.round (; 144 ;) (type $FF) (param $0 f64) (result f64) (func $~lib/math/NativeMath.round (; 144 ;) (type $FF) (param $0 f64) (result f64)
(local $1 i64) (local $1 i64)