/** * Copyright (C) 2010-2017 Gordon Fraser, Andrea Arcuri and EvoSuite * contributors * * This file is part of EvoSuite. * * EvoSuite is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3.0 of the License, or * (at your option) any later version. * * EvoSuite is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with EvoSuite. If not, see <http://www.gnu.org/licenses/>. */ package org.evosuite.symbolic.instrument; import static org.evosuite.dse.util.Assertions.check; import static org.evosuite.dse.util.Assertions.notNull; import static org.evosuite.symbolic.instrument.ConcolicConfig.BII_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.BYTECODE_NAME; import static org.evosuite.symbolic.instrument.ConcolicConfig.CII_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.DGGG_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.DII_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.D_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.FGGG_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.FII_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.F_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.GGGII_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.GGG_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.GI_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.G_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.IGGG_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.IG_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.III_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.II_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.INT; import static org.evosuite.symbolic.instrument.ConcolicConfig.INT_ARR; import static org.evosuite.symbolic.instrument.ConcolicConfig.I_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.JGGG_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.JII_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.J_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.LGGG_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.LG_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.LII_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.LI_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.L_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.REF; import static org.evosuite.symbolic.instrument.ConcolicConfig.SII_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.STR; import static org.evosuite.symbolic.instrument.ConcolicConfig.VM_FQ; import static org.evosuite.symbolic.instrument.ConcolicConfig.VOID; import static org.evosuite.symbolic.instrument.ConcolicConfig.V_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.ZGGG_V; import static org.evosuite.symbolic.instrument.ConcolicConfig.ZII_V; import static org.objectweb.asm.Opcodes.AALOAD; import static org.objectweb.asm.Opcodes.AASTORE; import static org.objectweb.asm.Opcodes.ALOAD; import static org.objectweb.asm.Opcodes.ANEWARRAY; import static org.objectweb.asm.Opcodes.ARRAYLENGTH; import static org.objectweb.asm.Opcodes.ASTORE; import static org.objectweb.asm.Opcodes.ATHROW; import static org.objectweb.asm.Opcodes.BALOAD; import static org.objectweb.asm.Opcodes.BASTORE; import static org.objectweb.asm.Opcodes.BIPUSH; import static org.objectweb.asm.Opcodes.CALOAD; import static org.objectweb.asm.Opcodes.CASTORE; import static org.objectweb.asm.Opcodes.CHECKCAST; import static org.objectweb.asm.Opcodes.DALOAD; import static org.objectweb.asm.Opcodes.DASTORE; import static org.objectweb.asm.Opcodes.DDIV; import static org.objectweb.asm.Opcodes.DLOAD; import static org.objectweb.asm.Opcodes.DREM; import static org.objectweb.asm.Opcodes.DUP; import static org.objectweb.asm.Opcodes.DUP2; import static org.objectweb.asm.Opcodes.FALOAD; import static org.objectweb.asm.Opcodes.FASTORE; import static org.objectweb.asm.Opcodes.FDIV; import static org.objectweb.asm.Opcodes.FLOAD; import static org.objectweb.asm.Opcodes.FREM; import static org.objectweb.asm.Opcodes.GETFIELD; import static org.objectweb.asm.Opcodes.GOTO; import static org.objectweb.asm.Opcodes.IALOAD; import static org.objectweb.asm.Opcodes.IASTORE; import static org.objectweb.asm.Opcodes.IDIV; import static org.objectweb.asm.Opcodes.IFEQ; import static org.objectweb.asm.Opcodes.IFGE; import static org.objectweb.asm.Opcodes.IFGT; import static org.objectweb.asm.Opcodes.IFLE; import static org.objectweb.asm.Opcodes.IFLT; import static org.objectweb.asm.Opcodes.IFNE; import static org.objectweb.asm.Opcodes.IFNONNULL; import static org.objectweb.asm.Opcodes.IFNULL; import static org.objectweb.asm.Opcodes.IF_ACMPEQ; import static org.objectweb.asm.Opcodes.IF_ACMPNE; import static org.objectweb.asm.Opcodes.IF_ICMPEQ; import static org.objectweb.asm.Opcodes.IF_ICMPGE; import static org.objectweb.asm.Opcodes.IF_ICMPGT; import static org.objectweb.asm.Opcodes.IF_ICMPLE; import static org.objectweb.asm.Opcodes.IF_ICMPLT; import static org.objectweb.asm.Opcodes.IF_ICMPNE; import static org.objectweb.asm.Opcodes.ILOAD; import static org.objectweb.asm.Opcodes.INSTANCEOF; import static org.objectweb.asm.Opcodes.INVOKEINTERFACE; import static org.objectweb.asm.Opcodes.INVOKESPECIAL; import static org.objectweb.asm.Opcodes.INVOKESTATIC; import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; import static org.objectweb.asm.Opcodes.IREM; import static org.objectweb.asm.Opcodes.ISTORE; import static org.objectweb.asm.Opcodes.JSR; import static org.objectweb.asm.Opcodes.LALOAD; import static org.objectweb.asm.Opcodes.LASTORE; import static org.objectweb.asm.Opcodes.LDC; import static org.objectweb.asm.Opcodes.LDIV; import static org.objectweb.asm.Opcodes.LLOAD; import static org.objectweb.asm.Opcodes.LOOKUPSWITCH; import static org.objectweb.asm.Opcodes.LREM; import static org.objectweb.asm.Opcodes.MULTIANEWARRAY; import static org.objectweb.asm.Opcodes.NEW; import static org.objectweb.asm.Opcodes.NEWARRAY; import static org.objectweb.asm.Opcodes.PUTFIELD; import static org.objectweb.asm.Opcodes.SALOAD; import static org.objectweb.asm.Opcodes.SASTORE; import static org.objectweb.asm.Opcodes.SIPUSH; import static org.objectweb.asm.Opcodes.TABLESWITCH; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.evosuite.TestGenerationContext; import org.evosuite.coverage.branch.Branch; import org.evosuite.coverage.branch.BranchPool; import org.evosuite.instrumentation.InstrumentingClassLoader; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.tree.LabelNode; /* This class is taken and adapted from the DSC tool developed by Christoph Csallner. Link at : http://ranger.uta.edu/~csallner/dsc/index.html */ /** * Main instrumentation class * * <p> * Before each user ByteCode instruction, add a call to one of our static * methods, that reflects the particular ByteCode. In a few cases noted below, * we add our callback after the user ByteCode, instead of before. * * @author csallner@uta.edu (Christoph Csallner) */ public final class ConcolicMethodAdapter extends GeneratorAdapter { private static final String THIS$0 = "this$0"; private static final String INIT = "<init>"; //$NON-NLS-1$ private static final String CLINIT = "<clinit>"; //$NON-NLS-1$ private static final String METHOD_BEGIN = "METHOD_BEGIN"; //$NON-NLS-1$ private static final String METHOD_BEGIN_PARAM = "METHOD_BEGIN_PARAM"; //$NON-NLS-1$ private static final String METHOD_BEGIN_RECEIVER = "METHOD_BEGIN_RECEIVER"; //$NON-NLS-1$ private static final String CALL_RESULT = "CALL_RESULT"; //$NON-NLS-1$ private final int access; private final String className; private final String methName; private final String methDescription; private final OperandStack stack; /** * Constructor */ ConcolicMethodAdapter(MethodVisitor mv, int access, String className, String methName, String desc) { super(Opcodes.ASM4, mv, access, methName, desc); this.access = access; this.className = notNull(className); this.methName = notNull(methName); this.methDescription = notNull(desc); this.stack = new OperandStack(mv); } /** * Before first ByteCode of the method/constructor * * <p> * Issue one call per argument, excluding the "this" receiver for * non-constructor instance methods. Work left to right, starting with * receiver. * * <ol> * <li> * METHOD_BEGIN_RECEIVER(value) -- optional</li> * <li> * METHOD_BEGIN_PARAM(value, 0, 0)</li> * <li> * ...</li> * <li> * METHOD_BEGIN_PARAM(value, nrArgs-1, localsIndex)</li> * </ol> */ @Override public void visitCode() { super.visitCode(); /* * standard access flags may also fit into signed 16-bit. That would * allow using short and sipush. But Asm pseudo access flag * ACC_DEPRECATED is too large. */ stack.pushInt(access); stack.pushStrings(className, methName, methDescription); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, METHOD_BEGIN, IGGG_V); final boolean needThis = !AccessFlags.isStatic(access) && !CLINIT.equals(methName); if (needThis && !INIT.equals(methName)) { mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, METHOD_BEGIN_RECEIVER, L_V); } Type[] paramTypes = Type.getArgumentTypes(methDescription); // does not // include // "this" /* * Category-2 parameters require two slots in locals, so the index into * the locals increases by two. */ int paramNr = 0; int calleeLocalsIndex = 0; if (needThis) calleeLocalsIndex += 1; for (Type type : paramTypes) { String dscMethParamSign = null; switch (type.getSort()) { case Type.BOOLEAN: mv.visitVarInsn(ILOAD, calleeLocalsIndex); dscMethParamSign = ZII_V; break; case Type.BYTE: mv.visitVarInsn(ILOAD, calleeLocalsIndex); dscMethParamSign = BII_V; break; case Type.CHAR: mv.visitVarInsn(ILOAD, calleeLocalsIndex); dscMethParamSign = CII_V; break; case Type.SHORT: mv.visitVarInsn(ILOAD, calleeLocalsIndex); dscMethParamSign = SII_V; break; case Type.INT: mv.visitVarInsn(ILOAD, calleeLocalsIndex); dscMethParamSign = III_V; break; case Type.LONG: mv.visitVarInsn(LLOAD, calleeLocalsIndex); dscMethParamSign = JII_V; break; case Type.DOUBLE: mv.visitVarInsn(DLOAD, calleeLocalsIndex); dscMethParamSign = DII_V; break; case Type.FLOAT: mv.visitVarInsn(FLOAD, calleeLocalsIndex); dscMethParamSign = FII_V; break; case Type.ARRAY: case Type.OBJECT: mv.visitVarInsn(ALOAD, calleeLocalsIndex); dscMethParamSign = LII_V; break; } stack.pushInt(paramNr); stack.pushInt(calleeLocalsIndex); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, METHOD_BEGIN_PARAM, dscMethParamSign); paramNr += 1; calleeLocalsIndex += type.getSize(); } } /** * Insert call to our method directly before the corresponding user * instruction */ @Override public void visitInsn(int opcode) { /* Divide instructions: Pass second argument (top of stack) to listeners */ switch (opcode) { case IDIV: case IREM: mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], I_V); break; case LDIV: case LREM: mv.visitInsn(DUP2); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], J_V); break; case FDIV: case FREM: mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], F_V); break; case DDIV: case DREM: mv.visitInsn(DUP2); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], D_V); break; /* * ..., arrayref, index ==> ..., value */ case IALOAD: // http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc6.html#iaload case LALOAD: case DALOAD: // http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc3.html#daload case FALOAD: case AALOAD: case BALOAD: case CALOAD: case SALOAD: mv.visitInsn(DUP2); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], LI_V); break; /* * ..., arrayref, index, value ==> ... */ case IASTORE: // http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc6.html#iastore case FASTORE: case AASTORE: case BASTORE: case CASTORE: case SASTORE: /* ..., arrayref, index, value */ stack.c1b1a1__c1b1a1c1(); /* ..., arrayref, index, value, arrayref */ stack.c1b1a1__c1b1a1c1(); /* ..., arrayref, index, value, arrayref, index */ mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], LI_V); break; case LASTORE: case DASTORE: /* ..., arrayref, index, value */ stack.c1b1a2__c1b1a2c1(); /* ..., arrayref, index, value, arrayref */ stack.c1b2a1__c1b2a1c1(); /* ..., arrayref, index, value, arrayref, index */ mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], LI_V); break; case ATHROW: case ARRAYLENGTH: // http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc.html#ARRAYLENGTH mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], L_V); break; default: mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], V_V); } super.visitInsn(opcode); // user code ByteCode instruction } private int branchCounter = 1; @Override public void visitJumpInsn(int opcode, Label label) { // The use of branchCounter is inlined so that branchIds match // what EvoSuite produces // switch (opcode) { case IFEQ: // http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc6.html#ifcond case IFNE: case IFLT: case IFGE: case IFGT: case IFLE: mv.visitInsn(DUP); mv.visitLdcInsn(className); mv.visitLdcInsn(methName); mv.visitLdcInsn(branchCounter++); String IGGI_V = "(" + INT + STR + STR + INT + ")" + VOID; mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], IGGI_V); break; case IF_ICMPEQ: // http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc6.html#if_icmpcond case IF_ICMPNE: case IF_ICMPLT: case IF_ICMPGE: case IF_ICMPGT: case IF_ICMPLE: mv.visitInsn(DUP2); mv.visitLdcInsn(className); mv.visitLdcInsn(methName); mv.visitLdcInsn(branchCounter++); String IIGGI_V = "(" + INT + INT + STR + STR + INT + ")" + VOID; mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], IIGGI_V); break; case IF_ACMPEQ: // http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc6.html#if_acmpcond case IF_ACMPNE: mv.visitInsn(DUP2); mv.visitLdcInsn(className); mv.visitLdcInsn(methName); mv.visitLdcInsn(branchCounter++); String LLGGI_V = "(" + REF + REF + STR + STR + INT + ")" + VOID; mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], LLGGI_V); break; case IFNULL: // http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc6.html#ifnull case IFNONNULL: mv.visitInsn(DUP); mv.visitLdcInsn(className); mv.visitLdcInsn(methName); mv.visitLdcInsn(branchCounter++); String LGGI_V = "(" + REF + STR + STR + INT + ")" + VOID; mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], LGGI_V); break; case GOTO: // http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc5.html#goto case JSR: mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], V_V); break; case 200: // http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc5.html#goto_w case 201: mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], V_V); break; default: check(false, BYTECODE_NAME[opcode] + " is not a jump instruction."); } /* All our code is added now to the basic block, before the jump */ super.visitJumpInsn(opcode, label); // user's jump instruction } @Override public void visitLineNumber(int line, Label start) { stack.pushInt(line); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, "SRC_LINE_NUMBER", I_V); //$NON-NLS-1$ super.visitLineNumber(line, start); } /** * Pseudo-instruction, inserted directly before the corresponding target * instruction. * * <p> * Our instrumentation code does not change the shape of the instrumented * method's control flow graph. So hopefully we do not need to modify any * label and trust that ASM will recompute the concrete offsets correctly * for us. */ @Override public void visitLabel(Label label) { super.visitLabel(label); if (!exceptionHandlers.contains(label)) { mv.visitMethodInsn(INVOKESTATIC, VM_FQ, "BB_BEGIN", V_V); //$NON-NLS-1$ return; } /* Exception handler basic block */ stack.pushInt(access); stack.pushStrings(className, methName, methDescription); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, "HANDLER_BEGIN", IGGG_V); //$NON-NLS-1$ } /** * <ul> * <li> * BIPUSH: push one byte from instruction stream to operand stack</li> * <li> * SIPUSH: push two bytes from instruction stream to operand stack</li> * <li> * NEWARRAY</li> * </ul> */ @Override public void visitIntInsn(int opcode, int operand) { switch (opcode) { case BIPUSH: // @see // http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc1.html#bipush case SIPUSH: // @see // http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc13.html#sipush super.visitIntInsn(opcode, operand); // user ByteCode instruction mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], I_V); return; case NEWARRAY: // @see // http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc10.html#newarray mv.visitInsn(DUP); // duplicate array length mv.visitIntInsn(BIPUSH, operand); // push array componenet type mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], II_V); super.visitIntInsn(opcode, operand); // user ByteCode instruction return; default: check(false); } } /** * <ul> * <li> * LDC, (LDC_W) -- push category one constant from constant pool</li> * <li> * LDC2_W -- push category two constant from constant pool</li> * </ul> * * Insert call to our method after user ByteCode instruction, allows us to * use the result of LDC. * * @see http * ://java.sun.com/docs/books/jvms/second_edition/html/Instructions2 * .doc8.html#ldc * @see http * ://java.sun.com/docs/books/jvms/second_edition/html/Instructions2 * .doc8.html#ldc2_w */ @Override public void visitLdcInsn(Object constant) { super.visitLdcInsn(constant); // Put our callback after LDC instruction, // so we can send result notNull(constant); if (constant instanceof Integer) { mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[LDC], I_V); } else if (constant instanceof Float) { mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[LDC], F_V); } else if (constant instanceof String) { mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[LDC], G_V); } else if (constant instanceof Type) { mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[LDC], "(Ljava/lang/Class;)V"); //$NON-NLS-1$ } else { /* LDC2_W */ mv.visitInsn(DUP2); if (constant instanceof Long) mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[20], J_V); else if (constant instanceof Double) mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[20], D_V); else check(false); } } /** * ILOAD, ISTORE, ILOAD_0, ILOAD_1, etc. * * <p> * These may follow a WIDE instruction. */ @Override public void visitVarInsn(int opcode, int var) { check(var >= 0); if ((ILOAD <= opcode) && (opcode <= ALOAD) || (ISTORE <= opcode) && (opcode <= ASTORE)) { /* * Bytecode has an explicit parameter, e.g., ILOAD 33. In this case, * we need to push value 33 onto the user's operand stack. */ stack.pushInt(var); // push local variable index mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], I_V); } else { /* * Bytecode does not have an explicit parameter, the parameter is * hard-coded into the ByteCode, e.g., ILOAD_2 */ mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], V_V); } super.visitVarInsn(opcode, var); // user ByteCode instruction } /** * GETFIELD, PUTFIELD, GETSTATIC, PUTSTATIC * * http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2. * doc5.html#getfield * http://java.sun.com/docs/books/jvms/second_edition/html * /Instructions2.doc11.html#putfield */ @Override public void visitFieldInsn(int opcode, String owner, String name, String desc) { if (!name.equals(THIS$0)) { String signInvoke = GGG_V; Type fieldType = Type.getType(desc); /* * ..., objectref ==> ..., value */ if (opcode == GETFIELD) { // top of stack == receiver instance mv.visitInsn(DUP); signInvoke = LGGG_V; } /* * ..., objectref, value ==> ... */ if (opcode == PUTFIELD) { // top-1 == receiver instance signInvoke = LGGG_V; if (fieldType.getSize() == 1) stack.b1a1__b1a1b1(); else if (fieldType.getSize() == 2) stack.b1a2__b1a2b1(); else check(false); } stack.pushStrings(owner, name, desc); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], signInvoke); } super.visitFieldInsn(opcode, owner, name, desc); // user ByteCode // instruction } /** * Invoke a method/constructor/class initializer: * <ol> * <li> * INVOKE_X -- signature and receiver (!) of invoked method</li> * <li> * CALLER_STACK_PARAM -- last parameter</li> * <li> * CALLER_STACK_PARAM -- next-to-last parameter</li> * <li> * make call</li> * <li> * CALL_RESULT</li> * </ol> * * <p> * Add callback before any invocation. Besides all method calls (static, * instance, private, public, native, etc.) and all constructor calls, this * also works for the special case of the (implicit) super call that the * Java compiler adds to the beginning of each constructor: * "InvokeSpecial MySuperType <init>" * * <p> * Although the Java compiler does not allow any statement before this super * call, we can still add a callback at the JVM level. It seems that the * Java compiler (and language) is overly restrictive here. * * <p> * TODO: Pass receiver for more than two parameters. * * http://java.sun.com/docs/books/jvms/second_edition/html/Concepts.doc.html * #16411 */ @Override public void visitMethodInsn(int opcode, String owner, String name, String desc) { Type[] argTypes = Type.getArgumentTypes(desc); // does not include // "this" String signInvoke = GGG_V; if (opcode == INVOKEVIRTUAL || opcode == INVOKEINTERFACE || (opcode == INVOKESPECIAL && !INIT.equals(name))) { // pop arguments Type[] args = Type.getArgumentTypes(desc); Map<Integer, Integer> to = new HashMap<Integer, Integer>(); for (int i = args.length - 1; i >= 0; i--) { int loc = newLocal(args[i]); storeLocal(loc); to.put(i, loc); } // duplicate receiver dup();// callee // push arguments for (int i = 0; i < args.length; i++) { loadLocal(to.get(i)); Type ownerType = Type.getType(owner); swap(ownerType, args[i]); } /* INVOKE_X with receiver */ // signInvoke = LGGG_V; signInvoke = LGGG_V; stack.pushStrings(owner, name, desc); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], signInvoke); } else { /* INVOKE_X with no receiver */ signInvoke = GGG_V; stack.pushStrings(owner, name, desc); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], signInvoke); } /* * CALLER_STACK_PARAM Pass non-receiver-parameters one at a time, right * to left, downwards the operand stack */ final boolean needThis = opcode == INVOKEVIRTUAL || opcode == INVOKEINTERFACE || opcode == INVOKESPECIAL; passCallerStackParams(argTypes, needThis); /* User's actual invoke instruction */ super.visitMethodInsn(opcode, owner, name, desc); /* CALL_RESULT */ Type returnType = Type.getReturnType(desc); if (returnType.getSort() == Type.VOID) { /* return; */// returns nothing } /* Pass returned value and method signature to our VM */ else if (returnType.getSize() == 2) mv.visitInsn(DUP2); else mv.visitInsn(DUP); String signResult = null; switch (returnType.getSort()) { case Type.VOID: signResult = GGG_V; break; case Type.DOUBLE: signResult = DGGG_V; break; case Type.FLOAT: signResult = FGGG_V; break; case Type.LONG: signResult = JGGG_V; break; case Type.OBJECT: signResult = LGGG_V; break; case Type.ARRAY: signResult = LGGG_V; break; case Type.BOOLEAN: signResult = ZGGG_V; break; case Type.SHORT: case Type.BYTE: case Type.CHAR: case Type.INT: signResult = IGGG_V; break; default: check(false); } stack.pushStrings(owner, name, desc); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, CALL_RESULT, signResult); } private void passCallerStackParams(Type[] argTypes, boolean needThis) { if (argTypes == null || argTypes.length == 0) return; // pop arguments and store them as locals Map<Integer, Integer> to = new HashMap<Integer, Integer>(); for (int i = argTypes.length - 1; i >= 0; i--) { int loc = newLocal(argTypes[i]); storeLocal(loc); to.put(i, loc); } // restore all arguments for user invocation for (int i = 0; i < argTypes.length; i++) { loadLocal(to.get(i)); } // push arguments copy and paramNr and calleLocalsIndex int calleeLocalsIndex = calculateCalleeLocalsIndex(argTypes, needThis); for (int i = argTypes.length - 1; i >= 0; i--) { int localVarIndex = to.get(i); loadLocal(localVarIndex); Type argType = argTypes[i]; stack.passCallerStackParam(argType, i, calleeLocalsIndex); calleeLocalsIndex -= argType.getSize(); } } private int calculateCalleeLocalsIndex(Type[] argTypes, boolean needThis) { int calleeLocalsIndex = 0; for (Type type : argTypes) calleeLocalsIndex += type.getSize(); if (needThis) calleeLocalsIndex += 1; return calleeLocalsIndex; } /** * IINC * * <p> * Increment i-th local (int) variable by constant (int) value. * * <p> * May follow a WIDE instruction. */ @Override public void visitIincInsn(int i, int value) { stack.pushInt(i); // push local variable index stack.pushInt(value); // push increment value mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[132], II_V); super.visitIincInsn(i, value); // user ByteCode instruction } /** * MULTIANEWARRAY * * <pre> * boolean[] b1 = new boolean[1]; // NEWARRAY T_BOOLEAN * Boolean[] B1 = new Boolean[1]; // ANEWARRAY java/lang/Boolean * boolean[][] b2 = new boolean[1][2]; // MULTIANEWARRAY [[Z 2 * Boolean[][] B2 = new Boolean[1][2]; // MULTIANEWARRAY [[Ljava/lang/Boolean; 2 * </pre> */ @Override public void visitMultiANewArrayInsn(String arrayTypeDesc, int nrDimensions) { // FIXME: Also pass concrete dimension ints mv.visitLdcInsn(arrayTypeDesc); // push name of nrDimensions-dimensional // array type mv.visitIntInsn(SIPUSH, nrDimensions); // push number of dimensions // (unsigned byte) mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[MULTIANEWARRAY], GI_V); super.visitMultiANewArrayInsn(arrayTypeDesc, nrDimensions); // user // ByteCode // instruction } /** * TABLESWITCH */ @Override public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) { int currentBranchIndex = branchCounter++; mv.visitInsn(DUP); // pass concrete int value stack.pushInt(min); stack.pushInt(max); // push branch className, methName and index mv.visitLdcInsn(className); mv.visitLdcInsn(methName); mv.visitLdcInsn(currentBranchIndex); String IIIGGI_V = "(" + INT + INT + INT + STR + STR + INT + ")" + VOID; mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[TABLESWITCH], IIIGGI_V); super.visitTableSwitchInsn(min, max, dflt, labels); // user ByteCode // instruction } /** * LOOKUPSWITCH * * <p> * TODO: Optimize this. Do we really need to create a new array every time * we execute this switch statement? */ @Override public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { int currentBranchIndex = branchCounter++; mv.visitInsn(DUP); // pass concrete int value stack.pushInt(keys.length); // pass keys as a new array mv.visitIntInsn(NEWARRAY, 10); for (int i = 0; i < keys.length; i++) { mv.visitInsn(DUP); stack.pushInt(i); stack.pushInt(keys[i]); mv.visitInsn(IASTORE); // write the i-th case target into the new // array } // push branch className, methName and index mv.visitLdcInsn(className); mv.visitLdcInsn(methName); mv.visitLdcInsn(currentBranchIndex); String IRGGI_V = "(" + INT + INT_ARR + STR + STR + INT + ")" + VOID; mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[LOOKUPSWITCH], IRGGI_V); super.visitLookupSwitchInsn(dflt, keys, labels); // user ByteCode // instruction } /** * CHECKCAST, INSTANCEOF, NEW, ANEWARRAY */ @Override public void visitTypeInsn(int opcode, String type) { switch (opcode) { case CHECKCAST: case INSTANCEOF: mv.visitInsn(DUP); // duplicate reference to be cast mv.visitLdcInsn(type); // pass name of type to be cast to mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], LG_V); break; case NEW: mv.visitLdcInsn(type); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], G_V); break; case ANEWARRAY: mv.visitInsn(DUP); // duplicate array length mv.visitLdcInsn(type); // name of component type mv.visitMethodInsn(INVOKESTATIC, VM_FQ, BYTECODE_NAME[opcode], IG_V); break; default: check(false); } super.visitTypeInsn(opcode, type); // user ByteCode instruction } private final Set<Label> exceptionHandlers = new HashSet<Label>(); @Override public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { exceptionHandlers.add(handler); super.visitTryCatchBlock(start, end, handler, type); } @Override public void visitMaxs(int maxStack, int maxLocals) { stack.pushStrings(className, methName, methDescription); stack.pushInt(maxStack); stack.pushInt(maxLocals); mv.visitMethodInsn(INVOKESTATIC, VM_FQ, "METHOD_MAXS", GGGII_V); //$NON-NLS-1$ super.visitMaxs(maxStack, maxLocals); } }