Progress and a tiny WASM binary parser

This commit is contained in:
dcodeIO
2018-04-03 23:56:48 +02:00
parent 06198a3723
commit 5823e35f37
58 changed files with 12075 additions and 3964 deletions

95
lib/parse/README.md Normal file
View File

@ -0,0 +1,95 @@
# ![AS](https://avatars1.githubusercontent.com/u/28916798?s=48) parse
A WebAssembly binary parser in WebAssembly.
API
---
* **parse**(binary: `Uint8Array`, options?: `ParseOptions`): `void`<br />
Parses the contents of a WebAssembly binary according to the specified options.
* **ParseOptions**<br />
Options specified to the parser. The `onSection` callback determines the sections being evaluated in detail.
* **onSection**?(id: `SectionId`, payloadOff: `number`, payloadLen: `number`, nameOff: `number`, nameLen: `number`): `boolean`<br />
Called with each section in the binary. Returning `true` evaluates the section.
* **onType**?(index: `number`, form: `number`): `void`<br />
Called with each function type if the type section is evaluated.
* **onTypeParam**?(index: `number`, paramIndex: `number`, paramType: `Type`): `void`<br />
Called with each function parameter if the type section is evaluated.
* **onTypeReturn**?(index: `number`, returnIndex: `number`, returnType: `Type`): `void`<br />
Called with each function return type if the type section is evaluated.
* **onImport**?(index: `number`, kind: `ExternalKind`, moduleOff: `number`, moduleLen: `number`, fieldOff: `number`, fieldLen: `number`): `void`<br />
Called with each import if the import section is evaluated.
* **onFunctionImport**?(index: `number`, type: `number`): `void`<br />
Called with each function import if the import section is evaluated.
* **onTableImport**?(index: `number`, type: `Type`, initial: `number`, maximum: `number`, flags: `number`): `void`<br />
Called with each table import if the import section is evaluated.
* **onMemoryImport**?(index: `number`, initial: `number`, maximum: `number`, flags: `number`): `void`<br />
Called with each memory import if the import section is evaluated.
* **onGlobalImport**?(index: `number`, type: `Type`, mutability: `number`): `void`<br />
Called with each global import if the import section is evaluated.
* **onMemory**?(index: `number`, initial: `number`, maximum: `number`, flags: `number`): `void`<br />
Called with each memory if the memory section is evaluated.
* **onFunction**?(index: `number`, typeIndex: `number`): `void`<br />
Called with each function if the function section is evaluated.
* **onGlobal**?(index: `number`, type: `Type`, mutability: `number`): `void`<br />
Called with each global if the global section is evaluated.
* **onStart**?(index: `number`): `void`<br />
Called with the start function index if the start section is evaluated.
* **onExport**?(index: `number`, kind: `ExternalKind`, kindIndex: `number`, nameOff: `number`, nameLen: `number`): `void`<br />
Called with each export if the export section is evaluated.
* **Type**<br />
A value or element type, depending on context.
| Name | Value
|---------|-------
| i32 | 0x7f
| i64 | 0x7e
| f32 | 0x7d
| f64 | 0x7c
| anyfunc | 0x70
| func | 0x60
| none | 0x40
* **SectionId**<br />
Numerical id of the current section.
| Name | Value
|----------|-------
| Custom | 0
| Type | 1
| Import | 2
| Function | 3
| Table | 4
| Memory | 5
| Global | 6
| Export | 7
| Start | 8
| Element | 9
| Code | 10
| Data | 11
* **ExternalKind**<br />
Kind of an export or import.
| Name | Value
|----------|-------
| Function | 0
| Table | 1
| Memory | 2
| Global | 3

285
lib/parse/assembly/index.ts Normal file
View File

