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

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"
]
}