Search in sources :

Example 1 with JumpMarker

use of org.fakereplace.util.JumpMarker in project fakereplace by fakereplace.

the class ReflectionFieldAccessManipulator method transformClass.

public boolean transformClass(ClassFile file, ClassLoader loader, boolean modifiableClass, final Set<MethodInfo> modifiedMethods) {
    Map<Integer, RewriteData> methodCallLocations = new HashMap<>();
    Map<RewriteData, Integer> newClassPoolLocations = new HashMap<>();
    Integer fieldAccessLocation = null;
    // first we need to scan the constant pool looking for
    // CONSTANT_method_info_ref structures
    ConstPool pool = file.getConstPool();
    for (int i = 1; i < pool.getSize(); ++i) {
        // we have a method call
        if (pool.getTag(i) == ConstPool.CONST_Methodref || pool.getTag(i) == ConstPool.CONST_InterfaceMethodref) {
            String className, methodName;
            if (pool.getTag(i) == ConstPool.CONST_Methodref) {
                className = pool.getMethodrefClassName(i);
                methodName = pool.getMethodrefName(i);
            } else {
                className = pool.getInterfaceMethodrefClassName(i);
                methodName = pool.getInterfaceMethodrefName(i);
            }
            if (className.equals(Field.class.getName())) {
                RewriteData data = manipulationData.get(methodName);
                if (data != null) {
                    // store the location in the const pool of the method ref
                    methodCallLocations.put(i, data);
                    // method in the const pool
                    if (!newClassPoolLocations.containsKey(data)) {
                        if (fieldAccessLocation == null) {
                            fieldAccessLocation = pool.addClassInfo("org.fakereplace.reflection.FieldReflection");
                        }
                        int newNameAndType = pool.addNameAndTypeInfo(data.getMethodName(), data.getNewMethodDescriptor());
                        newClassPoolLocations.put(data, newNameAndType);
                    }
                }
            }
        }
    }
    // through the methods and replace instances of the call
    if (fieldAccessLocation != null) {
        List<MethodInfo> methods = file.getMethods();
        for (MethodInfo m : methods) {
            try {
                // ignore abstract methods
                if (m.getCodeAttribute() == null) {
                    continue;
                }
                CodeIterator it = m.getCodeAttribute().iterator();
                while (it.hasNext()) {
                    // loop through the bytecode
                    int index = it.next();
                    int op = it.byteAt(index);
                    // if the bytecode is a method invocation
                    if (op == CodeIterator.INVOKEVIRTUAL || op == CodeIterator.INVOKESTATIC || op == CodeIterator.INVOKEINTERFACE) {
                        int val = it.s16bitAt(index + 1);
                        // replacing
                        if (methodCallLocations.containsKey(val)) {
                            RewriteData data = methodCallLocations.get(val);
                            Bytecode b = new Bytecode(file.getConstPool());
                            prepareForIsFakeFieldCall(b, data);
                            b.addInvokestatic(fieldAccessLocation, "isFakeField", "(Ljava/lang/reflect/Field;)Z");
                            b.add(Opcode.IFEQ);
                            JumpMarker performRealCall = JumpUtils.addJumpInstruction(b);
                            // now perform the fake call
                            b.addInvokestatic(fieldAccessLocation, data.getMethodName(), data.getNewMethodDescriptor());
                            b.add(Opcode.GOTO);
                            JumpMarker finish = JumpUtils.addJumpInstruction(b);
                            performRealCall.mark();
                            b.addInvokevirtual(Field.class.getName(), data.getMethodName(), data.getMethodDescriptor());
                            finish.mark();
                            it.writeByte(CodeIterator.NOP, index);
                            it.writeByte(CodeIterator.NOP, index + 1);
                            it.writeByte(CodeIterator.NOP, index + 2);
                            if (op == CodeIterator.INVOKEINTERFACE) {
                                // INVOKEINTERFACE has some extra parameters
                                it.writeByte(CodeIterator.NOP, index + 3);
                                it.writeByte(CodeIterator.NOP, index + 4);
                            }
                            it.insertEx(b.get());
                            modifiedMethods.add(m);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("Bad byte code transforming " + file.getName() + "." + m.getName(), e);
            }
        }
        return true;
    } else {
        return false;
    }
}
Also used : ConstPool(javassist.bytecode.ConstPool) JumpMarker(org.fakereplace.util.JumpMarker) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) Field(java.lang.reflect.Field) CodeIterator(javassist.bytecode.CodeIterator) MethodInfo(javassist.bytecode.MethodInfo) Bytecode(javassist.bytecode.Bytecode)

Example 2 with JumpMarker

use of org.fakereplace.util.JumpMarker in project fakereplace by fakereplace.

the class ReflectionMethodAccessManipulator method transformClass.

public boolean transformClass(ClassFile file, ClassLoader loader, boolean modifiableClass, final Set<MethodInfo> modifiedMethods) {
    Set<Integer> methodCallLocations = new HashSet<>();
    Integer newCallLocation = null;
    Integer methodReflectionLocation = null;
    // first we need to scan the constant pool looking for
    // CONSTANT_method_info_ref structures
    ConstPool pool = file.getConstPool();
    for (int i = 1; i < pool.getSize(); ++i) {
        // we have a method call
        if (pool.getTag(i) == ConstPool.CONST_Methodref) {
            String className = pool.getMethodrefClassName(i);
            String methodName = pool.getMethodrefName(i);
            if (className.equals(Method.class.getName())) {
                if (methodName.equals("invoke")) {
                    // store the location in the const pool of the method ref
                    methodCallLocations.add(i);
                    // method in the const pool
                    if (newCallLocation == null) {
                        methodReflectionLocation = pool.addClassInfo("org.fakereplace.reflection.MethodReflection");
                        newCallLocation = pool.addNameAndTypeInfo(METHOD_NAME, REPLACED_METHOD_DESCRIPTOR);
                    }
                }
            }
        }
    }
    // through the methods and replace instances of the call
    if (newCallLocation != null) {
        List<MethodInfo> methods = file.getMethods();
        for (MethodInfo m : methods) {
            try {
                // ignore abstract methods
                if (m.getCodeAttribute() == null) {
                    continue;
                }
                CodeIterator it = m.getCodeAttribute().iterator();
                while (it.hasNext()) {
                    // loop through the bytecode
                    int index = it.next();
                    int op = it.byteAt(index);
                    // if the bytecode is a method invocation
                    if (op == CodeIterator.INVOKEVIRTUAL) {
                        int val = it.s16bitAt(index + 1);
                        // replacing
                        if (methodCallLocations.contains(val)) {
                            Bytecode b = new Bytecode(file.getConstPool());
                            // our stack looks like Method, instance,params
                            // we need Method, instance, params , Method
                            b.add(Opcode.DUP_X2);
                            b.add(Opcode.POP);
                            b.add(Opcode.DUP_X2);
                            b.add(Opcode.POP);
                            b.add(Opcode.DUP_X2);
                            b.addInvokestatic(methodReflectionLocation, "fakeCallRequired", "(Ljava/lang/reflect/Method;)Z");
                            b.add(Opcode.IFEQ);
                            JumpMarker performRealCall = JumpUtils.addJumpInstruction(b);
                            // now perform the fake call
                            b.addInvokestatic(methodReflectionLocation, "invoke", REPLACED_METHOD_DESCRIPTOR);
                            b.add(Opcode.GOTO);
                            JumpMarker finish = JumpUtils.addJumpInstruction(b);
                            performRealCall.mark();
                            b.addInvokevirtual(Method.class.getName(), METHOD_NAME, METHOD_DESCRIPTOR);
                            finish.mark();
                            it.writeByte(CodeIterator.NOP, index);
                            it.writeByte(CodeIterator.NOP, index + 1);
                            it.writeByte(CodeIterator.NOP, index + 2);
                            it.insertEx(b.get());
                            modifiedMethods.add(m);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("Bad byte code transforming " + file.getName(), e);
            }
        }
        return true;
    } else {
        return false;
    }
}
Also used : ConstPool(javassist.bytecode.ConstPool) JumpMarker(org.fakereplace.util.JumpMarker) Method(java.lang.reflect.Method) CodeIterator(javassist.bytecode.CodeIterator) MethodInfo(javassist.bytecode.MethodInfo) Bytecode(javassist.bytecode.Bytecode) HashSet(java.util.HashSet)

Example 3 with JumpMarker

use of org.fakereplace.util.JumpMarker in project fakereplace by fakereplace.

the class ReflectionConstructorAccessManipulator method transformClass.

public boolean transformClass(ClassFile file, ClassLoader loader, boolean modifiableClass, final Set<MethodInfo> modifiedMethods) {
    Set<Integer> methodCallLocations = new HashSet<>();
    Integer constructorReflectionLocation = null;
    // first we need to scan the constant pool looking for
    // CONSTANT_method_info_ref structures
    ConstPool pool = file.getConstPool();
    for (int i = 1; i < pool.getSize(); ++i) {
        // we have a method call
        if (pool.getTag(i) == ConstPool.CONST_Methodref) {
            String className = pool.getMethodrefClassName(i);
            String methodName = pool.getMethodrefName(i);
            if (className.equals(Constructor.class.getName())) {
                if (methodName.equals(METHOD_NAME)) {
                    // store the location in the const pool of the method ref
                    methodCallLocations.add(i);
                    // method in the const pool
                    if (constructorReflectionLocation == null) {
                        constructorReflectionLocation = pool.addClassInfo(ConstructorReflection.class.getName());
                    }
                }
            }
        }
    }
    // through the methods and replace instances of the call
    if (constructorReflectionLocation != null) {
        List<MethodInfo> methods = file.getMethods();
        for (MethodInfo m : methods) {
            try {
                // ignore abstract methods
                if (m.getCodeAttribute() == null) {
                    continue;
                }
                CodeIterator it = m.getCodeAttribute().iterator();
                while (it.hasNext()) {
                    // loop through the bytecode
                    int index = it.next();
                    int op = it.byteAt(index);
                    // if the bytecode is a method invocation
                    if (op == CodeIterator.INVOKEVIRTUAL) {
                        int val = it.s16bitAt(index + 1);
                        // replacing
                        if (methodCallLocations.contains(val)) {
                            Bytecode b = new Bytecode(file.getConstPool());
                            // our stack looks like Constructor,params
                            // we need Constructor, params, Constructor
                            b.add(Opcode.SWAP);
                            b.add(Opcode.DUP_X1);
                            b.addInvokestatic(constructorReflectionLocation, "fakeCallRequired", "(Ljava/lang/reflect/Constructor;)Z");
                            b.add(Opcode.IFEQ);
                            JumpMarker performRealCall = JumpUtils.addJumpInstruction(b);
                            // now perform the fake call
                            b.addInvokestatic(constructorReflectionLocation, METHOD_NAME, REPLACED_METHOD_DESCRIPTOR);
                            b.add(Opcode.GOTO);
                            JumpMarker finish = JumpUtils.addJumpInstruction(b);
                            performRealCall.mark();
                            b.addInvokevirtual(Constructor.class.getName(), METHOD_NAME, METHOD_DESCRIPTOR);
                            finish.mark();
                            it.writeByte(CodeIterator.NOP, index);
                            it.writeByte(CodeIterator.NOP, index + 1);
                            it.writeByte(CodeIterator.NOP, index + 2);
                            it.insertEx(b.get());
                            modifiedMethods.add(m);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("Bad byte code transforming " + file.getName(), e);
            }
        }
        return true;
    } else {
        return false;
    }
}
Also used : ConstPool(javassist.bytecode.ConstPool) JumpMarker(org.fakereplace.util.JumpMarker) Constructor(java.lang.reflect.Constructor) CodeIterator(javassist.bytecode.CodeIterator) MethodInfo(javassist.bytecode.MethodInfo) Bytecode(javassist.bytecode.Bytecode) HashSet(java.util.HashSet)

Example 4 with JumpMarker

use of org.fakereplace.util.JumpMarker in project fakereplace by fakereplace.

the class ClassLoaderInstrumentation method redefineClassLoader.

/**
 * This method instruments class loaders so that they can load our helper
 * classes.
 */
@SuppressWarnings("unchecked")
public static boolean redefineClassLoader(ClassFile classFile, Set<MethodInfo> modifiedMethods) throws BadBytecode {
    boolean modified = false;
    for (MethodInfo method : (List<MethodInfo>) classFile.getMethods()) {
        if (Modifier.isStatic(method.getAccessFlags())) {
            continue;
        }
        if (method.getName().equals("loadClass") && (method.getDescriptor().equals("(Ljava/lang/String;)Ljava/lang/Class;") || method.getDescriptor().equals("(Ljava/lang/String;Z)Ljava/lang/Class;"))) {
            modifiedMethods.add(method);
            modified = true;
            if (method.getCodeAttribute().getMaxLocals() < 4) {
                method.getCodeAttribute().setMaxLocals(4);
            }
            // now we instrument the loadClass
            // if the system requests a class from the generated class package
            // then
            // we check to see if it is already loaded.
            // if not we try and get the class definition from GlobalData
            // we do not need to delegate as GlobalData will only
            // return the data to the correct classloader.
            // if the data is not null then we define the class, link
            // it if requested and return it.
            final CodeIterator iterator = method.getCodeAttribute().iterator();
            final Bytecode b = new Bytecode(classFile.getConstPool());
            b.addAload(1);
            b.addAload(0);
            b.addInvokestatic(ClassLookupManager.class.getName(), "getClassData", "(Ljava/lang/String;Ljava/lang/Object;)[B");
            b.add(Opcode.DUP);
            b.add(Opcode.IFNULL);
            JumpMarker jumpEnd = JumpUtils.addJumpInstruction(b);
            // now we need to do the findLoadedClasses thing
            b.addAload(0);
            b.addAload(1);
            b.addInvokevirtual("java.lang.ClassLoader", "findLoadedClass", "(Ljava/lang/String;)Ljava/lang/Class;");
            b.add(Opcode.DUP);
            b.add(Opcode.IFNULL);
            JumpMarker notFound = JumpUtils.addJumpInstruction(b);
            b.add(Opcode.ARETURN);
            notFound.mark();
            b.add(Opcode.POP);
            b.addAstore(3);
            b.addAload(0);
            b.addAload(1);
            b.addAload(3);
            b.addIconst(0);
            b.addAload(3);
            b.add(Opcode.ARRAYLENGTH);
            b.addInvokevirtual("java.lang.ClassLoader", "defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;");
            if (method.getDescriptor().equals("Ljava/lang/String;Z)Ljava/lang/Class;")) {
                b.addIload(2);
            } else {
                b.addIconst(0);
            }
            b.add(Opcode.IFEQ);
            final JumpMarker linkJumpEnd = JumpUtils.addJumpInstruction(b);
            b.add(Opcode.DUP);
            b.addAload(0);
            b.add(Opcode.SWAP);
            b.addInvokevirtual("java.lang.ClassLoader", "resolveClass", "(Ljava/lang/Class;)V");
            linkJumpEnd.mark();
            b.add(Opcode.ARETURN);
            jumpEnd.mark();
            b.add(Opcode.POP);
            if (!classFile.getName().startsWith("java.") && !classFile.getName().startsWith("com.sun") && !classFile.getName().startsWith("sun") && !classFile.getName().startsWith("jdk.internal")) {
                // now we need to check if this is a fakereplace class
                // and if so always delegate to the appropriate loader
                b.addAload(1);
                b.addLdc("org.fakereplace");
                b.addInvokevirtual(String.class.getName(), "startsWith", "(Ljava/lang/String;)Z");
                b.add(Opcode.IFEQ);
                JumpMarker notFakereplace = JumpUtils.addJumpInstruction(b);
                // so this is a fakereplace class, delegate to the system loader
                b.addInvokestatic(ClassLoader.class.getName(), "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
                b.addAload(1);
                b.addInvokevirtual(ClassLoader.class.getName(), "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
                b.add(Opcode.ARETURN);
                notFakereplace.mark();
            }
            iterator.insert(b.get());
            method.getCodeAttribute().computeMaxStack();
        }
    }
    return modified;
}
Also used : JumpMarker(org.fakereplace.util.JumpMarker) CodeIterator(javassist.bytecode.CodeIterator) MethodInfo(javassist.bytecode.MethodInfo) List(java.util.List) BadBytecode(javassist.bytecode.BadBytecode) Bytecode(javassist.bytecode.Bytecode)

Aggregations

Bytecode (javassist.bytecode.Bytecode)4 CodeIterator (javassist.bytecode.CodeIterator)4 MethodInfo (javassist.bytecode.MethodInfo)4 JumpMarker (org.fakereplace.util.JumpMarker)4 ConstPool (javassist.bytecode.ConstPool)3 HashSet (java.util.HashSet)2 Constructor (java.lang.reflect.Constructor)1 Field (java.lang.reflect.Field)1 Method (java.lang.reflect.Method)1 HashMap (java.util.HashMap)1 List (java.util.List)1 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)1 BadBytecode (javassist.bytecode.BadBytecode)1