@ -0,0 +1,285 @@
/** A WebAssembly module that parses WebAssembly modules. */
// Common constants shared between AssemblyScript and TypeScript
import {
Type,
SectionId,
ExternalKind,
MAX_PAGES,
MAX_TABLES,
Opcode
} from "../src/common";
/** Current offset in memory. */
var off: usize = 0;
/** Reads an unsigned integer from memory. */
function readUint<T>(): u32 {
var pos = off;
var val = <u32>load<T>(pos);
off = pos + sizeof<T>();
return val;
}
/** Reads an unsigned 64-bit integer from memory. */
function readUint64(): u64 {
var pos = off;
var val = load<u64>(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<u8>(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<u8>(pos++);
val |= (byt & 0x7F) << shl;
shl += 7;
} while (byt & 0x80);
off = pos;
return select<u32>(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<u8>(pos++);
val |= (byt & 0x7F) << shl;
shl += 7;
} while (byt & 0x80);
off = pos;
return select<u64>(val | (~0 << shl), val, shl < 64 && (byt & 0x40) != 0);
}
// Imported callbacks
declare function onSection(id: u32, offset: u32, length: u32, nameOffset: u32, nameLength: u32): bool;
declare function onType(index: u32, form: u32): void;
declare function onTypeParam(index: u32, paramIndex: u32, paramType: u32): void;
declare function onTypeReturn(index: u32, returnIndex: u32, returnType: u32): void;
declare function onImport(index: u32, kind: u32, moduleOff: u32, moduleLen: u32, fieldOff: u32, fieldLen: u32): void;
declare function onFunctionImport(index: u32, type: u32): void;
declare function onTableImport(index: u32, type: u32, initial: u32, maximum: u32, flags: u32): void;
declare function onMemoryImport(index: u32, initial: u32, maximum: u32, flags: u32): void;
declare function onGlobalImport(index: u32, type: u32, mutability: u32): void;
declare function onMemory(index: u32, initial: u32, maximum: u32, flags: u32): void;
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;
/** Starts parsing the module that has been placed in memory. */
function parse(): void {
var magic = readUint<u32>();
if (magic != 0x6D736100) unreachable();
var version = readUint<u32>();
if (version != 1) unreachable();
var end: usize = current_memory() << 16;
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) {
name_len = readVaruint(32);
if (!name_len) {
off = section_off;
break;
}
name_off = off;
off += name_len;
} else if (id > <u32>SectionId.Data) unreachable();
let payload_off = off;
if (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;
onType(index, form);
let paramCount = readVaruint(32);
for (let paramIndex: u32 = 0; paramIndex < paramCount; ++paramIndex) {
let paramType = readVarint(7) & 0x7f;
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);
}
}
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_off;
let field_len = readVaruint(32);
let field_off = off;
off += field_off;
let kind = readUint<u8>();
onImport(
index,
kind,
module_off,
module_len,
field_off,
field_len
);
switch (kind) {
case ExternalKind.Function: {
let type = readVaruint(32);
onFunctionImport(index, type);
break;
}
case ExternalKind.Table: {
let type = readVarint(7) & 0x7f;
let flags = readVaruint(1);
let initial = readVaruint(32);
let maximum: u32 = flags & 1 ? readVaruint(32) : MAX_TABLES;
onTableImport(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);
break;
}
case ExternalKind.Global: {
let type = readVarint(7) & 0x7f;
let mutability = readVaruint(1);
onGlobalImport(index, type, mutability);
break;
}
default: unreachable();
}
}
break;
}
case SectionId.Function: {
let count = readVaruint(32);
for (let index: u32 = 0; index < count; ++index) {
let typeIndex = readVaruint(32);
onFunction(index, typeIndex);
}
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: u32 = flags ? readVaruint(32) : MAX_PAGES;
onMemory(index, initial, maximum, flags);
}
break;
}
case SectionId.Global: {
let count = readVaruint(32);
for (let index: u32 = 0; index < count; ++index) {
let type = readVarint(7) & 0x7f;
let mutability = readVaruint(1);
let op = readUint<u8>();
switch (op) {
case Opcode.i32_const: {
readVarint(32);
break;
}
case Opcode.i64_const: {
readVarint64();
break;
}
case Opcode.f32_const: {
readUint<u32>();
break;
}
case Opcode.f64_const: {
readUint64();
break;
}
case Opcode.get_global: {
readVaruint(32);
break;
}
default: unreachable();
}
op = readUint<u8>();
if (op != Opcode.end) unreachable();
onGlobal(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<u8>();
let kind_index = readVaruint(32);
onExport(
index,
kind,
kind_index,
field_off,
field_len
);
}
break;
}
case SectionId.Start: {
let index = readVaruint(32);
onStart(
index
);
break;
}
case SectionId.Custom:
case SectionId.Code: { // TODO
off += payload_len;
break;
}
default: unreachable();
}
} else { // skip
off += payload_len;
}
}
}
// Start parsing immediately
parse();

