package org.coldswap.asm.method;
/**
* (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/8/13
* Time: 7:30 PM
*/
import org.coldswap.asm.MemberReplacer;
import org.coldswap.util.ClassUtil;
import org.coldswap.util.MethodUtil;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;
/**
* The scope of this class is to remove any method that was not inserted in the helper
* method so that redefinition will be successful. Please note that code might break during runtime
* because calls to the removed method will not be fixed.
*/
public class MethodCleaner implements MemberReplacer {
private static final Logger logger = Logger.getLogger(MethodCleaner.class.getName());
private int counter = 0;
private Class<?> aClass;
private byte[] bytes;
static {
logger.setLevel(ClassUtil.logLevel);
}
public MethodCleaner(Class<?> aClass, byte[] bytes) {
this.aClass = aClass;
this.bytes = bytes;
}
@Override
public byte[] replace() {
final ClassNode cn = new ClassNode(Opcodes.ASM5);
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
cr.accept(cn, 0);
// get a list of public/protected/package/private methods but exclude
// inherited methods.
Method[] methods = aClass.getDeclaredMethods();
List asmMethods = cn.methods;
Iterator iterator = asmMethods.iterator();
while (iterator.hasNext()) {
final MethodNode mNode = (MethodNode) iterator.next();
// exclude <init>() and <clinit>()
if (!"<clinit>".equals(mNode.name) && !"<init>".equals(mNode.name)) {
boolean foundIt = false;
int publicAccessor = Opcodes.ACC_PUBLIC;
// search only for public methods
if ((mNode.access == publicAccessor)) {
// check if this method exist in the old loaded class
// and if not proceed with the trick.
Type mRetType = MethodUtil.getReturnType(mNode);
Type[] mParamType = MethodUtil.getParamsType(mNode);
for (Method method : methods) {
if (mNode.name.equals(method.getName())) {
Type fRetType = Type.getType(method).getReturnType();
if (mRetType.equals(fRetType)) {
Type[] fParamType = MethodUtil.classToType(method.getParameterTypes());
boolean tmp = true;
if (mParamType.length == fParamType.length) {
for (int i = 0; i < mParamType.length; i++) {
if (!mParamType[i].equals(fParamType[i])) {
tmp = false;
}
}
}
foundIt = tmp;
}
}
}
if (!foundIt) {
// remove
counter++;
iterator.remove();
}
}
}
}
// note user about removed methods.
if (counter > 0) {
logger.severe(counter + " new methods found and removed. Please consider stopping the application because" +
" there might be calls to this methods that would break application state.");
}
cn.accept(cw);
return cw.toByteArray();
}
}