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; } }