"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.kStart = exports.Module = void 0;
var _ast = require("@webassemblyjs/ast");
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
// $FlowIgnore
var kStart = Symbol("_start");
exports.kStart = kStart;
function createContext(ast) {
var context = {
funcs: []
};
(0, _ast.traverse)(ast, {
ModuleImport: function (_ModuleImport) {
function ModuleImport(_x) {
return _ModuleImport.apply(this, arguments);
}
ModuleImport.toString = function () {
return _ModuleImport.toString();
};
return ModuleImport;
}(function (path) {
if ((0, _ast.isFuncImportDescr)(path.node.descr)) {
context.funcs.push({
isImplemented: false
});
}
}),
Func: function (_Func) {
function Func(_x2) {
return _Func.apply(this, arguments);
}
Func.toString = function () {
return _Func.toString();
};
return Func;
}(function (path) {
context.funcs.push({
isImplemented: true,
node: path.node
});
})
});
return context;
}
var Module = /*#__PURE__*/function () {
function Module(ast) {
_classCallCheck(this, Module);
this._labels = [];
this._program = [];
this._currentFunc = null;
this._context = createContext(ast);
}
_createClass(Module, [{
key: "_emit",
value: function _emit(node) {
var offset = (0, _ast.getStartByteOffset)(node);
this._program.push({
offset: offset,
node: node
});
}
}, {
key: "beginFuncBody",
value: function beginFuncBody(func) {
this._labels = [];
this._program = [];
this._currentFunc = func;
this._labels.push(func);
}
}, {
key: "onFuncInstruction",
value: function onFuncInstruction(node) {
var _this = this;
if ((0, _ast.isCallInstruction)(node)) {
// $FlowIgnore: it's ensured by the node matcher
if (!(node.numeric !== null)) {
throw new Error('node.numeric !== null' + " error: " + (undefined || "unknown"));
}
var funcIndex = null; // $FlowIgnore: it's ensured by the node matcher
if ((0, _ast.isNumberLiteral)(node.index)) {
funcIndex = parseInt(node.index.value);
} // $FlowIgnore: it's ensured by the node matcher
if ((0, _ast.isIdentifier)(node.index)) {
// $FlowIgnore: it's ensured by the node matcher
funcIndex = parseInt(node.numeric.value);
}
if (!(funcIndex !== null)) {
throw new Error('funcIndex !== null' + " error: " + (undefined || "unknown"));
}
// $FlowIgnore: ensured by the assertion
var funcInContext = this._context.funcs[funcIndex];
if (!(_typeof(funcInContext) === "object")) {
throw new Error('typeof funcInContext === "object"' + " error: " + (undefined || "unknown"));
}
if (funcInContext.isImplemented === true) {
var func = funcInContext.node; // transform module index into byte offset
// $FlowIgnore
node.index.value = (0, _ast.getFunctionBeginingByteOffset)(func);
this._emit(node);
} else {
var internalCallExternNode = (0, _ast.internalCallExtern)(funcIndex);
internalCallExternNode.loc = node.loc;
this._emit(internalCallExternNode);
}
return;
}
if ((0, _ast.isBlock)(node)) {
this._labels.push(node);
}
if ((0, _ast.isLoopInstruction)(node)) {
this._labels.push(node);
}
if (node.id === "br" || node.id === "br_if") {
// $FlowIgnore
var depth = node.args[0].value; // $FlowIgnore
var target = this._labels[this._labels.length - depth - 1];
if (!(_typeof(target) === "object")) {
throw new Error('typeof target === "object"' + " error: " + ("Label ".concat(String(depth), " not found") || "unknown"));
}
if ((0, _ast.isLoopInstruction)(target) && depth === 0) {
// $FlowIgnore
node.args[0].value = (0, _ast.getStartBlockByteOffset)(target);
} else {
// $FlowIgnore
node.args[0].value = (0, _ast.getEndBlockByteOffset)(target);
}
}
if ((0, _ast.isIfInstruction)(node)) {
// $FlowIgnore
var alternateOffset = (0, _ast.getStartByteOffset)(node.alternate[0]);
var internalBrUnlessNode = (0, _ast.internalBrUnless)(alternateOffset);
internalBrUnlessNode.loc = node.loc;
this._emit(internalBrUnlessNode); // $FlowIgnore
node.consequent.forEach(function (n) {
return _this._emit(n);
}); // Skipping the alternate once the consequent block has been executed.
// We inject a goto at the offset of the else instruction
//
// TODO(sven): properly replace the else instruction instead, keep it in
// the ast.
var internalGotoNode = (0, _ast.internalGoto)( // $FlowIgnore
(0, _ast.getEndByteOffset)(node.alternate[node.alternate.length - 1]));
internalGotoNode.loc = {
start: {
line: -1,
// $FlowIgnore
column: node.alternate[0].loc.start.column - 1
}
};
this._emit(internalGotoNode); // $FlowIgnore
node.alternate.forEach(function (n) {
return _this._emit(n);
});
return;
}
this._emit(node);
}
}, {
key: "emitStartFunc",
value: function emitStartFunc(index) {
var funcInContext = this._context.funcs[index];
if (!(_typeof(funcInContext) === "object")) {
throw new Error('typeof funcInContext === "object"' + " error: " + (undefined || "unknown"));
}
if (!funcInContext.isImplemented) {
throw new Error('funcInContext.isImplemented' + " error: " + (undefined || "unknown"));
}
var func = funcInContext.node;
return {
name: kStart,
startAt: (0, _ast.getFunctionBeginingByteOffset)(func)
};
}
}, {
key: "finalizeFunc",
value: function finalizeFunc(func) {
this._labels.pop();
// transform the function body `end` into a return
var lastInstruction = this._program[this._program.length - 1];
var internalEndAndReturnNode = (0, _ast.internalEndAndReturn)();
internalEndAndReturnNode.loc = lastInstruction.node.loc; // will be emited at the same location, basically replacing the lastInstruction
this._emit(internalEndAndReturnNode); // clear current function from context
this._currentFunc = null;
return {
name: func.name ? func.name.value : null,
startAt: (0, _ast.getFunctionBeginingByteOffset)(func),
instructions: this._program
};
}
}]);
return Module;
}();
exports.Module = Module; |