UNPKG

java-class-tools

Version:

Read and write java class files in node or browser.

197 lines (154 loc) 9.81 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.InstructionParser = exports.Instruction = void 0; var _opcode = _interopRequireDefault(require("./opcode")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); } function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } } 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); return Constructor; } // -1 = variable length // index = opcode var opcodeOperandCount = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, -1, -1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 4, 4, 2, 1, 2, 0, 0, 2, 2, 0, 0, -1, 3, 2, 2, 4, 4, 0]; // index = opcode var opcodeMnemonics = ['nop', 'aconst_null', 'iconst_m1', 'iconst_0', 'iconst_1', 'iconst_2', 'iconst_3', 'iconst_4', 'iconst_5', 'lconst_0', 'lconst_1', 'fconst_0', 'fconst_1', 'fconst_2', 'dconst_0', 'dconst_1', 'bipush', 'sipush', 'ldc', 'ldc_w', 'ldc2_w', 'iload', 'lload', 'fload', 'dload', 'aload', 'iload_0', 'iload_1', 'iload_2', 'iload_3', 'lload_0', 'lload_1', 'lload_2', 'lload_3', 'fload_0', 'fload_1', 'fload_2', 'fload_3', 'dload_0', 'dload_1', 'dload_2', 'dload_3', 'aload_0', 'aload_1', 'aload_2', 'aload_3', 'iaload', 'laload', 'faload', 'daload', 'aaload', 'baload', 'caload', 'saload', 'istore', 'lstore', 'fstore', 'dstore', 'astore', 'istore_0', 'istore_1', 'istore_2', 'istore_3', 'lstore_0', 'lstore_1', 'lstore_2', 'lstore_3', 'fstore_0', 'fstore_1', 'fstore_2', 'fstore_3', 'dstore_0', 'dstore_1', 'dstore_2', 'dstore_3', 'astore_0', 'astore_1', 'astore_2', 'astore_3', 'iastore', 'lastore', 'fastore', 'dastore', 'aastore', 'bastore', 'castore', 'sastore', 'pop', 'pop2', 'dup', 'dup_x1', 'dup_x2', 'dup2', 'dup2_x1', 'dup2_x2', 'swap', 'iadd', 'ladd', 'fadd', 'dadd', 'isub', 'lsub', 'fsub', 'dsub', 'imul', 'lmul', 'fmul', 'dmul', 'idiv', 'ldiv', 'fdiv', 'ddiv', 'irem', 'lrem', 'frem', 'drem', 'ineg', 'lneg', 'fneg', 'dneg', 'ishl', 'lshl', 'ishr', 'lshr', 'iushr', 'lushr', 'iand', 'land', 'ior', 'lor', 'ixor', 'lxor', 'iinc', 'i2l', 'i2f', 'i2d', 'l2i', 'l2f', 'l2d', 'f2i', 'f2l', 'f2d', 'd2i', 'd2l', 'd2f', 'i2b', 'i2c', 'i2s', 'lcmp', 'fcmpl', 'fcmpg', 'dcmpl', 'dcmpg', 'ifeq', 'ifne', 'iflt', 'ifge', 'ifgt', 'ifle', 'if_icmpeq', 'if_icmpne', 'if_icmplt', 'if_icmpge', 'if_icmpgt', 'if_icmple', 'if_acmpeq', 'if_acmpne', 'goto', 'jsr', 'ret', 'tableswitch', 'lookupswitch', 'ireturn', 'lreturn', 'freturn', 'dreturn', 'areturn', 'return', 'getstatic', 'putstatic', 'getfield', 'putfield', 'invokevirtual', 'invokespecial', 'invokestatic', 'invokeinterface', 'invokedynamic', 'new', 'newarray', 'anewarray', 'arraylength', 'athrow', 'checkcast', 'instanceof', 'monitorenter', 'monitorexit', 'wide', 'multianewarray', 'ifnull', 'ifnonnull', 'goto_w', 'jsr_w', 'breakpoint']; var Instruction = /*#__PURE__*/ function () { function Instruction(opcode, operands, bytecodeOffset) { _classCallCheck(this, Instruction); if (typeof opcode !== 'number') throw TypeError('opcode must be a number'); if (!Array.isArray(operands)) throw TypeError('operands must be an array'); this.opcode = opcode; this.operands = operands; this.bytecodeOffset = bytecodeOffset; } _createClass(Instruction, [{ key: "toString", value: function toString() { var opcodeMnemonic = opcodeMnemonics[this.opcode] || (this.opcode === 0xFE ? 'impdep1' : this.opcode === 0xFF ? 'impdep2' : undefined); return "Instruction { opcode: ".concat(opcodeMnemonic, ", operands: [").concat(this.operands, "] }"); } }]); return Instruction; }(); exports.Instruction = Instruction; var InstructionParser = /*#__PURE__*/ function () { function InstructionParser() { _classCallCheck(this, InstructionParser); } _createClass(InstructionParser, null, [{ key: "toBytecode", /** * Converts Instruction objects into raw bytecode. * * @param {Instruction[]} instruction - Instructions to convert. * @see {@link https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5} * @returns {Number[]} */ value: function toBytecode(instructions) { if (!Array.isArray(instructions)) { throw TypeError('instructions must be an array.'); } var bytecode = []; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = instructions[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _step$value = _step.value, opcode = _step$value.opcode, operands = _step$value.operands; bytecode.push(opcode); if (opcode === _opcode["default"].TABLESWITCH || opcode === _opcode["default"].LOOKUPSWITCH) { var padding = bytecode.length % 4 ? 4 - bytecode.length % 4 : 0; while (padding-- > 0) { bytecode.push(0); } } // We assume we are given valid operands bytecode.push.apply(bytecode, _toConsumableArray(operands)); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator["return"] != null) { _iterator["return"](); } } finally { if (_didIteratorError) { throw _iteratorError; } } } return bytecode; } /** * Converts raw bytecode into Instruction objects. * * @param {number[]} bytecode - An array of bytes containing the jvm bytecode. * @see {@link https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5} * @returns {Instruction[]} */ }, { key: "fromBytecode", value: function fromBytecode(bytecode) { if (!Array.isArray(bytecode)) { throw TypeError('bytecode must be an array of bytes.'); } var instructions = []; var offset = 0; while (offset < bytecode.length) { var bytecodeOffset = offset; var opcode = bytecode[offset++]; var numOperandBytes = void 0; switch (opcode) { // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.lookupswitch case _opcode["default"].LOOKUPSWITCH: { var padding = offset % 4 ? 4 - offset % 4 : 0; offset += padding; // Skip padding var npairs = bytecode[offset + 4] << 24 | bytecode[offset + 5] << 16 | bytecode[offset + 6] << 8 | bytecode[offset + 7]; numOperandBytes = 8 + npairs * 8; break; } // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.tableswitch case _opcode["default"].TABLESWITCH: { var _padding = offset % 4 ? 4 - offset % 4 : 0; offset += _padding; // Skip padding var low = bytecode[offset + 4] << 24 | bytecode[offset + 5] << 16 | bytecode[offset + 6] << 8 | bytecode[offset + 7]; var high = bytecode[offset + 8] << 24 | bytecode[offset + 9] << 16 | bytecode[offset + 10] << 8 | bytecode[offset + 11]; var numJumpOffsets = high - low + 1; numOperandBytes = 3 * 4 + numJumpOffsets * 4; break; } // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.wide case _opcode["default"].WIDE: numOperandBytes = bytecode[offset] === _opcode["default"].IINC ? 5 : 3; break; default: numOperandBytes = opcodeOperandCount[opcode]; if (numOperandBytes === undefined) { throw Error("Unexpected opcode: ".concat(opcode)); } break; } var operands = bytecode.slice(offset, offset + numOperandBytes); var instruction = new Instruction(opcode, operands, bytecodeOffset); instructions.push(instruction); offset += numOperandBytes; } return instructions; } }]); return InstructionParser; }(); exports.InstructionParser = InstructionParser;