mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-07-09 03:21:58 +00:00
Progress and a tiny WASM binary parser
This commit is contained in:
README.mdwebpack.config.js
bin
dist
lib
lint
parse
src
std/assembly/allocator
tests
allocators
compiler
closure.optimized.watclosure.tsclosure.untouched.wat
std
allocator_arena.optimized.watallocator_arena.untouched.watarray.optimized.watarray.untouched.watarraybuffer.optimized.watarraybuffer.untouched.watconstructor.optimized.watconstructor.untouched.watnew.optimized.watnew.untouched.watoperator-overloading.optimized.watoperator-overloading.untouched.watset.optimized.watset.untouched.watstatic-array.optimized.watstatic-array.untouched.watstring.optimized.watstring.untouched.wat
151
lib/parse/src/index.ts
Normal file
151
lib/parse/src/index.ts
Normal 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++;
|
Reference in New Issue
Block a user