View File

@ -0,0 +1,6 @@
{
"extends": "../../../std/assembly.json",
"include": [
"./**/*.ts"
]
}

2
lib/parse/build/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.wasm
*.wasm.map

1
lib/parse/index.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export * from "./src";

2
lib/parse/index.js Normal file

File diff suppressed because one or more lines are too long

1
lib/parse/index.js.map Normal file

File diff suppressed because one or more lines are too long

6884
lib/parse/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

30
lib/parse/package.json Normal file
View File

@ -0,0 +1,30 @@
{
"name": "@assemblyscript/parse",
"version": "1.0.0",
"license": "Apache-2.0",
"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": "npm run build:as && webpack --mode production --display-modules",
"test": "ts-node tests/"
},
"files": [
"package.json",
"index.d.ts",
"index.js",
"index.js.map",
"src/",
"README.md"
],
"dependencies": {},
"devDependencies": {
"@types/webassembly-js-api": "0.0.1",
"assemblyscript": "AssemblyScript/assemblyscript",
"ts-loader": "^4.1.0",
"ts-node": "^5.0.1",
"typescript": "^2.8.1",
"webpack": "^4.4.1",
"webpack-cli": "^2.0.13"
}
}

218
lib/parse/src/common.ts Normal file
View File

@ -0,0 +1,218 @@
/** Common constants shared between AssemblyScript and TypeScript. */
/** WebAssembly types. */
export enum Type {
i32 = 0x7f,
i64 = 0x7e,
f32 = 0x7d,
f64 = 0x7c,
anyfunc = 0x70,
func = 0x60,
none = 0x40
}
/** WebAssembly section ids. */
export enum SectionId {
Custom = 0,
Type = 1,
Import = 2,
Function = 3,
Table = 4,
Memory = 5,
Global = 6,
Export = 7,
Start = 8,
Element = 9,
Code = 10,
Data = 11
}
/** WebAssembly external kinds. */
export enum ExternalKind {
Function = 0,
Table = 1,
Memory = 2,
Global = 3
}
/** Maximum number of pages. */
export const MAX_PAGES = 0xffff;
/** Maximum number of tables. */
export const MAX_TABLES = 1; // MVP
/** WebAssembly opcodes. */
export enum Opcode { // just a few of these are actually used
// unreachable = 0x00,
// nop = 0x01,
// block = 0x02,
// loop = 0x03,
// if_ = 0x04,
// else_ = 0x05,
end = 0x0b,
// br = 0x0c,
// br_if = 0x0d,
// br_table = 0x0e,
// return_ = 0x0f,
// call = 0x10,
// call_indirect = 0x11,
// drop = 0x1a,
// select = 0x1b,
// get_local = 0x20,
// set_local = 0x21,
// tee_local = 0x22,
get_global = 0x23,
// set_global = 0x24,
// i32_load = 0x28,
// i64_load = 0x29,
// f32_load = 0x2a,
// f64_load = 0x2b,
// i32_load8_s = 0x2c,
// i32_load8_u = 0x2d,
// i32_load16_s = 0x2e,
// i32_load16_u = 0x2f,
// i64_load8_s = 0x30,
// i64_load8_u = 0x31,
// i64_load16_s = 0x32,
// i64_load16_u = 0x33,
// i64_load32_s = 0x34,
// i64_load32_u = 0x35,
// i32_store = 0x36,
// i64_store = 0x37,
// f32_store = 0x38,
// f64_store = 0x39,
// i32_store8 = 0x3a,
// i32_store16 = 0x3b,
// i64_store8 = 0x3c,
// i64_store16 = 0x3d,
// i64_store32 = 0x3e,
// current_memory = 0x3f,
// grow_memory = 0x40,
i32_const = 0x41,
i64_const = 0x42,
f32_const = 0x43,
f64_const = 0x44
// i32_eqz = 0x45,
// i32_eq = 0x46,
// i32_ne = 0x47,
// i32_lt_s = 0x48,
// i32_lt_u = 0x49,
// i32_gt_s = 0x4a,
// i32_gt_u = 0x4b,
// i32_le_s = 0x4c,
// i32_le_u = 0x4d,
// i32_ge_s = 0x4e,
// i32_ge_u = 0x4f,
// i64_eqz = 0x50,
// i64_eq = 0x51,
// i64_ne = 0x52,
// i64_lt_s = 0x53,
// i64_lt_u = 0x54,
// i64_gt_s = 0x55,
// i64_gt_u = 0x56,
// i64_le_s = 0x57,
// i64_le_u = 0x58,
// i64_ge_s = 0x59,
// i64_ge_u = 0x5a,
// f32_eq = 0x5b,
// f32_ne = 0x5c,
// f32_lt = 0x5d,
// f32_gt = 0x5e,
// f32_le = 0x5f,
// f32_ge = 0x60,
// f64_eq = 0x61,
// f64_ne = 0x62,
// f64_lt = 0x63,
// f64_gt = 0x64,
// f64_le = 0x65,
// f64_ge = 0x66,
// i32_clz = 0x67,
// i32_ctz = 0x68,
// i32_popcnt = 0x69,
// i32_add = 0x6a,
// i32_sub = 0x6b,
// i32_mul = 0x6c,
// i32_div_s = 0x6d,
// i32_div_u = 0x6e,
// i32_rem_s = 0x6f,
// i32_rem_u = 0x70,
// i32_and = 0x71,
// i32_or = 0x72,
// i32_xor = 0x73,
// i32_shl = 0x74,
// i32_shr_s = 0x75,
// i32_shr_u = 0x76,
// i32_rotl = 0x77,
// i32_rotr = 0x78,
// i64_clz = 0x79,
// i64_ctz = 0x7a,
// i64_popcnt = 0x7b,
// i64_add = 0x7c,
// i64_sub = 0x7d,
// i64_mul = 0x7e,
// i64_div_s = 0x7f,
// i64_div_u = 0x80,
// i64_rem_s = 0x81,
// i64_rem_u = 0x82,
// i64_and = 0x83,
// i64_or = 0x84,
// i64_xor = 0x85,
// i64_shl = 0x86,
// i64_shr_s = 0x87,
// i64_shr_u = 0x88,
// i64_rotl = 0x89,
// i64_rotr = 0x8a,
// f32_abs = 0x8b,
// f32_neg = 0x8c,
// f32_ceil = 0x8d,
// f32_floor = 0x8e,
// f32_trunc = 0x8f,
// f32_nearest = 0x90,
// f32_sqrt = 0x91,
// f32_add = 0x92,
// f32_sub = 0x93,
// f32_mul = 0x94,
// f32_div = 0x95,
// f32_min = 0x96,
// f32_max = 0x97,
// f32_copysign = 0x98,
// f64_abs = 0x99,
// f64_neg = 0x9a,
// f64_ceil = 0x9b,
// f64_floor = 0x9c,
// f64_trunc = 0x9d,
// f64_nearest = 0x9e,
// f64_sqrt = 0x9f,
// f64_add = 0xa0,
// f64_sub = 0xa1,
// f64_mul = 0xa2,
// f64_div = 0xa3,
// f64_min = 0xa4,
// f64_max = 0xa5,
// f64_copysign = 0xa6,
// i32_wrap_i64 = 0xa7,
// i32_trunc_s_f32 = 0xa8,
// i32_trunc_u_f32 = 0xa9,
// i32_trunc_s_f64 = 0xaa,
// i32_trunc_u_f64 = 0xab,
// i64_extend_s_i32 = 0xac,
// i64_extend_u_i32 = 0xad,
// i64_trunc_s_f32 = 0xae,
// i64_trunc_u_f32 = 0xaf,
// i64_trunc_s_f64 = 0xb0,
// i64_trunc_u_f64 = 0xb1,
// f32_convert_s_i32 = 0xb2,
// f32_convert_u_i32 = 0xb3,
// f32_convert_s_i64 = 0xb4,
// f32_convert_u_i64 = 0xb5,
// f32_demote_f64 = 0xb6,
// f64_convert_s_i32 = 0xb7,
// f64_convert_u_i32 = 0xb8,
// f64_convert_s_i64 = 0xb9,
// f64_convert_u_i64 = 0xba,
// f64_promote_f32 = 0xbb,
// i32_reinterpret_f32 = 0xbc,
// i64_reinterpret_f64 = 0xbd,
// f32_reinterpret_i32 = 0xbe,
// f64_reinterpret_i64 = 0xbf
}

