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

com.nerdvision.agent.inst.asm.Visitor Maven / Gradle / Ivy

package com.nerdvision.agent.inst.asm;

import com.google.common.collect.Sets;
import com.nerdvision.agent.api.IBreakpoint;
import java.com.nerdvision.agent.snapshot.ProxyCallback;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ASM7;
import static org.objectweb.asm.Opcodes.ASM8;
import static org.objectweb.asm.Opcodes.DLOAD;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.FLOAD;
import static org.objectweb.asm.Opcodes.GETSTATIC;
import static org.objectweb.asm.Opcodes.ILOAD;
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.LLOAD;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.POP;

public class Visitor extends ClassVisitor
{
    // these are the local variables that we want to capture when using CF, if we try to get all locals we get verify errors.
    // and we do not care about all the locals for CF.
    private static final List CF_VARS = Arrays.asList( "__localScope", "instance", "__arguments", "this", "parentPage" );
    private static final Logger LOGGER = LoggerFactory.getLogger( Visitor.class );
    private static final SkipException EXCEPTION = new SkipException();
    private static final boolean DEBUG = false;

    private final Collection bps;
    private final boolean isCf;
    private final Map> lineNos;

    private String classname;
    private String filename;

    private boolean changed = false;

    public static final Class CALLBACK_CLASS;

    static
    {
        // this is here to make the tests easier.
        // we cannot use java. classes in the tests without screwing with the class loaders
        // so in the tests we use the 'nv.callback.class' which is the CallBack.class
        // at runtime we use the ProxyCallback.class so we can bypass the osgi classloading restrictions
        final String property = System.getProperty( "nv.callback.class" );
        if( property == null )
        {
            CALLBACK_CLASS = ProxyCallback.class;
        }
        else
        {
            Class callbackClass;
            try
            {
                callbackClass = Class.forName( property );
            }
            catch( ClassNotFoundException e )
            {
                callbackClass = ProxyCallback.class;
            }
            CALLBACK_CLASS = callbackClass;
        }
    }

    public Visitor( final ClassVisitor v, final Collection bps, final boolean isCf )
    {
        super( ASM8, v );
        this.bps = bps;
        this.isCf = isCf;
        lineNos = new HashMap<>();
        for( final IBreakpoint bp : bps )
        {
            final long lineNo = bp.getLineNo();
            List list = lineNos.get( lineNo );

            if( list == null )
            {
                list = new ArrayList<>();
                lineNos.put( lineNo, list );
            }
            list.add( bp );
        }
    }


    public boolean wasChanged()
    {
        return changed;
    }


    public String getFilename()
    {
        return filename;
    }


    @Override
    public void visit( int version, int access, String name, String signature, String superName, String[] interfaces )
    {
        LOGGER.debug( "visit {}", name );
        this.classname = name;
        super.visit( version, access, name, signature, superName, interfaces );
    }


    @Override
    public void visitSource( String source, String debug )
    {
        LOGGER.debug( "visitSource {} {}", classname, source );
        super.visitSource( source, debug );

        // No source filename
        if( source == null )
        {
            throw EXCEPTION;
        }

        filename = source;
    }


    @Override
    public MethodVisitor visitMethod( int access, String name, String desc, String signature, String[] exceptions )
    {
        LOGGER.debug( "visitMethod {} {}", classname, name );
        final MethodVisitor methodVisitor = super.visitMethod( access, name, desc, signature, exceptions );

        return new MethodNode( ASM7, access, name, desc, signature, exceptions )
        {
            @Override
            public void visitEnd()
            {
                // Cannot go from line number label offset to label offset as getOffset throws.
                // java.lang.IllegalStateException: Label offset position has not been resolved yet
                // no idea why as the java local var table knows exactly at what byte code op vars are valid
                final Set




© 2015 - 2024 Weber Informatics LLC | Privacy Policy