com.sun.tdk.jcov.instrument.DeferringMethodClassAdapter.java Source code

Java tutorial

Introduction

Here is the source code for com.sun.tdk.jcov.instrument.DeferringMethodClassAdapter.java

Source

/*
 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.sun.tdk.jcov.instrument;

import com.sun.tdk.jcov.util.Utils;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import static org.objectweb.asm.Opcodes.*;
import org.objectweb.asm.tree.MethodNode;

/**
 * @author Dmitry Fazunenko
 * @author Alexey Fedorchenko
 */
class DeferringMethodClassAdapter extends ClassVisitor {

    private final DataClass k;
    private final InstrumentationParams params;

    public DeferringMethodClassAdapter(final ClassVisitor cv, DataClass k, InstrumentationParams params) {
        super(ASM4, cv);
        this.k = k;
        this.params = params;
    }

    @Override
    public void visit(final int version, final int access, final String name, final String signature,
            final String superName, final String[] interfaces) {
        k.setInfo(access, signature, superName, interfaces);
        super.visit(version, access, name, signature, superName, interfaces);
    }

    @Override
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
        // the (access & ACC_STATIC) == 0 || value ==null means
        // that class is not substitued by value  during comilation
        FieldVisitor fv = super.visitField(access, name, desc, signature, value);
        if (params.isInstrumentFields() && ((access & ACC_STATIC) == 0 || value == null)) {
            DataField fld = new DataField(k, access, name, desc, signature, value);
            fv = new FieldAnnotationVisitor(fv, fld);
            // this is needed, because DataField contains adding to DataClass
        }
        return fv;
    }

    @Override
    public void visitSource(String source, String debug) {
        k.setSource(source);
        super.visitSource(source, debug);
    }

    public MethodVisitor visitMethodCoverage(final int access, final String name, final String desc,
            final String signature, final String[] exceptions) {
        if (!InstrumentationOptions.isSkipped(k.getFullname(), name, access) && params.isDynamicCollect()
                && params.isInstrumentNative() && (access & ACC_NATIVE) != 0) {
            // Visit the native method, but change the access flags and rename it with a prefix
            int accessNative = access;
            if ((accessNative & ACC_STATIC) == 0) {
                // not static, then it needs to be final
                accessNative |= ACC_FINAL;
            }
            accessNative &= ~(ACC_PUBLIC | ACC_PROTECTED);
            accessNative |= ACC_PRIVATE;
            MethodVisitor mvNative = cv.visitMethod(accessNative, InstrumentationOptions.nativePrefix + name, desc,
                    signature, exceptions);
            if (mvNative == null) {
                throw new InternalError("Should not happen!");
            }

            // Write the native method, then visit and write the wrapper method
            MethodNode methodWrapper = new MethodNode(access & ~ACC_NATIVE, name, desc, signature, exceptions);
            DataMethodEntryOnly meth = new DataMethodEntryOnly(k, access, name, desc, signature, exceptions);
            return new NativeWrappingMethodAdapter(mvNative, methodWrapper, cv, meth, params);
        }
        MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
        //This code is executed both in dynamic and static mode
        if (InstrumentationOptions.isSkipped(k.getFullname(), name, access) || mv == null
                || (access & ACC_ABSTRACT) != 0 || (access & ACC_NATIVE) != 0) {
            // build method with no content, and we are done
            if ((access & ACC_ABSTRACT) != 0 && params.isInstrumentAbstract()
                    || ((access & ACC_NATIVE) != 0 && params.isInstrumentNative())
                    || InstrumentationOptions.isSkipped(k.getFullname(), name, access)) {
                DataMethod meth = new DataMethodInvoked(k, access, name, desc, signature, exceptions);
                return new MethodAnnotationAdapter(mv, meth);
            }
            return mv;
        }

        // method could not be instrumented
        // java.lang.VerifyError: (class: sun/awt/X11/XWindowPeer, method: handleButtonPressRelease signature: (IJ)V) Mismatched stack types
        // temporary use only method coverage
        if (k.getFullname().equals("sun/awt/X11/XWindowPeer") && name.equals("handleButtonPressRelease")) {
            DataMethodEntryOnly meth = new DataMethodEntryOnly(k, access, name, desc, signature, exceptions);
            return new EntryCodeMethodAdapter(mv, meth, params);
        }

        switch (params.getMode()) {
        case METHOD: {
            DataMethodEntryOnly meth = new DataMethodEntryOnly(k, access, name, desc, signature, exceptions);
            return new EntryCodeMethodAdapter(mv, meth, params);
        }
        case BLOCK: {
            DataMethodWithBlocks meth = new DataMethodWithBlocks(k, access, name, desc, signature, exceptions);
            return new BlockCodeMethodAdapter(mv, meth, params);
        }
        case BRANCH: {
            DataMethodWithBlocks meth = new DataMethodWithBlocks(k, access, name, desc, signature, exceptions);
            return new BranchCodeMethodAdapter(mv, meth, params);
        }
        default:
            break;
        }

        return null;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        if (!params.isInstrumentSynthetic() && (access & ACC_SYNTHETIC) != 0) {
            return super.visitMethod(access, name, desc, signature, exceptions);
            //            return null;
        }
        MethodVisitor mv = visitMethodCoverage(access, name, desc, signature, exceptions);

        if ("<clinit>".equals(name) && !params.isDynamicCollect()
                && (k.getPackageName().startsWith("java/lang/"))) {
            mv = new MethodVisitor(Opcodes.ASM4, mv) {
                public void visitCode() {
                    mv.visitMethodInsn(INVOKESTATIC, "com/sun/tdk/jcov/runtime/Collect", "init", "()V");
                    super.visitCode();
                }
            };
        }

        if (params.isCallerFilterOn() && params.isCallerFilterAccept(k.getFullname())
                && !params.isDynamicCollect()) {

            if (name.equals("<clinit>")) {
                int id = (name + desc).hashCode();
                mv.visitLdcInsn(id);
                mv.visitMethodInsn(INVOKESTATIC, "com/sun/tdk/jcov/runtime/CollectDetect", "setExpected", "(I)V");
            }

        }

        if (params.isInnerInvacationsOff() && Utils.isAdvanceStaticInstrAllowed(k.getFullname(), name)) {

            if (name.equals("<clinit>")) {
                mv.visitMethodInsn(INVOKESTATIC, "com/sun/tdk/jcov/runtime/CollectDetect", "enterClinit", "()V");
            }

        }

        // System.out.println("Seeing " + k.fullname + "." + name);
        if (params.isDataSaveFilterAccept(k.getFullname(), name, true)) {
            mv = new SavePointsMethodAdapter(mv, true);
        }

        if (params.isDataSaveFilterAccept(k.getFullname(), name, false)) {
            mv = new SavePointsMethodAdapter(mv, false);
        }
        mv = new MethodVisitor(Opcodes.ASM4, mv) {
            @Override
            public void visitLocalVariable(String arg0, String arg1, String arg2, Label arg3, Label arg4,
                    int arg5) {
                //super.visitLocalVariable(arg0, arg1, arg2, arg3, arg4, arg5);
            }
        };
        if (params.isDynamicCollect()) {
            mv = new InvokeMethodAdapter(mv, k.getFullname(), params);
        } else {
            mv = new StaticInvokeMethodAdapter(mv, k.getFullname(), name, access, params);
        }
        return mv;
    }

    @Override
    public AnnotationVisitor visitAnnotation(String anno, boolean b) {
        k.addAnnotation(anno);
        return super.visitAnnotation(anno, b);
    }

    @Override
    public void visitInnerClass(String fullClassName, String parentClass, String className, int i) {
        if (!params.isInstrumentAnonymous()) {
            return; // ignore
        }
        // fullClassName can't be null - it's generated if the class is anonym
        // className is individual name as written in the source - anonym classes have null there
        try {
            if (k.getFullname().equals(fullClassName)) {
                k.setInner(true);
                k.setAnonym(className == null);
            }
        } catch (Exception e) {
        }
        super.visitInnerClass(fullClassName, parentClass, className, i);
    }
}