microvium
Version:
A compact, embeddable scripting engine for microcontrollers for executing small scripts written in a subset of JavaScript.
101 lines • 5.21 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.labelOperandsOfOperation = exports.maxOperandCount = exports.minOperandCount = exports.blockTerminatingOpcodes = exports.opcodes = void 0;
const utils_1 = require("./utils");
// For opcodes that don't have a fixed effect on the stack, these functions
// calculate the corresponding stack change given the specific operands
const stackChanges = {
call: argCount => -count(argCount) - 1,
awaitCall: argCount => -count(argCount) - 1,
pop: popCount => -count(popCount),
};
/**
* The set of opcodes and metadata about the opcodes
*
* Note: `stackChange` is the "dynamic" number describing how much the stack is
* expected to change after executing the operation. See also
* `IL.calcDynamicStackChangeOfOp` and `IL.calcStaticStackChangeOfOp`.
*/
exports.opcodes = {
'ArrayGet': { operands: ['LiteralOperand'], stackChange: 0 },
'ArrayNew': { operands: [], stackChange: 1 },
'ArraySet': { operands: ['LiteralOperand'], stackChange: -2 },
'AsyncComplete': { operands: [], stackChange: -3 },
'AsyncResume': { operands: ['CountOperand', 'CountOperand'], stackChange: 1 /*inverse of Await*/ },
'AsyncReturn': { operands: [], stackChange: undefined },
'AsyncStart': { operands: ['CountOperand', 'FlagOperand'], stackChange: 3 },
'Await': { operands: [], stackChange: -1 },
'AwaitCall': { operands: ['CountOperand'], stackChange: stackChanges.awaitCall },
'BinOp': { operands: ['OpOperand'], stackChange: -1 },
'Branch': { operands: ['LabelOperand', 'LabelOperand'], stackChange: -1 },
'Call': { operands: ['CountOperand', 'FlagOperand'], stackChange: stackChanges.call },
'ClassCreate': { operands: [], stackChange: -1 },
'ClosureNew': { operands: [], stackChange: 0 },
'EndTry': { operands: [], stackChange: undefined },
'EnqueueJob': { operands: [], stackChange: 0 },
'Jump': { operands: ['LabelOperand'], stackChange: 0 },
'Literal': { operands: ['LiteralOperand'], stackChange: 1 },
'LoadArg': { operands: ['IndexOperand'], stackChange: 1 },
'LoadGlobal': { operands: ['NameOperand'], stackChange: 1 },
'LoadReg': { operands: ['NameOperand'], stackChange: 1 },
'LoadScoped': { operands: ['IndexOperand'], stackChange: 1 },
'LoadVar': { operands: ['IndexOperand'], stackChange: 1 },
'New': { operands: ['CountOperand'], stackChange: stackChanges.call },
'Nop': { operands: ['CountOperand'], stackChange: 0 },
'ObjectGet': { operands: [], stackChange: -1 },
'ObjectKeys': { operands: [], stackChange: 0 },
'ObjectNew': { operands: [], stackChange: 1 },
'ObjectSet': { operands: [], stackChange: -3 },
'Pop': { operands: ['CountOperand'], stackChange: stackChanges.pop },
'Return': { operands: [], stackChange: 1 },
'ScopeClone': { operands: [], stackChange: 0 },
'ScopeDiscard': { operands: [], stackChange: 0 },
'ScopeNew': { operands: ['CountOperand'], stackChange: 0 },
'ScopePop': { operands: [], stackChange: 0 },
'ScopePush': { operands: ['CountOperand'], stackChange: 0 },
'ScopeSave': { operands: [], stackChange: 1 },
'StartTry': { operands: ['LabelOperand'], stackChange: 2 },
'StoreGlobal': { operands: ['NameOperand'], stackChange: -1 },
'StoreScoped': { operands: ['IndexOperand'], stackChange: -1 },
'StoreVar': { operands: ['IndexOperand'], stackChange: -1 },
'Throw': { operands: [], stackChange: -1 },
'TypeCodeOf': { operands: [], stackChange: 0 },
'Uint8ArrayNew': { operands: [], stackChange: 0 },
'UnOp': { operands: ['OpOperand'], stackChange: 0 },
};
exports.blockTerminatingOpcodes = new Set(['Jump', 'Branch', 'Return', 'AsyncReturn', 'Throw', 'AsyncComplete']);
function count(operand) {
if (!operand || operand.type !== 'CountOperand')
(0, utils_1.unexpected)();
return operand.count;
}
const _minOperandCount = new Map(Object.keys(exports.opcodes).map(opcode => [
opcode,
exports.opcodes[opcode].operands.filter(operand => !operand.endsWith('?')).length
]));
// The minimum number of operands that a particular operation can take
function minOperandCount(op) {
return _minOperandCount.get(op) ?? (0, utils_1.unexpected)();
}
exports.minOperandCount = minOperandCount;
const _maxOperandCount = new Map(Object.keys(exports.opcodes).map(opcode => [
opcode,
exports.opcodes[opcode].operands.length
]));
// The maximum number of operands that a particular operation can take
function maxOperandCount(op) {
return _maxOperandCount.get(op) ?? (0, utils_1.unexpected)();
}
exports.maxOperandCount = maxOperandCount;
function labelOperandsOfOperation(op) {
const meta = exports.opcodes[op.opcode] ?? (0, utils_1.unexpected)();
const result = [];
for (const [operandI, operandType] of meta.operands.entries()) {
if (operandType === 'LabelOperand') {
result.push(op.operands[operandI] ?? (0, utils_1.unexpected)());
}
}
return result;
}
exports.labelOperandsOfOperation = labelOperandsOfOperation;
//# sourceMappingURL=il-opcodes.js.map