151
lib/parse/src/index.ts Normal file
View File

@ -0,0 +1,151 @@
import { Type, SectionId, ExternalKind } from "./common";
export { Type, SectionId, ExternalKind };
/** Cached compiled parser. */
var compiled: WebAssembly.Module | null = null;
declare var WASM_DATA: string; // injected by webpack
if (typeof WASM_DATA !== "string") WASM_DATA = require("fs").readFileSync(__dirname + "/../build/index.wasm", "base64");
/** Options specified to the parser. The `onSection` callback determines the sections being evaluated in detail. */
export interface ParseOptions {
/** Called with each section in the binary. Returning `true` evaluates the section. */
onSection?(id: SectionId, payloadOff: number, payloadLen: number, nameOff: number, nameLen: number): boolean;
/** Called with each function type if the type section is evaluated. */
onType?(index: number, form: number): void;
/** Called with each function parameter if the type section is evaluated. */
onTypeParam?(index: number, paramIndex: number, paramType: Type): void;
/** Called with each function return type if the type section is evaluated. */
onTypeReturn?(index: number, returnIndex: number, returnType: Type): void;
/** Called with each import if the import section is evaluated. */
onImport?(index: number, kind: ExternalKind, moduleOff: number, moduleLen: number, fieldOff: number, fieldLen: number): void;
/** Called with each function import if the import section is evaluated. */
onFunctionImport?(index: number, type: number): void;
/** Called with each table import if the import section is evaluated. */
onTableImport?(index: number, type: Type, initial: number, maximum: number, flags: number): void;
/** Called with each memory import if the import section is evaluated. */
onMemoryImport?(index: number, initial: number, maximum: number, flags: number): void;
/** Called with each global import if the import section is evaluated. */
onGlobalImport?(index: number, type: Type, mutability: number): void;
/** Called with each memory if the memory section is evaluated.*/
onMemory?(index: number, initial: number, maximum: number, flags: number): void;
/** Called with each function if the function section is evaluated. */
onFunction?(index: number, typeIndex: number): void;
/** Called with each global if the global section is evaluated. */
onGlobal?(index: number, type: Type, mutability: number): void;
/** 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);
}
/** Parses the contents of a WebAssembly binary according to the specified options. */
export function parse(binary: Uint8Array, options?: ParseOptions): void {
if (!options) options = {};
// compile the parser if not yet compiled
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 buffer = new Uint8Array(memory.buffer);
buffer.set(binary);
// provide a way to read strings from memory
parse.readString = (offset: number, length: number): string => utf8_read(buffer, offset, offset + length);
// instantiate the parser and return its exports
function nop(): void {}
var imports = {
env: {
memory: memory
}
};
[
"onSection",
"onType",
"onTypeParam",
"onTypeReturn",
"onImport",
"onFunctionImport",
"onTableImport",
"onMemoryImport",
"onGlobalImport",
"onMemory",
"onFunction",
"onGlobal",
"onExport",
"onStart"
].forEach((name: string): void => imports.env[name] = options[name] || nop);
var instance = new WebAssembly.Instance(compiled, imports);
return instance.exports;
}
export declare namespace parse {
/** Utility function for reading an UTF8 encoded string from memory while parsing. */
function readString(offset: number, length: number): string;
}
// see: https://github.com/dcodeIO/protobuf.js/tree/master/lib/utf8
function utf8_read(buffer: Uint8Array, start: number, end: number): string {
var len = end - start;
if (len < 1) return "";
var parts: string[] | null = null,
chunk: number[] = [],
i = 0, // char offset
t = 0; // temporary
while (start < end) {
t = buffer[start++];
if (t < 128) {
chunk[i++] = t;
} else if (t > 191 && t < 224) {
chunk[i++] = (t & 31) << 6 | buffer[start++] & 63;
} else if (t > 239 && t < 365) {
t = ((t & 7) << 18 | (buffer[start++] & 63) << 12 | (buffer[start++] & 63) << 6 | buffer[start++] & 63) - 0x10000;
chunk[i++] = 0xD800 + (t >> 10);
chunk[i++] = 0xDC00 + (t & 1023);
} else {
chunk[i++] = (t & 15) << 12 | (buffer[start++] & 63) << 6 | buffer[start++] & 63;
}
if (i > 8191) {
(parts || (parts = [])).push(String.fromCharCode.apply(String, chunk));
i = 0;
}
}
if (parts) {
if (i) parts.push(String.fromCharCode.apply(String, chunk.slice(0, i)));
return parts.join("");
}
return String.fromCharCode.apply(String, chunk.slice(0, i));
}
// see: https://github.com/dcodeIO/protobuf.js/tree/master/lib/base64
function base64_decode(string: string): Uint8Array {
var length = string.length;
if (length) {
let n = 0,
p = length;
while (--p % 4 > 1 && string.charCodeAt(p) === 61) ++n;
length = Math.ceil(length * 3) / 4 - n;
}
var buffer = new Uint8Array(length);
var j = 0, o = 0, t = 0;
for (let i = 0, k = string.length; i < k;) {
let c = string.charCodeAt(i++);
if (c === 61 && j > 1) break;
if ((c = s64[c]) === undefined) throw Error();
switch (j) {
case 0: { t = c; j = 1; break; }
case 1: { buffer[o++] = t << 2 | (c & 48) >> 4; t = c; j = 2; break; }
case 2: { buffer[o++] = (t & 15) << 4 | (c & 60) >> 2; t = c; j = 3; break; }
case 3: { buffer[o++] = (t & 3) << 6 | c; j = 0; break; }
}
}
if (j === 1) throw Error();
return buffer;
}
var s64 = new Array(123);
for (let i = 0; i < 64;) s64[i < 26 ? i + 65 : i < 52 ? i + 71 : i < 62 ? i - 4 : i - 59 | 43] = i++;

View File

@ -0,0 +1,8 @@
{
"compilerOptions": {
"module": "commonjs"
},
"include": [
"./**/*.ts"
]
}

87
lib/parse/tests/index.ts Normal file
View File

@ -0,0 +1,87 @@
import * as fs from "fs";
import {
Type,
SectionId,
ExternalKind,
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];
console.log(name + " section at " + offset + ".." + (offset + length));
return true;
}
function onType(index: number, form: Type): void {
console.log("- FunctionType[" + index + "] is " + Type[form]);
}
function onTypeParam(index: number, paramIndex: number, paramType: Type): void {
console.log(" > param[" + paramIndex + "] = " + Type[paramType]);
}
function onTypeReturn(index: number, returnIndex: number, returnType: Type): void {
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 + "'");
}
function onFunctionImport(index: number, type: number): void {
console.log(" > FunctionType[" + type + "]");
}
function onTableImport(index: number, type: Type, initial: number, maximum: number, flags: number): void {
console.log(" > " + Type[type] + ", initial=" + initial + ", maximum=" + maximum);
}
function onMemoryImport(index: number, initial: number, maximum: number, flags: number): void {
console.log(" > initial=" + initial + ", maximum=" + maximum);
}
function onGlobalImport(index: number, type: Type, mutability: number): void {
console.log(" > " + (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 onFunction(index: number, typeIndex: number): void {
console.log("- Function[" + index + "]: FunctionType[" + typeIndex + "]");
}
function onGlobal(index: number, type: Type, mutability: number): void {
console.log("- Global[" + index + "]: " + (mutability & 1 ? "mutable " : "const ") + Type[type]);
}
function onStart(index: number): void {
console.log("- Start: Function[" + index + "]");
}
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 + "]");
}
const result = parse(testBinary, {
onSection,
onType,
onTypeParam,
onTypeReturn,
onImport,
onFunctionImport,
onTableImport,
onMemoryImport,
onGlobalImport,
onMemory,
onFunction,
onGlobal,
onStart,
onExport
});

BIN
lib/parse/tests/libm.wasm Normal file

Binary file not shown.

View File

@ -0,0 +1,37 @@
const fs = require("fs");
const path = require("path");
const webpack = require("webpack");
const wasmData = fs.readFileSync(__dirname + "/build/index.wasm");
module.exports = {
entry: [ "./src/index.ts" ],
module: {
rules: [
{
test: /\.ts$/,
use: "ts-loader",
exclude: /node_modules/
}
]
},
resolve: {
extensions: [ ".ts", ".js" ]
},
node: {
fs: false
},
output: {
filename: "index.js",
path: __dirname,
library: "asparse",
libraryTarget: "umd",
globalObject: "typeof self !== 'undefined' ? self : this"
},
plugins: [
new webpack.DefinePlugin({
WASM_DATA: JSON.stringify(wasmData.toString("base64"))
})
],
devtool: "source-map"
};