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
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();
});

View File

@ -2047,7 +2047,9 @@ export namespace NativeMathf {
}
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

View File

@ -12221,9 +12221,20 @@
)
)
(func $~lib/math/NativeMathf.random (; 143 ;) (type $f) (result f32)
(f32.demote/f64
(call $~lib/math/NativeMath.random)
(local $0 f32)
(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)
(local $1 f64)

View File

@ -14455,11 +14455,26 @@
)
)
(func $~lib/math/NativeMathf.random (; 143 ;) (type $f) (result f32)
(return
(f32.demote/f64
(call $~lib/math/NativeMath.random)
(local $0 f32)
(nop)
(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)
(local $1 i64)