killawat
Version:
KillaWAT is a WebAssembly Text Macro Assembler
286 lines (238 loc) • 8.81 kB
JavaScript
const { RED, logError, valtypeMap, globalSymbolTable, globalSymbolMap,
binaryen, WasmModule, funcSymbolTable, funcSymbolMap,
memoryTable, memoryMap, WasmTables, WasmTableMap } = require('./shared');
class ImportTable {
constructor(externalModuleName, externalBaseName, tableName, initialSize, maxSize) {
this.externalModuleName = externalModuleName;
this.externalBaseName = externalBaseName;
this.name = tableName;
this.initialSize = initialSize;
this.maxSize = maxSize;
// Module#addTableImport(internalName: string, externalModuleName: string, externalBaseName: string): void
WasmModule.addTableImport(this.name.slice(1),
this.externalModuleName,
this.externalBaseName
);
}
}
class ImportFunc {
constructor(externalModuleName, externalBaseName, funcName, paramTypeArray, resultType) {
this.externalModuleName = externalModuleName;
this.externalBaseName = externalBaseName;
this.name = funcName;
this.params = paramTypeArray;
this.result = resultType;
this.index = funcSymbolTable.length;
funcSymbolTable.push(this);
funcSymbolMap.set(this.name, this);
// Module#addFunctionImport(internalName: string, externalModuleName: string,
// externalBaseName: string, params: Type, results: Type):
WasmModule.addFunctionImport(this.name.slice(1),
this.externalModuleName,
this.externalBaseName,
binaryen.createType(this.params),
this.resultType
);
}
}
class ImportMem {
constructor(externalModuleName, externalBaseName, memoryName, initialSize, maxSize) {
this.externalModuleName = externalModuleName;
this.externalBaseName = externalBaseName;
this.name = memoryName;
this.initialSize = initialSize;
this.maxSize = maxSize;
this.index = memoryTable.length;
memoryTable.push(this);
memoryMap.set(this.name, this);
//Module#addMemoryImport(internalName: string, externalModuleName: string, externalBaseName: string): void
WasmModule.addMemoryImport('0', // right now the memory name has to be '0'
this.externalModuleName,
this.externalBaseName,
);
}
}
class ImportGlobal {
constructor(externalModuleName, externalBaseName, globalName, type) {
this.externalModuleName = externalModuleName;
this.externalBaseName = externalBaseName;
this.id = globalName;
this.globalType = type;
this.index = globalSymbolTable.length;
globalSymbolTable.push(this);
globalSymbolMap.set(this.id, this);
WasmModule.addGlobalImport(this.id.slice(1),
this.externalModuleName,
this.externalBaseName,
this.globalType
);
}
}
class Import {
constructor(tokenArray) {
this.meta = tokenArray.pop();
this.externalModuleName = tokenArray[0].value;
this.externalBaseName = tokenArray[1].value;
if (tokenArray[2].type !== 'lp') {
logError(`expected '(' instead of ${tokenArray[2].text}`, tokenArray[2]);
return;
}
this.importTypeToken = tokenArray[3];
this.importType = this.importTypeToken.text;
if (this.importType === 'func') {
this.funcParams = [];
this.parseFunc(tokenArray.slice(4, -1));
}
else if (this.importType === 'memory') {
this.parseMemory(tokenArray.slice(4, -1));
}
else if (this.importType === 'global') {
this.parseGlobal(tokenArray.slice(4, -1));
}
else if (this.importType === 'table') {
this.parseTable(tokenArray.slice(4, -1));
}
else {
logError(`unknown import type ${this.importType}`, this.importTypeToken);
return;
}
}
parseFuncParam(tokenArray) {
for (let i = 0; i < tokenArray.length; i++) {
let type = valtypeMap.get(tokenArray[i].text)
if (type == null) {
logError(`expected valid Wasm type, instead found ${tokenArray[i].text}`, tokenArray[i]);
return;
}
this.funcParams.push(type);
}
}
parseFuncResult(tokenArray) {
let typeToken = tokenArray[0];
if (this.funcResult != undefined) {
logError(`The result has already been set for this function`, typeToken);
return;
}
if (tokenArray.length > 1) {
logError(`functions can only have one result`, typeToken);
return;
}
if (typeToken.type !== 'valtype') {
logError(`expected valid Wasm type, instead found ${typeToken.text}`, typeToken);
return;
}
this.funcResult = typeToken.value;
}
parseFunc(tokenArray) {
// (func (param i32) (result i32)
let startIndex = 0;
this.funcName = `$func_${funcSymbolTable.length}`;
if (tokenArray[0].type === 'name') {
this.funcName = tokenArray[0].text;
startIndex = 1;
}
for (let i = startIndex; i < tokenArray.length; i++) {
if (tokenArray[i].type !== 'lp') {
logError(`expected '(' token, found ${tokenArray[i].text}`, tokenArray[i]);
return;
}
if (tokenArray[i + 1].text === 'param') {
this.parseFuncParam(tokenArray.slice(i + 2, tokenArray[i].endTokenOffset + i));
i += tokenArray[i].endTokenOffset;
}
else if (tokenArray[i + 1].text === 'result') {
this.parseFuncResult(tokenArray.slice(i + 2, tokenArray[i].endTokenOffset + i));
i += tokenArray[i].endTokenOffset;
}
else {
logError(`unrecognized function attribute '${tokenArray[i + 1].text}'`, tokenArray[i]);
return;
}
}
this.importFunc = new ImportFunc(this.externalModuleName, this.externalBaseName, this.funcName,
this.funcParams, this.funcResult);
}
parseMemory(tokenArray) {
// (memory 1 2) | (memory $name 1 2)
if (tokenArray.length === 0 || tokenArray.length > 3) {
logError(`wrong number of arguments in memory import`,
tokenArray[0]);
}
this.memoryName = `$mem_${memoryTable.length}`;
let current_index = 0;
if (tokenArray[current_index].type === 'name') {
this.memoryName = tokenArray[current_index].value;
current_index++;
}
this.initialMemPages = tokenArray[current_index].value;
this.maxMemPages = tokenArray[current_index + 1] == undefined ?
this.initialMemPages : tokenArray[current_index + 1].value;
this.importMem = new ImportMem(
this.externalModuleName,
this.externalModuleName,
this.memoryName,
this.initialMemPages,
this.maxMemPages);
}
parseGlobal(tokenArray) {
// (global $x i32)
let current_index = 0;
if (tokenArray[current_index].type === 'name') {
this.globalName = tokenArray[0].text;
current_index++;
}
let typeToken = tokenArray[current_index];
if (typeToken.type !== 'valtype') {
logError(`expected type but found ${typeToken.text}`, typeToken);
return;
}
this.globalType = typeToken.value;
this.importGlobal = new ImportGlobal(
this.externalModuleName,
this.externalModuleName,
this.globalName,
this.globalType
);
}
parseTable(tokenArray) {
// (table $name 10 20 anyfunc)
if (tokenArray.length === 0) {
logError(`memory expression requires a minimum number of pages and can't have more than two values`,
tokenArray[0]);
}
let current_index = 0;
if (tokenArray[current_index].type === 'name') {
this.tableName = tokenArray[current_index].text;
current_index++;
}
this.initialTableEntries = tokenArray[current_index].value;
this.maxTableEntries = tokenArray[current_index + 1] == undefined ?
this.initialTableEntries : tokenArray[current_index + 1].value;
// I'm assuming token 3 is anyfunc right now.
// WasmTables, WasmTableMap
/*
this.importGlobal = new ImportGlobal(
this.externalModuleName,
this.externalModuleName,
this.globalName,
this.globalType
);
*/
}
}
module.exports.Import = Import;
/*
(module
(import "spectest" "global_i32" (global i32))
(import "spectest" "global_i32" (global $x i32))
(import "test" "func-i32" (func (param i32)))
)
*/
/*
Module#addFunctionImport(internalName: string, externalModuleName: string, externalBaseName: string,
params: Type, results: Type): void
Module#addTableImport(internalName: string, externalModuleName: string, externalBaseName: string): void
Module#addMemoryImport(internalName: string, externalModuleName: string, externalBaseName: string): void
Module#addGlobalImport(internalName: string, externalModuleName: string, externalBaseName: string,
globalType: Type): void
*/