mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 15:12:12 +00:00
159 lines
6.8 KiB
TypeScript
159 lines
6.8 KiB
TypeScript
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): 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. */
|
|
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 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);
|
|
|
|
// 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 } };
|
|
[ "onSection",
|
|
"onType",
|
|
"onTypeParam",
|
|
"onTypeReturn",
|
|
"onImport",
|
|
"onFunctionImport",
|
|
"onTableImport",
|
|
"onMemoryImport",
|
|
"onGlobalImport",
|
|
"onMemory",
|
|
"onFunction",
|
|
"onGlobal",
|
|
"onExport",
|
|
"onStart",
|
|
"onSourceMappingURL",
|
|
"onModuleName",
|
|
"onFunctionName",
|
|
"onLocalName"
|
|
].forEach((name: string): void => imports.env[name] = options[name] || nop);
|
|
var instance = new WebAssembly.Instance(compiled, imports);
|
|
instance.exports.parse(0, nBytes);
|
|
}
|
|
|
|
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++;
|