UNPKG

microvium

Version:

A compact, embeddable scripting engine for microcontrollers for executing small scripts written in a subset of JavaScript.

101 lines 5.21 kB
"use strict"; 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