langtools/src/share/classes/com/sun/tools/classfile/Instruction.java
changeset 2512 70eb5f17c5f8
child 4411 84c397e2ee67
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/classfile/Instruction.java	Mon Mar 30 15:08:09 2009 -0700
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.tools.classfile;
+
+/**
+ * See JVMS3, chapter 6.
+ *
+ *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
+ *  you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ *
+ * @see Code_attribute#getInstructions
+ */
+public class Instruction {
+    /** The kind of an instruction, as determined by the position, size and
+     *  types of its operands. */
+    public static enum Kind {
+        /** Opcode is not followed by any operands. */
+        NO_OPERANDS(1),
+        /** Opcode is followed by a byte indicating a type. */
+        ATYPE(2),
+        /** Opcode is followed by a 2-byte branch offset. */
+        BRANCH(3),
+        /** Opcode is followed by a 4-byte branch offset. */
+        BRANCH_W(5),
+        /** Opcode is followed by a signed byte value. */
+        BYTE(2),
+        /** Opcode is followed by a 1-byte index into the constant pool. */
+        CPREF(2),
+        /** Opcode is followed by a 2-byte index into the constant pool. */
+        CPREF_W(3),
+        /** Opcode is followed by a 2-byte index into the constant pool,
+         *  an unsigned byte value. */
+        CPREF_W_UBYTE(4),
+        /** Opcode is followed by a 2-byte index into the constant pool.,
+         *  an unsigned byte value, and a zero byte. */
+        CPREF_W_UBYTE_ZERO(5),
+        /** Opcode is followed by variable number of operands, depending
+         * on the instruction.*/
+        DYNAMIC(-1),
+        /** Opcode is followed by a 1-byte reference to a local variable. */
+        LOCAL(2),
+        /** Opcode is followed by a 1-byte reference to a local variable,
+         *  and a signed byte value. */
+        LOCAL_BYTE(3),
+        /** Opcode is followed by a signed short value. */
+        SHORT(3),
+        /** Wide opcode is not followed by any operands. */
+        WIDE_NO_OPERANDS(2),
+        /** Wide opcode is followed by a 2-byte index into the constant pool. */
+        WIDE_CPREF_W(4),
+        /** Wide opcode is followed by a 2-byte index into the constant pool,
+         *  and a signed short value. */
+        WIDE_CPREF_W_SHORT(6),
+        /** Opcode was not recognized. */
+        UNKNOWN(1);
+
+        Kind(int length) {
+            this.length = length;
+        }
+
+        /** The length, in bytes, of this kind of instruction, or -1 is the
+         *  length depends on the specific instruction. */
+        public final int length;
+    };
+
+    /** A utility visitor to help decode the operands of an instruction.
+     *  @see Instruction#accept */
+    public interface KindVisitor<R,P> {
+        /** See {@link Kind#NO_OPERANDS}, {@link Kind#WIDE_NO_OPERANDS}. */
+        R visitNoOperands(Instruction instr, P p);
+        /** See {@link Kind#ATYPE}. */
+        R visitArrayType(Instruction instr, TypeKind kind, P p);
+        /** See {@link Kind#BRANCH}, {@link Kind#BRANCH_W}. */
+        R visitBranch(Instruction instr, int offset, P p);
+        /** See {@link Kind#CPREF}, {@link Kind#CPREF_W}, {@link Kind#WIDE_CPREF_W}. */
+        R visitConstantPoolRef(Instruction instr, int index, P p);
+        /** See {@link Kind#CPREF_W_UBYTE}, {@link Kind#CPREF_W_UBYTE_ZERO}, {@link Kind#WIDE_CPREF_W_SHORT}. */
+        R visitConstantPoolRefAndValue(Instruction instr, int index, int value, P p);
+        /** See {@link Kind#LOCAL}. */
+        R visitLocal(Instruction instr, int index, P p);
+        /** See {@link Kind#LOCAL_UBYTE}. */
+        R visitLocalAndValue(Instruction instr, int index, int value, P p);
+        /** See {@link Kind#DYNAMIC}. */
+        R visitLookupSwitch(Instruction instr, int default_, int npairs, int[] matches, int[] offsets);
+        /** See {@link Kind#DYNAMIC}. */
+        R visitTableSwitch(Instruction instr, int default_, int low, int high, int[] offsets);
+        /** See {@link Kind#BYTE}, {@link Kind#SHORT}. */
+        R visitValue(Instruction instr, int value, P p);
+        /** Instruction is unrecognized. */
+        R visitUnknown(Instruction instr, P p);
+
+    }
+
+    /** The kind of primitive array type to create.
+     *  See JVMS chapter 6, newarray. */
+    public static enum TypeKind {
+        T_BOOLEAN(4, "boolean"),
+        T_CHAR(5, "char"),
+        T_FLOAT(6, "float"),
+        T_DOUBLE(7, "double"),
+        T_BYTE(8, "byte"),
+        T_SHORT(9, "short"),
+        T_INT (10, "int"),
+        T_LONG (11, "long");
+        TypeKind(int value, String name) {
+            this.value = value;
+            this.name = name;
+        }
+
+        public static TypeKind get(int value) {
+            switch (value) {
+                case  4: return T_BOOLEAN;
+                case  5: return T_CHAR;
+                case  6: return T_FLOAT;
+                case  7: return T_DOUBLE;
+                case  8: return T_BYTE;
+                case  9: return T_SHORT;
+                case  10: return T_INT;
+                case  11: return T_LONG;
+                default: return null;
+            }
+        }
+
+        public final int value;
+        public final String name;
+    }
+
+    /** An instruction is defined by its position in a bytecode array. */
+    public Instruction(byte[] bytes, int pc) {
+        this.bytes = bytes;
+        this.pc = pc;
+    }
+
+    /** Get the position of the instruction within the bytecode array. */
+    public int getPC() {
+        return pc;
+    }
+
+    /** Get a byte value, relative to the start of this instruction. */
+    public int getByte(int offset) {
+        return bytes[pc + offset];
+    }
+
+    /** Get an unsigned byte value, relative to the start of this instruction. */
+    public int getUnsignedByte(int offset) {
+        return getByte(offset) & 0xff;
+    }
+
+    /** Get a 2-byte value, relative to the start of this instruction. */
+    public int getShort(int offset) {
+        return (getByte(offset) << 8) | getUnsignedByte(offset + 1);
+    }
+
+    /** Get a unsigned 2-byte value, relative to the start of this instruction. */
+    public int getUnsignedShort(int offset) {
+        return getShort(offset) & 0xFFFF;
+    }
+
+    /** Get a 4-byte value, relative to the start of this instruction. */
+    public int getInt(int offset) {
+        return (getShort(offset) << 16) | (getUnsignedShort(offset + 2));
+    }
+
+    /** Get the Opcode for this instruction, or null if the instruction is
+     * unrecognized. */
+    public Opcode getOpcode() {
+        int b = getUnsignedByte(0);
+        switch (b) {
+            case Opcode.NONPRIV:
+            case Opcode.PRIV:
+            case Opcode.WIDE:
+                return Opcode.get(b, getUnsignedByte(1));
+        }
+        return Opcode.get(b);
+    }
+
+    /** Get the mnemonic for this instruction, or a default string if the
+     * instruction is unrecognized. */
+    public String getMnemonic() {
+        Opcode opcode = getOpcode();
+        if (opcode == null)
+            return "bytecode " + getUnsignedByte(0);
+        else
+            return opcode.toString().toLowerCase();
+    }
+
+    /** Get the length, in bytes, of this instruction, including the opcode
+     * and all its operands. */
+    public int length() {
+        Opcode opcode = getOpcode();
+        if (opcode == null)
+            return 1;
+
+        switch (opcode) {
+            case TABLESWITCH: {
+                int pad = align(pc + 1) - pc;
+                int low = getInt(pad + 4);
+                int high = getInt(pad + 8);
+                return pad + 12 + 4 * (high - low + 1);
+            }
+            case LOOKUPSWITCH: {
+                int pad = align(pc + 1) - pc;
+                int npairs = getInt(pad + 4);
+                return pad + 8 + 8 * npairs;
+
+            }
+            default:
+                return opcode.kind.length;
+        }
+    }
+
+    /** Get the {@link Kind} of this instruction. */
+    public Kind getKind() {
+        Opcode opcode = getOpcode();
+        return (opcode != null ? opcode.kind : Kind.UNKNOWN);
+    }
+
+    /** Invoke a method on the visitor according to the kind of this
+     * instruction, passing in the decoded operands for the instruction. */
+    public <R,P> R accept(KindVisitor<R,P> visitor, P p) {
+        switch (getKind()) {
+            case NO_OPERANDS:
+                return visitor.visitNoOperands(this, p);
+
+            case ATYPE:
+                return visitor.visitArrayType(
+                        this, TypeKind.get(getUnsignedByte(1)), p);
+
+            case BRANCH:
+                return visitor.visitBranch(this, getShort(1), p);
+
+            case BRANCH_W:
+                return visitor.visitBranch(this, getInt(1), p);
+
+            case BYTE:
+                return visitor.visitValue(this, getByte(1), p);
+
+            case CPREF:
+                return visitor.visitConstantPoolRef(this, getUnsignedByte(1), p);
+
+            case CPREF_W:
+                return visitor.visitConstantPoolRef(this, getUnsignedShort(1), p);
+
+            case CPREF_W_UBYTE:
+            case CPREF_W_UBYTE_ZERO:
+                return visitor.visitConstantPoolRefAndValue(
+                        this, getUnsignedShort(1), getUnsignedByte(3), p);
+
+            case DYNAMIC: {
+                switch (getOpcode()) {
+                    case TABLESWITCH: {
+                        int pad = align(pc + 1) - pc;
+                        int default_ = getInt(pad);
+                        int low = getInt(pad + 4);
+                        int high = getInt(pad + 8);
+                        int[] values = new int[high - low + 1];
+                        for (int i = 0; i < values.length; i++)
+                            values[i] = getInt(pad + 12 + 4 * i);
+                        return visitor.visitTableSwitch(
+                                this, default_, low, high, values);
+                    }
+                    case LOOKUPSWITCH: {
+                        int pad = align(pc + 1) - pc;
+                        int default_ = getInt(pad);
+                        int npairs = getInt(pad + 4);
+                        int[] matches = new int[npairs];
+                        int[] offsets = new int[npairs];
+                        for (int i = 0; i < npairs; i++) {
+                            matches[i] = getInt(pad +  8 + i * 8);
+                            offsets[i] = getInt(pad + 12 + i * 8);
+                        }
+                        return visitor.visitLookupSwitch(
+                                this, default_, npairs, matches, offsets);
+                    }
+                    default:
+                        throw new IllegalStateException();
+                }
+            }
+
+            case LOCAL:
+                return visitor.visitLocal(this, getUnsignedByte(1), p);
+
+            case LOCAL_BYTE:
+                return visitor.visitLocalAndValue(
+                        this, getUnsignedByte(1), getByte(2), p);
+
+            case SHORT:
+                return visitor.visitValue(this, getShort(1), p);
+
+            case WIDE_NO_OPERANDS:
+                return visitor.visitNoOperands(this, p);
+
+            case WIDE_CPREF_W:
+                return visitor.visitConstantPoolRef(this, getUnsignedShort(2), p);
+
+            case WIDE_CPREF_W_SHORT:
+                return visitor.visitConstantPoolRefAndValue(
+                        this, getUnsignedShort(2), getUnsignedByte(4), p);
+
+            case UNKNOWN:
+                return visitor.visitUnknown(this, p);
+
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    private static int align(int n) {
+        return (n + 3) & ~3;
+    }
+
+    private byte[] bytes;
+    private int pc;
+}