org.coldswap.asm.VirtualMethodReplacer.java Source code

Java tutorial

Introduction

Here is the source code for org.coldswap.asm.VirtualMethodReplacer.java

Source

package org.coldswap.asm;

import org.coldswap.util.AutoBoxing;
import org.coldswap.util.Constants;
import org.coldswap.util.TransformerNameGenerator;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;

import java.util.Iterator;

/**
 * (C) Copyright 2013 Faur Ioan-Aurel.
 * <p/>
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 * <p/>
 * This library 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 General Public License for more details.
 * <p/>
 * Created with IntelliJ IDEA.
 * User: faur
 * Date: 6/6/13
 * Time: 9:35 PM
 */
public class VirtualMethodReplacer extends MethodBox {
    private final int methodNumber;
    private final String methodType;

    /**
     * Constructs an object that will contain basic information about a
     * method whose call will be replaced.
     *
     * @param classContainer the name of the class containing the method
     *                       whose invoke should be replaced.
     * @param methodName     the name of the method whose invoke should be replaced.
     * @param retType        return {@link org.objectweb.asm.Type} of the method
     * @param paramType      parameters {@link org.objectweb.asm.Type}.
     * @param methodType     what kind of method should be replaced:"Object[]","int"
     * @param counter        a counter for method name generation.
     */
    public VirtualMethodReplacer(String classContainer, String methodName, Type retType, Type[] paramType,
            String methodType, int counter) {
        super(classContainer, methodName, retType, paramType);
        this.methodNumber = counter;
        this.methodType = methodType;
    }

    @Override
    public MethodNode replaceInvoke(MethodNode methodNode) {
        InsnList instructions = methodNode.instructions;
        Iterator it = instructions.iterator();
        while (it.hasNext()) {
            AbstractInsnNode code = (AbstractInsnNode) it.next();
            if (code.getOpcode() == Opcodes.INVOKEVIRTUAL) {
                // check if methodToReplace is called
                final boolean[] callFounded = new boolean[] { false };
                code.accept(new MethodVisitor(Opcodes.ASM5) {
                    @Override
                    public void visitMethodInsn(int i, String s, String s2, String s3) {
                        if (s.equals(classContainer) && s2.equals(methodName)) {
                            callFounded[0] = true;
                        }
                        super.visitMethodInsn(i, s, s2, s3);
                    }
                });

                if (callFounded[0]) {
                    // if the return type is primitive and the value is not discarded, unbox
                    if (AutoBoxing.isPrimitive(retType.getDescriptor())) {
                        AbstractInsnNode codeNext = code.getNext();
                        boolean discarded = false;
                        // if returning primitive double or long and it is discarded with a pop2 than discard with
                        // simple pop, because we use an Object as return value.
                        if (codeNext.getOpcode() == Opcodes.POP2
                                && (retType.getDescriptor().equals("D") || retType.getDescriptor().equals("J"))) {
                            instructions.set(codeNext, new InsnNode(Opcodes.POP));

                        }
                        if (codeNext.getOpcode() == Opcodes.POP || codeNext.getOpcode() == Opcodes.POP2) {
                            discarded = true;
                        }
                        if (!discarded) {
                            instructions.insert(code, AutoBoxing.unbox(retType));
                        }
                    }

                    // replace call with a custom call
                    String newMethodName;
                    AbstractInsnNode newInvoke = null;
                    if (Constants.VAROBJECT.equals(methodType)) {
                        newMethodName = TransformerNameGenerator.getObjectMethodNameWithCounter(classContainer,
                                methodNumber);
                        newInvoke = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, classContainer, newMethodName,
                                "([Ljava/lang/Object;)Ljava/lang/Object;");
                    } else if (Constants.INT.equals(methodType)) {
                        newMethodName = TransformerNameGenerator.getIntMethodNameWithCounter(classContainer,
                                methodNumber);
                        newInvoke = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, classContainer, newMethodName,
                                "(I)Ljava/lang/Object;");
                    } else if (Constants.FLOAT.equals(methodType)) {
                        newMethodName = TransformerNameGenerator.getFloatMethodNameWithCounter(classContainer,
                                methodNumber);
                        newInvoke = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, classContainer, newMethodName,
                                "(F)Ljava/lang/Object;");
                    } else if (Constants.STRING.equals(methodType)) {
                        newMethodName = TransformerNameGenerator.getStringMethodNameWithCounter(classContainer,
                                methodNumber);
                        newInvoke = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, classContainer, newMethodName,
                                "(Ljava/lang/String;)Ljava/lang/Object;");
                    } else if (Constants.LONG.equals(methodType)) {
                        newMethodName = TransformerNameGenerator.getLongMethodNameWithCounter(classContainer,
                                methodNumber);
                        newInvoke = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, classContainer, newMethodName,
                                "(J)Ljava/lang/Object;");
                    }
                    if (newInvoke != null) {
                        instructions.set(code, newInvoke);
                    }
                }
            }
        }
        return methodNode;
    }
}