// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz) // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the <organization> nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL DAVID J. PEARCE BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package jasm.lang; import jasm.util.Pair; import java.io.ByteArrayOutputStream; import java.util.*; public abstract class Bytecode { /** * This method determines the change in stack resulting from this bytecode. * For example, +1 indicates the instruction puts one thing onto the stack. * Likewise, -2 means this instruction takes two things off the stack. The * primary purpose of this method is to help compute the maxStack * requirement for a method. * * @return */ public abstract int stackDiff(); /** * This method adds any constant pool items required by the bytecode to the * constantPool. * * @param constantPool */ public void addPoolItems(Set<Constant.Info> constantPool) { // in the default case, we do nothing. } /** * Translate this Java bytecode into bytes. If the bytecode requires a * constant pool item which is not present in the constantPool map, then the * appropriate CONSTANT_Info object is added, and given the next available * index. * * @param offset * Offset of this bytecode * @param labelOffsets * Offsets of any labels used in branch bytecodes * @param constantPool * Indices of constant pool items used in various bytecodes (e.g. * ldc, putfield, etc) * * @return */ public abstract byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool); // =============================== // ======= JAVA BYTECODES ======== // =============================== /** * Represents all bytecodes for storing to a local variable, including * istore, astore, lstore etc. */ public final static class Store extends Bytecode { public final int slot; public final JvmType type; public Store(int slot, JvmType type) { if(slot < 0 || slot > 65535) { throw new IllegalArgumentException("invalid local variable: " + slot); } typeChar(type); // check valid type this.slot=slot; this.type=type; } public int stackDiff() { return -ClassFile.slotSize(type); } public String toString() { if(slot >= 0 && slot <= 3) { return typeChar(type) + "store_" + slot; } else { return typeChar(type) + "store " + slot; } } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); if(slot >= 0 && slot <= 3) { write_u1(out,ISTORE_0 + (4*typeOffset(type)) + slot); } else if(slot <= 255) { write_u1(out,ISTORE + typeOffset(type)); write_u1(out,slot); } else { write_u1(out,WIDE); write_u1(out,ISTORE + typeOffset(type)); write_u2(out,slot); } return out.toByteArray(); } public boolean equals(Object o) { if (o instanceof Store) { Store s = (Store) o; return type.equals(s.type) && slot == s.slot; } return false; } public int hashCode() { return type.hashCode() + slot; } } /** * Represents all bytecodes for loading from a local variable, including * iload, aload, lload etc. */ public final static class Load extends Bytecode { public final int slot; public final JvmType type; public Load(int slot, JvmType type) { if(slot < 0 || slot > 65535) { throw new IllegalArgumentException("invalid local variable: " + slot); } typeChar(type); // check valid type this.slot=slot; this.type=type; } public int stackDiff() { return ClassFile.slotSize(type); } public String toString() { if(slot >= 0 && slot <= 3) { return typeChar(type) + "load_" + slot; } else { return typeChar(type) + "load " + slot; } } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); if(slot >= 0 && slot <= 3) { write_u1(out,ILOAD_0 + (4*typeOffset(type)) + slot); } else if(slot <= 255){ write_u1(out,ILOAD + typeOffset(type)); write_u1(out,slot); } else { write_u1(out,WIDE); write_u1(out,ILOAD + typeOffset(type)); write_u2(out,slot); } return out.toByteArray(); } public boolean equals(Object o) { if (o instanceof Load) { Load l = (Load) o; return type.equals(l.type) && slot == l.slot; } return false; } public int hashCode() { return type.hashCode() + slot; } } /** * Represents the iinc bytecode for incrementing a local variable. */ public final static class Iinc extends Bytecode { public final int slot; public final int increment; public Iinc(int slot, int increment) { if(increment < Byte.MIN_VALUE || increment > Byte.MAX_VALUE) { throw new IllegalArgumentException("illegal iinc increment --- must be between -127 and 128"); } this.slot=slot; this.increment = increment; } public int stackDiff() { return 0; } public String toString() { return "iinc " + slot + ", " + increment; } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); write_u1(out,IINC); write_u1(out,slot); write_i1(out,increment); return out.toByteArray(); } public boolean equals(Object o) { if (o instanceof Iinc) { Iinc i = (Iinc) o; return increment == i.increment && slot == i.slot; } return false; } public int hashCode() { return increment + slot; } } /** * Represents the arrayload bytecode. */ public static final class ArrayLoad extends Bytecode { public final JvmType.Array type; public ArrayLoad(JvmType.Array type) { this.type = type; } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); write_u1(out,IALOAD + typeArrayOffset(type.element())); return out.toByteArray(); } public int stackDiff() { return ClassFile.slotSize(type.element())-2; } public String toString() { return typeArrayChar(type.element()) + "aload"; } public boolean equals(Object o) { if (o instanceof ArrayLoad) { ArrayLoad i = (ArrayLoad) o; return type.equals(i.type); } return false; } public int hashCode() { return type.hashCode(); } } /** * Represents the arraystore bytecode. */ public static final class ArrayStore extends Bytecode { public final JvmType.Array type; public ArrayStore(JvmType.Array type) { this.type = type; } public int stackDiff() { return -(2 + ClassFile.slotSize(type.element())); } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); write_u1(out,IASTORE + typeArrayOffset(type.element())); return out.toByteArray(); } public String toString() { return typeArrayChar(type.element()) + "astore"; } public boolean equals(Object o) { if (o instanceof ArrayStore) { ArrayStore i = (ArrayStore) o; return type.equals(i.type); } return false; } public int hashCode() { return type.hashCode(); } } /** * Represents all bytecodes for loading constants. Including, iconst, bipush, * sipush, ldc, ldc_w */ public static final class LoadConst extends Bytecode { public final Object constant; public LoadConst(Object constant) { if(constant instanceof Boolean) { Boolean b = (Boolean) constant; constant = b ? 1 : 0; } else if(constant instanceof Character) { constant = (int)((Character) constant); } else if(constant instanceof Byte || constant instanceof Short) { constant = ((Number)constant).intValue(); } this.constant = constant; } public int stackDiff() { if (constant instanceof Long || constant instanceof Double) { return 2; } return 1; } public void addPoolItems(Set<Constant.Info> constantPool) { if (constant instanceof Integer) { int v = ((Number) constant).intValue(); if (!(v >= -1 && v <= 5) && !(v >= -128 && v <= 127) && !(v >= -32768 && v <= 32767)) { Constant.addPoolItem(new Constant.Integer(v),constantPool); } } else if (constant instanceof Long) { long v = (Long) constant; if (v != 0 && v != 1) { Constant.addPoolItem(new Constant.Long(v), constantPool); } } else if(constant instanceof Float) { float v = (Float) constant; if(v != 0.0F && v != 1.0F && v != 2.0F) { Constant.addPoolItem(new Constant.Float(v),constantPool); } } else if(constant instanceof Double) { double v = (Double) constant; if (v != 0.0D && v != 1.0D) { Constant.addPoolItem(new Constant.Double(v), constantPool); } } else if(constant instanceof String) { String v = (String) constant; Constant.addPoolItem(new Constant.String(new Constant.Utf8(v)), constantPool); } else if(constant instanceof JvmType.Reference) { JvmType.Reference ref = (JvmType.Reference) constant; Constant.addPoolItem(Constant.buildClass(ref),constantPool); } } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); if (constant instanceof Integer) { int v = (Integer) constant; if (v >= -1 && v <= 5) { write_u1(out, ICONST_0 + v); } else if (v >= -128 && v <= 127) { write_u1(out, BIPUSH); write_u1(out, v); } else if (v >= -32768 && v <= 32767) { write_u1(out, SIPUSH); write_u2(out, v); } else { int idx = constantPool.get(new Constant.Integer(v)); if (idx < 255) { write_u1(out, LDC); write_u1(out, idx); } else { write_u1(out, LDC_W); write_u2(out, idx); } } } else if(constant instanceof Long) { long v = (Long) constant; if(v == 0 || v == 1) { write_u1(out,LCONST_0 + (int) v); } else { int idx = constantPool.get(new Constant.Long(v)); write_u1(out,LDC2_W); write_u2(out,idx); } } else if(constant instanceof Float) { float v = (Float) constant; if(v == 0.0F) { write_u1(out,FCONST_0); } else if(v == 1.0F) { write_u1(out,FCONST_1); } else if(v == 2.0F) { write_u1(out,FCONST_2); } else { int idx = constantPool.get(new Constant.Float(v)); if(idx < 255) { write_u1(out,LDC); write_u1(out,idx); } else { write_u1(out,LDC_W); write_u2(out,idx); } } } else if(constant instanceof Double) { double v = (Double) constant; if(v == 0.0D) { write_u1(out,DCONST_0); } else if(v == 1.0D) { write_u1(out,DCONST_1); } else { int idx = constantPool.get(new Constant.Double(v)); write_u1(out,LDC2_W); write_u2(out,idx); } } else if(constant instanceof String) { String v = (String) constant; int idx = constantPool.get(new Constant.String(new Constant.Utf8(v))); if(idx < 255) { write_u1(out,LDC); write_u1(out,idx); } else { write_u1(out,LDC_W); write_u2(out,idx); } } else if(constant instanceof JvmType) { JvmType.Reference ref = (JvmType.Reference) constant; int idx = constantPool.get(Constant.buildClass(ref)); write_u1(out, LDC_W); write_u2(out, idx); } else { write_u1(out,ACONST_NULL); } return out.toByteArray(); } public String toString() { if (constant instanceof Integer) { int v = (Integer) constant; if (v >= -1 && v <= 5) { return "iconst_" + v; } else if (v >= -128 && v <= 127) { return "bipush " + v; } else if (v >= -32768 && v <= 32767) { return "sipush " + v; } else { return "ldc " + v; } } else if (constant instanceof Long) { long v = (Long) constant; if (v == 0 || v == 1) { return "lconst_" + v; } else { return "ldc2_w " + v; } } else if (constant instanceof Float) { float v = (Float) constant; if (v == 0.0F) { return "fconst_0"; } else if (v == 1.0F) { return "fconst_1"; } else if (v == 2.0F) { return "fconst_2"; } else { return "ldc " + v; } } else if (constant instanceof Double) { double v = (Double) constant; if (v == 0.0D) { return "dconst_0"; } else if (v == 1.0D) { return "dconst_1"; } else { return "ldc2_w " + v; } } else if (constant instanceof String) { String v = (String) constant; return "ldc \"" + v + "\""; } else if (constant instanceof JvmType.Reference) { return "ldc_w " + constant; } else { return "aconst_null"; } } public boolean equals(Object o) { if (o instanceof LoadConst) { LoadConst i = (LoadConst) o; if(constant == null) { return constant == i.constant; } else { return constant.equals(i.constant); } } return false; } public int hashCode() { if(constant == null) { return 0; } else { return constant.hashCode(); } } } /** * Represents return bytecodes, including ireturn, areturn, etc. */ public static final class Return extends Bytecode { public final JvmType type; public Return(JvmType type) { if(type != null) { typeChar(type); } // check valid this.type = type; } public int stackDiff() { if(type == null) { return 0; } else { return ClassFile.slotSize(type); } } public byte[] toBytes(int offset, Map<String, Integer> labelOffsets, Map<Constant.Info, Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); if (type == null) { write_u1(out, RETURN); } else { write_u1(out, IRETURN + typeOffset(type)); } return out.toByteArray(); } public String toString() { if(type == null) { return "return"; } else { return typeChar(type) + "return"; } } public boolean equals(Object o) { if (o instanceof Return) { Return i = (Return) o; return type.equals(i.type); } return false; } public int hashCode() { return type.hashCode(); } } /** * This does not actually correspond to a bytecode per se. Rather it is an * imaginary bytecode which is used to mark the destination of branching * instructions. */ public static final class Label extends Bytecode { public final String name; public Label(String name) { this.name = name; } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { return new byte[0]; } public String toString() { return name + ":"; } public int stackDiff() { return 0; } public boolean equals(Object o) { if (o instanceof Label) { Label i = (Label) o; return name.equals(i.name); } return false; } public int hashCode() { return name.hashCode(); } } public static final class Neg extends Bytecode { public final JvmType type; public Neg(JvmType type) { typeChar(type); // check valid type this.type = type; } public int stackDiff() { return 0; } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); write_u1(out,INEG + typeOffset(type)); return out.toByteArray(); } public String toString() { return typeChar(type) + "neg"; } public boolean equals(Object o) { if (o instanceof Neg) { Neg i = (Neg) o; return type.equals(i.type); } return false; } public int hashCode() { return type.hashCode(); } } /** * This represents all binary operators involving two operands on the stack. * Examples include iadd, fsub, ldiv, ishr, lxor, etc. */ public static final class BinOp extends Bytecode { public static final int ADD = 0; public static final int SUB = 1; public static final int MUL = 2; public static final int DIV = 3; public static final int REM = 4; public static final int SHL = 5; public static final int SHR = 6; public static final int USHR = 7; public static final int AND = 8; public static final int OR = 9; public static final int XOR = 10; private static final int[] base = {IADD, ISUB, IMUL, IDIV, IREM, ISHL, ISHR, IUSHR, IAND, IOR, IXOR}; private static final String[] str = {"add", "sub", "mul", "div", "rem", "shl", "shr", "ushr", "and", "or", "xor"}; public final JvmType type; public final int op; public BinOp(int op, JvmType type) { typeChar(type); // check valid type assert op >= 0 && op <= XOR; this.op = op; this.type = type; } public int stackDiff() { return -ClassFile.slotSize(type); } public byte[] toBytes(int offset, Map<String, Integer> labelOffsets, Map<Constant.Info, Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); write_u1(out, base[op] + typeOffset(type)); return out.toByteArray(); } public String toString() { return typeChar(type) + str[op]; } public boolean equals(Object o) { if (o instanceof BinOp) { BinOp i = (BinOp) o; return op == i.op && type.equals(i.type); } return false; } public int hashCode() { return op + type.hashCode(); } } public enum InvokeMode { STATIC, VIRTUAL, INTERFACE, SPECIAL }; public enum FieldMode { STATIC, NONSTATIC }; /** * This represents the putfield and putstatic bytecodes. */ public static final class PutField extends Bytecode { public final JvmType.Clazz owner; public final JvmType type; public final String name; public final FieldMode mode; public PutField(JvmType.Clazz owner, String name, JvmType type, FieldMode mode) { this.owner = owner; this.type = type; this.name = name; this.mode = mode; } public int stackDiff() { if(mode == FieldMode.STATIC) { return -ClassFile.slotSize(type); } else { return -1 - ClassFile.slotSize(type); } } public void addPoolItems(Set<Constant.Info> constantPool) { Constant.addPoolItem(Constant.buildFieldRef(owner, name, type), constantPool); } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); int idx = constantPool.get(Constant.buildFieldRef(owner, name, type)); if(mode == FieldMode.STATIC) { write_u1(out,PUTSTATIC); } else { write_u1(out,PUTFIELD); } write_u2(out,idx); return out.toByteArray(); } public String toString() { if(mode == FieldMode.STATIC) { return "putstatic " + owner + "." + name + ":" + ClassFile.descriptor(type,false); } else { return "putfield " + owner + "." + name + ":" + ClassFile.descriptor(type,false); } } public boolean equals(Object o) { if (o instanceof PutField) { PutField b = (PutField) o; return mode == b.mode && name.equals(b.name) && owner.equals(b.owner) && type.equals(b.type); } return false; } public int hashCode() { return name.hashCode() + type.hashCode() + owner.hashCode(); } } /** * This represents the getfield and getstatic bytecodes. */ public static final class GetField extends Bytecode { public final JvmType.Clazz owner; public final JvmType type; public final String name; public final FieldMode mode; public GetField(JvmType.Clazz owner, String name, JvmType type, FieldMode mode) { this.owner = owner; this.type = type; this.name = name; this.mode = mode; } public int stackDiff() { if(mode == FieldMode.STATIC) { return ClassFile.slotSize(type); } else { return -1 + ClassFile.slotSize(type); } } public void addPoolItems(Set<Constant.Info> constantPool) { Constant.addPoolItem(Constant.buildFieldRef(owner, name, type), constantPool); } public byte[] toBytes(int offset, Map<String, Integer> labelOffsets, Map<Constant.Info, Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); int idx = constantPool.get(Constant .buildFieldRef(owner, name, type)); if (mode == FieldMode.STATIC) { write_u1(out, GETSTATIC); } else { write_u1(out, GETFIELD); } write_u2(out, idx); return out.toByteArray(); } public String toString() { if(mode == FieldMode.STATIC) { return "getstatic " + owner + "." + name + ":" + ClassFile.descriptor(type,false); } else { return "getfield " + owner + "." + name + ":" + ClassFile.descriptor(type,false); } } public boolean equals(Object o) { if (o instanceof GetField) { GetField b = (GetField) o; return mode == b.mode && name.equals(b.name) && owner.equals(b.owner) && type.equals(b.type); } return false; } public int hashCode() { return name.hashCode() + type.hashCode() + owner.hashCode(); } } /** * This represents the invokevirtual, invokespecial, invokestatic and * invokeinterface bytecodes */ public static final class Invoke extends Bytecode { public final JvmType.Reference owner; public final JvmType.Function type; public final String name; public final InvokeMode mode; public Invoke(JvmType.Reference owner, String name, JvmType.Function type, InvokeMode mode) { this.owner = owner; this.type = type; this.name = name; this.mode = mode; } public int stackDiff() { int diff = mode == InvokeMode.STATIC ? 0 : -1; if(!(type.returnType() instanceof JvmType.Void)) { diff += ClassFile.slotSize(type.returnType()); } for(JvmType pt : type.parameterTypes()) { diff -= ClassFile.slotSize(pt); } return diff; } public void addPoolItems(Set<Constant.Info> constantPool) { if(mode != InvokeMode.INTERFACE) { Constant.addPoolItem(Constant.buildMethodRef(owner, name, type),constantPool); } else { Constant.addPoolItem(Constant.buildInterfaceMethodRef(owner, name, type),constantPool); } } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); int idx; if(mode != InvokeMode.INTERFACE) { idx = constantPool.get(Constant.buildMethodRef(owner, name, type)); } else { idx = constantPool.get(Constant.buildInterfaceMethodRef(owner, name, type)); } if(mode == InvokeMode.STATIC) { write_u1(out,INVOKESTATIC); } else if(mode == InvokeMode.VIRTUAL) { write_u1(out,INVOKEVIRTUAL); } else if(mode == InvokeMode.SPECIAL){ write_u1(out,INVOKESPECIAL); } else { write_u1(out,INVOKEINTERFACE); } write_u2(out,idx); if(mode == InvokeMode.INTERFACE) { int ps = 1; // 1 for the "this" reference! for(JvmType t : type.parameterTypes()) { ps += ClassFile.slotSize(t); } write_u1(out,ps); write_u1(out,0); return out.toByteArray(); } return out.toByteArray(); } public String toString() { if(mode == InvokeMode.STATIC) { return "invokestatic " + owner + "." + name + " " + ClassFile.descriptor(type,false); } else if(mode == InvokeMode.VIRTUAL) { return "invokevirtual " + owner + "." + name + " " + ClassFile.descriptor(type,false); } else if(mode == InvokeMode.SPECIAL) { return "invokespecial " + owner + "." + name + " " + ClassFile.descriptor(type,false); } else { return "invokeinterface " + owner + "." + name + " " + ClassFile.descriptor(type,false); } } public boolean equals(Object o) { if (o instanceof Invoke) { Invoke b = (Invoke) o; return mode == b.mode && name.equals(b.name) && owner.equals(b.owner) && type.equals(b.type); } return false; } public int hashCode() { return name.hashCode() + type.hashCode() + owner.hashCode(); } } /** * This represents the family of primitive conversion operations, such as * i2f, d2f, l2i etc. Observe that in some cases (e.g. converting from a * long to a byte) several bytecodes will be produced (e.g. l2i,i2b). */ public static final class Conversion extends Bytecode { public final JvmType.Primitive from; public final JvmType.Primitive to; public Conversion(JvmType.Primitive from, JvmType.Primitive to) { this.from = from; this.to = to; // Now, sanity check this conversion operator if(from instanceof JvmType.Int || from instanceof JvmType.Short || from instanceof JvmType.Byte || from instanceof JvmType.Char) { // i2l, i2f, i2d, i2c, i2b, i2s if(to instanceof JvmType.Long) { return; } else if(to instanceof JvmType.Float) { return; } else if(to instanceof JvmType.Double) { return; } else if(to instanceof JvmType.Char && !(from instanceof JvmType.Char)) { return; } else if(to instanceof JvmType.Short && !(from instanceof JvmType.Short)) { return; } else if(to instanceof JvmType.Byte && !(from instanceof JvmType.Byte)) { return; } } else if(from instanceof JvmType.Long) { // l2i, l2f, l2d if(to instanceof JvmType.Char) { return; } else if(to instanceof JvmType.Byte) { return; } else if(to instanceof JvmType.Short) { return; } else if(to instanceof JvmType.Int) { return; } else if(to instanceof JvmType.Float) { return; } else if(to instanceof JvmType.Double) { return; } } else if(from instanceof JvmType.Float) { // f2i, f2l, f2d if(to instanceof JvmType.Char) { return; } else if(to instanceof JvmType.Byte) { return; } else if(to instanceof JvmType.Short) { return; } else if(to instanceof JvmType.Int) { return; } else if(to instanceof JvmType.Long) { return; } else if(to instanceof JvmType.Double) { return; } } else if(from instanceof JvmType.Double) { // d2i, d2l, d2f if(to instanceof JvmType.Char) { return; } else if(to instanceof JvmType.Byte) { return; } else if(to instanceof JvmType.Short) { return; } else if(to instanceof JvmType.Int) { return; } else if(to instanceof JvmType.Long) { return; } else if(to instanceof JvmType.Float) { return; } } throw new IllegalArgumentException("no valid conversion from " + from + " into " + to); } public int stackDiff() { return ClassFile.slotSize(to) - ClassFile.slotSize(from); } public byte[] toBytes(int offset, Map<String, Integer> labelOffsets, Map<Constant.Info, Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); if (from instanceof JvmType.Int || from instanceof JvmType.Short || from instanceof JvmType.Byte || from instanceof JvmType.Char) { // i2l, i2f, i2d, i2c, i2b, i2s if(to instanceof JvmType.Long) { write_u1(out,I2L); } else if(to instanceof JvmType.Float) { write_u1(out,I2F); } else if(to instanceof JvmType.Double) { write_u1(out,I2D); } else if(to instanceof JvmType.Char && !(from instanceof JvmType.Char)) { write_u1(out,I2C); } else if(to instanceof JvmType.Short && !(from instanceof JvmType.Short)) { write_u1(out,I2S); } else if(to instanceof JvmType.Byte && !(from instanceof JvmType.Byte)) { write_u1(out,I2B); } } else if(from instanceof JvmType.Long) { // l2i, l2f, l2d if(to instanceof JvmType.Char) { write_u1(out,L2I); write_u1(out,I2C); } else if(to instanceof JvmType.Byte) { write_u1(out,L2I); write_u1(out,I2B); } else if(to instanceof JvmType.Short) { write_u1(out,L2I); write_u1(out,I2S); } else if(to instanceof JvmType.Int) { write_u1(out,L2I); } else if(to instanceof JvmType.Float) { write_u1(out,L2F); } else if(to instanceof JvmType.Double) { write_u1(out,L2D); } } else if(from instanceof JvmType.Float) { // f2i, f2l, f2d if(to instanceof JvmType.Char) { write_u1(out,F2I); write_u1(out,I2C); } else if(to instanceof JvmType.Byte) { write_u1(out,F2I); write_u1(out,I2B); } else if(to instanceof JvmType.Short) { write_u1(out,F2I); write_u1(out,I2S); } else if(to instanceof JvmType.Int) { write_u1(out,F2I); } else if(to instanceof JvmType.Long) { write_u1(out,F2L); } else if(to instanceof JvmType.Double) { write_u1(out,F2D); } } else if(from instanceof JvmType.Double) { // d2i, d2l, d2f if(to instanceof JvmType.Char) { write_u1(out,D2I); write_u1(out,I2C); } else if(to instanceof JvmType.Byte) { write_u1(out,D2I); write_u1(out,I2B); } else if(to instanceof JvmType.Short) { write_u1(out,D2I); write_u1(out,I2S); } if(to instanceof JvmType.Int) { write_u1(out,D2I); } else if(to instanceof JvmType.Long) { write_u1(out,D2L); } else if(to instanceof JvmType.Float) { write_u1(out,D2F); } } return out.toByteArray(); } public String toString() { if(from instanceof JvmType.Int || from instanceof JvmType.Short || from instanceof JvmType.Byte || from instanceof JvmType.Char) { // i2l, i2f, i2d, i2c, i2b, i2s if(to instanceof JvmType.Long) { return "i2l"; } else if(to instanceof JvmType.Float) { return "i2f"; } else if(to instanceof JvmType.Double) { return "i2d"; } else if(to instanceof JvmType.Char && !(from instanceof JvmType.Char)) { return "i2c"; } else if(to instanceof JvmType.Short && !(from instanceof JvmType.Short)) { return "i2s"; } else if(to instanceof JvmType.Byte && !(from instanceof JvmType.Byte)) { return "i2b"; } } else if(from instanceof JvmType.Long) { // l2i, l2f, l2d if(to instanceof JvmType.Char) { return "l2i ; i2c"; } else if(to instanceof JvmType.Byte) { return "l2i ; i2b"; } else if(to instanceof JvmType.Short) { return "l2i ; i2s"; } else if(to instanceof JvmType.Int) { return "l2i"; } else if(to instanceof JvmType.Float) { return "l2f"; } else if(to instanceof JvmType.Double) { return "l2d"; } } else if(from instanceof JvmType.Float) { // f2i, f2l, f2d if(to instanceof JvmType.Char) { return "f2i ; i2c"; } else if(to instanceof JvmType.Byte) { return "f2i ; i2b"; } else if(to instanceof JvmType.Short) { return "f2i ; i2s"; } else if(to instanceof JvmType.Int) { return "f2i"; } else if(to instanceof JvmType.Long) { return "f2l"; } else if(to instanceof JvmType.Double) { return "f2d"; } } else if(from instanceof JvmType.Double) { // d2i, d2l, d2f if(to instanceof JvmType.Char) { return "d2i ; i2c"; } else if(to instanceof JvmType.Byte) { return "d2i ; i2b"; } else if(to instanceof JvmType.Short) { return "d2i ; i2s"; } else if(to instanceof JvmType.Int) { return "d2i"; } else if(to instanceof JvmType.Long) { return "d2l"; } else if(to instanceof JvmType.Float) { return "d2f"; } } // following should be unreachable throw new RuntimeException("invalid conversion operator (" + from + "=>" + to + ")"); } public boolean equals(Object o) { if (o instanceof Conversion) { Conversion b = (Conversion) o; return from.equals(b.from) && to.equals(b.to); } return false; } public int hashCode() { return from.hashCode() + to.hashCode(); } } /** * This class abstracts different kinds of branching statements (e.g. goto, * ifacmp, etc). It probably should abstract switches as well, although it * currently doesn't, */ public static abstract class Branch extends Bytecode { public final String label; public final boolean islong; public Branch(String label) { this.label = label; this.islong = false; } public Branch(String label, boolean islong) { this.label = label; this.islong = islong; } // The purpose of this method is to force a branch to be long. public abstract Branch fixLong(); } /** * This class abstracts the unconditional branch bytecode goto. */ public static class Goto extends Branch { public Goto(String label) { super(label); } public Goto(String label, boolean islong) { super(label,islong); } public int stackDiff() { return 0; } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { if (!labelOffsets.keySet().contains(label)) { throw new IllegalArgumentException("unable to resolve label \"" + label + "\" in labelOffsets"); } ByteArrayOutputStream out = new ByteArrayOutputStream(); // here, need to figure out how far away we're going int target = labelOffsets.get(label) - offset; if(-32768 <= target && target <= 32767 && !islong) { write_u1(out,GOTO); write_i2(out,target); } else { write_u1(out,GOTO_W); write_i4(out,target); } return out.toByteArray(); } public Goto fixLong() { return new Goto(label,true); } public String toString() { return "goto " + label; } public boolean equals(Object o) { if (o instanceof Goto) { Goto b = (Goto) o; return label.equals(b.label) && islong == b.islong; } return false; } public int hashCode() { return label.hashCode(); } } public enum IfMode { EQ { public String toString() { return "eq"; } }, NE { public String toString() { return "ne"; } }, LT { public String toString() { return "lt"; } }, GE { public String toString() { return "ge"; } }, GT { public String toString() { return "gt"; } }, LE { public String toString() { return "le"; } }, NULL { public String toString() { return "null"; } }, NONNULL { public String toString() { return "nonnull"; } }; public IfMode invert() { switch(this) { case EQ: return NE; case NE: return EQ; case LT: return GE; case LE: return GT; case GT: return LE; case GE: return GT; case NULL: return NONNULL; case NONNULL: return NULL; } throw new IllegalArgumentException("invalid IfMode encountered"); } } /** * This represents the bytecodes ifeq, ifne, iflt, ifge, ifgt, ifle. */ public static class If extends Branch { public final IfMode cond; public If(IfMode cond, String label) { super(label); this.cond=cond; } public If(IfMode cond, String label, boolean islong) { super(label,islong); this.cond=cond; } public int stackDiff() { return -1; } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { if (!labelOffsets.keySet().contains(label)) { throw new IllegalArgumentException("unable to resolve label \"" + label + "\" in labelOffsets"); } ByteArrayOutputStream out = new ByteArrayOutputStream(); // here, need to figure out how far away we're going int target = labelOffsets.get(label) - offset; if(-32768 <= target && target <= 32767 && !islong) { write_u1(out,opcode(cond)); write_i2(out,target); } else { // In this case, we cannot perform a direct conditional branch. // Instead, we simply fake it by using a wide GOTO // first, invert comparison write_u1(out,opcode(cond.invert())); write_i2(out, 8); // now do the big jump write_u1(out,GOTO_W); write_i4(out,target-3); } return out.toByteArray(); } public If fixLong() { return new If(cond,label,true); } public String toString() { return "if" + cond.toString() + " " + label; } public boolean equals(Object o) { if (o instanceof If) { If b = (If) o; return label.equals(b.label) && cond == b.cond && islong == b.islong; } return false; } public int hashCode() { return label.hashCode() + opcode(cond); } private int opcode(IfMode mode) { switch(mode) { case EQ: return IFEQ; case NE: return IFNE; case LT: return IFLT; case LE: return IFLE; case GT: return IFGT; case GE: return IFGE; case NULL: return IFNULL; case NONNULL: return IFNONNULL; } throw new IllegalArgumentException("invalid IfMode encountered"); } } /** * This represents the bytecodes fcmpl, fcmpg, dcmpl, dcmpg and lcmp */ public static class Cmp extends Bytecode { public final static int EQ=0; public final static int LT=1; public final static int GT=2; public final static String[] str = { "", "lt", "gt" }; public final int op; public final JvmType type; /** * Construct a cmp bytecode. * * @param type * either Type.Float, Type.Double or Type.Long * @param op * op == LT || OP == GT if type == Float or type == Double, * otherwise op == EQ */ public Cmp(JvmType type, int op){ assert (type instanceof JvmType.Double && op >= 1 && op <= 2) || (type instanceof JvmType.Float && op >= 1 && op <= 2) || (type instanceof JvmType.Long && op == 0); this.type=type; this.op = op; } public int stackDiff() { return 1 - (2 * ClassFile.slotSize(type)); } public byte[] toBytes(int offset, Map<String, Integer> labelOffsets, Map<Constant.Info, Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); if (type instanceof JvmType.Double) { if(op == LT) { write_u1(out, DCMPL); } else { write_u1(out, DCMPG); } } else if (type instanceof JvmType.Float) { if(op == LT) { write_u1(out, FCMPL); } else { write_u1(out, FCMPG); } } else if (type instanceof JvmType.Long) { write_u1(out, LCMP); } return out.toByteArray(); } public String toString(){ if(type instanceof JvmType.Double) { if(op == LT) { return "dcmpl"; } else { return "dcmpg"; } } else if(type instanceof JvmType.Float) { if(op == LT) { return "fcmpl"; } else { return "fcmpg"; } } else { return "lcmpl"; } } public boolean equals(Object o) { if (o instanceof Cmp) { Cmp b = (Cmp) o; return op == b.op && type.equals(b.type); } return false; } public int hashCode() { return op + type.hashCode(); } } /** * This represents the bytecodes ifacmp_XX and ificmp_xx */ public static class IfCmp extends Branch { public final static int EQ=0; public final static int NE=1; public final static int LT=2; public final static int GE=3; public final static int GT=4; public final static int LE=5; public final static String[] str = { "eq", "ne", "lt", "ge", "gt", ",le" }; public final int cond; public final JvmType type; public IfCmp(int cond, JvmType type, String label) { super(label); this.cond = cond; this.type = type; } public IfCmp(int cond, JvmType type, String label, boolean islong) { super(label,islong); assert type instanceof JvmType.Int || type instanceof JvmType.Reference; assert cond >=0 && ((type instanceof JvmType.Int && cond <= LE) || (type instanceof JvmType.Reference && cond <= NE)); this.cond = cond; this.type = type; } public int stackDiff() { return 1-(2*ClassFile.slotSize(type)); } public byte[] toBytes(int offset, Map<String, Integer> labelOffsets, Map<Constant.Info, Integer> constantPool) { assert labelOffsets.containsKey(label); ByteArrayOutputStream out = new ByteArrayOutputStream(); // here, need to figure out how far away we're going int target = labelOffsets.get(label) - offset; if (-32768 <= target && target <= 32767 && !islong) { if (type instanceof JvmType.Primitive) { write_u1(out, IF_ICMPEQ + cond); } else { write_u1(out, IF_ACMPEQ + cond); } write_i2(out, target); } else { // In this case, we cannot perform a direct conditional branch. // Instead, we simply fake it by using a wide GOTO // first, invert comparison int c = cond; if((c % 2) == 0) { // even, so increment c++; } else { // odd, so decrement c--; } if (type instanceof JvmType.Primitive) { write_u1(out, IF_ICMPEQ + c); } else { write_u1(out, IF_ACMPEQ + c); } write_i2(out, 8); // now do the big jump write_u1(out,GOTO_W); write_i4(out,target-3); } return out.toByteArray(); } public IfCmp fixLong() { return new IfCmp(cond,type,label,true); } public String toString() { if(type instanceof JvmType.Int) { return "if_icmp" + str[cond] + " " + label; } else { return "if_acmp" + str[cond] + " " + label; } } public boolean equals(Object o) { if (o instanceof IfCmp) { IfCmp b = (IfCmp) o; return cond == b.cond && type.equals(b.type) && label.equals(b.label) && islong == b.islong; } return false; } public int hashCode() { return label.hashCode() + type.hashCode(); } } public static final Comparator<Pair<Integer, String>> switchcomp = new Comparator<Pair<Integer, String>>() { public int compare(Pair<Integer, String> p1, Pair<Integer, String> p2) { return p1.first().compareTo(p2.first()); } }; /** * This represents the bytecodes tableswitch and lookupswitch */ public static class Switch extends Bytecode { public final String defaultLabel; public final List<Pair<Integer, String>> cases; public final int type; public Switch(String def, List<Pair<Integer, String>> cases) { this.defaultLabel = def; this.cases = cases; Collections.sort(cases, switchcomp); if(cases.size() > 0) { long lo = cases.get(0).first(); long hi = cases.get(cases.size()-1).first(); long tableSize = 4+4*(hi-lo+1); long lookupSize = 8*(cases.size()); if (tableSize < lookupSize) { this.type = TABLESWITCH; } else { this.type = LOOKUPSWITCH; } } else { // yes, whilst this case may seem completely perverse, it is // indeed possible to have a switch statement with no cases. // I have seen some real code which does this! this.type = LOOKUPSWITCH; } } public int stackDiff() { return -1; } public int getSize(int offset) { int padding = 3 - (offset%4); int len = 1 + padding + 4; if (type == LOOKUPSWITCH) { len += 4 + cases.size() * 8; } else { int lo = cases.get(0).first(); int hi = cases.get(cases.size()-1).first(); len += 8 + 4 * (hi-lo+1); } return len; } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { if (!labelOffsets.keySet().contains(defaultLabel)) { throw new IllegalArgumentException("unable to resolve label \"" + defaultLabel + "\" in labelOffsets"); } ByteArrayOutputStream out = new ByteArrayOutputStream(); write_u1(out, type); for (int i = 0; i < (3-(offset%4)); i++) { write_u1(out, 0); // padding } int def = labelOffsets.get(defaultLabel) - offset; write_i4(out, def); if (type == LOOKUPSWITCH) { write_i4(out, cases.size()); for (Pair<Integer, String> c : cases) { write_i4(out, c.first()); write_i4(out, labelOffsets.get(c.second()) - offset); } } else { int lo = cases.get(0).first(); int hi = cases.get(cases.size()-1).first(); write_i4(out, lo); write_i4(out, hi); int index = 0; for (int i = lo; i <= hi; i++) { Pair<Integer,String> c = cases.get(index); if (c.first() == i) { int target = labelOffsets.get(c.second()) - offset; write_i4(out, target); index++; } else { write_i4(out, def); } } } return out.toByteArray(); } public String toString() { String out = "lookupswitch \n"; out += "\tdefault\t: "+defaultLabel+"\n"; for (Pair<Integer, String> c: cases) { out += "\t"+c.first()+"\t: "+c.second()+"\n"; } return out; } public boolean equals(Object o) { if (o instanceof Switch) { Switch b = (Switch) o; return type == b.type && defaultLabel.equals(b.defaultLabel) && cases.equals(b.cases); } return false; } public int hashCode() { return defaultLabel.hashCode() + cases.hashCode(); } } /** * Represents the pop and pop2 bytecodes */ public static final class Pop extends Bytecode { public final JvmType type; public Pop(JvmType type) { this.type = type; } public int stackDiff() { return -ClassFile.slotSize(type); } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); if(ClassFile.slotSize(type) > 1) { write_u1(out,POP2); } else { write_u1(out,POP); } return out.toByteArray(); } public String toString() { if(ClassFile.slotSize(type) > 1) { return "pop2"; } else { return "pop"; } } public boolean equals(Object o) { if (o instanceof Pop) { Pop b = (Pop) o; return type.equals(b.type); } return false; } public int hashCode() { return type.hashCode(); } } /** * Represents the dup and dup2 bytecodes */ public static final class Dup extends Bytecode { public final JvmType type; public Dup(JvmType type) { this.type = type; } public int stackDiff() { return ClassFile.slotSize(type); } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); if(ClassFile.slotSize(type) > 1) { write_u1(out,DUP2); } else { write_u1(out,DUP); } return out.toByteArray(); } public String toString() { if(ClassFile.slotSize(type) > 1) { return "dup2"; } else { return "dup"; } } public boolean equals(Object o) { if (o instanceof Dup) { Dup b = (Dup) o; return type.equals(b.type); } return false; } public int hashCode() { return type.hashCode(); } } /** * Represents the dupx1 bytecode */ public static final class DupX1 extends Bytecode { public int stackDiff() { return 1; } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); write_u1(out,DUP_X1); return out.toByteArray(); } public String toString() { return "dup_x1"; } public boolean equals(Object o) { return o instanceof DupX1; } public int hashCode() { return 12231; } } /** * Represents the dupx2 bytecode */ public static final class DupX2 extends Bytecode { public int stackDiff() { return 2; } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); write_u1(out,DUP_X2); return out.toByteArray(); } public String toString() { return "dup_x2"; } public boolean equals(Object o) { return o instanceof DupX2; } public int hashCode() { return 389282; } } /** * Represents the swap bytecode */ public static final class Swap extends Bytecode { public int stackDiff() { return 0; } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); write_u1(out,SWAP); return out.toByteArray(); } public String toString() { return "swap"; } public boolean equals(Object o) { return o instanceof Swap; } public int hashCode() { return 389282; } } /** * Represents the new, newarray and anewarray, multinewarray bytecodes. */ public static final class New extends Bytecode { public final JvmType type; public final int dims; public New(JvmType type) { assert type instanceof JvmType.Reference || type instanceof JvmType.Array; this.type = type; this.dims = -1; } public New(JvmType type, int dims) { assert type instanceof JvmType.Reference || type instanceof JvmType.Array; this.type = type; this.dims = dims; } public int stackDiff() { return 1; } public void addPoolItems(Set<Constant.Info> constantPool) { if(type instanceof JvmType.Array) { JvmType.Array atype = (JvmType.Array) type; JvmType elementType = atype.element(); if(dims > 1) { Constant.addPoolItem(Constant .buildClass((JvmType.Array) type),constantPool); } else if(elementType instanceof JvmType.Reference) { Constant.addPoolItem(Constant .buildClass((JvmType.Reference) elementType),constantPool); } } else { Constant.addPoolItem(Constant .buildClass((JvmType.Clazz) type),constantPool); } } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); if(type instanceof JvmType.Array) { JvmType.Array atype = (JvmType.Array) type; if(dims > 1) { int idx = constantPool.get(Constant .buildClass((JvmType.Array) type)); write_u1(out,MULTIANEWARRAY); write_u2(out,idx); write_u1(out,dims); } else { JvmType elementType = atype.element(); if(elementType instanceof JvmType.Reference) { int idx = constantPool.get(Constant .buildClass((JvmType.Reference) elementType)); write_u1(out,ANEWARRAY); write_u2(out,idx); } else { write_u1(out,NEWARRAY); if(elementType instanceof JvmType.Bool) { write_u1(out, T_BOOLEAN); } else if(elementType instanceof JvmType.Char) { write_u1(out, T_CHAR); } else if(elementType instanceof JvmType.Byte) { write_u1(out, T_BYTE); } else if(elementType instanceof JvmType.Int) { write_u1(out, T_INT); } else if(elementType instanceof JvmType.Short) { write_u1(out, T_SHORT); } else if(elementType instanceof JvmType.Long) { write_u1(out, T_LONG); } else if(elementType instanceof JvmType.Float) { write_u1(out, T_FLOAT); } else if(elementType instanceof JvmType.Double) { write_u1(out, T_DOUBLE); } else { throw new RuntimeException("internal failure constructing " + elementType); } } } } else { int idx = constantPool.get(Constant .buildClass((JvmType.Reference) type)); write_u1(out,NEW); write_u2(out,idx); } return out.toByteArray(); } public String toString() { if(type instanceof JvmType.Array) { JvmType.Array atype = (JvmType.Array) type; JvmType elementType = atype.element(); if(dims > 1) { return "multianewarray " + type + ", " + dims; } else if(elementType instanceof JvmType.Reference || elementType instanceof JvmType.Array) { return "anewarray " + type; } else { return "newarray " + type; } } else { return "new " + type; } } public boolean equals(Object o) { if (o instanceof New) { New b = (New) o; return dims == b.dims && type.equals(b.type); } return false; } public int hashCode() { return type.hashCode() + dims; } } /** * Represents the arraylength bytecode */ public static final class ArrayLength extends Bytecode { public int stackDiff() { return 1; } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); write_u1(out,ARRAYLENGTH); return out.toByteArray(); } public String toString() { return "arraylength"; } public boolean equals(Object o) { return o instanceof ArrayLength; } public int hashCode() { return 122199; } } /** * Represents the check cast bytecode. */ public static final class CheckCast extends Bytecode { public final JvmType type; /** * Check a reference on the stack has the given type. * * @param type --- must be either Type.Array or Type.Clazz */ public CheckCast(JvmType type) { if(!(type instanceof JvmType.Clazz) && !(type instanceof JvmType.Array)) { throw new IllegalArgumentException("checkcast cannot accept " + type); } this.type = type; } public int stackDiff() { return 0; } public void addPoolItems(Set<Constant.Info> constantPool) { Constant.addPoolItem( Constant.buildClass((JvmType.Reference) type), constantPool); } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); int idx; idx = constantPool.get(Constant .buildClass((JvmType.Reference) type)); write_u1(out,CHECKCAST); write_u2(out,idx); return out.toByteArray(); } public String toString() { return "checkcast " + ClassFile.descriptor(type,false); } public boolean equals(Object o) { if (o instanceof CheckCast) { CheckCast b = (CheckCast) o; return type.equals(b.type); } return false; } public int hashCode() { return type.hashCode(); } } /** * Represents the instanceof bytecode */ public static final class InstanceOf extends Bytecode { public final JvmType.Reference type; public InstanceOf(JvmType.Reference type) { this.type = type; } public int stackDiff() { return 0; } public void addPoolItems(Set<Constant.Info> constantPool) { Constant.addPoolItem(Constant.buildClass(type), constantPool); } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); int idx = constantPool.get(Constant .buildClass(type)); write_u1(out,INSTANCEOF); write_u2(out,idx); return out.toByteArray(); } public String toString() { return "instanceof " + type; } public boolean equals(Object o) { if (o instanceof InstanceOf) { InstanceOf b = (InstanceOf) o; return type.equals(b.type); } return false; } public int hashCode() { return type.hashCode(); } } /** * Represents the nop bytecode. */ public static final class Nop extends Bytecode { public int stackDiff() { return 0; } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); write_u1(out,NOP); return out.toByteArray(); } public String toString() { return "nop"; } public boolean equals(Object o) { return o instanceof Nop; } public int hashCode() { return 97364; } } /** * Represents the athrow bytecode. */ public static final class Throw extends Bytecode { public int stackDiff() { return 1; // this is slightly annoying, since it's not always needed!! } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); write_u1(out,ATHROW); return out.toByteArray(); } public String toString() { return "athrow"; } public boolean equals(Object o) { return o instanceof Throw; } public int hashCode() { return 44520; } } /** * Represents a monitorenter bytecode */ public static final class MonitorEnter extends Bytecode { public int stackDiff() { return -1; } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); write_u1(out,MONITORENTER); return out.toByteArray(); } public String toString() { return "monitorenter"; } public boolean equals(Object o) { return o instanceof MonitorEnter; } public int hashCode() { return 998654; } } /** * Represents a monitorexit bytecode */ public static final class MonitorExit extends Bytecode { public int stackDiff() { return -1; } public byte[] toBytes(int offset, Map<String,Integer> labelOffsets, Map<Constant.Info,Integer> constantPool) { ByteArrayOutputStream out = new ByteArrayOutputStream(); write_u1(out,MONITOREXIT); return out.toByteArray(); } public String toString() { return "monitorexit"; } public boolean equals(Object o) { return o instanceof MonitorExit; } public int hashCode() { return 12354; } } // ============================== // ======= HELPER METHODS ======= // ============================== public static String[] get() { String[] map = new String[256]; // initialise the map using reflection! try { Class<?> c = Class.forName("jkit.util.Bytecode"); for(java.lang.reflect.Field f : c.getDeclaredFields()) { String name = f.getName(); if(name.contains("STOP")) { } else { // instruction opcode int i = f.getInt(null); map[i] = name; } } } catch(IllegalAccessException e) { throw new RuntimeException("illegal access exception"); } catch(ClassNotFoundException e) { throw new RuntimeException("unable to initialise OpcodeMap"); } return map; } private static char typeChar(JvmType type) { if(type instanceof JvmType.Reference || type instanceof JvmType.Null || type instanceof JvmType.Array) { return 'a'; } else if(type instanceof JvmType.Int || type instanceof JvmType.Byte || type instanceof JvmType.Char || type instanceof JvmType.Short || type instanceof JvmType.Bool || type instanceof JvmType.Byte) { return 'i'; } else if(type instanceof JvmType.Long) { return 'l'; } else if(type instanceof JvmType.Float) { return 'f'; } else if(type instanceof JvmType.Double) { return 'd'; } else { throw new RuntimeException("unknown type encountered (" + type + ")"); } } private static int typeOffset(JvmType type) { if(type instanceof JvmType.Reference || type instanceof JvmType.Null || type instanceof JvmType.Array || type instanceof JvmType.Variable) { return 4; } else if(type instanceof JvmType.Int || type instanceof JvmType.Byte || type instanceof JvmType.Char || type instanceof JvmType.Short || type instanceof JvmType.Bool || type instanceof JvmType.Byte) { return 0; } else if(type instanceof JvmType.Long) { return 1; } else if(type instanceof JvmType.Float) { return 2; } else if(type instanceof JvmType.Double) { return 3; } else { throw new RuntimeException("unknown type encountered (" + type + ")"); } } private static int typeArrayOffset(JvmType type) { if (type instanceof JvmType.Int) { return 0; } else if (type instanceof JvmType.Long) { return 1; } else if(type instanceof JvmType.Float) { return 2; } else if(type instanceof JvmType.Double) { return 3; } else if(type instanceof JvmType.Array) { return 4; } else if (type instanceof JvmType.Reference || type instanceof JvmType.Variable || type instanceof JvmType.Wildcard) { return 4; //same as array } else if(type instanceof JvmType.Byte) { return 5; } else if(type instanceof JvmType.Bool) { return 5; //same as byte } else if(type instanceof JvmType.Char) { return 6; } else if(type instanceof JvmType.Short) { return 7; } else { throw new RuntimeException("unknown type in array: " + type); } } private static char typeArrayChar(JvmType type) { if(type instanceof JvmType.Byte || type instanceof JvmType.Bool) { return 'b'; } else if(type instanceof JvmType.Char) { return 'c'; } else if(type instanceof JvmType.Short) { return 's'; } else { return typeChar(type); } } private static void write_u1(ByteArrayOutputStream output, int w) { output.write(w & 0xFF); } private static void write_u2(ByteArrayOutputStream output, int w) { output.write((w >> 8) & 0xFF); output.write(w & 0xFF); } private static void write_u4(ByteArrayOutputStream output, long w) { output.write((int) (w >> 24) & 0xFF); output.write((int) (w >> 16) & 0xFF); output.write((int) (w >> 8) & 0xFF); output.write((int) (w & 0xFF)); } @SuppressWarnings("unused") private static void write_u8(ByteArrayOutputStream output, long w) { write_u4(output, ((w >> 32) & 0xFFFFFFFFL)); write_u4(output, (w & 0xFFFFFFFFL)); } private static void write_i1(ByteArrayOutputStream output, int w) { output.write(w); } private static void write_i2(ByteArrayOutputStream output, int w) { output.write(w >> 8); output.write(w); } private static void write_i4(ByteArrayOutputStream output, int w) { output.write(w >> 24); output.write(w >> 16); output.write(w >> 8); output.write(w); } // opcodes public static final int NOP = 0; public static final int ACONST_NULL = 1; public static final int ICONST_M1 = 2; public static final int ICONST_0 = 3; public static final int ICONST_1 = 4; public static final int ICONST_2 = 5; public static final int ICONST_3 = 6; public static final int ICONST_4 = 7; public static final int ICONST_5 = 8; public static final int LCONST_0 = 9; public static final int LCONST_1 = 10; public static final int FCONST_0 = 11; public static final int FCONST_1 = 12; public static final int FCONST_2 = 13; public static final int DCONST_0 = 14; public static final int DCONST_1 = 15; public static final int BIPUSH = 16; public static final int SIPUSH = 17; public static final int LDC = 18; public static final int LDC_W = 19; public static final int LDC2_W = 20; public static final int ILOAD = 21; public static final int LLOAD = 22; public static final int FLOAD = 23; public static final int DLOAD = 24; public static final int ALOAD = 25; public static final int ILOAD_0 = 26; public static final int ILOAD_1 = 27; public static final int ILOAD_2 = 28; public static final int ILOAD_3 = 29; public static final int LLOAD_0 = 30; public static final int LLOAD_1 = 31; public static final int LLOAD_2 = 32; public static final int LLOAD_3 = 33; public static final int FLOAD_0 = 34; public static final int FLOAD_1 = 35; public static final int FLOAD_2 = 36; public static final int FLOAD_3 = 37; public static final int DLOAD_0 = 38; public static final int DLOAD_1 = 39; public static final int DLOAD_2 = 40; public static final int DLOAD_3 = 41; public static final int ALOAD_0 = 42; public static final int ALOAD_1 = 43; public static final int ALOAD_2 = 44; public static final int ALOAD_3 = 45; public static final int IALOAD = 46; public static final int LALOAD = 47; public static final int FALOAD = 48; public static final int DALOAD = 49; public static final int AALOAD = 50; public static final int BALOAD = 51; public static final int CALOAD = 52; public static final int SALOAD = 53; public static final int ISTORE = 54; public static final int LSTORE = 55; public static final int FSTORE = 56; public static final int DSTORE = 57; public static final int ASTORE = 58; public static final int ISTORE_0 = 59; public static final int ISTORE_1 = 60; public static final int ISTORE_2 = 61; public static final int ISTORE_3 = 62; public static final int LSTORE_0 = 63; public static final int LSTORE_1 = 64; public static final int LSTORE_2 = 65; public static final int LSTORE_3 = 66; public static final int FSTORE_0 = 67; public static final int FSTORE_1 = 68; public static final int FSTORE_2 = 69; public static final int FSTORE_3 = 70; public static final int DSTORE_0 = 71; public static final int DSTORE_1 = 72; public static final int DSTORE_2 = 73; public static final int DSTORE_3 = 74; public static final int ASTORE_0 = 75; public static final int ASTORE_1 = 76; public static final int ASTORE_2 = 77; public static final int ASTORE_3 = 78; public static final int IASTORE = 79; public static final int LASTORE = 80; public static final int FASTORE = 81; public static final int DASTORE = 82; public static final int AASTORE = 83; public static final int BASTORE = 84; public static final int CASTORE = 85; public static final int SASTORE = 86; public static final int POP = 87; public static final int POP2 = 88; public static final int DUP = 89; public static final int DUP_X1 = 90; public static final int DUP_X2 = 91; public static final int DUP2 = 92; public static final int DUP2_X1 = 93; public static final int DUP2_X2 = 94; public static final int SWAP = 95; public static final int IADD = 96; public static final int LADD = 97; public static final int FADD = 98; public static final int DADD = 99; public static final int ISUB = 100; public static final int LSUB = 101; public static final int FSUB = 102; public static final int DSUB = 103; public static final int IMUL = 104; public static final int LMUL = 105; public static final int FMUL = 106; public static final int DMUL = 107; public static final int IDIV = 108; public static final int LDIV = 109; public static final int FDIV = 110; public static final int DDIV = 111; public static final int IREM = 112; public static final int LREM = 113; public static final int FREM = 114; public static final int DREM = 115; public static final int INEG = 116; public static final int LNEG = 117; public static final int FNEG = 118; public static final int DNEG = 119; public static final int ISHL = 120; public static final int LSHL = 121; public static final int ISHR = 122; public static final int LSHR = 123; public static final int IUSHR = 124; public static final int LUSHR = 125; public static final int IAND = 126; public static final int LAND = 127; public static final int IOR = 128; public static final int LOR = 129; public static final int IXOR = 130; public static final int LXOR = 131; public static final int IINC = 132; public static final int I2L = 133; public static final int I2F = 134; public static final int I2D = 135; public static final int L2I = 136; public static final int L2F = 137; public static final int L2D = 138; public static final int F2I = 139; public static final int F2L = 140; public static final int F2D = 141; public static final int D2I = 142; public static final int D2L = 143; public static final int D2F = 144; public static final int I2B = 145; public static final int I2C = 146; public static final int I2S = 147; public static final int LCMP = 148; public static final int FCMPL = 149; public static final int FCMPG = 150; public static final int DCMPL = 151; public static final int DCMPG = 152; public static final int IFEQ = 153; public static final int IFNE = 154; public static final int IFLT = 155; public static final int IFGE = 156; public static final int IFGT = 157; public static final int IFLE = 158; public static final int IF_ICMPEQ = 159; public static final int IF_ICMPNE = 160; public static final int IF_ICMPLT = 161; public static final int IF_ICMPGE = 162; public static final int IF_ICMPGT = 163; public static final int IF_ICMPLE = 164; public static final int IF_ACMPEQ = 165; public static final int IF_ACMPNE = 166; public static final int GOTO = 167; public static final int JSR = 168; public static final int RET = 169; public static final int TABLESWITCH = 170; public static final int LOOKUPSWITCH = 171; public static final int IRETURN = 172; public static final int LRETURN = 173; public static final int FRETURN = 174; public static final int DRETURN = 175; public static final int ARETURN = 176; public static final int RETURN = 177; public static final int GETSTATIC = 178; public static final int PUTSTATIC = 179; public static final int GETFIELD = 180; public static final int PUTFIELD = 181; public static final int INVOKEVIRTUAL = 182; public static final int INVOKESPECIAL = 183; public static final int INVOKESTATIC = 184; public static final int INVOKEINTERFACE = 185; public static final int UNUSED = 186; public static final int NEW = 187; public static final int NEWARRAY = 188; public static final int ANEWARRAY = 189; public static final int ARRAYLENGTH = 190; public static final int ATHROW = 191; public static final int CHECKCAST = 192; public static final int INSTANCEOF = 193; public static final int MONITORENTER = 194; public static final int MONITOREXIT = 195; public static final int WIDE = 196; public static final int MULTIANEWARRAY = 197; public static final int IFNULL = 198; public static final int IFNONNULL = 199; public static final int GOTO_W = 200; public static final int JSR_W = 201; public static final int BREAKPOINT = 202; // reserved public static final int IMPDEP1 = 254; // reserved public static final int IMPDEP2 = 255; // reserved // Array Types public static final int T_BOOLEAN = 4; public static final int T_CHAR = 5; public static final int T_FLOAT = 6; public static final int T_DOUBLE = 7; public static final int T_BYTE = 8; public static final int T_SHORT = 9; public static final int T_INT = 10; public static final int T_LONG = 11; }