/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.vm.bytecode; import java.nio.ByteBuffer; import org.jnode.vm.classmgr.VmByteCode; import org.jnode.vm.classmgr.VmCP; import org.jnode.vm.classmgr.VmConstClass; import org.jnode.vm.classmgr.VmConstDouble; import org.jnode.vm.classmgr.VmConstFieldRef; import org.jnode.vm.classmgr.VmConstFloat; import org.jnode.vm.classmgr.VmConstIMethodRef; import org.jnode.vm.classmgr.VmConstInt; import org.jnode.vm.classmgr.VmConstLong; import org.jnode.vm.classmgr.VmConstObject; import org.jnode.vm.classmgr.VmConstString; import org.jnode.vm.classmgr.VmMethod; /** * <description> * * @author epr */ public class BytecodeParser { private final VmByteCode bc; private final VmCP cp; private ByteBuffer bytecode; private final BytecodeVisitor handler; private int address; private boolean wide; private int opcode; private int paddedAddress; private int continueAt; private int endPC; /** * @return The padded address */ public final int getPaddedAddress() { return this.paddedAddress; } /** * Create a new instance * * @param bc * @param handler */ protected BytecodeParser(VmByteCode bc, BytecodeVisitor handler) { this.bc = bc; this.bytecode = bc.getBytecode(); this.cp = bc.getCP(); this.handler = handler; } /** * Parse a given bytecode * * @param bc * @param handler * @throws ClassFormatError */ public static void parse(VmByteCode bc, BytecodeVisitor handler) throws ClassFormatError { new BytecodeParser(bc, handler).parse(); } /** * Parse a given bytecode * * @param bc * @param handler * @param startPC The program counter where to start parsing (inclusive) * @param endPC The program counter where to stop parsing (exclusive) * @param startEndMethod Should startMethod and endMethod be called. * @throws ClassFormatError */ public static void parse(VmByteCode bc, BytecodeVisitor handler, int startPC, int endPC, boolean startEndMethod) throws ClassFormatError { new BytecodeParser(bc, handler).parse(startPC, endPC, startEndMethod); } /** * Parse a given bytecode * * @throws ClassFormatError */ public void parse() throws ClassFormatError { parse(0, bytecode.limit(), true); } /** * Parse a selected region of the bytecode * * @param startPC The program counter where to start parsing (inclusive) * @param endPCArg The program counter where to stop parsing (exclusive) * @throws ClassFormatError */ public void parse(int startPC, int endPCArg, boolean startEndMethod) throws ClassFormatError { final BytecodeVisitor handler = this.handler; bytecode.position(startPC); this.endPC = endPCArg; handler.setParser(this); if (startEndMethod) { fireStartMethod(bc.getMethod()); } while (bytecode.position() < endPC) { // The address(offset) of the current instruction this.continueAt = -1; this.address = bytecode.position(); this.wide = false; fireStartInstruction(address); this.opcode = getu1(); final int cpIdx; //counters[ opcode]++; switch (opcode) { // -- 0 -- case 0x00: handler.visit_nop(); break; case 0x01: handler.visit_aconst_null(); break; case 0x02: handler.visit_iconst(-1); break; case 0x03: handler.visit_iconst(0); break; case 0x04: handler.visit_iconst(1); break; case 0x05: handler.visit_iconst(2); break; case 0x06: handler.visit_iconst(3); break; case 0x07: handler.visit_iconst(4); break; case 0x08: handler.visit_iconst(5); break; case 0x09: handler.visit_lconst(0L); break; // -- 10 -- case 0x0a: handler.visit_lconst(1l); break; case 0x0b: handler.visit_fconst(0.0f); break; case 0x0c: handler.visit_fconst(1.0f); break; case 0x0d: handler.visit_fconst(2.0f); break; case 0x0e: handler.visit_dconst(0.0); break; case 0x0f: handler.visit_dconst(1.0); break; case 0x10: handler.visit_iconst(gets1()); // bipush break; case 0x11: handler.visit_iconst(gets2()); // sipush break; case 0x12: case 0x13: { if (opcode == 0x12) { cpIdx = getu1(); } else { cpIdx = getu2(); } final VmConstObject o = (VmConstObject) cp.getAny(cpIdx); switch (o.getConstType()) { case VmConstObject.CONST_INT: handler.visit_iconst(((VmConstInt) o).intValue()); break; case VmConstObject.CONST_FLOAT: handler.visit_fconst(((VmConstFloat) o).floatValue()); break; case VmConstObject.CONST_CLASS: handler.visit_ldc((VmConstClass) o); break; case VmConstObject.CONST_STRING: handler.visit_ldc((VmConstString) o); break; default: throw new ClassFormatError("Unknown constant pool type: " + o.getConstType()); } break; } // -- 20 -- case 0x14: { final VmConstObject o = (VmConstObject) cp.getAny(getu2()); switch (o.getConstType()) { case VmConstObject.CONST_LONG: handler.visit_lconst(((VmConstLong) o).longValue()); break; case VmConstObject.CONST_DOUBLE: handler.visit_dconst(((VmConstDouble) o).doubleValue()); break; default: throw new ClassFormatError("Unknown constant pool type: " + o.getConstType()); } break; } case 0x15: handler.visit_iload(getu1()); break; case 0x16: handler.visit_lload(getu1()); break; case 0x17: handler.visit_fload(getu1()); break; case 0x18: handler.visit_dload(getu1()); break; case 0x19: handler.visit_aload(getu1()); break; case 0x1a: handler.visit_iload(0); break; case 0x1b: handler.visit_iload(1); break; case 0x1c: handler.visit_iload(2); break; case 0x1d: handler.visit_iload(3); break; // -- 30 -- case 0x1e: handler.visit_lload(0); break; case 0x1f: handler.visit_lload(1); break; case 0x20: handler.visit_lload(2); break; case 0x21: handler.visit_lload(3); break; case 0x22: handler.visit_fload(0); break; case 0x23: handler.visit_fload(1); break; case 0x24: handler.visit_fload(2); break; case 0x25: handler.visit_fload(3); break; case 0x26: handler.visit_dload(0); break; case 0x27: handler.visit_dload(1); break; // -- 40 -- case 0x28: handler.visit_dload(2); break; case 0x29: handler.visit_dload(3); break; case 0x2a: handler.visit_aload(0); break; case 0x2b: handler.visit_aload(1); break; case 0x2c: handler.visit_aload(2); break; case 0x2d: handler.visit_aload(3); break; case 0x2e: handler.visit_iaload(); break; case 0x2f: handler.visit_laload(); break; case 0x30: handler.visit_faload(); break; case 0x31: handler.visit_daload(); break; // -- 50 -- case 0x32: handler.visit_aaload(); break; case 0x33: handler.visit_baload(); break; case 0x34: handler.visit_caload(); break; case 0x35: handler.visit_saload(); break; case 0x36: handler.visit_istore(getu1()); break; case 0x37: handler.visit_lstore(getu1()); break; case 0x38: handler.visit_fstore(getu1()); break; case 0x39: handler.visit_dstore(getu1()); break; case 0x3a: handler.visit_astore(getu1()); break; case 0x3b: handler.visit_istore(0); break; // -- 60 -- case 0x3c: handler.visit_istore(1); break; case 0x3d: handler.visit_istore(2); break; case 0x3e: handler.visit_istore(3); break; case 0x3f: handler.visit_lstore(0); break; case 0x40: handler.visit_lstore(1); break; case 0x41: handler.visit_lstore(2); break; case 0x42: handler.visit_lstore(3); break; case 0x43: handler.visit_fstore(0); break; case 0x44: handler.visit_fstore(1); break; case 0x45: handler.visit_fstore(2); break; // -- 70 -- case 0x46: handler.visit_fstore(3); break; case 0x47: handler.visit_dstore(0); break; case 0x48: handler.visit_dstore(1); break; case 0x49: handler.visit_dstore(2); break; case 0x4a: handler.visit_dstore(3); break; case 0x4b: handler.visit_astore(0); break; case 0x4c: handler.visit_astore(1); break; case 0x4d: handler.visit_astore(2); break; case 0x4e: handler.visit_astore(3); break; case 0x4f: handler.visit_iastore(); break; // -- 80 -- case 0x50: handler.visit_lastore(); break; case 0x51: handler.visit_fastore(); break; case 0x52: handler.visit_dastore(); break; case 0x53: handler.visit_aastore(); break; case 0x54: handler.visit_bastore(); break; case 0x55: handler.visit_castore(); break; case 0x56: handler.visit_sastore(); break; case 0x57: handler.visit_pop(); break; case 0x58: handler.visit_pop2(); break; case 0x59: handler.visit_dup(); break; // -- 90 -- case 0x5a: handler.visit_dup_x1(); break; case 0x5b: handler.visit_dup_x2(); break; case 0x5c: handler.visit_dup2(); break; case 0x5d: handler.visit_dup2_x1(); break; case 0x5e: handler.visit_dup2_x2(); break; case 0x5f: handler.visit_swap(); break; case 0x60: handler.visit_iadd(); break; case 0x61: handler.visit_ladd(); break; case 0x62: handler.visit_fadd(); break; case 0x63: handler.visit_dadd(); break; // -- 100 -- case 0x64: handler.visit_isub(); break; case 0x65: handler.visit_lsub(); break; case 0x66: handler.visit_fsub(); break; case 0x67: handler.visit_dsub(); break; case 0x68: handler.visit_imul(); break; case 0x69: handler.visit_lmul(); break; case 0x6a: handler.visit_fmul(); break; case 0x6b: handler.visit_dmul(); break; case 0x6c: handler.visit_idiv(); break; case 0x6d: handler.visit_ldiv(); break; // -- 110 -- case 0x6e: handler.visit_fdiv(); break; case 0x6f: handler.visit_ddiv(); break; case 0x70: handler.visit_irem(); break; case 0x71: handler.visit_lrem(); break; case 0x72: handler.visit_frem(); break; case 0x73: handler.visit_drem(); break; case 0x74: handler.visit_ineg(); break; case 0x75: handler.visit_lneg(); break; case 0x76: handler.visit_fneg(); break; case 0x77: handler.visit_dneg(); break; // -- 120 -- case 0x78: handler.visit_ishl(); break; case 0x79: handler.visit_lshl(); break; case 0x7a: handler.visit_ishr(); break; case 0x7b: handler.visit_lshr(); break; case 0x7c: handler.visit_iushr(); break; case 0x7d: handler.visit_lushr(); break; case 0x7e: handler.visit_iand(); break; case 0x7f: handler.visit_land(); break; case 0x80: handler.visit_ior(); break; case 0x81: handler.visit_lor(); break; // -- 130 -- case 0x82: handler.visit_ixor(); break; case 0x83: handler.visit_lxor(); break; case 0x84: { int idx = getu1(); handler.visit_iinc(idx, gets1()); break; } case 0x85: handler.visit_i2l(); break; case 0x86: handler.visit_i2f(); break; case 0x87: handler.visit_i2d(); break; case 0x88: handler.visit_l2i(); break; case 0x89: handler.visit_l2f(); break; case 0x8a: handler.visit_l2d(); break; case 0x8b: handler.visit_f2i(); break; // -- 140 -- case 0x8c: handler.visit_f2l(); break; case 0x8d: handler.visit_f2d(); break; case 0x8e: handler.visit_d2i(); break; case 0x8f: handler.visit_d2l(); break; case 0x90: handler.visit_d2f(); break; case 0x91: handler.visit_i2b(); break; case 0x92: handler.visit_i2c(); break; case 0x93: handler.visit_i2s(); break; case 0x94: handler.visit_lcmp(); break; case 0x95: handler.visit_fcmpl(); break; // -- 150 -- case 0x96: handler.visit_fcmpg(); break; case 0x97: handler.visit_dcmpl(); break; case 0x98: handler.visit_dcmpg(); break; case 0x99: handler.visit_ifeq(address + gets2()); break; case 0x9a: handler.visit_ifne(address + gets2()); break; case 0x9b: handler.visit_iflt(address + gets2()); break; case 0x9c: handler.visit_ifge(address + gets2()); break; case 0x9d: handler.visit_ifgt(address + gets2()); break; case 0x9e: handler.visit_ifle(address + gets2()); break; case 0x9f: handler.visit_if_icmpeq(address + gets2()); break; // -- 160 -- case 0xa0: handler.visit_if_icmpne(address + gets2()); break; case 0xa1: handler.visit_if_icmplt(address + gets2()); break; case 0xa2: handler.visit_if_icmpge(address + gets2()); break; case 0xa3: handler.visit_if_icmpgt(address + gets2()); break; case 0xa4: handler.visit_if_icmple(address + gets2()); break; case 0xa5: handler.visit_if_acmpeq(address + gets2()); break; case 0xa6: handler.visit_if_acmpne(address + gets2()); break; case 0xa7: handler.visit_goto(address + gets2()); break; case 0xa8: handler.visit_jsr(address + gets2()); break; case 0xa9: handler.visit_ret(getu1()); break; // -- 170 -- case 0xaa: { skipPadding(); int defAddress = address + gets4(); int lowValue = gets4(); int highValue = gets4(); if (highValue < lowValue) { throw new ClassFormatError( "tableSwitch high < low! (high=" + highValue + ", low=" + lowValue + ')'); } int cnt = highValue - lowValue + 1; int addresses[] = new int[cnt]; for (int i = 0; i < cnt; i++) { addresses[i] = address + gets4(); } handler.visit_tableswitch(defAddress, lowValue, highValue, addresses); break; } case 0xab: { skipPadding(); int defAddress = address + gets4(); int cnt = getu4(); int matches[] = new int[cnt]; int addresses[] = new int[cnt]; for (int i = 0; i < cnt; i++) { matches[i] = gets4(); addresses[i] = address + gets4(); } handler.visit_lookupswitch(defAddress, matches, addresses); break; } case 0xac: handler.visit_ireturn(); break; case 0xad: handler.visit_lreturn(); break; case 0xae: handler.visit_freturn(); break; case 0xaf: handler.visit_dreturn(); break; case 0xb0: handler.visit_areturn(); break; case 0xb1: handler.visit_return(); break; case 0xb2: { VmConstFieldRef field = cp.getConstFieldRef(getu2()); handler.visit_getstatic(field); break; } case 0xb3: { VmConstFieldRef field = cp.getConstFieldRef(getu2()); handler.visit_putstatic(field); break; } // -- 180 -- case 0xb4: { VmConstFieldRef field = cp.getConstFieldRef(getu2()); handler.visit_getfield(field); break; } case 0xb5: { VmConstFieldRef field = cp.getConstFieldRef(getu2()); handler.visit_putfield(field); break; } case 0xb6: handler.visit_invokevirtual(cp.getConstMethodRef(getu2())); break; case 0xb7: handler.visit_invokespecial(cp.getConstMethodRef(getu2())); break; case 0xb8: handler.visit_invokestatic(cp.getConstMethodRef(getu2())); break; case 0xb9: { VmConstIMethodRef ref = cp.getConstIMethodRef(getu2()); int count = getu1(); skip(); handler.visit_invokeinterface(ref, count); break; } //case 0xba: handler.throw_invalid_opcode ; unused case 0xbb: handler.visit_new(cp.getConstClass(getu2())); break; case 0xbc: handler.visit_newarray(getu1()); break; case 0xbd: handler.visit_anewarray(cp.getConstClass(getu2())); break; // -- 190 -- case 0xbe: handler.visit_arraylength(); break; case 0xbf: handler.visit_athrow(); break; case 0xc0: handler.visit_checkcast(cp.getConstClass(getu2())); break; case 0xc1: handler.visit_instanceof(cp.getConstClass(getu2())); break; case 0xc2: handler.visit_monitorenter(); break; case 0xc3: handler.visit_monitorexit(); break; case 0xc4: { wide = true; int opcode = getu1(); if (opcode == 0x84) { int idx = getu2(); int constValue = gets2(); handler.visit_iinc(idx, constValue); } else { int idx = getu2(); switch (opcode) { case 0x15: handler.visit_iload(idx); break; case 0x16: handler.visit_lload(idx); break; case 0x17: handler.visit_fload(idx); break; case 0x18: handler.visit_dload(idx); break; case 0x19: handler.visit_aload(idx); break; case 0x36: handler.visit_istore(idx); break; case 0x37: handler.visit_lstore(idx); break; case 0x38: handler.visit_fstore(idx); break; case 0x39: handler.visit_dstore(idx); break; case 0x3a: handler.visit_astore(idx); break; default: throw new ClassFormatError( "Invalid opcode in wide instruction"); } } break; } case 0xc5: { VmConstClass clazz = cp.getConstClass(getu2()); int dims = getu1(); handler.visit_multianewarray(clazz, dims); break; } case 0xc6: handler.visit_ifnull(address + gets2()); break; case 0xc7: handler.visit_ifnonnull(address + gets2()); break; // -- 200 -- case 0xc8: handler.visit_goto(address + gets4()); break; case 0xc9: handler.visit_jsr(address + gets4()); break; default: throw new ClassFormatError("Invalid opcode"); } fireEndInstruction(); if (continueAt >= 0) { bytecode.position(continueAt); } } if (startEndMethod) { fireEndMethod(); } } /** * Get an unsigned byte from the next bytecode position * * @return int */ private final int getu1() { return bytecode.get() & 0xFF; } /** * Get an unsigned short from the next bytecode position * * @return int */ private final int getu2() { int v1 = bytecode.get() & 0xFF; int v2 = bytecode.get() & 0xFF; return (v1 << 8) | v2; } /** * Get an unsigned int from the next bytecode position * * @return int */ private final int getu4() { int v1 = bytecode.get() & 0xFF; int v2 = bytecode.get() & 0xFF; int v3 = bytecode.get() & 0xFF; int v4 = bytecode.get() & 0xFF; return (v1 << 24) | (v2 << 16) | (v3 << 8) | v4; } /** * Get a byte from the next bytecode position * * @return byte */ private final byte gets1() { return bytecode.get(); } /** * Get a short from the next bytecode positions * * @return short */ private final short gets2() { int v1 = bytecode.get() & 0xFF; int v2 = bytecode.get() & 0xFF; return (short) ((v1 << 8) | v2); } /** * Get an int from the next bytecode position * * @return int */ private final int gets4() { int v1 = bytecode.get() & 0xFF; int v2 = bytecode.get() & 0xFF; int v3 = bytecode.get() & 0xFF; int v4 = bytecode.get() & 0xFF; return (v1 << 24) | (v2 << 16) | (v3 << 8) | v4; } private final void skipPadding() { while (bytecode.position() % 4 != 0) { bytecode.get(); } paddedAddress = bytecode.position(); } private final void skip() { bytecode.get(); } /** * Gets the address of the current instruction in the parse method. * * @return int */ public final int getAddress() { return this.address; } /** * Gets the address of the next instruction in the parse method. * * @return int */ public final int getNextAddress() { return bytecode.position(); } /** * Is the current instruction a wide instruction * * @return boolean */ public final boolean isWide() { return this.wide; } /** * Gets the opcode of the current instruction * * @return int */ public final int getOpcode() { return this.opcode; } public final void setContinueAt(int offset) { continueAt = offset; } public final void setCode(ByteBuffer bytecode) { this.bytecode = bytecode; } public final void adjustEndPC(int delta) { endPC += delta; } public final void setEndPC(int offset) { endPC = offset; } /** * @return The end PC of the parsable block */ public int getEndPC() { return this.endPC; } /** * Call the startInstruction method of the handler. * * @param address */ protected void fireStartInstruction(int address) { handler.startInstruction(address); } /** * Call the endInstruction method of the handler. */ protected void fireEndInstruction() { handler.endInstruction(); } /** * Call the startInstruction method of the handler. * * @param method */ protected void fireStartMethod(VmMethod method) { handler.startMethod(method); } /** * Call the endMethod method of the handler. */ protected void fireEndMethod() { handler.endMethod(); } public static void dumpStatistics() { /*for (int i = 0; i < 256; i++) { if (counters[ i] != 0) { System.out.println("0x" + Integer.toHexString(i) + "\t" + counters[ i]); } }*/ } }