microvium
Version:
A compact, embeddable scripting engine for microcontrollers for executing small scripts written in a subset of JavaScript.
124 lines • 5.28 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.calcStaticStackChangeOfOp = exports.calcDynamicStackChangeOfOp = exports.ExecutionFlag = exports.referenceValue = exports.hostFunctionValue = exports.functionValue = exports.emptyString = exports.stringValue = exports.numberValue = exports.trueValue = exports.falseValue = exports.nullValue = exports.undefinedValue = exports.noOpFunction = exports.deletedValue = exports.isLiteralOperand = exports.isLabelOperand = exports.isNameOperand = exports.dynamicEncoding = exports.ExportID = exports.MAX_COUNT = exports.MAX_INDEX = exports.opcodes = void 0;
/*
IL is a data format for virtual machine state.
*/
const utils_1 = require("./utils");
const runtime_types_1 = require("./runtime-types");
const il_opcodes_1 = require("./il-opcodes");
var il_opcodes_2 = require("./il-opcodes");
Object.defineProperty(exports, "opcodes", { enumerable: true, get: function () { return il_opcodes_2.opcodes; } });
exports.MAX_INDEX = 0x3FFF;
exports.MAX_COUNT = 0x3FFF;
const ExportID = (exportID) => {
(0, utils_1.hardAssert)((0, runtime_types_1.isUInt16)(exportID));
return exportID;
};
exports.ExportID = ExportID;
exports.dynamicEncoding = Object.freeze({ type: 'DynamicEncoding' });
//| "void"
//| "delete"
function isNameOperand(value) {
return value.type === 'NameOperand';
}
exports.isNameOperand = isNameOperand;
function isLabelOperand(value) {
return value.type === 'LabelOperand';
}
exports.isLabelOperand = isLabelOperand;
function isLiteralOperand(value) {
return value.type === 'LiteralOperand';
}
exports.isLiteralOperand = isLiteralOperand;
exports.deletedValue = Object.freeze({
type: 'DeletedValue',
value: undefined
});
exports.noOpFunction = Object.freeze({
type: 'NoOpFunction'
});
exports.undefinedValue = Object.freeze({
type: 'UndefinedValue',
value: undefined
});
exports.nullValue = Object.freeze({
type: 'NullValue',
value: null
});
exports.falseValue = Object.freeze({
type: 'BooleanValue',
value: false
});
exports.trueValue = Object.freeze({
type: 'BooleanValue',
value: true
});
const numberValue = (n) => Object.freeze({
type: 'NumberValue',
value: n
});
exports.numberValue = numberValue;
const stringValue = (s) => Object.freeze({
type: 'StringValue',
value: s
});
exports.stringValue = stringValue;
exports.emptyString = (0, exports.stringValue)('');
const functionValue = (functionID) => Object.freeze({
type: 'FunctionValue',
value: functionID
});
exports.functionValue = functionValue;
const hostFunctionValue = (hostFunctionID) => Object.freeze({
type: 'HostFunctionValue',
value: hostFunctionID
});
exports.hostFunctionValue = hostFunctionValue;
const referenceValue = (allocationID) => Object.freeze({
type: 'ReferenceValue',
value: allocationID
});
exports.referenceValue = referenceValue;
var ExecutionFlag;
(function (ExecutionFlag) {
ExecutionFlag[ExecutionFlag["FloatSupport"] = 0] = "FloatSupport";
ExecutionFlag[ExecutionFlag["CompiledWithOverflowChecks"] = 1] = "CompiledWithOverflowChecks";
})(ExecutionFlag = exports.ExecutionFlag || (exports.ExecutionFlag = {}));
function calcDynamicStackChangeOfOp(operation) {
const meta = il_opcodes_1.opcodes[operation.opcode];
let stackChange = meta.stackChange;
if (typeof stackChange === 'function')
stackChange = stackChange(...operation.operands);
return stackChange;
}
exports.calcDynamicStackChangeOfOp = calcDynamicStackChangeOfOp;
// The static stack change gives you stack depth at the instruction that follows
// statically (physically) rather than the one that follows dynamically (from
// runtime control flow). This is the same as the dynamic stack depth except in
// the case of control flow instructions for which these diverge.
function calcStaticStackChangeOfOp(operation) {
// Control flow operations
switch (operation.opcode) {
case 'Return': return -1; // Return pops the result off the stack
case 'AsyncReturn': return -1; // Return pops the result off the stack
case 'Branch': return -1; // Pops predicate off the stack
case 'Jump': return 0;
case 'Call': {
const forCall = calcDynamicStackChangeOfOp(operation) ?? (0, utils_1.unexpected)(); // Arguments popped from stack
const isVoidCall = operation.operands[1];
(0, utils_1.hardAssert)(isVoidCall.type === 'FlagOperand');
const forReturn = isVoidCall.flag ? 0 : 1; // Return value pushed to the stack
return forCall + forReturn;
}
case 'AwaitCall': {
const forCall = calcDynamicStackChangeOfOp(operation) ?? (0, utils_1.unexpected)(); // Arguments popped from stack
const forReturn = 1;
return forCall + forReturn;
}
case 'New': return (0, utils_1.notUndefined)(calcDynamicStackChangeOfOp(operation)) + 1; // Includes the pushed return value
default: return calcDynamicStackChangeOfOp(operation);
}
}
exports.calcStaticStackChangeOfOp = calcStaticStackChangeOfOp;
//# sourceMappingURL=il.js.map