From 2d01111d8fa5e7bd8985b5897014dcec4bfb3fcd Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 9 Feb 1999 18:05:11 +0000 Subject: [PATCH] Initial revision git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@251 379699f6-c40d-0410-875b-85095c16579e --- jode/jode/bytecode/GrowableConstantPool.java | 157 +++++ jode/jode/decompiler/Opcodes.java | 673 +++++++++++++++++++ 2 files changed, 830 insertions(+) create mode 100644 jode/jode/bytecode/GrowableConstantPool.java create mode 100644 jode/jode/decompiler/Opcodes.java diff --git a/jode/jode/bytecode/GrowableConstantPool.java b/jode/jode/bytecode/GrowableConstantPool.java new file mode 100644 index 0000000..743bfe8 --- /dev/null +++ b/jode/jode/bytecode/GrowableConstantPool.java @@ -0,0 +1,157 @@ +/* jode.bytecode.GrowableConstantPool Copyright (C) 1998 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ +package jode.bytecode; +import java.io.*; +import jode.Type; +import java.util.Hashtable; + +/** + * This class represent a constant pool, where new constants can be added to. + * + * @author Jochen Hoenicke + */ +public class GrowableConstantPool extends ConstantPool { + Hashtable entryToIndex = new Hashtable(); + + public GrowableConstantPool () { + count = 1; + tags = new int[1]; + indices1 = new int[1]; + indices2 = new int[1]; + constants = new Object[1]; + } + + public final void grow(int wantedSize) { + if (tags.length < wantedSize) { + int newSize = Math.max(tags.length*2, wantedSize); + int[] tmpints = new int[newSize]; + System.arraycopy(tags, 0, tmpints, 0, count); + tags = tmpints; + tmpints = new int[newSize]; + System.arraycopy(indices1, 0, tmpints, 0, count); + indices1 = tmpints; + tmpints = new int[newSize]; + System.arraycopy(indices2, 0, tmpints, 0, count); + indices2 = tmpints; + Object[] tmpobjs = new Object[newSize]; + System.arraycopy(constants, 0, tmpobjs, 0, count); + constants = tmpobjs; + } + } + + public int putConstant(int tag, Object constant) { + String key = tag+"C"+constant; + Integer index = (Integer) entryToIndex.get(key); + if (index != null) + return index.intValue(); + int newIndex = count; + grow(count+1); + tags[newIndex] = tag; + constants[newIndex] = constant; + entryToIndex.put(key, new Integer(newIndex)); + count++; + if (tag == DOUBLE || tag == LONG) + count++; + return newIndex; + } + + public int putIndexed(int tag, int index1, int index2) { + String key = tag+"I"+index1+","+index2; + Integer index = (Integer) entryToIndex.get(key); + if (index != null) + return index.intValue(); + grow(count+1); + tags[count] = tag; + indices1[count] = index1; + indices2[count] = index2; + entryToIndex.put(key, new Integer(count)); + return count++; + } + + public final int putUTF(String utf) { + return putConstant(UTF8, utf); + } + + public int putClassRef(String name) { + return putIndexed(CLASS, putUTF(name.replace('.','/')), 0); + } + + public int putRef(int tag, String[] names) { + int classIndex = putClassRef(names[0]); + int nameIndex = putUTF(names[1]); + int typeIndex = putUTF(names[2]); + int nameTypeIndex = putIndexed(NAMEANDTYPE, nameIndex, typeIndex); + return putIndexed(tag, classIndex, nameTypeIndex); + } + + public int copyConstant(ConstantPool cp, int index) + throws ClassFormatException { + if (cp.tags[index] == STRING) + return putIndexed(STRING, + putUTF(cp.getUTF8(cp.indices1[index])), 0); + else + return putConstant(cp.tags[index], cp.constants[index]); + } + + public void write(DataOutputStream stream) + throws IOException { + stream.writeShort(count); + for (int i=1; i< count; i++) { + int tag = tags[i]; + stream.writeByte(tag); + switch (tag) { + case CLASS: + stream.writeShort(indices1[i]); + break; + case FIELDREF: + case METHODREF: + case INTERFACEMETHODREF: + stream.writeShort(indices1[i]); + stream.writeShort(indices2[i]); + break; + case STRING: + stream.writeShort(indices1[i]); + break; + case INTEGER: + stream.writeInt(((Integer)constants[i]).intValue()); + break; + case FLOAT: + stream.writeFloat(((Float)constants[i]).floatValue()); + break; + case LONG: + stream.writeLong(((Long)constants[i]).longValue()); + i++; + break; + case DOUBLE: + stream.writeDouble(((Double)constants[i]).doubleValue()); + i++; + break; + case NAMEANDTYPE: + stream.writeShort(indices1[i]); + stream.writeShort(indices2[i]); + break; + case UTF8: + stream.writeUTF((String)constants[i]); + break; + default: + throw new ClassFormatException("unknown constant tag"); + } + } + } +} diff --git a/jode/jode/decompiler/Opcodes.java b/jode/jode/decompiler/Opcodes.java new file mode 100644 index 0000000..0eae6a7 --- /dev/null +++ b/jode/jode/decompiler/Opcodes.java @@ -0,0 +1,673 @@ +/* + * Opcodes (c) 1998 Jochen Hoenicke + * + * You may distribute under the terms of the GNU General Public License. + * + * IN NO EVENT SHALL JOCHEN HOENICKE BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF JOCHEN HOENICKE + * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * JOCHEN HOENICKE SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND JOCHEN HOENICKE HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * $Id$ + */ + +package jode.decompiler; +import jode.CodeAnalyzer; +import jode.LocalInfo; +import jode.Type; +import jode.MethodType; +import jode.flow.*; +import jode.bytecode.*; +import java.io.*; +import java.util.Vector; + +/** + * This is an abstract class which creates flow blocks for the + * opcodes in a byte stream. + */ +public abstract class Opcodes implements jode.bytecode.Opcodes { + + private final static Type ALL_INT_TYPE = Type.tUInt; + private final static Type BOOL_INT_TYPE = Type.tBoolInt; + private final static Type INT_TYPE = Type.tInt; + private final static Type LONG_TYPE = Type.tLong; + private final static Type FLOAT_TYPE = Type.tFloat; + private final static Type DOUBLE_TYPE = Type.tDouble; + private final static Type OBJECT_TYPE = Type.tUObject; + private final static Type BOOLEAN_TYPE = Type.tBoolean; + private final static Type BYTEBOOL_TYPE = Type.tBoolByte; + private final static Type BYTE_TYPE = Type.tByte; + private final static Type CHAR_TYPE = Type.tChar; + private final static Type SHORT_TYPE = Type.tShort; + private final static Type VOID_TYPE = Type.tVoid; + + private final static Type types[][] = { + {BOOL_INT_TYPE, LONG_TYPE, FLOAT_TYPE, DOUBLE_TYPE, OBJECT_TYPE }, + { INT_TYPE, LONG_TYPE, FLOAT_TYPE, DOUBLE_TYPE, OBJECT_TYPE, + BYTEBOOL_TYPE, CHAR_TYPE, SHORT_TYPE }, + { BYTE_TYPE, CHAR_TYPE, SHORT_TYPE }, + { ALL_INT_TYPE, LONG_TYPE, FLOAT_TYPE, DOUBLE_TYPE, OBJECT_TYPE } + }; + + private static StructuredBlock createNormal(CodeAnalyzer ca, + int addr, int length, + Expression instr) + { + return new InstructionBlock(instr, new Jump(addr+length)); + } + + private static StructuredBlock createSpecial(CodeAnalyzer ca, + int addr, int length, + int type, int stackcount, int param) + { + return new SpecialBlock(type, stackcount, param, + new Jump(addr+length)); + } + + private static StructuredBlock createGoto(CodeAnalyzer ca, + int addr, int length, int destAddr) + { + return new EmptyBlock(new Jump(destAddr)); + } + + private static StructuredBlock createJsr(CodeAnalyzer ca, + int addr, int length, + int destAddr) + { + return new JsrBlock(new Jump(addr+length), + new Jump(destAddr)); + } + + private static StructuredBlock createIfGoto(CodeAnalyzer ca, + int addr, int length, + int destAddr, Expression instr) + { + return new ConditionalBlock(instr, + new Jump(destAddr), + new Jump(addr+length)); + } + + private static StructuredBlock createSwitch(CodeAnalyzer ca, + int addr, int length, + int[] cases, int[] dests) + { + return new SwitchBlock(new NopOperator(Type.tUInt), cases, dests); + } + + private static StructuredBlock createBlock(CodeAnalyzer ca, + int addr, int length, + StructuredBlock block) + { + return block; + } + + private static StructuredBlock createRet(CodeAnalyzer ca, + int addr, int length, + LocalInfo local) + { + return new RetBlock(local); + } + + /** + * Read an opcode out of a data input stream and determine its size + * and its successors. + * @param addr The current address. + * @param stream The stream containing the java byte code. + * @return An array of ints; the first entry is the size of the + * instruction, the remaining are the successors. + */ + public static int[] getSizeAndSuccs(int addr, DataInputStream stream) + throws IOException + { + int opcode = stream.readUnsignedByte(); + switch (opcode) { + case opc_multianewarray: + stream.skip(3); + return new int[] { 4, addr+4 }; + case opc_invokeinterface: + stream.skip(4); + return new int[] { 5, addr+5 }; + + case opc_wide: { + switch (opcode = stream.readUnsignedByte()) { + case opc_iload: case opc_lload: + case opc_fload: case opc_dload: case opc_aload: + case opc_istore: case opc_lstore: + case opc_fstore: case opc_dstore: case opc_astore: + stream.skip(2); + return new int[] { 4, addr+4 }; + + case opc_iinc: + stream.skip(4); + return new int[] { 6, addr+6 }; + case opc_ret: + stream.skip(2); + return new int[] { 4 }; + default: + throw new ClassFormatError("Invalid wide opcode "+opcode); + } + } + case opc_ret: + stream.skip(1); + return new int[] { 2 }; + + case opc_jsr_w: + return new int[] { 5, addr + 5, addr + stream.readInt() }; + + case opc_goto: + return new int[] { 3, addr + stream.readShort() }; + case opc_goto_w: + return new int[] { 5, addr + stream.readInt() }; + + case opc_tableswitch: { + int length = 3-(addr % 4); + stream.skip(length); + int def = addr + stream.readInt(); + int low = stream.readInt(); + int high = stream.readInt(); + int[] dests = new int[high-low+3]; + for (int i=0; i+low <= high; i++) { + dests[i+1] = addr + stream.readInt(); + } + dests[dests.length-1] = def; + dests[0] = length + 13 + 4 * (high-low+1); + return dests; + } + case opc_lookupswitch: { + int length = 3-(addr % 4); + stream.skip(length); + int def = addr + stream.readInt(); + int npairs = stream.readInt(); + int[] dests = new int[npairs+2]; + for (int i=0; i < npairs; i++) { + int value = stream.readInt(); + dests[i+1] = addr + stream.readInt(); + } + dests[npairs+1] = def; + dests[0] = length + 9 + 8 * npairs; + return dests; + } + + case opc_ifnull: case opc_ifnonnull: + case opc_jsr: + return new int[] { 3, addr + 3, addr + stream.readShort() }; + + case opc_sipush: + case opc_ldc_w: + case opc_ldc2_w: + case opc_iinc: + case opc_getstatic: + case opc_getfield: + case opc_putstatic: + case opc_putfield: + case opc_invokevirtual: + case opc_invokespecial: + case opc_invokestatic: + case opc_new: + case opc_anewarray: + case opc_checkcast: + case opc_instanceof: + stream.skip(2); + return new int[] { 3, addr+3 }; + } + + if (opcode == opc_newarray + || (opcode >= opc_bipush && opcode <= opc_aload) + || (opcode >= opc_istore && opcode <= opc_astore)) { + stream.skip(1); + return new int[] { 2, addr+2 }; + } + + if (opcode == opc_athrow + || opcode >= opc_ireturn && opcode <= opc_return) + return new int[] { 1 }; + if (opcode >= opc_ifeq && opcode <= opc_if_acmpne) + return new int[] { 3, addr + 3, addr + stream.readShort() }; + if (opcode == opc_xxxunusedxxx || opcode > opc_breakpoint) + throw new ClassFormatError("Invalid opcode "+opcode); + + return new int[] { 1, addr+1 }; + } + + /** + * Read an opcode out of a data input stream containing the bytecode. + * @param addr The current address. + * @param stream The stream containing the java byte code. + * @param ca The Code Analyzer + * (where further information can be get from). + * @return The FlowBlock representing this opcode + * or null if the stream is empty. + * @exception IOException if an read error occured. + * @exception ClassFormatError if an invalid opcode is detected. + */ + public static StructuredBlock readOpcode(ConstantPool cpool, + int addr, DataInputStream stream, + CodeAnalyzer ca) + throws IOException, ClassFormatError + { + int opcode = stream.readUnsignedByte(); + switch (opcode) { + case opc_nop: + return createBlock + (ca, addr, 1, new EmptyBlock(new Jump(addr+1))); + case opc_aconst_null: + return createNormal + (ca, addr, 1, new ConstOperator(OBJECT_TYPE, "null")); + case opc_iconst_0: case opc_iconst_1: + return createNormal + (ca, addr, 1, new ConstOperator + (Type.tBoolInt, Integer.toString(opcode - opc_iconst_0))); + case opc_iconst_m1: case opc_iconst_2: + case opc_iconst_3: case opc_iconst_4: case opc_iconst_5: + return createNormal + (ca, addr, 1, new ConstOperator + (ALL_INT_TYPE, Integer.toString(opcode - opc_iconst_0))); + case opc_lconst_0: case opc_lconst_1: + return createNormal + (ca, addr, 1, new ConstOperator + (LONG_TYPE, + Integer.toString(opcode - opc_lconst_0))); + case opc_fconst_0: case opc_fconst_1: case opc_fconst_2: + return createNormal + (ca, addr, 1, new ConstOperator + (FLOAT_TYPE, + Integer.toString(opcode - opc_fconst_0) + ".0")); + case opc_dconst_0: case opc_dconst_1: + return createNormal + (ca, addr, 1, new ConstOperator + (DOUBLE_TYPE, + Integer.toString(opcode - opc_dconst_0) + ".0")); + case opc_bipush: + return createNormal + (ca, addr, 2, new ConstOperator + (ALL_INT_TYPE, Integer.toString(stream.readByte()))); + case opc_sipush: { + short value = stream.readShort(); + return createNormal + (ca, addr, 3, new ConstOperator + ((value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) + /* yes javac codes -128 with sipush :-( */ + ? Type.tRange(Type.tInt, Type.tChar) : ALL_INT_TYPE, + Integer.toString(value))); + } + case opc_ldc: { + int index = stream.readUnsignedByte(); + return createNormal + (ca, addr, 2, new ConstOperator + (cpool.getConstantType(index), + cpool.getConstantString(index))); + } + case opc_ldc_w: + case opc_ldc2_w: { + int index = stream.readUnsignedShort(); + return createNormal + (ca, addr, 3, new ConstOperator + (cpool.getConstantType(index), + cpool.getConstantString(index))); + } + case opc_iload: case opc_lload: + case opc_fload: case opc_dload: case opc_aload: + return createNormal + (ca, addr, 2, new LocalLoadOperator + (types[0][opcode-opc_iload], + ca.getLocalInfo(addr, stream.readUnsignedByte()))); + case opc_iload_0: case opc_iload_1: case opc_iload_2: case opc_iload_3: + case opc_lload_0: case opc_lload_1: case opc_lload_2: case opc_lload_3: + case opc_fload_0: case opc_fload_1: case opc_fload_2: case opc_fload_3: + case opc_dload_0: case opc_dload_1: case opc_dload_2: case opc_dload_3: + case opc_aload_0: case opc_aload_1: case opc_aload_2: case opc_aload_3: + return createNormal + (ca, addr, 1, new LocalLoadOperator + (types[0][(opcode-opc_iload_0)/4], + ca.getLocalInfo(addr, (opcode-opc_iload_0) & 3))); + case opc_iaload: case opc_laload: + case opc_faload: case opc_daload: case opc_aaload: + case opc_baload: case opc_caload: case opc_saload: + return createNormal + (ca, addr, 1, new ArrayLoadOperator + (types[1][opcode - opc_iaload])); + case opc_istore: case opc_lstore: + case opc_fstore: case opc_dstore: case opc_astore: + return createNormal + (ca, addr, 2, new LocalStoreOperator + (types[0][opcode-opc_istore], + ca.getLocalInfo(addr+2, stream.readUnsignedByte()), + Operator.ASSIGN_OP)); + case opc_istore_0: case opc_istore_1: + case opc_istore_2: case opc_istore_3: + case opc_lstore_0: case opc_lstore_1: + case opc_lstore_2: case opc_lstore_3: + case opc_fstore_0: case opc_fstore_1: + case opc_fstore_2: case opc_fstore_3: + case opc_dstore_0: case opc_dstore_1: + case opc_dstore_2: case opc_dstore_3: + case opc_astore_0: case opc_astore_1: + case opc_astore_2: case opc_astore_3: + return createNormal + (ca, addr, 1, new LocalStoreOperator + (types[0][(opcode-opc_istore_0)/4], + ca.getLocalInfo(addr+1, (opcode-opc_istore_0) & 3), + Operator.ASSIGN_OP)); + case opc_iastore: case opc_lastore: + case opc_fastore: case opc_dastore: case opc_aastore: + case opc_bastore: case opc_castore: case opc_sastore: + return createNormal + (ca, addr, 1, new ArrayStoreOperator + (types[1][opcode - opc_iastore])); + case opc_pop: case opc_pop2: + return createSpecial + (ca, addr, 1, SpecialBlock.POP, opcode - opc_pop + 1, 0); + case opc_dup: case opc_dup_x1: case opc_dup_x2: + case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: + return createSpecial + (ca, addr, 1, SpecialBlock.DUP, + (opcode - opc_dup)/3+1, (opcode - opc_dup)%3); + case opc_swap: + return createSpecial(ca, addr, 1, SpecialBlock.SWAP, 1, 0); + case opc_iadd: case opc_ladd: case opc_fadd: case opc_dadd: + case opc_isub: case opc_lsub: case opc_fsub: case opc_dsub: + case opc_imul: case opc_lmul: case opc_fmul: case opc_dmul: + case opc_idiv: case opc_ldiv: case opc_fdiv: case opc_ddiv: + case opc_irem: case opc_lrem: case opc_frem: case opc_drem: + return createNormal + (ca, addr, 1, new BinaryOperator + (types[3][(opcode - opc_iadd)%4], + (opcode - opc_iadd)/4+Operator.ADD_OP)); + case opc_ineg: case opc_lneg: case opc_fneg: case opc_dneg: + return createNormal + (ca, addr, 1, new UnaryOperator + (types[3][opcode - opc_ineg], Operator.NEG_OP)); + case opc_ishl: case opc_lshl: + case opc_ishr: case opc_lshr: + case opc_iushr: case opc_lushr: + return createNormal + (ca, addr, 1, new ShiftOperator + (types[3][(opcode - opc_ishl)%2], + (opcode - opc_ishl)/2 + Operator.SHIFT_OP)); + case opc_iand: case opc_land: + case opc_ior : case opc_lor : + case opc_ixor: case opc_lxor: + return createNormal + (ca, addr, 1, new BinaryOperator + (types[0][(opcode - opc_iand)%2], + (opcode - opc_iand)/2 + Operator.AND_OP)); + case opc_iinc: { + int local = stream.readUnsignedByte(); + int value = stream.readByte(); + int operation = Operator.ADD_OP; + if (value < 0) { + value = -value; + operation = Operator.NEG_OP; + } + LocalInfo li = ca.getLocalInfo(addr, local); + li.setType(ALL_INT_TYPE); + return createNormal + (ca, addr, 3, new IIncOperator + (li, Integer.toString(value), + operation + Operator.OPASSIGN_OP)); + } + case opc_i2l: case opc_i2f: case opc_i2d: + case opc_l2i: case opc_l2f: case opc_l2d: + case opc_f2i: case opc_f2l: case opc_f2d: + case opc_d2i: case opc_d2l: case opc_d2f: { + int from = (opcode-opc_i2l)/3; + int to = (opcode-opc_i2l)%3; + if (to >= from) + to++; + return createNormal + (ca, addr, 1, new ConvertOperator(types[3][from], + types[3][to])); + } + case opc_i2b: case opc_i2c: case opc_i2s: + return createNormal + (ca, addr, 1, new ConvertOperator + (ALL_INT_TYPE, types[2][opcode-opc_i2b])); + case opc_lcmp: + case opc_fcmpl: case opc_fcmpg: + case opc_dcmpl: case opc_dcmpg: + return createNormal + (ca, addr, 1, new CompareToIntOperator + (types[3][(opcode-(opc_lcmp-3))/2], + (opcode-(opc_lcmp-3))%2)); + case opc_ifeq: case opc_ifne: + return createIfGoto + (ca, addr, 3, addr+stream.readShort(), + new CompareUnaryOperator + (BOOL_INT_TYPE, opcode - (opc_ifeq-Operator.COMPARE_OP))); + case opc_iflt: case opc_ifge: case opc_ifgt: case opc_ifle: + return createIfGoto + (ca, addr, 3, addr+stream.readShort(), + new CompareUnaryOperator + (ALL_INT_TYPE, opcode - (opc_ifeq-Operator.COMPARE_OP))); + case opc_if_icmpeq: case opc_if_icmpne: + return createIfGoto + (ca, addr, 3, addr+stream.readShort(), + new CompareBinaryOperator + (Type.tBoolInt, + opcode - (opc_if_icmpeq-Operator.COMPARE_OP))); + case opc_if_icmplt: case opc_if_icmpge: + case opc_if_icmpgt: case opc_if_icmple: + return createIfGoto + (ca, addr, 3, addr+stream.readShort(), + new CompareBinaryOperator + (ALL_INT_TYPE, + opcode - (opc_if_icmpeq-Operator.COMPARE_OP))); + case opc_if_acmpeq: case opc_if_acmpne: + return createIfGoto + (ca, addr, 3, addr+stream.readShort(), + new CompareBinaryOperator + (OBJECT_TYPE, + opcode - (opc_if_acmpeq-Operator.COMPARE_OP))); + case opc_goto: + return createGoto + (ca, addr, 3, addr+stream.readShort()); + case opc_jsr: + return createJsr + (ca, addr, 3, addr+stream.readShort()); + case opc_ret: + return createRet + (ca, addr, 2, + ca.getLocalInfo(addr, stream.readUnsignedByte())); + case opc_tableswitch: { + int length = 3-(addr % 4); + stream.skip(length); + int def = addr + stream.readInt(); + int low = stream.readInt(); + int high = stream.readInt(); + int[] cases = new int[high-low+1]; + int[] dests = new int[high-low+2]; + for (int i=0; i+low <= high; i++) { + cases[i] = i+low; + dests[i] = addr + stream.readInt(); + } + dests[cases.length] = def; + length += 13 + 4 * cases.length; + return createSwitch + (ca, addr, length, cases, dests); + } + case opc_lookupswitch: { + int length = 3-(addr % 4); + stream.skip(length); + int def = addr + stream.readInt(); + int npairs = stream.readInt(); + int[] cases = new int[npairs]; + int[] dests = new int[npairs+1]; + for (int i=0; i < npairs; i++) { + cases[i] = stream.readInt(); + dests[i] = addr + stream.readInt(); + } + dests[npairs] = def; + length += 9 + 8 * npairs; + return createSwitch + (ca, addr, length, cases, dests); + } + case opc_ireturn: case opc_lreturn: + case opc_freturn: case opc_dreturn: case opc_areturn: { + /* Address -1 is interpreted as end of method */ + Type retType = Type.tSubType(ca.getMethod().getReturnType()); + return createBlock + (ca, addr, 1, new ReturnBlock(new NopOperator(retType))); + } + case opc_return: + return createBlock + (ca, addr, 1, new EmptyBlock(new Jump(-1))); + case opc_getstatic: + case opc_getfield: { + String[] ref = cpool.getRef(stream.readUnsignedShort()); + return createNormal + (ca, addr, 3, new GetFieldOperator + (ca, opcode == opc_getstatic, + Type.tClass(ref[0]), Type.tType(ref[2]), ref[1])); + } + case opc_putstatic: + case opc_putfield: { + String[] ref = cpool.getRef(stream.readUnsignedShort()); + return createNormal + (ca, addr, 3, new PutFieldOperator + (ca, opcode == opc_putstatic, + Type.tClass(ref[0]), Type.tType(ref[2]), ref[1])); + } + case opc_invokevirtual: + case opc_invokespecial: + case opc_invokestatic : + case opc_invokeinterface: { + String[] ref = cpool.getRef(stream.readUnsignedShort()); + StructuredBlock block = createNormal + (ca, addr, opcode == opc_invokeinterface ? 5 : 3, + new InvokeOperator + (ca, opcode == opc_invokespecial, + Type.tClass(ref[0]), + new MethodType(opcode == opc_invokestatic, ref[2]), + ref[1])); + if (opcode == opc_invokeinterface) + stream.readUnsignedShort(); + return block; + } + case opc_new: { + Type type = Type.tClassOrArray + (cpool.getClassName(stream.readUnsignedShort())); + type.useType(); + return createNormal(ca, addr, 3, new NewOperator(type)); + } + case opc_newarray: { + Type type; + switch (stream.readUnsignedByte()) { + case 4: type = Type.tBoolean; break; + case 5: type = Type.tChar ; break; + case 6: type = Type.tFloat ; break; + case 7: type = Type.tDouble ; break; + case 8: type = Type.tByte ; break; + case 9: type = Type.tShort ; break; + case 10: type = Type.tInt ; break; + case 11: type = Type.tLong ; break; + default: + throw new ClassFormatError("Invalid newarray operand"); + } + type.useType(); + return createNormal + (ca, addr, 2, new NewArrayOperator(Type.tArray(type), 1)); + } + case opc_anewarray: { + Type type = Type.tClassOrArray + (cpool.getClassName(stream.readUnsignedShort())); + type.useType(); + return createNormal + (ca, addr, 3, new NewArrayOperator(Type.tArray(type), 1)); + } + case opc_arraylength: + return createNormal + (ca, addr, 1, new ArrayLengthOperator()); + case opc_athrow: + return createBlock + (ca, addr, 1, + new ThrowBlock(new NopOperator(Type.tUObject))); + case opc_checkcast: { + Type type = Type.tClassOrArray + (cpool.getClassName(stream.readUnsignedShort())); + type.useType(); + return createNormal + (ca, addr, 3, new CheckCastOperator(type)); + } + case opc_instanceof: { + Type type = Type.tClassOrArray + (cpool.getClassName(stream.readUnsignedShort())); + type.useType(); + return createNormal + (ca, addr, 3, new InstanceOfOperator(type)); + } + case opc_monitorenter: + return createNormal(ca, addr, 1, + new MonitorEnterOperator()); + case opc_monitorexit: + return createNormal(ca, addr, 1, + new MonitorExitOperator()); + case opc_wide: { + switch (opcode=stream.readUnsignedByte()) { + case opc_iload: case opc_lload: + case opc_fload: case opc_dload: case opc_aload: + return createNormal + (ca, addr, 4, + new LocalLoadOperator + (types[0][opcode-opc_iload], + ca.getLocalInfo(addr, stream.readUnsignedShort()))); + case opc_istore: case opc_lstore: + case opc_fstore: case opc_dstore: case opc_astore: + return createNormal + (ca, addr, 4, + new LocalStoreOperator + (types[0][opcode-opc_istore], + ca.getLocalInfo(addr+4, stream.readUnsignedShort()), + Operator.ASSIGN_OP)); + case opc_iinc: { + int local = stream.readUnsignedShort(); + int value = stream.readShort(); + int operation = Operator.ADD_OP; + if (value < 0) { + value = -value; + operation = Operator.NEG_OP; + } + LocalInfo li = ca.getLocalInfo(addr, local); + li.setType(ALL_INT_TYPE); + return createNormal + (ca, addr, 6, new IIncOperator + (li, Integer.toString(value), + operation + Operator.OPASSIGN_OP)); + } + case opc_ret: + return createRet + (ca, addr, 4, + ca.getLocalInfo(addr, stream.readUnsignedShort())); + default: + throw new ClassFormatError("Invalid wide opcode "+opcode); + } + } + case opc_multianewarray: { + Type type = Type.tClassOrArray + (cpool.getClassName(stream.readUnsignedShort())); + int dimension = stream.readUnsignedByte(); + return createNormal + (ca, addr, 4, + new NewArrayOperator(type, dimension)); + } + case opc_ifnull: case opc_ifnonnull: + return createIfGoto + (ca, addr, 3, addr+stream.readShort(), + new CompareUnaryOperator + (OBJECT_TYPE, opcode - (opc_ifnull-Operator.COMPARE_OP))); + case opc_goto_w: + return createGoto + (ca, addr, 5, addr + stream.readInt()); + case opc_jsr_w: + return createJsr + (ca, addr, 5, addr+stream.readInt()); + default: + throw new ClassFormatError("Invalid opcode "+opcode); + } + } +}