pku.sei.checkedcoverage.tracer.instrumentation.TracingClassInstrumenter.java Source code

Java tutorial

Introduction

Here is the source code for pku.sei.checkedcoverage.tracer.instrumentation.TracingClassInstrumenter.java

Source

/** License information:
 *    Component: checkedcoverage-tracer
 *    Package:   pku.sei.checkedcoverage.tracer.instrumentation
 *    Class:     TracingClassInstrumenter
 *    Filename:  checkedcoverage-tracer/src/main/java/de/unisb/cs/st/checkedcoverage/tracer/instrumentation/TracingClassInstrumenter.java
 *
 * This file is part of the checkedcoverage tool, developed by Clemens Hammacher at Saarland University.
 * See http://www.st.cs.uni-saarland.de/checkedcoverage/ for more information.
 *
 * This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
 * To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send a
 * letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
 */
package pku.sei.checkedcoverage.tracer.instrumentation;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TryCatchBlockNode;

import pku.sei.checkedcoverage.common.classRepresentation.ReadClass;
import pku.sei.checkedcoverage.common.classRepresentation.ReadMethod;
import pku.sei.checkedcoverage.common.classRepresentation.instructions.AbstractInstruction;
import pku.sei.checkedcoverage.tracer.Tracer;

public class TracingClassInstrumenter implements Opcodes {

    private final Tracer tracer;
    private final ReadClass readClass;

    public TracingClassInstrumenter(final ReadClass readClass, final Tracer tracer) {
        this(readClass, tracer, true);
    }

    protected TracingClassInstrumenter(final ReadClass readClass, final Tracer tracer, final boolean printDebug) {
        if (tracer.debug && printDebug)
            System.out.println("instrumenting " + readClass.getName());
        this.tracer = tracer;
        this.readClass = readClass;
    }

    @SuppressWarnings("unchecked")
    public void transform(final ClassNode classNode) {
        final ListIterator<MethodNode> methodIt = classNode.methods.listIterator();
        while (methodIt.hasNext()) {
            transformMethod(classNode, methodIt.next(), methodIt);
        }
        this.readClass.ready();
    }

    protected void transformMethod(final ClassNode classNode, final MethodNode method,
            final ListIterator<MethodNode> methodIt) {
        final ReadMethod readMethod = new ReadMethod(this.readClass, method.access, method.name, method.desc,
                AbstractInstruction.getNextIndex());
        this.readClass.addMethod(readMethod);

        // do not instrument <clinit> methods (break (linear) control flow)
        // because these methods may call other methods, we have to pause tracing when they are entered
        if ("<clinit>".equals(method.name)) {
            new PauseTracingInstrumenter(null, this.tracer).transformMethod(method, methodIt,
                    this.readClass.getName());
            return;
        }

        MethodNode oldMethod;
        // only copy the old method if it has more than 2000 instructions
        if (method.instructions.size() > 2000) {
            oldMethod = new MethodNode();
            copyMethod(method, oldMethod);
        } else {
            oldMethod = null;
        }

        new TracingMethodInstrumenter(this.tracer, readMethod, classNode, method).transform(methodIt);

        // test the size of the instrumented method
        final ClassWriter testCW = new ClassWriter(0);
        method.accept(testCW);
        final int byteCodeSize = testCW.toByteArray().length;
        if (byteCodeSize >= 1 << 16) {
            System.err.format(
                    "WARNING: instrumented method \"%s.%s%s\" is larger than 64k bytes. undoing instrumentation.%n",
                    this.readClass.getName(), readMethod.getName(), readMethod.getDesc());
            if (oldMethod == null) {
                System.err.println(
                        "ERROR: uninstrumented method had less than 2000 instructions, so we cannot roll back the instrumentation...");
            } else {
                System.err.format("#instructions old: %d; #instructions new: %d; size new: %d%n",
                        oldMethod.instructions.size(), method.instructions.size(), byteCodeSize);
                copyMethod(oldMethod, method);
            }
        }

        // reset the labels
        final Iterator<?> insnIt = method.instructions.iterator();
        while (insnIt.hasNext()) {
            final Object insn = insnIt.next();
            if (insn instanceof LabelNode)
                ((LabelNode) insn).resetLabel();
        }

    }

