/* * Copyright 2016, Stuart Douglas, and individual contributors as indicated * by the @authors tag. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.fakereplace.manip; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import javassist.ClassPool; import javassist.LoaderClassPath; import javassist.bytecode.BadBytecode; import javassist.bytecode.ClassFile; import javassist.bytecode.MethodInfo; import org.fakereplace.manip.data.AddedFieldData; import org.fakereplace.manip.data.FakeMethodCallData; /** * Class that maintains a set of manipulations to apply to classes * * @author stuart */ public class Manipulator { private final VirtualToStaticManipulator virtualToStaticManipulator = new VirtualToStaticManipulator(); private final FieldManipulator instanceFieldManapulator = new FieldManipulator(); private final ConstructorInvocationManipulator constructorInvocationManipulator = new ConstructorInvocationManipulator(); private final ReflectionConstructorAccessManipulator reflectionConstructorAccessManipulator = new ReflectionConstructorAccessManipulator(); private final SubclassVirtualCallManipulator subclassVirtualCallManilulator = new SubclassVirtualCallManipulator(); private final FinalMethodManipulator finalMethodManipulator = new FinalMethodManipulator(); private final ReflectionFieldAccessManipulator reflectionFieldAccessManipulator = new ReflectionFieldAccessManipulator(); private final ReflectionMethodAccessManipulator reflectionMethodAccessManipulator = new ReflectionMethodAccessManipulator(); private final FakeMethodCallManipulator fakeMethodCallManipulator = new FakeMethodCallManipulator(); private final Set<ClassManipulator> manipulators = new CopyOnWriteArraySet<ClassManipulator>(); public Manipulator() { manipulators.add(virtualToStaticManipulator); manipulators.add(instanceFieldManapulator); manipulators.add(constructorInvocationManipulator); manipulators.add(subclassVirtualCallManilulator); manipulators.add(finalMethodManipulator); manipulators.add(reflectionFieldAccessManipulator); manipulators.add(reflectionMethodAccessManipulator); manipulators.add(reflectionConstructorAccessManipulator); manipulators.add(fakeMethodCallManipulator); } public void removeRewrites(String className, ClassLoader classLoader) { for (ClassManipulator m : manipulators) { m.clearRewrites(className, classLoader); } } public void rewriteConstructorAccess(String clazz, String descriptor, int methodNo, ClassLoader classLoader) { constructorInvocationManipulator.rewriteConstructorCalls(clazz, descriptor, methodNo, classLoader); } public void rewriteInstanceFieldAccess(AddedFieldData data) { instanceFieldManapulator.addField(data); } public void rewriteSubclassCalls(String className, ClassLoader classLoader, String parentName, ClassLoader parentClassLoader, String methodName, String methodDesc) { subclassVirtualCallManilulator.addClassData(className, classLoader, parentName, parentClassLoader, methodName, methodDesc); } /** * This can also be used to replace a static invokation with another static * invokation * */ public void replaceVirtualMethodInvokationWithStatic(String oldClass, String newClass, String methodName, String methodDesc, String newStaticMethodDesc, ClassLoader classLoader) { virtualToStaticManipulator.replaceVirtualMethodInvokationWithStatic(oldClass, newClass, methodName, methodDesc, newStaticMethodDesc, classLoader); } public void replaceVirtualMethodInvokationWithLocal(String oldClass, String methodName, String newMethodName, String methodDesc, String newStaticMethodDesc, ClassLoader classLoader) { virtualToStaticManipulator.replaceVirtualMethodInvokationWithLocal(oldClass, methodName, newMethodName, methodDesc, newStaticMethodDesc, classLoader); } public void addFakeMethodCallRewrite(FakeMethodCallData fakeMethodCallData) { fakeMethodCallManipulator.addFakeMethodCall(fakeMethodCallData); } public boolean transformClass(ClassFile file, ClassLoader classLoader, boolean modifiable, Set<MethodInfo> modifiedMethods) throws BadBytecode { try { boolean modified = false; // first we are going to transform virtual method calls to static ones for (ClassManipulator m : manipulators) { if (m.transformClass(file, classLoader, modifiable, modifiedMethods)) { modified = true; } } return modified; } catch (RuntimeException e) { e.printStackTrace(); throw e; } } }