org.evosuite.symbolic.instrument.ConcolicMethodAdapter Maven / Gradle / Ivy
/**
* Copyright (C) 2010-2018 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 .
*/
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
*
*
* 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 [email protected] (Christoph Csallner)
*/
public final class ConcolicMethodAdapter extends GeneratorAdapter {
private static final String THIS$0 = "this$0";
private static final String INIT = ""; //$NON-NLS-1$
private static final String 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
*
*
* Issue one call per argument, excluding the "this" receiver for
* non-constructor instance methods. Work left to right, starting with
* receiver.
*
*
* -
* METHOD_BEGIN_RECEIVER(value) -- optional
* -
* METHOD_BEGIN_PARAM(value, 0, 0)
* -
* ...
* -
* METHOD_BEGIN_PARAM(value, nrArgs-1, localsIndex)
*
*/
@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.
*
*
* 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$
}
/**
*
* -
* BIPUSH: push one byte from instruction stream to operand stack
* -
* SIPUSH: push two bytes from instruction stream to operand stack
* -
* NEWARRAY
*
*/
@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);
}
}
/**
*
* -
* LDC, (LDC_W) -- push category one constant from constant pool
* -
* LDC2_W -- push category two constant from constant pool
*
*
* 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.
*
*
* 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:
*
* -
* INVOKE_X -- signature and receiver (!) of invoked method
* -
* CALLER_STACK_PARAM -- last parameter
* -
* CALLER_STACK_PARAM -- next-to-last parameter
* -
* make call
* -
* CALL_RESULT
*
*
*
* 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 "
*
*
* 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.
*
*
* 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 to = new HashMap();
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 to = new HashMap();
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
*
*
* Increment i-th local (int) variable by constant (int) value.
*
*
* 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
*
*
* 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
*
*/
@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
*
*
* 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