    @SuppressWarnings("unchecked")
    private static void copyMethod(final MethodNode from, final MethodNode to) {
        to.access = from.access;
        to.annotationDefault = from.annotationDefault;
        to.attrs = from.attrs == null ? null : new ArrayList<Attribute>(from.attrs);
        to.desc = from.desc;
        to.exceptions = from.exceptions == null ? null : new ArrayList<String>(from.exceptions);
        to.instructions.clear();
        final Iterator<?> insnIt = from.instructions.iterator();
        final Map<LabelNode, LabelNode> labelsMap = new HashMap<LabelNode, LabelNode>() {
            private static final long serialVersionUID = 6883684625241587713L;

            @Override
            public LabelNode get(final Object key) {
                LabelNode label = super.get(key);
                if (label == null)
                    put((LabelNode) key, label = new LabelNode());
                return label;
            }
        };
        while (insnIt.hasNext()) {
            final AbstractInsnNode insn = (AbstractInsnNode) insnIt.next();
            to.instructions.add(insn.clone(labelsMap));
        }
        to.invisibleAnnotations = from.invisibleAnnotations == null ? null
                : new ArrayList<AnnotationNode>(from.invisibleAnnotations);
        if (from.invisibleParameterAnnotations == null) {
            to.invisibleParameterAnnotations = null;
        } else {
            to.invisibleParameterAnnotations = new List[from.invisibleParameterAnnotations.length];
            for (int i = 0; i < from.invisibleParameterAnnotations.length; ++i) {
                to.invisibleParameterAnnotations[i] = from.invisibleParameterAnnotations[i] == null ? null
                        : new ArrayList<AnnotationNode>(from.invisibleParameterAnnotations[i]);
            }
        }
        if (from.localVariables == null) {
            to.localVariables = null;
        } else {
            to.localVariables = new ArrayList<LocalVariableNode>(from.localVariables.size());
            for (final Object lvObj : from.localVariables) {
                final LocalVariableNode lv = (LocalVariableNode) lvObj;
                to.localVariables.add(new LocalVariableNode(lv.name, lv.desc, lv.signature, labelsMap.get(lv.start),
                        labelsMap.get(lv.end), lv.index));
            }
        }
        to.maxLocals = from.maxLocals;
        to.maxStack = from.maxStack;
        to.name = from.name;
        to.signature = from.signature;
        if (from.tryCatchBlocks == null) {
            to.tryCatchBlocks = null;
        } else {
            to.tryCatchBlocks = new ArrayList<TryCatchBlockNode>(from.tryCatchBlocks.size());
            for (final Object tcbObj : from.tryCatchBlocks) {
                final TryCatchBlockNode tcb = (TryCatchBlockNode) tcbObj;
                to.tryCatchBlocks.add(new TryCatchBlockNode(labelsMap.get(tcb.start), labelsMap.get(tcb.end),
                        labelsMap.get(tcb.handler), tcb.type));
            }
        }
        to.visibleAnnotations = from.visibleAnnotations == null ? null
                : new ArrayList<AnnotationNode>(from.visibleAnnotations);
        if (from.visibleParameterAnnotations == null) {
            to.visibleParameterAnnotations = null;
        } else {
            to.visibleParameterAnnotations = new List[from.visibleParameterAnnotations.length];
            for (int i = 0; i < from.visibleParameterAnnotations.length; ++i) {
                to.visibleParameterAnnotations[i] = from.visibleParameterAnnotations[i] == null ? null
                        : new ArrayList<AnnotationNode>(from.visibleParameterAnnotations[i]);
            }
        }
    }

}