All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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. * *

    *
  1. * METHOD_BEGIN_RECEIVER(value) -- optional
  2. *
  3. * METHOD_BEGIN_PARAM(value, 0, 0)
  4. *
  5. * ...
  6. *
  7. * METHOD_BEGIN_PARAM(value, nrArgs-1, localsIndex)
  8. *
*/ @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: *

    *
  1. * INVOKE_X -- signature and receiver (!) of invoked method
  2. *
  3. * CALLER_STACK_PARAM -- last parameter
  4. *
  5. * CALLER_STACK_PARAM -- next-to-last parameter
  6. *
  7. * make call
  8. *
  9. * CALL_RESULT
  10. *
* *

* 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





© 2015 - 2024 Weber Informatics LLC | Privacy Policy